From 520949c97cc1734c1915fa0f13fc3348e26f56ca Mon Sep 17 00:00:00 2001 From: Gus Mueller Date: Tue, 11 Nov 2008 22:13:06 +0000 Subject: [PATCH] a coda plugin --- JSCocoaCodaLoader/CodaPlugInsController.h | 178 ++ .../English.lproj/InfoPlist.strings | Bin 0 -> 210 bytes JSCocoaCodaLoader/Info.plist | 26 + .../JSCocoa/BridgeSupportController.h | 34 + .../JSCocoa/BridgeSupportController.m | 179 ++ JSCocoaCodaLoader/JSCocoa/JSCocoaController.h | 121 + JSCocoaCodaLoader/JSCocoa/JSCocoaController.m | 2127 +++++++++++++++++ .../JSCocoa/JSCocoaFFIArgument.h | 76 + .../JSCocoa/JSCocoaFFIArgument.m | 952 ++++++++ JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.h | 36 + JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.m | 171 ++ .../JSCocoa/JSCocoaPrivateObject.h | 62 + .../JSCocoa/JSCocoaPrivateObject.m | 118 + .../JSCocoaLoader.xcodeproj/project.pbxproj | 320 +++ JSCocoaCodaLoader/JSCocoaLoaderPlugIn.h | 14 + JSCocoaCodaLoader/JSCocoaLoaderPlugIn.m | 70 + JSCocoaCodaLoader/JSCocoaLoader_Prefix.pch | 10 + JSCocoaCodaLoader/Uppercase.js | 9 + davtest/FMWebDAVRequest.m | 25 +- randomscripts/webkitupdate.py | 10 +- .../DocumentView.xcodeproj/gus.mode1v3 | 74 +- .../DocumentView.xcodeproj/gus.pbxuser | 65 +- .../DocumentView.xcodeproj/project.pbxproj | 6 +- .../documentview/DocumentViewPlugin.m | 56 +- .../DocumentViewWindow.nib/info.nib | 12 +- .../DocumentViewWindow.nib/keyedobjects.nib | Bin 3672 -> 3677 bytes 26 files changed, 4665 insertions(+), 86 deletions(-) create mode 100644 JSCocoaCodaLoader/CodaPlugInsController.h create mode 100644 JSCocoaCodaLoader/English.lproj/InfoPlist.strings create mode 100644 JSCocoaCodaLoader/Info.plist create mode 100644 JSCocoaCodaLoader/JSCocoa/BridgeSupportController.h create mode 100644 JSCocoaCodaLoader/JSCocoa/BridgeSupportController.m create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaController.h create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaController.m create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.h create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.m create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.h create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.m create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.h create mode 100644 JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.m create mode 100644 JSCocoaCodaLoader/JSCocoaLoader.xcodeproj/project.pbxproj create mode 100644 JSCocoaCodaLoader/JSCocoaLoaderPlugIn.h create mode 100644 JSCocoaCodaLoader/JSCocoaLoaderPlugIn.m create mode 100644 JSCocoaCodaLoader/JSCocoaLoader_Prefix.pch create mode 100644 JSCocoaCodaLoader/Uppercase.js diff --git a/JSCocoaCodaLoader/CodaPlugInsController.h b/JSCocoaCodaLoader/CodaPlugInsController.h new file mode 100644 index 0000000..17b2790 --- /dev/null +++ b/JSCocoaCodaLoader/CodaPlugInsController.h @@ -0,0 +1,178 @@ +#import + +// +// This object is passed during initialization. You must register your +// available functionality with one of the methods implemented by the +// plug-in controller +// + +@class CodaTextView; + +@interface CodaPlugInsController : NSObject +{ + NSMutableArray* plugins; + NSMutableDictionary* loadedMenuItemsDict; +} + +// The following methods are available to plugin developers +// in Coda 1.0.4 and later: + +- (NSString*)codaVersion:(id)sender; + +// codaVersion returns the version of Coda that is hosting the plugin, +// such as "1.0.4" + +- (void)registerActionWithTitle:(NSString*)title target:(id)target selector:(SEL)selector; + +// registerActionWithTitle:target:selector: exposes to the user a plugin action (a menu item) +// with the given title, that will perform the given selector on the target + +- (CodaTextView*)focusedTextView:(id)sender; + +// focusedTextView returns to the plugin an abstract object representing the text view +// in Coda that currently has focus + +// ### +// The following methods are available to plugin developers +// in Coda 1.5.2 and later: + +- (int)apiVersion; + +// apiVersion returns 2 as of Coda 1.5.2. It does not exist in previous versions. + +- (void)displayHTMLString:(NSString*)html; + +- (void)registerActionWithTitle:(NSString*)title + underSubmenuWithTitle:(NSString*)submenuTitle + target:(id)target + selector:(SEL)selector + representedObject:(id)repOb + keyEquivalent:(NSString*)keyEquivalent + pluginName:(NSString*)aName; + +- (void)saveAll; + +@end + + +// +// This is your hook to a text view in Coda. You can use this to provide +// manipulation of files. +// + +@class StudioPlainTextEditor; + +@interface CodaTextView : NSObject +{ + StudioPlainTextEditor* editor; +} + +// The following methods are available to plugin developers +// in Coda 1.0.4 and later: + +- (void)insertText:(NSString*)inText; + +// insertText: inserts the given string at the insertion point + +- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString; + +// replaces characters in the given range with the given string + +- (NSRange)selectedRange; + +// selectedRange returns the range of currently selected characters + +- (NSString*)selectedText; + +// selectedText returns the currently selected text, or nil if none + +- (void)setSelectedRange:(NSRange)range; + +// setSelectedRange: selects the given character range + +// The following methods are available to plugin developers +// in Coda 1.5.2 and later: + +- (NSString*)currentLine; + +// currentLine returns a string containing the entire content of the +// line that the insertion point is on + +- (unsigned int)currentLineNumber; + +// currentLineNumber returns the line number corresponding to the +// location of the insertion point + +- (void)deleteSelection; + +// deleteSelection deletes the selected text range + +- (NSString*)lineEnding; + +// lineEnding returns the current line ending of the file + +- (NSRange)rangeOfCurrentLine; + +// Returns the character range of the entire line the insertion point +// is on + +- (unsigned int)startOfLine; + +// startOfLine returns the character index (relative to the beginning of the document) +// of the start of the line the insertion point is on + +- (NSString*)string; + +// string returns the entire document as a plain string + +- (NSString*)stringWithRange:(NSRange)range; + +// stringWithRange: returns the specified ranged substring of the entire document + +- (int)tabWidth; + +//tabWidth: returns the width of tabs as spaces + +- (NSRange)previousWordRange; + +// previousWordRange: returns the range of the word previous to the insertion point + +- (BOOL)usesTabs; + +// usesTabs returns if the editor is currently uses tabs instead of spaces for indentation + +- (void)save; + +// saves the document you are working on + +- (void)beginUndoGrouping; +- (void)endUndoGrouping; + +// allows for multiple text manipulations to be considered one "undo/redo" +// operation + +- (NSWindow*)window; + +// returns the window the editor is located in (useful for showing sheets) + +// - (NSString*)path; - Coming in the next beta + +// returns the path to the text view's file (may be nil for unsaved documents) + +@end + + +// +// Your plug-in must conform to this protocol +// + +@protocol CodaPlugIn + +- (id)initWithPlugInController:(CodaPlugInsController*)aController bundle:(NSBundle*)yourBundle; +- (NSString*)name; + +@end + + + + diff --git a/JSCocoaCodaLoader/English.lproj/InfoPlist.strings b/JSCocoaCodaLoader/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..0562be3d38cfb04c112ef0d9f91447bae0e23563 GIT binary patch literal 210 zcmZvW!4APt5Jk`0S2SHVqGDqqRyHIe!WUGvG-#U^34i1#ada({yqURo=Dhj5V(ucA~#{@ pR>n+1?M5U2z_DXY<%EBom(GZxF}7NF#KMdFcE5X4uL+s>>laTvBz^z@ literal 0 HcmV?d00001 diff --git a/JSCocoaCodaLoader/Info.plist b/JSCocoaCodaLoader/Info.plist new file mode 100644 index 0000000..0f8404e --- /dev/null +++ b/JSCocoaCodaLoader/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.flyingmeat.Coda.JSCocoaLoader + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + JSCocoaLoaderPlugIn + + diff --git a/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.h b/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.h new file mode 100644 index 0000000..62a9c0a --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.h @@ -0,0 +1,34 @@ +// +// BridgeSupportController.h +// JSCocoa +// +// Created by Patrick Geiller on 08/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + +@interface BridgeSupportController : NSObject { + + + NSMutableArray* paths; + NSMutableArray* xmlDocuments; + + NSMutableDictionary* hash; +} + ++ (id)sharedController; + +- (BOOL)loadBridgeSupport:(NSString*)path; +- (BOOL)isBridgeSupportLoaded:(NSString*)path; +- (NSUInteger)bridgeSupportIndexForString:(NSString*)string; + +/* +- (NSString*)query:(NSString*)name withType:(NSString*)type; +- (NSString*)query:(NSString*)name withType:(NSString*)type inBridgeSupportFile:(NSString*)file; +*/ +- (NSString*)queryName:(NSString*)name; +- (NSString*)queryName:(NSString*)name type:(NSString*)type; + + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.m b/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.m new file mode 100644 index 0000000..f516501 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/BridgeSupportController.m @@ -0,0 +1,179 @@ +// +// BridgeSupportController.m +// JSCocoa +// +// Created by Patrick Geiller on 08/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "BridgeSupportController.h" + + +@implementation BridgeSupportController + + ++ (id)sharedController +{ + static id singleton; + @synchronized(self) + { + if (!singleton) + singleton = [[BridgeSupportController alloc] init]; + return singleton; + } + return singleton; +} + + +- (id)init +{ + id r = [super init]; + + paths = [[NSMutableArray alloc] init]; + xmlDocuments = [[NSMutableArray alloc] init]; + hash = [[NSMutableDictionary alloc] init]; + + return r; +} + +- (void)dealloc +{ + [hash release]; + [paths release]; + [xmlDocuments release]; + + [super dealloc]; +} + +- (BOOL)loadBridgeSupport:(NSString*)path +{ + NSError* error = nil; + + /* + Adhoc parser + NSXMLDocument is too slow + loading xml document as string then querying on-demand is too slow + can't get CFXMLParserRef to work + don't wan't to delve into expat + -> ad hoc : load file, build a hash of { name : xmlTagString } + */ + NSString* xmlDocument = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; + if (error) return NSLog(@"loadBridgeSupport : %@", error), NO; + + char* c = (char*)[xmlDocument UTF8String]; + + // Start parsing + for (; *c; c++) + { + if (*c == '<') + { + char startTagChar = c[1]; + if (startTagChar == 0) return NO; + + if ((c[1] == 'c' && c[2] == 'o') || startTagChar == 'e' || (c[1] == 'f' && c[2] == 'u') || (c[1] == 's' && c[2] == 't')) + { + // Extract name + char* tagStart = c; + for (; *c && *c != '\''; c++); + c++; + char* c0 = c; + for (; *c && *c != '\''; c++); + + id name = [[NSString alloc] initWithBytes:c0 length:c-c0 encoding:NSUTF8StringEncoding]; + + // Move to tag end + BOOL foundEndTag = NO; + BOOL foundOpenTag = NO; + c++; + for (; *c && !foundEndTag; c++) + { + if (*c == '<') foundOpenTag = YES; + else + if (*c == '/') + { + if (!foundOpenTag) + { + if(c[1] == '>') foundEndTag = YES, c++; + } + else + { + if (startTagChar == c[1]) + { + foundEndTag = YES; + // Skip to end of tag + for (; *c && *c != '>'; c++); + } + } + } + } + + c0 = tagStart; + id value = [[NSString alloc] initWithBytes:c0 length:c-c0 encoding:NSUTF8StringEncoding]; + + [hash setValue:value forKey:name]; + [value release]; + [name release]; + } + } + } + + [paths addObject:path]; + [xmlDocuments addObject:xmlDocument]; + + return YES; +} + + +- (BOOL)isBridgeSupportLoaded:(NSString*)path +{ + NSUInteger idx = [self bridgeSupportIndexForString:path]; + return idx == NSNotFound ? NO : YES; +} + +// +// bridgeSupportIndexForString +// given 'AppKit', return index of '/System/Library/Frameworks/AppKit.framework/Versions/C/Resources/BridgeSupport/AppKitFull.bridgesupport' +// +- (NSUInteger)bridgeSupportIndexForString:(NSString*)string +{ + int i, l = [paths count]; + for (i=0; i +#define MACOSX +#import +#import +#import "BridgeSupportController.h" +#import "JSCocoaPrivateObject.h" +#import "JSCocoaFFIArgument.h" +#import "JSCocoaFFIClosure.h" + + +// JS value container, used by methods wanting a straight JSValue and not a converted JS->ObjC value. +struct JSValueRefAndContextRef +{ + JSValueRef value; + JSContextRef ctx; +}; + + +typedef struct JSValueRefAndContextRef JSValueRefAndContextRef; + +@interface JSCocoaController : NSObject { + + JSGlobalContextRef ctx; + + id closureHash; + // Given a jsFunction, retrieve its selector + id jsFunctionSelectors; + // Given a jsFunction, retrieve which class it's attached to + id jsFunctionClasses; + + // Given a class + methodName, retrieve its jsFunction + id jsFunctionHash; + + // Instance stats + id instanceStats; + + // Used to convert callbackObject (zero call) + JSObjectRef callbackObjectValueOfCallback; + + BOOL useAutoCall; + BOOL isSpeaking; +} + +@property BOOL useAutoCall; +@property BOOL isSpeaking; + ++ (id)sharedController; ++ (void)garbageCollect; +//+ (void)garbageCollectNow; + ++ (void)upJSCocoaPrivateObjectCount; ++ (void)downJSCocoaPrivateObjectCount; ++ (int)JSCocoaPrivateObjectCount; + ++ (void)upJSValueProtectCount; ++ (void)downJSValueProtectCount; ++ (int)JSValueProtectCount; + ++ (void)logInstanceStats; + ++ (JSObjectRef)jsCocoaPrivateObjectInContext:(JSContextRef)ctx; ++ (NSMutableArray*)parseObjCMethodEncoding:(const char*)typeEncoding; ++ (NSMutableArray*)parseCFunctionEncoding:(NSString*)xml functionName:(NSString**)functionNamePlaceHolder; + +- (JSGlobalContextRef)ctx; +- (id)instanceStats; +- (JSObjectRef)callbackObjectValueOfCallback; +- (void)ensureJSValueIsObjectAfterInstanceAutocall:(JSValueRef)value; +- (NSString*)formatJSException:(JSValueRef)exception; +- (BOOL)evalJSFile:(NSString*)path; +- (JSValueRefAndContextRef)evalJSString:(NSString*)script; +- (BOOL)isMaybeSplitCall:(NSString*)start forClass:(id)class; + + +- (BOOL)loadFrameworkWithName:(NSString*)name; +- (BOOL)loadFrameworkWithName:(NSString*)frameworkName inPath:(NSString*)path; + + +- (id)selectorForJSFunction:(JSObjectRef)function; + +- (BOOL)overloadInstanceMethod:(NSString*)methodName class:(Class)class jsFunction:(JSValueRefAndContextRef)valueAndContext; +- (BOOL)overloadClassMethod:(NSString*)methodName class:(Class)class jsFunction:(JSValueRefAndContextRef)valueAndContext; + +- (BOOL)addClassMethod:(NSString*)methodName class:(Class)class jsFunction:(JSValueRefAndContextRef)valueAndContext encoding:(char*)encoding; +- (BOOL)addInstanceMethod:(NSString*)methodName class:(Class)class jsFunction:(JSValueRefAndContextRef)valueAndContext encoding:(char*)encoding; + + + +@end + + +id NSStringFromJSValue(JSValueRef value, JSContextRef ctx); + +void* malloc_autorelease(size_t size); + + +// +// From PyObjC : when to call objc_msgSendStret, for structure return +// Depending on structure size & architecture, structures are returned as function first argument (done transparently by ffi) or via registers +// + +#if defined(__ppc__) +# define SMALL_STRUCT_LIMIT 4 +#elif defined(__ppc64__) +# define SMALL_STRUCT_LIMIT 8 +#elif defined(__i386__) +# define SMALL_STRUCT_LIMIT 8 +#elif defined(__x86_64__) +# define SMALL_STRUCT_LIMIT 16 +#else +# error "Unsupported MACOSX platform" +#endif + diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaController.m b/JSCocoaCodaLoader/JSCocoa/JSCocoaController.m new file mode 100644 index 0000000..d71003c --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaController.m @@ -0,0 +1,2127 @@ +// +// JSCocoa.m +// JSCocoa +// +// Created by Patrick Geiller on 09/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "JSCocoaController.h" + +#pragma mark JS objects forward definitions + +// Global object +static JSValueRef OSXObject_getPropertyCallback(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*); + +// Private JS object +static void jsCocoaObject_initialize(JSContextRef, JSObjectRef); +static void jsCocoaObject_finalize(JSObjectRef); +static JSValueRef jsCocoaObject_callAsFunction(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef [], JSValueRef*); +static JSValueRef jsCocoaObject_getProperty(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*); +static bool jsCocoaObject_setProperty(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*); +static bool jsCocoaObject_deleteProperty(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*); +static void jsCocoaObject_getPropertyNames(JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef); +static JSObjectRef jsCocoaObject_callAsConstructor(JSContextRef, JSObjectRef, size_t, const JSValueRef [], JSValueRef*); +static JSValueRef jsCocoaObject_convertToType(JSContextRef ctx, JSObjectRef object, JSType type, JSValueRef* exception); + +static JSValueRef valueOfCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception); +//static JSValueRef instanceValueOfCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception); + + +static void throwException(JSContextRef ctx, JSValueRef* exception, NSString* reason); + +static JSClassRef OSXObjectClass; +static JSClassRef jsCocoaObjectClass; +static JSClassRef exceptionClass; +static JSClassRef hashObjectClass; + + +#define JSCocoaInternalAttribute kJSPropertyAttributeDontEnum + +#pragma mark JSCocoaController + +@implementation JSCocoaController + +@synthesize useAutoCall, isSpeaking; + + + + + ++ (id)sharedController +{ + static id singleton = NULL; + @synchronized(self) + { + if (!singleton) + { + // 1. alloc + // 2. store pointer + // 3. call init + // + // Why ? if init is calling sharedController, the pointer won't have been set and it will call itself over and over again. + // + singleton = [self alloc]; + [singleton init]; + } + } + return singleton; +} + +// +// Collect on top of the run loop, not in some JS function +// ++ (void)garbageCollect +{ + JSGarbageCollect(NULL); +} ++ (void)garbageCollectJustRightAfter +{ + [self performSelector:@selector(_garbageCollectJustRightAfter) withObject:nil afterDelay:0]; +} ++ (void)_garbageCollectJustRightAfter +{ + JSGarbageCollect(NULL); +} ++ (void)log:(NSString*)string +{ + NSLog(@"%@", string); +} +- (void)log:(NSString*)string +{ + NSLog(@"%@", string); +} +- (id)system:(NSString*)string +{ + system([string UTF8String]); + return nil; +} + ++ (void)logAndSay:(NSString*)string +{ + [self log:string]; + if ([[self sharedController] isSpeaking]) system([[NSString stringWithFormat:@"say %@ &", string] UTF8String]); +} + ++ (JSObjectRef)jsCocoaPrivateObjectInContext:(JSContextRef)ctx +{ + JSCocoaPrivateObject* private = [[JSCocoaPrivateObject alloc] init]; + JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, private); + [private release]; + return o; +} + + + +- (id)init +{ + id o = [super init]; + + closureHash = [[NSMutableDictionary alloc] init]; + jsFunctionSelectors = [[NSMutableDictionary alloc] init]; + jsFunctionClasses = [[NSMutableDictionary alloc] init]; + jsFunctionHash = [[NSMutableDictionary alloc] init]; + instanceStats = [[NSMutableDictionary alloc] init]; + + + useAutoCall = YES; + isSpeaking = YES; + + // + // OSX object javascript definition + // + JSClassDefinition OSXObjectDefinition = kJSClassDefinitionEmpty; + OSXObjectDefinition.getProperty = OSXObject_getPropertyCallback; + OSXObjectClass = JSClassCreate(&OSXObjectDefinition); + + + // + // Private object, used for holding references to objects, classes, function names, structs + // + JSClassDefinition jsCocoaObjectDefinition = kJSClassDefinitionEmpty; + jsCocoaObjectDefinition.initialize = jsCocoaObject_initialize; + jsCocoaObjectDefinition.finalize = jsCocoaObject_finalize; + jsCocoaObjectDefinition.getProperty = jsCocoaObject_getProperty; + jsCocoaObjectDefinition.setProperty = jsCocoaObject_setProperty; + jsCocoaObjectDefinition.deleteProperty = jsCocoaObject_deleteProperty; + jsCocoaObjectDefinition.getPropertyNames = jsCocoaObject_getPropertyNames; + jsCocoaObjectDefinition.callAsFunction = jsCocoaObject_callAsFunction; + jsCocoaObjectDefinition.callAsConstructor = jsCocoaObject_callAsConstructor; + jsCocoaObjectDefinition.convertToType = jsCocoaObject_convertToType; + + jsCocoaObjectClass = JSClassCreate(&jsCocoaObjectDefinition); + + // + // Exception object + // + JSClassDefinition exceptionClassDefinition = kJSClassDefinitionEmpty; + exceptionClass = JSClassCreate(&exceptionClassDefinition); + + // + // Private Hash of derived classes, storing js values + // + JSClassDefinition jsCocoaHashObjectDefinition = kJSClassDefinitionEmpty; + hashObjectClass = JSClassCreate(&jsCocoaHashObjectDefinition); + + + + // + // Start context + // + ctx = JSGlobalContextCreate(JSClassCreate(&OSXObjectDefinition)); + + + // Create callback used for autocall, set as property on JavascriptCore's [CallbackObject] + callbackObjectValueOfCallback = JSObjectMakeFunctionWithCallback(ctx, NULL, valueOfCallback); + // And protect it from GC + JSValueProtect(ctx, callbackObjectValueOfCallback); + + [self loadFrameworkWithName:@"AppKit"]; + [self loadFrameworkWithName:@"CoreFoundation"]; + [self loadFrameworkWithName:@"Foundation"]; + [self loadFrameworkWithName:@"CoreGraphics" inPath:@"/System/Library/Frameworks/ApplicationServices.framework/Frameworks"]; + + return o; +} + +- (void)dealloc +{ + +// JSValueUnprotect(ctx, callbackObjectInstanceValueOfCallback); + JSValueUnprotect(ctx, callbackObjectValueOfCallback); + + JSGarbageCollect(NULL); + JSGlobalContextRelease(ctx); + + [instanceStats dealloc]; + [jsFunctionHash dealloc]; + [jsFunctionClasses dealloc]; + [jsFunctionSelectors dealloc]; + [closureHash dealloc]; + + + [super dealloc]; +} + +- (JSGlobalContextRef)ctx +{ + return ctx; +} + +- (id)instanceStats +{ + return instanceStats; +} + + +- (JSObjectRef)callbackObjectValueOfCallback +{ + return callbackObjectValueOfCallback; +} + +// +// Set a valueOf callback on a jsObject +// +- (void)setValueOfCallBackOnJSObject:(JSObjectRef)jsObject +{ + JSStringRef jsName = JSStringCreateWithUTF8CString("valueOf"); + JSObjectSetProperty(ctx, jsObject, jsName, [self callbackObjectValueOfCallback], JSCocoaInternalAttribute, NULL); + JSStringRelease(jsName); +} + +// On auto calling 'instance' (eg NSString.instance), call is not done on property get (unlike NSWorkspace.sharedWorkspace) +// Instancing can't happen on get as instance may have parameters. +// Instancing will therefore be delayed and must happen +// * in fromJSValueRef +// * in property get (NSString.instance.count, getting 'count') +// * in valueOf (handled automatically as JavascriptCore will request 'valueOf' through property get) +- (void)ensureJSValueIsObjectAfterInstanceAutocall:(JSValueRef)jsValue +{ + // It's an instance if it has a property 'thisObject', holding the class name + // value is an object holding the method name, 'instance' - its only use is storing 'thisObject' + JSObjectRef jsObject = JSValueToObject(ctx, jsValue, NULL); + + JSStringRef name = JSStringCreateWithUTF8CString("thisObject"); + BOOL hasProperty = JSObjectHasProperty(ctx, jsObject, name); + JSValueRef thisObjectValue = JSObjectGetProperty(ctx, jsObject, name, NULL); + if (hasProperty) JSObjectDeleteProperty(ctx, jsObject, name, NULL); + JSStringRelease(name); + + if (!hasProperty) return; + + // Returning NULL will crash + if (!thisObjectValue) return; + JSObjectRef thisObject = JSValueToObject(ctx, thisObjectValue, NULL); + if (!thisObject) return; + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(thisObject); + if (!thisObject) return; + +// NSLog(@"Instance autocall on class %@", [privateObject object]); + + // Create new instance and patch it into object + id newInstance = [[[privateObject object] alloc] init]; + JSCocoaPrivateObject* instanceObject = JSObjectGetPrivate(jsObject); + instanceObject.type = @"@"; + [instanceObject setObject:newInstance]; + // Make JS object sole owner + [newInstance release]; +} + +#pragma mark Common encoding parsing +// Use method_copyArgumentType ? ++ (NSMutableArray*)parseObjCMethodEncoding:(const char*)typeEncoding +{ + id argumentEncodings = [NSMutableArray array]; + char* argsParser = (char*)typeEncoding; + for(; *argsParser; argsParser++) + { + // Skip ObjC argument order + if (*argsParser >= '0' && *argsParser <= '9') continue; + else + // Skip ObjC 'const', 'oneway' markers + if (*argsParser == 'r' || *argsParser == 'V') continue; + else + if (*argsParser == '{') + { + // Parse structure encoding + int count = 0; + [JSCocoaFFIArgument typeEncodingsFromStructureTypeEncoding:[NSString stringWithUTF8String:argsParser] parsedCount:&count]; + + id encoding = [[NSString alloc] initWithBytes:argsParser length:count encoding:NSASCIIStringEncoding]; + id argumentEncoding = [[JSCocoaFFIArgument alloc] init]; + // Set return value + if ([argumentEncodings count] == 0) [argumentEncoding setIsReturnValue:YES]; + [argumentEncoding setStructureTypeEncoding:encoding]; + [argumentEncodings addObject:argumentEncoding]; + [argumentEncoding release]; + + [encoding release]; + argsParser += count; + } + else + { + // Custom handling for pointers as they're not one char long. + // ##TOFIX : copy pointer type (^i, ^{NSRect}) to the argumentEncoding + char type = *argsParser; + if (*argsParser == '^') + while (*argsParser && !(*argsParser >= '0' && *argsParser <= '9')) argsParser++; + + id argumentEncoding = [[JSCocoaFFIArgument alloc] init]; + // Set return value + if ([argumentEncodings count] == 0) [argumentEncoding setIsReturnValue:YES]; + [argumentEncoding setTypeEncoding:type]; + [argumentEncodings addObject:argumentEncoding]; + [argumentEncoding release]; + } + if (!*argsParser) break; + } + return argumentEncodings; +} + ++ (NSMutableArray*)parseCFunctionEncoding:(NSString*)xml functionName:(NSString**)functionNamePlaceHolder +{ + id argumentEncodings = [NSMutableArray array]; + id xmlDocument = [[NSXMLDocument alloc] initWithXMLString:xml options:0 error:nil]; + [xmlDocument autorelease]; + + id rootElement = [xmlDocument rootElement]; + *functionNamePlaceHolder = [[rootElement attributeForName:@"name"] stringValue]; + + // Parse children and return alue + int i, numChildren = [rootElement childCount]; + id returnValue = NULL; + for (i=0; i NSArray[name1, name2] + id names = [NSMutableArray array]; + int i, nameCount = JSPropertyNameArrayGetCount(jsNames); + // Length of target selector = length of method + length of each (argument + ':') + int targetSelectorLength = [methodName length]; + // Actual arguments + JSValueRef* actualArguments = malloc_autorelease(sizeof(JSValueRef)*nameCount); + for (i=0; i>>evaling %@", filePath); + BOOL evaled = [self evalJSFile:filePath]; +// NSLog(@">>>EVALED %d, %@", evaled, filePath); + if (!evaled) + { + id error = [NSString stringWithFormat:@"test %@ failed", file]; + [JSCocoaController logAndSay:error]; + return NO; + } + [JSCocoaController garbageCollect]; + } + return YES; +} + + + + +#pragma mark Garbage Collect debug + +// Boxing object, set as a Javascript object's private data +static int JSCocoaPrivateObjectCount = 0; ++ (void)upJSCocoaPrivateObjectCount { JSCocoaPrivateObjectCount++; } ++ (void)downJSCocoaPrivateObjectCount { JSCocoaPrivateObjectCount--; } ++ (int)JSCocoaPrivateObjectCount { return JSCocoaPrivateObjectCount; } + +// Javascript hash, set on classes created with JSCocoaController.createClass +// - used to store js values on instances ( someClassDerivedInJS['someValue'] = 'hello !' ) +static int JSCocoaHashCount = 0; ++ (void)upJSCocoaHashCount { JSCocoaHashCount++; } ++ (void)downJSCocoaHashCount { JSCocoaHashCount--; } ++ (int)JSCocoaHashCount { return JSCocoaHashCount; } + + +// Value protect +static int JSValueProtectCount = 0; ++ (void)upJSValueProtectCount { JSValueProtectCount++; } ++ (void)downJSValueProtectCount { JSValueProtectCount--; } ++ (int)JSValueProtectCount { return JSValueProtectCount; } + +// Instance count +int fullInstanceCount = 0; +int liveInstanceCount = 0; ++ (void)upInstanceCount:(id)o +{ + fullInstanceCount++; + liveInstanceCount++; + + id stats = [[JSCocoaController sharedController] instanceStats]; + id key = [NSMutableString stringWithFormat:@"%@", [o class]]; + + id existingCount = [stats objectForKey:key]; + int count = 0; + if (existingCount) count = [existingCount intValue]; + + count++; + [stats setObject:[NSNumber numberWithInt:count] forKey:key]; +} ++ (void)downInstanceCount:(id)o +{ + liveInstanceCount--; + + id stats = [[JSCocoaController sharedController] instanceStats]; + id key = [NSMutableString stringWithFormat:@"%@", [o class]]; + + id existingCount = [stats objectForKey:key]; + if (!existingCount) + { + NSLog(@"downInstanceCount on %@ without any up", o); + return; + } + int count = [existingCount intValue]; + count--; + + if (count) [stats setObject:[NSNumber numberWithInt:count] forKey:key]; + else [stats removeObjectForKey:key]; +} ++ (int)liveInstanceCount:(Class)c +{ + id key = [NSMutableString stringWithFormat:@"%@", c]; + + id stats = [[JSCocoaController sharedController] instanceStats]; + id existingCount = [stats objectForKey:key]; + if (!existingCount) return 0; + return [existingCount intValue]; +} ++ (id)liveInstanceHash +{ + return [[JSCocoaController sharedController] instanceStats]; +} + + ++ (void)logInstanceStats +{ + id stats = [[JSCocoaController sharedController] instanceStats]; + id allKeys = [stats allKeys]; + NSLog(@"====instanceStats : %d classes spawned %d live instances (%d since launch, %d dead)====", [allKeys count], liveInstanceCount, fullInstanceCount, fullInstanceCount-liveInstanceCount); + for (id key in allKeys) NSLog(@"%@=%d", key, [[stats objectForKey:key] intValue]); +} + + +@end + + + + + + + +#pragma mark Javascript setter functions +// Hold these methods in a derived NSObject class : only derived classes created with a __jsHash (capable of hosting js objects) will get them +@interface JSCocoaMethodHolder : NSObject +@end +// Stored there for convenience. They won't be used by JSCocoaPrivateObject but will be patched in for any derived class +@implementation JSCocoaMethodHolder +- (BOOL)setJSValue:(JSValueRefAndContextRef)valueAndContext forJSName:(JSValueRefAndContextRef)nameAndContext +{ + if (class_getInstanceVariable([self class], "__jsHash")) + { + JSContextRef c = [[JSCocoaController sharedController] ctx]; + JSStringRef name = JSValueToStringCopy(c, nameAndContext.value, NULL); + + JSObjectRef hash = NULL; + object_getInstanceVariable(self, "__jsHash", (void**)&hash); + if (!hash) + { + hash = JSObjectMake(c, hashObjectClass, NULL); + object_setInstanceVariable(self, "__jsHash", (void*)hash); + JSValueProtect(c, hash); + [JSCocoaController upJSValueProtectCount]; + [JSCocoaController upJSCocoaHashCount]; + } + +// NSLog(@"SET JS VALUE %x %@", valueAndContext.value, [JSStringCopyCFString(kCFAllocatorDefault, name) autorelease]); + JSObjectSetProperty(c, hash, name, valueAndContext.value, kJSPropertyAttributeNone, NULL); + JSStringRelease(name); + return YES; + } + return NO; +} +- (JSValueRefAndContextRef)JSValueForJSName:(JSValueRefAndContextRef)nameAndContext +{ + JSValueRefAndContextRef valueAndContext = { JSValueMakeNull(nameAndContext.ctx), NULL }; + if (class_getInstanceVariable([self class], "__jsHash")) + { + JSContextRef c = [[JSCocoaController sharedController] ctx]; + JSStringRef name = JSValueToStringCopy(c, nameAndContext.value, NULL); + + JSObjectRef hash = NULL; + object_getInstanceVariable(self, "__jsHash", (void**)&hash); + if (!hash) return valueAndContext; + if (!JSObjectHasProperty(c, hash, name)) return valueAndContext; + + valueAndContext.ctx = c; + valueAndContext.value = JSObjectGetProperty(c, hash, name, NULL); + JSStringRelease(name); + return valueAndContext; + } + return valueAndContext; +} + +- (BOOL)deleteJSValueForJSName:(JSValueRefAndContextRef)nameAndContext +{ + if (class_getInstanceVariable([self class], "__jsHash")) + { + JSContextRef c = [[JSCocoaController sharedController] ctx]; + JSStringRef name = JSValueToStringCopy(c, nameAndContext.value, NULL); + + JSObjectRef hash = NULL; + object_getInstanceVariable(self, "__jsHash", (void**)&hash); + if (!hash) return NO; + if (!JSObjectHasProperty(c, hash, name)) return NO; + bool r = JSObjectDeleteProperty(c, hash, name, NULL); + JSStringRelease(name); + return r; + } + return NO; +} + +// Unprotect js hash +- (void)deallocAndCleanupJS +{ + JSObjectRef hash = NULL; + object_getInstanceVariable(self, "__jsHash", (void**)&hash); + if (hash) + { + JSValueUnprotect([[JSCocoaController sharedController] ctx], hash); + [JSCocoaController downJSCocoaHashCount]; + } + [JSCocoaController downInstanceCount:self]; + [super dealloc]; +} + +// Instance count debug ++ (id)alloc +{ + id o = [super alloc]; + [JSCocoaController upInstanceCount:o]; +// NSLog(@"alloc %x", o); + return o; +} ++ (id)allocWithZone:(NSZone*)zone +{ + id o = [super allocWithZone:zone]; + [JSCocoaController upInstanceCount:o]; +// NSLog(@"allocWithZone %x", o); + return o; +} + +@end + + +#pragma mark Common instance method +// Class.instance == class.alloc.init + release (jsObject retains object) +// Class.instance( { withA : ... andB : ... } ) == class.alloc.initWithA:... andB:... + release +@implementation NSObject(CommonInstance) ++ (JSValueRef)instanceWithContext:(JSContextRef)ctx argumentCount:(size_t)argumentCount arguments:(JSValueRef*)arguments exception:(JSValueRef*)exception +{ + id methodName = @"init"; + // Recover init method + if (argumentCount == 1) + { + id splitMethodName = @"init"; + BOOL isSplitCall = [[JSCocoaController sharedController] trySplitCall:&splitMethodName class:self argumentCount:&argumentCount arguments:&arguments ctx:ctx]; + if (isSplitCall) methodName = splitMethodName; + else return throwException(ctx, exception, @"Instance split call did not find an init method"), NULL; + } +// NSLog(@"=>Called instance on %@ with init=%@", self, methodName); + + // Allocate new instance + id newInstance = [self alloc]; + + // Set it as new object + JSObjectRef thisObject = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + JSCocoaPrivateObject* private = JSObjectGetPrivate(thisObject); + private.type = @"@"; + [private setObject:newInstance]; + + // Create function object boxing our init method + JSObjectRef function = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + private = JSObjectGetPrivate(function); + private.type = @"method"; + private.methodName = methodName; + + // Call callAsFunction on our new instance with our init method + JSValueRef exceptionFromInitCall = NULL; + JSValueRef returnValue = jsCocoaObject_callAsFunction(ctx, function, thisObject, argumentCount, arguments, &exceptionFromInitCall); + if (exceptionFromInitCall) return *exception = exceptionFromInitCall, NULL; + + // Release object + JSObjectRef returnObject = JSValueToObject(ctx, returnValue, NULL); + // We can get nil when initWith... fails. (eg var image = NSImage.instance({withContentsOfFile:'DOESNOTEXIST'}) + // Return nil then. + if (returnObject == nil) return JSValueMakeNull(ctx); + private = JSObjectGetPrivate(returnObject); + [[private object] release]; + +// NSLog(@"returnValue from instanceWithContext=%x", returnValue); + return returnValue; +} +@end + + + + + + +#pragma mark JS OSX object + +/* + + Global resolver + +*/ +JSValueRef OSXObject_getPropertyCallback(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef* exception) +{ + NSString* propertyName = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS); + [propertyName autorelease]; + + +// NSLog(@"Asking for global property %@", propertyName); + + // + // ObjC class + // + Class objCClass = NSClassFromString(propertyName); + if (objCClass) + { + // Allocate private data to hold object type + JSCocoaPrivateObject* private = [[JSCocoaPrivateObject alloc] init]; + private.type = @"@"; + [private setObject:objCClass]; + JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, private); + // Make JS object sole owner + [private release]; + return o; + } + + id xml; + id type = nil; + // + // Query BridgeSupport for property + // + xml = [[BridgeSupportController sharedController] queryName:propertyName]; + if (xml) + { + id error; + id xmlDocument = [[NSXMLDocument alloc] initWithXMLString:xml options:0 error:&error]; + if (error) return NSLog(@"(OSX_getPropertyCallback) malformed xml while getting property %@ of type %@ : %@", propertyName, type, error), NULL; + [xmlDocument autorelease]; + + type = [[xmlDocument rootElement] name]; +// NSLog(@"type=%@", ); + + // + // Function + // + if ([type isEqualToString:@"function"]) + { + // Allocate private data to hold object type + JSCocoaPrivateObject* private = [[JSCocoaPrivateObject alloc] init]; + private.type = @"function"; + private.xml = xml; + JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, private); + // Make JS object sole owner + [private release]; + return o; + } + + // + // Struct + // + else + if ([type isEqualToString:@"struct"]) + { + // Allocate private data to hold object type + JSCocoaPrivateObject* private = [[JSCocoaPrivateObject alloc] init]; + private.type = @"struct"; + private.xml = xml; + JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, private); + // Make JS object sole owner + [private release]; + return o; + } + + // + // Constant + // + else + if ([type isEqualToString:@"constant"]) + { + // Check if constant's declared_type is NSString* + id declared_type = [[xmlDocument rootElement] attributeForName:@"declared_type"]; + if (!declared_type) declared_type = [[xmlDocument rootElement] attributeForName:@"type"]; + if (!declared_type || !([[declared_type stringValue] isEqualToString:@"NSString*"] + || [[declared_type stringValue] isEqualToString:@"@"] + || [[declared_type stringValue] isEqualToString:@"^{__CFString=}"] + )) + return NSLog(@"(OSX_getPropertyCallback) %@ not a NSString* constant : %@", propertyName, xml), NULL; + + // Grab symbol + void* symbol = dlsym(RTLD_DEFAULT, [propertyName UTF8String]); + if (!symbol) return NSLog(@"(OSX_getPropertyCallback) symbol %@ not found", propertyName), NULL; + NSString* str = *(NSString**)symbol; + + // Return symbol as a Javascript string + JSStringRef jsName = JSStringCreateWithUTF8CString([str UTF8String]); + JSValueRef jsString = JSValueMakeString(ctx, jsName); + JSStringRelease(jsName); + return jsString; + } + + // + // Enum + // + else + if ([type isEqualToString:@"enum"]) + { + // Check if constant's declared_type is NSString* + id value = [[xmlDocument rootElement] attributeForName:@"value"]; + if (!value) return NSLog(@"(OSX_getPropertyCallback) %@ enum has no value set", propertyName), NULL; + + // Try parsing value + double doubleValue = 0; + value = [value stringValue]; + if (![[NSScanner scannerWithString:value] scanDouble:&doubleValue]) return NSLog(@"(OSX_getPropertyCallback) scanning %@ enum failed", propertyName), NULL; + + return JSValueMakeNumber(ctx, doubleValue); + } + } + return NULL; +} + + + + + + + + + + + + + + + + + +#pragma mark JS Cocoa object + + +// +// From PyObjC : when to call objc_msgSendStret, for structure return +// Depending on structure size & architecture, structures are returned as function first argument (done transparently by ffi) or via registers +// +BOOL isUsingStret(id argumentEncodings) +{ + int resultSize = 0; + char returnEncoding = [[argumentEncodings objectAtIndex:0] typeEncoding]; + if (returnEncoding == _C_STRUCT_B) resultSize = [JSCocoaFFIArgument sizeOfStructure:[[argumentEncodings objectAtIndex:0] structureTypeEncoding]]; + if (returnEncoding == _C_STRUCT_B && + //#ifdef __ppc64__ + // ffi64_stret_needs_ptr(signature_to_ffi_return_type(rettype), NULL, NULL) + // + //#else /* !__ppc64__ */ + (resultSize > SMALL_STRUCT_LIMIT + #ifdef __i386__ + /* darwin/x86 ABI is slightly odd ;-) */ + || (resultSize != 1 + && resultSize != 2 + && resultSize != 4 + && resultSize != 8) + #endif + #ifdef __x86_64__ + /* darwin/x86-64 ABI is slightly odd ;-) */ + || (resultSize != 1 + && resultSize != 2 + && resultSize != 4 + && resultSize != 8 + && resultSize != 16 + ) + #endif + ) + //#endif /* !__ppc64__ */ + ) { +// callAddress = objc_msgSend_stret; +// usingStret = YES; + return YES; + } + return NO; +} + + +// Autocall : return value +JSValueRef valueOfCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) +{ +// NSLog(@"valueOf callback"); + // Holding a native JS value ? Return it + JSCocoaPrivateObject* thisPrivateObject = JSObjectGetPrivate(thisObject); + if ([thisPrivateObject.type isEqualToString:@"jsValueRef"]) + { + return [thisPrivateObject jsValueRef]; + } + + // Convert to string + NSString* toString = [NSString stringWithFormat:@"%@", [thisPrivateObject.type isEqualToString:@"@"] ? [[thisPrivateObject object] description] : @"JSCocoaPrivateObject"]; +// JSStringRef jsToString = JSStringCreateWithUTF8CString([toString UTF8String]); + JSStringRef jsToString = JSStringCreateWithCFString((CFStringRef)toString); + JSValueRef jsValueToString = JSValueMakeString(ctx, jsToString); + JSStringRelease(jsToString); +// NSLog(@"valueOf callback %@", toString); + return jsValueToString; +// return JSValueMakeNull(ctx); +} + +// +// initialize +// retain boxed object +// +static void jsCocoaObject_initialize(JSContextRef ctx, JSObjectRef object) +{ + id o = JSObjectGetPrivate(object); + [o retain]; +} + +// +// finalize +// release boxed object +// +static void jsCocoaObject_finalize(JSObjectRef object) +{ + // if dealloc is overloaded, releasing now will trigger JS code and fail + // As we're being called by GC, KJS might assert() in operationInProgress == NoOperation + id o = JSObjectGetPrivate(object); + // Immediate release if dealloc is not overloaded + [o release]; +} + + + +// +// getProperty +// Return property in object's internal hash if its contains propertyName +// else ... +// Get objC method matching propertyName, autocall it +// else ... +// method may be a split call -> return a private object +// +// At method start, handle special cases for arrays (integers, length) and dictionaries +// +static JSValueRef jsCocoaObject_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef* exception) +{ + NSString* propertyName = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS); + [propertyName autorelease]; + + // Autocall instance + if ([propertyName isEqualToString:@"thisObject"]) return NULL; + [[JSCocoaController sharedController] ensureJSValueIsObjectAfterInstanceAutocall:object]; + + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(object); +// NSLog(@"Asking for property %@ %@(%@)", propertyName, privateObject, privateObject.type); + + if ([privateObject.type isEqualToString:@"@"]) + { + // Special case for NSMutableArray get + if ([privateObject.object isKindOfClass:[NSArray class]]) + { + id array = privateObject.object; + id scan = [NSScanner scannerWithString:propertyName]; + NSInteger propertyIndex; + // Is asked property an int ? + BOOL convertedToInt = ([scan scanInteger:&propertyIndex]); + if (convertedToInt && [scan isAtEnd]) + { + if (propertyIndex < 0 || propertyIndex >= [array count]) return NULL; + + id o = [array objectAtIndex:propertyIndex]; + JSValueRef value = NULL; + [JSCocoaFFIArgument boxObject:o toJSValueRef:&value inContext:ctx]; + return value; + } + + // If we have 'length', switch it to 'count' + if ([propertyName isEqualToString:@"length"]) propertyName = @"count"; + } + + + // Special case for NSMutableDictionary get + if ([privateObject.object isKindOfClass:[NSDictionary class]]) + { + id dictionary = privateObject.object; + id o = [dictionary objectForKey:propertyName]; + if (o) + { + JSValueRef value = NULL; + [JSCocoaFFIArgument boxObject:o toJSValueRef:&value inContext:ctx]; + return value; + } + } + + + + // Check object's internal property in its jsHash + id callee = [privateObject object]; + if ([callee respondsToSelector:@selector(JSValueForJSName:)]) + { +// JSValueRef hashProperty = [callee JSValueForJSName:propertyNameJS]; + + JSValueRefAndContextRef name = { JSValueMakeNull(ctx), NULL } ; + name.value = JSValueMakeString(ctx, propertyNameJS); + JSValueRef hashProperty = [callee JSValueForJSName:name].value; + if (hashProperty && !JSValueIsNull(ctx, hashProperty)) + { + return hashProperty; + } + } + + + // + // Attempt Zero arg autocall + // Object.alloc().init() -> Object.alloc.init + // + if ([[JSCocoaController sharedController] useAutoCall]) + { + id callee = [privateObject object]; + SEL sel = NSSelectorFromString(propertyName); + // Go for zero arg call + if ([propertyName rangeOfString:@":"].location == NSNotFound && [callee respondsToSelector:sel]) + { + Method method = class_getInstanceMethod([callee class], sel); + if (!method) method = class_getClassMethod([callee class], sel); + + // Extract arguments + const char* typeEncoding = method_getTypeEncoding(method); + id argumentEncodings = [JSCocoaController parseObjCMethodEncoding:typeEncoding]; + + // + // From PyObjC : when to call objc_msgSendStret, for structure return + // Depending on structure size & architecture, structures are returned as function first argument (done transparently by ffi) or via registers + // + BOOL usingStret = isUsingStret(argumentEncodings); + void* callAddress = objc_msgSend; + if (usingStret) callAddress = objc_msgSend_stret; + + // + // ffi data + // + ffi_cif cif; + ffi_type* args[2]; + void* values[2]; + char* selector; + + selector = (char*)NSSelectorFromString(propertyName); + args[0] = &ffi_type_pointer; + args[1] = &ffi_type_pointer; + values[0] = (void*)&callee; + values[1] = (void*)&selector; + + // Get return value holder + id returnValue = [argumentEncodings objectAtIndex:0]; + + // Setup ffi + ffi_status prep_status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, [returnValue ffi_type], args); + // + // Call ! + // + if (prep_status == FFI_OK) + { + void* storage = [returnValue storage]; + if ([returnValue ffi_type] == &ffi_type_void) storage = NULL; + ffi_call(&cif, callAddress, storage, values); + } + + // Return now if our function returns void + // NO - box it +// if ([returnValue ffi_type] == &ffi_type_void) return NULL; + + + // Else, convert return value + JSValueRef jsReturnValue = NULL; + BOOL converted = [returnValue toJSValueRef:&jsReturnValue inContext:ctx]; + if (!converted) return throwException(ctx, exception, [NSString stringWithFormat:@"Return value not converted in %@", propertyName]), NULL; + + // When returning NULL or numbers, return value won't be an object — box it + if (jsReturnValue == NULL || JSValueGetType(ctx, jsReturnValue) != kJSTypeObject) + { + + JSObjectRef jsObject = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + + // Store our converted js value + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(jsObject); + [privateObject setType:@"jsValueRef"]; + [privateObject setJSValueRef:jsReturnValue ctx:ctx]; + jsReturnValue = jsObject; + + // Set the valueOf callback : JavascriptCore will call it when requesting default value + [[JSCocoaController sharedController] setValueOfCallBackOnJSObject:jsObject]; + } + + // If return value is an object, set a valueOf callback on it + if (JSValueGetType(ctx, jsReturnValue) == kJSTypeObject) + { + JSObjectRef jsObject = JSValueToObject(ctx, jsReturnValue, NULL); + // Set the valueOf callback : JavascriptCore will call it when requesting default value + [[JSCocoaController sharedController] setValueOfCallBackOnJSObject:jsObject]; + } + + id o = JSObjectGetPrivate(JSValueToObject(ctx, jsReturnValue, NULL)); + [o setIsAutoCall:YES]; + return jsReturnValue; + } + } + + + // + // Do some filtering here on property name : + // We're asked a property name and at this point we've checked the class's jsarray, autocall. + // If the property we're asked does not start a split call we'll return NULL. + // + // Check if the property is actually a method. + // If NO, replace underscores with colons + // add a ':' suffix + // + // If callee still fails to responds to that, check if propertyName maybe starts a split call. + // If NO, return null + // + id methodName = [NSMutableString stringWithString:propertyName]; + // If responds to selector, OK + if (![callee respondsToSelector:NSSelectorFromString(methodName)] + // non ObjC methods + && ![methodName isEqualToString:@"valueOf"] + && ![methodName isEqualToString:@"Super"] + && ![methodName isEqualToString:@"instance"]) + { + if ([methodName rangeOfString:@"_"].location != NSNotFound) + [methodName replaceOccurrencesOfString:@"_" withString:@":" options:0 range:NSMakeRange(0, [methodName length])]; + + if (![methodName hasSuffix:@":"]) [methodName appendString:@":"]; + + if (![callee respondsToSelector:NSSelectorFromString(methodName)]) + { + methodName = propertyName; + // Try split start + BOOL isMaybeSplit = [[JSCocoaController sharedController] isMaybeSplitCall:methodName forClass:[callee class]]; + if (!isMaybeSplit) + { +// NSLog(@"NON SPLIT %@.%@", callee, methodName); + return NULL; + } + } + } + + + // Get ready for method call + JSCocoaPrivateObject* private = [[JSCocoaPrivateObject alloc] init]; + private.type = @"method"; + private.methodName = methodName; + JSObjectRef o = JSObjectMake(ctx, jsCocoaObjectClass, private); + // Make JS object sole owner + [private release]; + + /* + setting valueOf allows us to return NULL when conversion is being done, eg 'string' + obj.property -> valueOf is called and returns NULL if property is a split call. + BUT JSObject::toBoolean always returns true, therefore even !!(new Object(false)) returns true : + this will yield false positives for properties that are detected as things that could be split calls but aren't. + */ + [[JSCocoaController sharedController] setValueOfCallBackOnJSObject:o]; + + // Special case for instance : setup a valueOf callback calling instance + if ([callee class] == callee && [propertyName isEqualToString:@"instance"]) + { + JSStringRef jsName = JSStringCreateWithUTF8CString("thisObject"); + JSObjectSetProperty(ctx, o, jsName, object, JSCocoaInternalAttribute, NULL); + JSStringRelease(jsName); + } + + + return o; + } + return NULL; +} + + +// +// setProperty +// call setter : propertyName -> setPropertyName +// +static bool jsCocoaObject_setProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef jsValue, JSValueRef* exception) +{ + // Autocall : ensure 'instance' has been called and we've got our new instance + [[JSCocoaController sharedController] ensureJSValueIsObjectAfterInstanceAutocall:object]; + + + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(object); + + NSString* propertyName = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS); + [propertyName autorelease]; + +// NSLog(@"****SET %@ in ctx %x on object %x (type=%@) method=%@", propertyName, ctx, object, privateObject.type, privateObject.methodName); + if ([privateObject.type isEqualToString:@"@"]) + { + + // Special case for NSMutableArray set + if ([privateObject.object isKindOfClass:[NSArray class]]) + { + id array = privateObject.object; + if (![array respondsToSelector:@selector(replaceObjectAtIndex:withObject:)]) return throwException(ctx, exception, @"Calling set on a non mutable array"), false; + id scan = [NSScanner scannerWithString:propertyName]; + NSInteger propertyIndex; + // Is asked property an int ? + BOOL convertedToInt = ([scan scanInteger:&propertyIndex]); + if (convertedToInt && [scan isAtEnd]) + { + if (propertyIndex < 0 || propertyIndex >= [array count]) return false; + + id property = NULL; + if ([JSCocoaFFIArgument unboxJSValueRef:jsValue toObject:&property inContext:ctx]) + { + [array replaceObjectAtIndex:propertyIndex withObject:property]; + return true; + } + else return false; + } + } + + + // Special case for NSMutableDictionary set + if ([privateObject.object isKindOfClass:[NSDictionary class]]) + { + id dictionary = privateObject.object; + if (![dictionary respondsToSelector:@selector(setObject:forKey:)]) return throwException(ctx, exception, @"Calling set on a non mutable dictionary"), false; + + id property = NULL; + if ([JSCocoaFFIArgument unboxJSValueRef:jsValue toObject:&property inContext:ctx]) + { + [dictionary setObject:property forKey:propertyName]; + return true; + } + else return false; + } + + + + // Try shorthand overload : obc[selector] = function + id callee = [privateObject object]; + if ([propertyName rangeOfString:@":"].location != NSNotFound) + { + JSValueRefAndContextRef v = { jsValue, ctx }; + [[JSCocoaController sharedController] overloadInstanceMethod:propertyName class:[callee class] jsFunction:v]; + return true; + } + + + // Can't use capitalizedString on the whole string as it will transform + // myValue + // to Myvalue + // we want MyValue +// NSString* setterName = [NSString stringWithFormat:@"set%@:", [propertyName capitalizedString]]; + // Capitalize only first letter + NSString* setterName = [NSString stringWithFormat:@"set%@%@:", + [[propertyName substringWithRange:NSMakeRange(0,1)] capitalizedString], + [propertyName substringWithRange:NSMakeRange(1, [propertyName length]-1)]]; + +// NSLog(@"SETTING %@ %@", propertyName, setterName); + + // + // Attempt Zero arg autocall for setter + // Object.alloc().init() -> Object.alloc.init + // + SEL sel = NSSelectorFromString(setterName); + if ([callee respondsToSelector:sel]) + { + Method method = class_getInstanceMethod([callee class], sel); + if (!method) method = class_getClassMethod([callee class], sel); + + // Extract arguments + const char* typeEncoding = method_getTypeEncoding(method); + id argumentEncodings = [JSCocoaController parseObjCMethodEncoding:typeEncoding]; + if ([[argumentEncodings objectAtIndex:0] typeEncoding] != 'v') return throwException(ctx, exception, [NSString stringWithFormat:@"(in setter) %@ must return void", setterName]), false; + + // + // From PyObjC : when to call objc_msgSendStret, for structure return + // Depending on structure size & architecture, structures are returned as function first argument (done transparently by ffi) or via registers + // + BOOL usingStret = isUsingStret(argumentEncodings); + void* callAddress = objc_msgSend; + if (usingStret) callAddress = objc_msgSend_stret; + + // + // ffi data + // + ffi_cif cif; + ffi_type* args[3]; + void* values[3]; + char* selector; + + selector = (char*)NSSelectorFromString(setterName); + args[0] = &ffi_type_pointer; + args[1] = &ffi_type_pointer; + values[0] = (void*)&callee; + values[1] = (void*)&selector; + + // Get arg (skip return value, instance, selector) + JSCocoaFFIArgument* arg = [argumentEncodings objectAtIndex:3]; + BOOL converted = [arg fromJSValueRef:jsValue inContext:ctx]; + if (!converted) return throwException(ctx, exception, [NSString stringWithFormat:@"(in setter) Argument %c not converted", [arg typeEncoding]]), false; + args[2] = [arg ffi_type]; + values[2] = [arg storage]; + + // Setup ffi + ffi_status prep_status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 3, &ffi_type_void, args); + // + // Call ! + // + if (prep_status == FFI_OK) + { + ffi_call(&cif, callAddress, NULL, values); + } + return true; + } + + if ([callee respondsToSelector:@selector(setJSValue:forJSName:)]) + { + // Set as instance variable +// BOOL set = [callee setJSValue:jsValue forJSName:propertyNameJS]; + JSValueRefAndContextRef value = { JSValueMakeNull(ctx), NULL }; + value.value = jsValue; + + JSValueRefAndContextRef name = { JSValueMakeNull(ctx), NULL } ; + name.value = JSValueMakeString(ctx, propertyNameJS); + BOOL set = [callee setJSValue:value forJSName:name]; + if (set) return true; + } + } + + // Special case for autocall : allow current js object to receive a custom valueOf method that will handle autocall + // And a thisObject property holding class for instance autocall + if ([propertyName isEqualToString:@"valueOf"]) return false; + if ([propertyName isEqualToString:@"thisObject"]) return false; + + // ## Setter should fail AND WARN if propertyName can't be set. + // Warning is disabled as set on structures need a special check, yet to be written +// return throwException(ctx, exception, [NSString stringWithFormat:@"(in setter) object does not support setting"]), false; + return false; +} + + +// +// deleteProperty +// delete property in hash +// +static bool jsCocoaObject_deleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef* exception) +{ + NSString* propertyName = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS); + [propertyName autorelease]; + + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(object); +// NSLog(@"Deleting property %@", propertyName); + + if (![privateObject.type isEqualToString:@"@"]) return false; + + id callee = [privateObject object]; + if (![callee respondsToSelector:@selector(setJSValue:forJSName:)]) return false; + JSValueRefAndContextRef name = { JSValueMakeNull(ctx), NULL } ; + name.value = JSValueMakeString(ctx, propertyNameJS); + return [callee deleteJSValueForJSName:name]; +} + + +// +// getPropertyNames +// enumerate dictionary keys +// +static void jsCocoaObject_getPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) +{ + // Autocall : ensure 'instance' has been called and we've got our new instance + [[JSCocoaController sharedController] ensureJSValueIsObjectAfterInstanceAutocall:object]; + + + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(object); + + // If we have a dictionary, add keys from allKeys + if ([privateObject.type isEqualToString:@"@"] && [privateObject.object isKindOfClass:[NSDictionary class]]) + { + id dictionary = privateObject.object; + id keys = [dictionary allKeys]; + + for (id key in keys) + { + JSStringRef jsString = JSStringCreateWithUTF8CString([key UTF8String]); + JSPropertyNameAccumulatorAddName(propertyNames, jsString); + JSStringRelease(jsString); + } + } +} + + + + +// +// callAsFunction +// enumerate dictionary keys +// +static JSValueRef _jsCocoaObject_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, JSValueRef arguments[], JSValueRef* exception, NSString* superSelector, Class superSelectorClass); + +// +// This first method handles Super : it retrieves the correct method name +// +static JSValueRef jsCocoaObject_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + JSCocoaPrivateObject* privateObject = JSObjectGetPrivate(function); + JSCocoaPrivateObject* thisPrivateObject = JSObjectGetPrivate(thisObject); + JSValueRef* arguments2 = NULL; + id superSelector = NULL; + id superSelectorClass = NULL; + + // Zero arg autocall + if ([privateObject isAutoCall]) + { + // Non kJSTypeObject return values, converted to a native JS type (number), boxed for auto call + if ([privateObject.type isEqualToString:@"jsValueRef"]) + { + // Returning NULL will crash, return jsNULL + if (![privateObject jsValueRef]) return JSValueMakeNull(ctx); + return [privateObject jsValueRef]; + } + // Boxed objects + return function; + } + + // Javascript custom methods + if ([privateObject.methodName isEqualToString:@"toString"] || [privateObject.methodName isEqualToString:@"valueOf"]) + { + // Custom handling for NSNumber + if ([privateObject.methodName isEqualToString:@"valueOf"] && [thisPrivateObject.object isKindOfClass:[NSNumber class]]) + { + return JSValueMakeNumber(ctx, [thisPrivateObject.object doubleValue]); + } + // Convert everything else to string + NSString* toString = [NSString stringWithFormat:@"%@", [thisPrivateObject.type isEqualToString:@"@"] ? [[thisPrivateObject object] description] : @"JSCocoaPrivateObject"]; +// JSStringRef jsToString = JSStringCreateWithUTF8CString([toString UTF8String]); + JSStringRef jsToString = JSStringCreateWithCFString((CFStringRef)toString); + JSValueRef jsValueToString = JSValueMakeString(ctx, jsToString); + JSStringRelease(jsToString); + return jsValueToString; + } + + // Super handling : get method name and move js arguments to C array + if ([privateObject.methodName isEqualToString:@"Super"]) + { + if (argumentCount != 1) return throwException(ctx, exception, @"Super wants one argument array"), NULL; + + // Get argument object + JSObjectRef argumentObject = JSValueToObject(ctx, arguments[0], NULL); + + // Get argument count + JSStringRef jsLengthName = JSStringCreateWithUTF8CString("length"); + JSValueRef jsLength = JSObjectGetProperty(ctx, argumentObject, jsLengthName, NULL); + JSStringRelease(jsLengthName); + if (JSValueGetType(ctx, jsLength) != kJSTypeNumber) return throwException(ctx, exception, @"Super has no arguments"), NULL; + + int i, superArgumentCount = (int)JSValueToNumber(ctx, jsLength, NULL); + if (superArgumentCount) + { + arguments2 = malloc(sizeof(JSValueRef)*superArgumentCount); + for (i=0; i 1) return throwException(ctx, exception, @"Invalid argument count in instance call : must be 0 or 1"), NULL; + return [callee instanceWithContext:ctx argumentCount:argumentCount arguments:arguments exception:exception]; + } + + // Check selector + if (![callee respondsToSelector:NSSelectorFromString(methodName)]) + { + // + // Split call + // set( { Value : '5', forKey : 'hello' } ) + // -> setValue:forKey: + // + if (![callee respondsToSelector:NSSelectorFromString(methodName)]) + { + id splitMethodName = privateObject.methodName; + BOOL isSplitCall = [[JSCocoaController sharedController] trySplitCall:&splitMethodName class:[callee class] argumentCount:&argumentCount arguments:&arguments ctx:ctx]; + if (isSplitCall) methodName = splitMethodName; + } + } + Method method = class_getInstanceMethod([callee class], NSSelectorFromString(methodName)); + if (!method) method = class_getClassMethod([callee class], NSSelectorFromString(methodName)); + + // Bail if we can't find a suitable method + if (!method) return throwException(ctx, exception, [NSString stringWithFormat:@"jsCocoaObject_callAsFunction : method %@ not found", methodName]), NULL; + + // Extract arguments + const char* typeEncoding = method_getTypeEncoding(method); +// NSLog(@"method %@ encoding=%s", methodName, typeEncoding); + argumentEncodings = [JSCocoaController parseObjCMethodEncoding:typeEncoding]; + // Function arguments is all arguments minus return value and [instance, selector] params to objc_send + callAddressArgumentCount = [argumentEncodings count]-3; + + // Get call address + callAddress = objc_msgSend; + + // + // From PyObjC : when to call objc_msgSendStret, for structure return + // Depending on structure size & architecture, structures are returned as function first argument (done transparently by ffi) or via registers + // + BOOL usingStret = isUsingStret(argumentEncodings); + if (usingStret) callAddress = objc_msgSend_stret; + } + + // + // C setup + // + if (!callingObjC) + { + if (!privateObject.xml) return throwException(ctx, exception, @"jsCocoaObject_callAsFunction : no xml in object = nothing to call") , NULL; + argumentEncodings = [JSCocoaController parseCFunctionEncoding:privateObject.xml functionName:&functionName]; + // Grab symbol + callAddress = dlsym(RTLD_DEFAULT, [functionName UTF8String]); + if (!callAddress) return throwException(ctx, exception, [NSString stringWithFormat:@"Function %@ not found", functionName]), NULL; + // Function arguments is all arguments minus return value + callAddressArgumentCount = [argumentEncodings count]-1; + } + + // Bail if argument count doesn't match + // TOFIX : check variadic call + if (callAddressArgumentCount != argumentCount) + return throwException(ctx, exception, [NSString stringWithFormat:@"Bad argument count in %@ : expected %d, got %d", functionName ? functionName : methodName, callAddressArgumentCount, argumentCount]), NULL; + + + // + // ffi data + // + ffi_cif cif; + ffi_type** args = NULL; + void** values = NULL; + char* selector; + // super call + struct objc_super _super; + void* superPointer; + + // Total number of arguments to ffi_call + int effectiveArgumentCount = callAddressArgumentCount + (callingObjC ? 2 : 0); + if (effectiveArgumentCount > 0) + { + args = malloc(sizeof(ffi_type*)*effectiveArgumentCount); + values = malloc(sizeof(void*)*effectiveArgumentCount); + + // If calling ObjC, setup instance and selector + int i, idx = 0; + if (callingObjC) + { + selector = (char*)NSSelectorFromString(methodName); + args[0] = &ffi_type_pointer; + args[1] = &ffi_type_pointer; + values[0] = (void*)&callee; + values[1] = (void*)&selector; + idx = 2; + + // Super handling + if (superSelector) + { + if (superSelectorClass == nil) return throwException(ctx, exception, [NSString stringWithFormat:@"Null superclass in %@", callee]), NULL; + callAddress = objc_msgSendSuper; + if (usingStret) callAddress = objc_msgSendSuper_stret; + _super.receiver = callee; + _super.class = [callee superclass]; + _super.class = superSelectorClass; + superPointer = &_super; + values[0] = &superPointer; +// NSLog(@"superClass=%@ (old=%@) (%@) function=%x", superSelectorClass, [callee superclass], [callee class], function); + } + } + + // Setup arguments, unboxing or converting data + for (i=0; i 0) + { + free(args); + free(values); + } + if (prep_status != FFI_OK) return throwException(ctx, exception, @"ffi_prep_cif failed"), NULL; + + // Return now if our function returns void + // Return null as a JSValueRef to avoid crashing + if ([returnValue ffi_type] == &ffi_type_void) return JSValueMakeNull(ctx); + + // Else, convert return value + JSValueRef jsReturnValue = NULL; + BOOL converted = [returnValue toJSValueRef:&jsReturnValue inContext:ctx]; + if (!converted) return throwException(ctx, exception, [NSString stringWithFormat:@"Return value not converted in %@", methodName?methodName:functionName]), NULL; + + return jsReturnValue; +} + + + +static JSObjectRef jsCocoaObject_callAsConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + NSLog(@"Call as constructor"); + return NULL; +} + +static JSValueRef jsCocoaObject_convertToType(JSContextRef ctx, JSObjectRef object, JSType type, JSValueRef* exception) +{ + // Only invoked when converting to strings and numbers. + // Would have been useful to be called on BOOLs too, to avoid false positives of ('varname' in object) when varname may start a split call. + + // Used on string conversions, eg jsHash[objcNSString] to convert objcNSString to a js string + return valueOfCallback(ctx, NULL, object, 0, NULL, NULL); +// return NULL; +} + + + + + +#pragma mark Helpers + +id NSStringFromJSValue(JSValueRef value, JSContextRef ctx) +{ + JSStringRef resultStringJS = JSValueToStringCopy(ctx, value, NULL); + NSString* resultString = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, resultStringJS); + JSStringRelease(resultStringJS); + return [resultString autorelease]; +} + +static void throwException(JSContextRef ctx, JSValueRef* exception, NSString* reason) +{ + NSLog(@"JSCocoa exception : %@", reason); + if ([[JSCocoaController sharedController] isSpeaking]) system([[NSString stringWithFormat:@"say \"%@\" &", reason] UTF8String]); + *exception = JSObjectMake(ctx, exceptionClass, NULL); +} + +void* malloc_autorelease(size_t size) +{ + void* p = malloc(size); + [NSData dataWithBytesNoCopy:p length:size freeWhenDone:YES]; + return p; +} diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.h b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.h new file mode 100644 index 0000000..cbe2493 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.h @@ -0,0 +1,76 @@ +// +// JSCocoaFFIArgument.h +// JSCocoa +// +// Created by Patrick Geiller on 14/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import +#define MACOSX +#include + + +@interface JSCocoaFFIArgument : NSObject { + char typeEncoding; + NSString* structureTypeEncoding; + + void* ptr; + + BOOL isReturnValue; + ffi_type structureType; + + id customData; + BOOL ownsStorage; +} + +- (void)setTypeEncoding:(char)encoding; +- (void)setTypeEncoding:(char)encoding withCustomStorage:(void*)storagePtr; +- (void)setStructureTypeEncoding:(NSString*)encoding; +- (void)setStructureTypeEncoding:(NSString*)encoding withCustomStorage:(void*)storagePtr; + ++ (int)sizeOfTypeEncoding:(char)encoding; ++ (int)alignmentOfTypeEncoding:(char)encoding; + ++ (ffi_type*)ffi_typeForTypeEncoding:(char)encoding; + ++ (int)sizeOfStructure:(NSString*)encoding; + + ++ (NSArray*)typeEncodingsFromStructureTypeEncoding:(NSString*)structureTypeEncoding; ++ (NSArray*)typeEncodingsFromStructureTypeEncoding:(NSString*)structureTypeEncoding parsedCount:(int*)count; + + ++ (NSString*)structureNameFromStructureTypeEncoding:(NSString*)structureTypeEncoding; ++ (NSString*)structureFullTypeEncodingFromStructureTypeEncoding:(NSString*)structureTypeEncoding; + ++ (BOOL)fromJSValueRef:(JSValueRef)value inContext:(JSContextRef)ctx withTypeEncoding:(char)typeEncoding withStructureTypeEncoding:(NSString*)structureTypeEncoding fromStorage:(void*)ptr; ++ (BOOL)toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx withTypeEncoding:(char)typeEncoding withStructureTypeEncoding:(NSString*)structureTypeEncoding fromStorage:(void*)ptr; + ++ (int)structureToJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx fromCString:(char*)c fromStorage:(void**)storage; ++ (int)structureFromJSObjectRef:(JSObjectRef)value inContext:(JSContextRef)ctx inParentJSValueRef:(JSValueRef)parentValue fromCString:(char*)c fromStorage:(void**)ptr; + ++ (void)alignPtr:(void**)ptr accordingToEncoding:(char)encoding; ++ (void)advancePtr:(void**)ptr accordingToEncoding:(char)encoding; + + +- (void*)allocateStorage; +- (void**)storage; +- (char)typeEncoding; +- (NSString*)structureTypeEncoding; + +- (void)setIsReturnValue:(BOOL)v; +//- (void)setCustomData:(id)data; + +- (BOOL)fromJSValueRef:(JSValueRef)value inContext:(JSContextRef)ctx; +- (BOOL)toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx; + + ++ (BOOL)boxObject:(id)o toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx; ++ (BOOL)unboxJSValueRef:(JSValueRef)value toObject:(id*)o inContext:(JSContextRef)ctx; + + +- (ffi_type*)ffi_type; + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.m b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.m new file mode 100644 index 0000000..c13edd8 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIArgument.m @@ -0,0 +1,952 @@ +// +// JSCocoaFFIArgument.m +// JSCocoa +// +// Created by Patrick Geiller on 14/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "JSCocoaFFIArgument.h" +#import "JSCocoaController.h" +#import "JSCocoaPrivateObject.h" +#import + +@implementation JSCocoaFFIArgument + + + +- (id)init +{ + id o = [super init]; + + ptr = NULL; + typeEncoding = 0; + isReturnValue = NO; + ownsStorage = YES; + + structureTypeEncoding = nil; + structureType.elements = NULL; + + // Used to store string data while converting JSStrings to char* + customData = nil; + + return o; +} + +- (void)cleanUp +{ + if (ptr && ownsStorage) free(ptr); + if (customData) [customData release]; + + if (structureType.elements) free(structureType.elements); + ptr = NULL; +} + + +- (void)dealloc +{ + if (structureTypeEncoding) [structureTypeEncoding release]; + [self cleanUp]; + [super dealloc]; +} + + + +#pragma mark Getters / Setters + +// +// Needed because libffi needs at least sizeof(long) as return value storage +// +- (void)setIsReturnValue:(BOOL)v +{ + isReturnValue = v; +} + +- (char)typeEncoding +{ + return typeEncoding; +} + +- (void)setTypeEncoding:(char)encoding +{ + if ([JSCocoaFFIArgument sizeOfTypeEncoding:encoding] == -1) { NSLog(@"Bad type encoding %c", encoding); return; }; + + typeEncoding = encoding; + [self allocateStorage]; +} + +- (void)setTypeEncoding:(char)encoding withCustomStorage:(void*)storagePtr +{ + typeEncoding = encoding; + ownsStorage = NO; + ptr = storagePtr; +} + +- (NSString*)structureTypeEncoding +{ + return structureTypeEncoding; +} + +- (void)setStructureTypeEncoding:(NSString*)encoding +{ + [self setStructureTypeEncoding:encoding withCustomStorage:NULL]; +} + +- (void)setStructureTypeEncoding:(NSString*)encoding withCustomStorage:(void*)storagePtr +{ + typeEncoding = '{'; + structureTypeEncoding = [[NSString alloc] initWithString:encoding]; + if (storagePtr) + { + ownsStorage = NO; + ptr = storagePtr; + } + else [self allocateStorage]; + + id types = [JSCocoaFFIArgument typeEncodingsFromStructureTypeEncoding:encoding]; + int elementCount = [types count]; + + // + // Build FFI type + // + structureType.size = 0; + structureType.alignment = 0; + structureType.type = FFI_TYPE_STRUCT; + structureType.elements = malloc(sizeof(ffi_type*)*(elementCount+1)); // +1 is trailing NULL + + int i = 0; + for (id type in types) + { + char encoding = *(char*)[type UTF8String]; + structureType.elements[i++] = [JSCocoaFFIArgument ffi_typeForTypeEncoding:encoding]; + } + structureType.elements[elementCount] = NULL; +} + + + + +- (ffi_type*)ffi_type +{ + if (!typeEncoding) return NULL; + + if (typeEncoding == '{') return &structureType; + + return [JSCocoaFFIArgument ffi_typeForTypeEncoding:typeEncoding]; +} + + +#pragma mark Storage + +- (void*)allocateStorage +{ + if (!typeEncoding) return NULL; + + [self cleanUp]; + // Special case for structs + if (typeEncoding == '{') + { +// NSLog(@"allocateStorage: Allocating struct"); + // Some front padding for alignment and tail padding for structure + // (http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/IA32.html) + // Structures are tail-padded to 32-bit multiples. + + // +16 for alignment + // +4 for tail padding + ptr = malloc([JSCocoaFFIArgument sizeOfStructure:structureTypeEncoding] + 16 + 4); + return ptr; + } + + int size = [JSCocoaFFIArgument sizeOfTypeEncoding:typeEncoding]; + + // Bail if we can't handle our type + if (size == -1) return NULL; + if (size >= 0) + { + int minimalReturnSize = sizeof(long); + if (isReturnValue && size < minimalReturnSize) size = minimalReturnSize; + ptr = malloc(size); + memset(ptr, size, 1); + } +// NSLog(@"Allocated size=%d %x for object %@", size, ptr, self); + return ptr; +} + +- (void**)storage +{ + if (typeEncoding == '{') + { + int alignOnSize = 16; + + int address = (int)ptr; + if ((address % alignOnSize) != 0) + address = (address+alignOnSize) & ~(alignOnSize-1); + return (void**)address; + } + + return ptr; +} + + ++ (void)alignPtr:(void**)ptr accordingToEncoding:(char)encoding +{ + int alignOnSize = [JSCocoaFFIArgument alignmentOfTypeEncoding:encoding]; + + int address = (int)*ptr; + if ((address % alignOnSize) != 0) + address = (address+alignOnSize) & ~(alignOnSize-1); +// NSLog(@"alignOf(%c)=%d", encoding, alignOnSize); + + *ptr = (void*)address; +} + ++ (void)advancePtr:(void**)ptr accordingToEncoding:(char)encoding +{ + int address = (int)*ptr; + address += [JSCocoaFFIArgument sizeOfTypeEncoding:encoding]; + *ptr = (void*)address; +} + + +#pragma mark Conversion + +// +// Convert from js value +// +- (BOOL)fromJSValueRef:(JSValueRef)value inContext:(JSContextRef)ctx +{ + BOOL r = [JSCocoaFFIArgument fromJSValueRef:value inContext:ctx withTypeEncoding:typeEncoding withStructureTypeEncoding:structureTypeEncoding fromStorage:ptr]; + if (!r) + { + NSLog(@"fromJSValueRef FAILED, jsType=%d encoding=%c structureEncoding=%@", JSValueGetType(ctx, value), typeEncoding, structureTypeEncoding); + } + return r; +} + ++ (BOOL)fromJSValueRef:(JSValueRef)value inContext:(JSContextRef)ctx withTypeEncoding:(char)typeEncoding withStructureTypeEncoding:(NSString*)structureTypeEncoding fromStorage:(void*)ptr; +{ + if (!typeEncoding) return NO; + +// JSType type = JSValueGetType(ctx, value); +// NSLog(@"JSType=%d encoding=%c self=%x", type, typeEncoding, self); + + switch (typeEncoding) + { + case _C_ID: + case _C_CLASS: + { + return [self unboxJSValueRef:value toObject:ptr inContext:ctx]; + } + + case _C_CHR: + case _C_UCHR: + case _C_SHT: + case _C_USHT: + case _C_INT: + case _C_UINT: + case _C_LNG: + case _C_ULNG: + case _C_LNG_LNG: + case _C_ULNG_LNG: + case _C_FLT: + case _C_DBL: + case _C_CHARPTR: + { + double number = JSValueToNumber(ctx, value, NULL); +// NSLog(@"type=%d n=%f", type, number); + + switch (typeEncoding) + { + case _C_CHR: *(char*)ptr = (char)number; break; + case _C_UCHR: *(unsigned char*)ptr = (unsigned char)number; break; + case _C_SHT: *(short*)ptr = (short)number; break; + case _C_USHT: *(unsigned short*)ptr = (unsigned short)number; break; + case _C_INT: + { +#ifdef __BIG_ENDIAN__ + // Two step conversion : to unsigned int then to int. One step conversion fails on PPC. + unsigned int uint = (unsigned int)number; + *(signed int*)ptr = (signed int)uint; + break; +#endif +#ifdef __LITTLE_ENDIAN__ + *(int*)ptr = (int)number; + break; +#endif + } + case _C_UINT: *(unsigned int*)ptr = (unsigned int)number; break; + case _C_LNG: *(long*)ptr = (long)number; break; + case _C_ULNG: *(unsigned long*)ptr = (unsigned long)number; break; + case _C_LNG_LNG: *(long long*)ptr = (long long)number; break; + case _C_ULNG_LNG: *(unsigned long long*)ptr = (unsigned long long)number; break; + case _C_FLT: *(float*)ptr = (float)number; break; + case _C_DBL: *(double*)ptr = (double)number; break; + case _C_CHARPTR: + { + id str = NSStringFromJSValue(value, ctx); + *(char**)ptr = (char*)[str UTF8String]; + break; + } + } + return YES; + } + case '{': + { + // Special case for getting raw JSValues to ObjC + BOOL isJSStruct = NSOrderedSame == [structureTypeEncoding compare:@"{JSValueRefAndContextRef" options:0 range:NSMakeRange(0, sizeof("{JSValueRefAndContextRef")-1)]; + if (isJSStruct) + { + // Beware ! This context is not the global context and will be valid only for that call. + // Other uses (closures) use the global context via JSCocoaController. + JSValueRefAndContextRef* jsStruct = (JSValueRefAndContextRef*)ptr; + jsStruct->value = value; + jsStruct->ctx = ctx; + return YES; + } + + if (!JSValueIsObject(ctx, value)) return NO; + JSObjectRef object = JSValueToObject(ctx, value, NULL); + + void* p = ptr; + id type = [JSCocoaFFIArgument structureFullTypeEncodingFromStructureTypeEncoding:structureTypeEncoding]; + int numParsed = [JSCocoaFFIArgument structureFromJSObjectRef:object inContext:ctx inParentJSValueRef:NULL fromCString:(char*)[type UTF8String] fromStorage:&p]; + return numParsed; + } + case _C_SEL: + { + id str = NSStringFromJSValue(value, ctx); + *(SEL*)ptr = NSSelectorFromString(str); + return YES; + } + case _C_BOOL: + { + bool b = JSValueToBoolean(ctx, value); + *(BOOL*)ptr = b; + return YES; + } + + case _C_PTR: + { + return [self unboxJSValueRef:value toObject:ptr inContext:ctx]; + } + + } + return NO; +} + + +// +// Convert to js value +// +- (BOOL)toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx +{ + void* p = ptr; +#ifdef __BIG_ENDIAN__ + long v; + // Return value was padded, need to do some shifting on PPC + if (isReturnValue) + { + int size = [JSCocoaFFIArgument sizeOfTypeEncoding:typeEncoding]; + int paddedSize = sizeof(long); + + if (size > 0 && size < paddedSize && paddedSize == 4) + { + v = *(long*)ptr; + v = CFSwapInt32(v); + p = &v; + } + } +#endif + BOOL r = [JSCocoaFFIArgument toJSValueRef:value inContext:ctx withTypeEncoding:typeEncoding withStructureTypeEncoding:structureTypeEncoding fromStorage:p]; + if (!r) NSLog(@"toJSValueRef FAILED"); + return r; +} + + ++ (BOOL)toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx withTypeEncoding:(char)typeEncoding withStructureTypeEncoding:(NSString*)structureTypeEncoding fromStorage:(void*)ptr +{ + if (!typeEncoding) return NO; + +// NSLog(@"toJSValueRef: %c", typeEncoding); + switch (typeEncoding) + { + case _C_ID: + case _C_CLASS: + { + id objcObject = *(id*)ptr; + return [self boxObject:(id)objcObject toJSValueRef:value inContext:ctx]; + + } + + case _C_VOID: + return YES; + + case _C_CHR: + case _C_UCHR: + case _C_SHT: + case _C_USHT: + case _C_INT: + case _C_UINT: + case _C_LNG: + case _C_ULNG: + case _C_LNG_LNG: + case _C_ULNG_LNG: + case _C_FLT: + case _C_DBL: + { + double number; + switch (typeEncoding) + { + case _C_CHR: number = *(char*)ptr; break; + case _C_UCHR: number = *(unsigned char*)ptr; break; + case _C_SHT: number = *(short*)ptr; break; + case _C_USHT: number = *(unsigned short*)ptr; break; + case _C_INT: number = *(int*)ptr; break; + case _C_UINT: number = *(unsigned int*)ptr; break; + case _C_LNG: number = *(long*)ptr; break; + case _C_ULNG: number = *(unsigned long*)ptr; break; + case _C_LNG_LNG: number = *(long long*)ptr; break; + case _C_ULNG_LNG: number = *(unsigned long long*)ptr; break; + case _C_FLT: number = *(float*)ptr; break; + case _C_DBL: number = *(double*)ptr; break; + } + *value = JSValueMakeNumber(ctx, number); + return YES; + } + + + case '{': + { + // Special case for getting raw JSValues from ObjC to JS + BOOL isJSStruct = NSOrderedSame == [structureTypeEncoding compare:@"{JSValueRefAndContextRef" options:0 range:NSMakeRange(0, sizeof("{JSValueRefAndContextRef")-1)]; + if (isJSStruct) + { + JSValueRefAndContextRef* jsStruct = (JSValueRefAndContextRef*)ptr; + *value = jsStruct->value; + return YES; + } + + void* p = ptr; + id type = [JSCocoaFFIArgument structureFullTypeEncodingFromStructureTypeEncoding:structureTypeEncoding]; + // Bail if structure not found + if (!type) return 0; + + JSObjectRef jsObject = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + JSCocoaPrivateObject* private = JSObjectGetPrivate(jsObject); + private.type = @"struct"; + int numParsed = [JSCocoaFFIArgument structureToJSValueRef:value inContext:ctx fromCString:(char*)[type UTF8String] fromStorage:&p]; + return numParsed; + } + + case _C_SEL: + { + SEL sel = *(SEL*)ptr; + id str = NSStringFromSelector(sel); +// JSStringRef jsName = JSStringCreateWithUTF8CString([str UTF8String]); + JSStringRef jsName = JSStringCreateWithCFString((CFStringRef)str); + *value = JSValueMakeString(ctx, jsName); + JSStringRelease(jsName); + return YES; + } + case _C_BOOL: + { + BOOL b = *(BOOL*)ptr; + *value = JSValueMakeBoolean(ctx, b); + return YES; + } + case _C_CHARPTR: + { +// JSStringRef jsName = JSStringCreateWithUTF8CString(*(char**)ptr); + NSString* name = [NSString stringWithUTF8String:*(char**)ptr]; + JSStringRef jsName = JSStringCreateWithCFString((CFStringRef)name); + *value = JSValueMakeString(ctx, jsName); + JSStringRelease(jsName); + return YES; + } + + case _C_PTR: + { + JSObjectRef o = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + JSCocoaPrivateObject* private = JSObjectGetPrivate(o); + private.type = @"rawPointer"; + [private setRawPointer:*(void**)ptr]; + *value = o; + return YES; + } + } + + return NO; +} + +/* + + *value MUST be NULL to be receive allocated JSValue + +*/ ++ (int)structureToJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx fromCString:(char*)c fromStorage:(void**)ptr +{ + // Build new structure object + JSObjectRef jsObject = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + JSCocoaPrivateObject* private = JSObjectGetPrivate(jsObject); + private.type = @"struct"; + private.structureName = [JSCocoaFFIArgument structureNameFromStructureTypeEncoding:[NSString stringWithUTF8String:c]]; + if (!*value) *value = jsObject; + + char* c0 = c; + // Skip '{' + c += 1; + // Skip '_' if it's there + if (*c == '_') c++; + // Skip structureName, '=' + c += [private.structureName length]+1; + + int openedBracesCount = 1; + int closedBracesCount = 0; + for (; *c && closedBracesCount != openedBracesCount; c++) + { + if (*c == '{') openedBracesCount++; + if (*c == '}') closedBracesCount++; + // Parse name then type + if (*c == '"') + { + char* c2 = c+1; + while (c2 && *c2 != '"') c2++; + id propertyName = [[[NSString alloc] initWithBytes:c+1 length:(c2-c-1) encoding:NSASCIIStringEncoding] autorelease]; + c = c2; + + // Skip '"' + c++; + char encoding = *c; + + JSValueRef valueJS = NULL; + if (encoding == '{') + { + int numParsed = [self structureToJSValueRef:&valueJS inContext:ctx fromCString:c fromStorage:ptr]; + c += numParsed; + } + else + { + // Align + [JSCocoaFFIArgument alignPtr:ptr accordingToEncoding:encoding]; + // Get value + [JSCocoaFFIArgument toJSValueRef:&valueJS inContext:ctx withTypeEncoding:encoding withStructureTypeEncoding:nil fromStorage:*ptr]; + // Advance ptr + [JSCocoaFFIArgument advancePtr:ptr accordingToEncoding:encoding]; + } + JSStringRef propertyNameJS = JSStringCreateWithCFString((CFStringRef)propertyName); + JSObjectSetProperty(ctx, jsObject, propertyNameJS, valueJS, 0, NULL); + JSStringRelease(propertyNameJS); + } + } + return c-c0-1; +} + ++ (int)structureFromJSObjectRef:(JSObjectRef)object inContext:(JSContextRef)ctx inParentJSValueRef:(JSValueRef)parentValue fromCString:(char*)c fromStorage:(void**)ptr +{ + id structureName = [JSCocoaFFIArgument structureNameFromStructureTypeEncoding:[NSString stringWithUTF8String:c]]; + char* c0 = c; + // Skip '{' + c += 1; + // Skip '_' if it's there + if (*c == '_') c++; + // Skip structureName, '=' + c += [structureName length]+1; + +// NSLog(@"%@", structureName); + int openedBracesCount = 1; + int closedBracesCount = 0; + for (; *c && closedBracesCount != openedBracesCount; c++) + { + if (*c == '{') openedBracesCount++; + if (*c == '}') closedBracesCount++; + // Parse name then type + if (*c == '"') + { + char* c2 = c+1; + while (c2 && *c2 != '"') c2++; + id propertyName = [[[NSString alloc] initWithBytes:c+1 length:(c2-c-1) encoding:NSASCIIStringEncoding] autorelease]; + c = c2; + + // Skip '"' + c++; + char encoding = *c; + + JSStringRef propertyNameJS = JSStringCreateWithUTF8CString([propertyName UTF8String]); + JSValueRef valueJS = JSObjectGetProperty(ctx, object, propertyNameJS, NULL); +// JSObjectRef objectProperty2 = JSValueToObject(ctx, valueJS, NULL); + +// NSLog(@"%c %@ %x %x", encoding, propertyName, valueJS, objectProperty2); + if (encoding == '{') + { + if (JSValueIsObject(ctx, valueJS)) + { + JSObjectRef objectProperty = JSValueToObject(ctx, valueJS, NULL); + int numParsed = [self structureFromJSObjectRef:objectProperty inContext:ctx inParentJSValueRef:NULL fromCString:c fromStorage:ptr]; + c += numParsed; + } + else return 0; + } + else + { + // Align + [JSCocoaFFIArgument alignPtr:ptr accordingToEncoding:encoding]; + // Get value + [JSCocoaFFIArgument fromJSValueRef:valueJS inContext:ctx withTypeEncoding:encoding withStructureTypeEncoding:nil fromStorage:*ptr]; + // Advance ptr + [JSCocoaFFIArgument advancePtr:ptr accordingToEncoding:encoding]; + } + + JSStringRelease(propertyNameJS); + } + } + return c-c0-1; +} + + + +#pragma mark Encoding size, alignment, FFI + ++ (int)sizeOfTypeEncoding:(char)encoding +{ + switch (encoding) + { + case _C_ID: return sizeof(id); + case _C_CLASS: return sizeof(Class); + case _C_SEL: return sizeof(SEL); + case _C_CHR: return sizeof(char); + case _C_UCHR: return sizeof(unsigned char); + case _C_SHT: return sizeof(short); + case _C_USHT: return sizeof(unsigned short); + case _C_INT: return sizeof(int); + case _C_UINT: return sizeof(unsigned int); + case _C_LNG: return sizeof(long); + case _C_ULNG: return sizeof(unsigned long); + case _C_LNG_LNG: return sizeof(long long); + case _C_ULNG_LNG:return sizeof(unsigned long long); + case _C_FLT: return sizeof(float); + case _C_DBL: return sizeof(double); + case _C_BOOL: return sizeof(BOOL); + case _C_VOID: return sizeof(void); + case _C_PTR: return sizeof(void*); + case _C_CHARPTR: return sizeof(char*); + } + return -1; +} + +/* + + __alignOf__ returns 8 for double, but its struct align is 4 + + use dummy structures to get struct alignment, each having a byte as first element +*/ +typedef struct { char a; id b; } struct_C_ID; +typedef struct { char a; char b; } struct_C_CHR; +typedef struct { char a; short b; } struct_C_SHT; +typedef struct { char a; int b; } struct_C_INT; +typedef struct { char a; long b; } struct_C_LNG; +typedef struct { char a; long long b; } struct_C_LNG_LNG; +typedef struct { char a; float b; } struct_C_FLT; +typedef struct { char a; double b; } struct_C_DBL; +typedef struct { char a; BOOL b; } struct_C_BOOL; + ++ (int)alignmentOfTypeEncoding:(char)encoding +{ + switch (encoding) + { + case _C_ID: return offsetof(struct_C_ID, b); + case _C_CLASS: return offsetof(struct_C_ID, b); + case _C_SEL: return offsetof(struct_C_ID, b); + case _C_CHR: return offsetof(struct_C_CHR, b); + case _C_UCHR: return offsetof(struct_C_CHR, b); + case _C_SHT: return offsetof(struct_C_SHT, b); + case _C_USHT: return offsetof(struct_C_SHT, b); + case _C_INT: return offsetof(struct_C_INT, b); + case _C_UINT: return offsetof(struct_C_INT, b); + case _C_LNG: return offsetof(struct_C_LNG, b); + case _C_ULNG: return offsetof(struct_C_LNG, b); + case _C_LNG_LNG: return offsetof(struct_C_LNG_LNG, b); + case _C_ULNG_LNG:return offsetof(struct_C_LNG_LNG, b); + case _C_FLT: return offsetof(struct_C_FLT, b); + case _C_DBL: return offsetof(struct_C_DBL, b); + case _C_BOOL: return offsetof(struct_C_BOOL, b); + case _C_PTR: return offsetof(struct_C_ID, b); + case _C_CHARPTR: return offsetof(struct_C_ID, b); + } + return -1; +} + + ++ (ffi_type*)ffi_typeForTypeEncoding:(char)encoding +{ + switch (encoding) + { + case _C_ID: + case _C_CLASS: + case _C_SEL: + case _C_PTR: + case _C_CHARPTR: return &ffi_type_pointer; + + case _C_CHR: return &ffi_type_sint8; + case _C_UCHR: return &ffi_type_uint8; + case _C_SHT: return &ffi_type_sint16; + case _C_USHT: return &ffi_type_uint16; + case _C_INT: + case _C_LNG: return &ffi_type_sint32; + case _C_UINT: + case _C_ULNG: return &ffi_type_uint32; + case _C_LNG_LNG: return &ffi_type_sint64; + case _C_ULNG_LNG: return &ffi_type_uint64; + case _C_FLT: return &ffi_type_float; + case _C_DBL: return &ffi_type_double; + case _C_BOOL: return &ffi_type_sint8; + case _C_VOID: return &ffi_type_void; + } + return NULL; +} + +/* + From + {_NSRect={_NSPoint=ff}{_NSSize=ff}} + + Return + {_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}} + +*/ + +#pragma mark Structure encoding, size + ++ (NSString*)structureNameFromStructureTypeEncoding:(NSString*)encoding +{ + // Extract structure name + // skip '{' + char* c = (char*)[encoding UTF8String]+1; + // skip '_' if it's there + if (*c == '_') c++; + char* c2 = c; + while (*c2 && *c2 != '=') c2++; + return [[[NSString alloc] initWithBytes:c length:(c2-c) encoding:NSASCIIStringEncoding] autorelease]; +} + ++ (NSMutableArray*)encodingsFromStructureTypeEncoding:(NSString*)encoding +{ + return nil; +} + ++ (NSString*)structureFullTypeEncodingFromStructureTypeEncoding:(NSString*)encoding +{ + id structureName = [JSCocoaFFIArgument structureNameFromStructureTypeEncoding:encoding]; + + // Fetch structure type encoding from BridgeSupport +// id xml = [[BridgeSupportController sharedController] query:structureName withType:@"struct"]; + id xml = [[BridgeSupportController sharedController] queryName:structureName type:@"struct"]; + + if (xml == nil) + { + NSLog(@"No structure encoding found for %@ (encoding=%@)", structureName, encoding); + return nil; + } + + id xmlDocument = [[NSXMLDocument alloc] initWithXMLString:xml options:0 error:nil]; + if (!xmlDocument) return NO; + id rootElement = [xmlDocument rootElement]; + id type = [[rootElement attributeForName:@"type"] stringValue]; + [xmlDocument release]; + return type; +} + + ++ (NSArray*)typeEncodingsFromStructureTypeEncoding:(NSString*)structureTypeEncoding +{ + return [self typeEncodingsFromStructureTypeEncoding:structureTypeEncoding parsedCount:nil]; +} + + ++ (NSArray*)typeEncodingsFromStructureTypeEncoding:(NSString*)structureTypeEncoding parsedCount:(int*)count +{ + id types = [[[NSMutableArray alloc] init] autorelease]; + char* c = (char*)[structureTypeEncoding UTF8String]; + char* c0 = c; + int openedBracesCount = 0; + int closedBracesCount = 0; + for (;*c; c++) + { + if (*c == '{') + { + openedBracesCount++; + while (*c && *c != '=') c++; + if (!*c) continue; + } + if (*c == '}') + { + closedBracesCount++; + continue; + } + if (*c == '=') continue; + + if (c0 != c && closedBracesCount == openedBracesCount) break; + + [types addObject:[NSString stringWithFormat:@"%c", *c]]; + + // Special case for pointers + if (*c == '^') + { + // Skip pointers to pointers (^^^) + while (*c && *c == '^') c++; + + // Skip type, special case for structure + if (*c == '{') + { + int openedBracesCount2 = 1; + int closedBracesCount2 = 0; + c++; + for (; *c && closedBracesCount2 != openedBracesCount2; c++) + { + if (*c == '{') openedBracesCount2++; + if (*c == '}') closedBracesCount2++; + } + c--; + } + else c++; + } + if (openedBracesCount == closedBracesCount) + { + break; + } + } + if (count) *count = c-c0; + if (closedBracesCount != openedBracesCount) return NSLog(@"Could not parse structure type encodings for %@", structureTypeEncoding), nil; + return types; +} + + ++ (int)sizeOfStructure:(NSString*)encoding +{ + id types = [self typeEncodingsFromStructureTypeEncoding:encoding]; + int computedSize = 0; + void** ptr = (void**)&computedSize; + for (id type in types) + { + char encoding = *(char*)[type UTF8String]; + // Align + [JSCocoaFFIArgument alignPtr:ptr accordingToEncoding:encoding]; + // Advance ptr + [JSCocoaFFIArgument advancePtr:ptr accordingToEncoding:encoding]; + } + return computedSize; +} + + +#pragma mark Object boxing / unboxing + +// +// Box +// ++ (BOOL)boxObject:(id)objcObject toJSValueRef:(JSValueRef*)value inContext:(JSContextRef)ctx +{ + // Return null if our pointer is null + if (!objcObject) + { + *value = JSValueMakeNull(ctx); + return YES; + } + + // Else, box the object + JSObjectRef jsObject = [JSCocoaController jsCocoaPrivateObjectInContext:ctx]; + JSCocoaPrivateObject* private = JSObjectGetPrivate(jsObject); + private.type = @"@"; + [private setObject:objcObject]; + *value = jsObject; + return YES; +} + +// +// Unbox +// ++ (BOOL)unboxJSValueRef:(JSValueRef)value toObject:(id*)o inContext:(JSContextRef)ctx +{ + /* + Boxing + + string -> NSString + null -> nil (no box) + number -> NSNumber + */ + + // null + if (!value || JSValueIsNull(ctx, value) || JSValueIsUndefined(ctx, value)) + { + *(id*)o = nil; + return YES; + } + + + // string + if (JSValueIsString(ctx, value)) + { + JSStringRef resultStringJS = JSValueToStringCopy(ctx, value, NULL); + NSString* resultString = (NSString*)JSStringCopyCFString(kCFAllocatorDefault, resultStringJS); + JSStringRelease(resultStringJS); + [resultString autorelease]; + *(id*)o = resultString; + return YES; + } + + + // number + if (JSValueIsNumber(ctx, value)) + { + double v = JSValueToNumber(ctx, value, NULL); + // Integer + if (fabs(round(v)-v) < 1e-6) + { + if (v < 0) + { + *(id*)o = [NSNumber numberWithInt:(int)v]; +// NSLog(@"int %d", (int)v); + } + else + { + *(id*)o = [NSNumber numberWithUnsignedInt:(unsigned int)v]; +// NSLog(@"UNSIGNED int %d", (unsigned int)v); + } + } + // Double + else + { + *(id*)o = [NSNumber numberWithDouble:v]; +// NSLog(@"double %f", v); + } + return YES; + } + + // number + if (JSValueIsBoolean(ctx, value)) + { + bool v = JSValueToBoolean(ctx, value); + if (v) *(id*)o = [NSNumber numberWithBool:YES]; + else *(id*)o = nil; + return YES; + } + + if (!JSValueIsObject(ctx, value)) + { + return NO; + } + [[JSCocoaController sharedController] ensureJSValueIsObjectAfterInstanceAutocall:value]; + + JSObjectRef jsObject = JSValueToObject(ctx, value, NULL); + JSCocoaPrivateObject* private = JSObjectGetPrivate(jsObject); + // Pure js hashes and arrays should be converted to NSArray and NSDictionary. ##Later. + if (!private) + { + return NO; + } + // ## Hmmm ? CGColorRef is returned as a pointer but CALayer.foregroundColor asks an objc object (@) + if ([private.type isEqualToString:@"rawPointer"]) *(id*)o = [private rawPointer]; + else *(id*)o = [private object]; + return YES; +} + + + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.h b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.h new file mode 100644 index 0000000..ef022e9 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.h @@ -0,0 +1,36 @@ +// +// JSCocoaFFIClosure.h +// JSCocoa +// +// Created by Patrick Geiller on 29/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import +#import "JSCocoaFFIArgument.h" +#define MACOSX +#import + + +@interface JSCocoaFFIClosure : NSObject { + + JSValueRef jsFunction; + JSContextRef ctx; + + ffi_cif cif; + ffi_closure closure; + ffi_type** argTypes; + + NSMutableArray* encodings; + + JSObjectRef jsThisObject; + + BOOL isObjC; +} + +- (void)setJSFunction:(JSValueRef)fn inContext:(JSContextRef)ctx argumentEncodings:(NSMutableArray*)argumentEncodings objC:(BOOL)objC; +- (void*)functionPointer; +- (void)calledByClosureWithArgs:(void**)args returnValue:(void*)returnValue; + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.m b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.m new file mode 100644 index 0000000..7b91dc7 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaFFIClosure.m @@ -0,0 +1,171 @@ +// +// JSCocoaFFIClosure.m +// JSCocoa +// +// Created by Patrick Geiller on 29/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "JSCocoaFFIClosure.h" +#import "JSCocoaController.h" + +@implementation JSCocoaFFIClosure + + +// +// Common closure function, calling back closure object +// +void closure_function(ffi_cif* cif, void* resp, void** args, void* userdata) +{ + [(id)userdata calledByClosureWithArgs:args returnValue:resp]; +} + +- (id)init +{ + id o = [super init]; + + argTypes = NULL; + encodings = NULL; + jsFunction = NULL; + + return o; +} + +- (void)dealloc +{ + if (encodings) [encodings release]; + if (argTypes) free(argTypes); + + if (jsFunction) + { + JSValueUnprotect(ctx, jsFunction); + [JSCocoaController downJSValueProtectCount]; + } +// NSLog(@"ClosureDealloc %x", self); + [super dealloc]; +} + +- (void*)functionPointer +{ + return &closure; +} + + + +// +// Bind a js function to closure. We'll jsValueProtect that function from GC. +// +- (void)setJSFunction:(JSValueRef)fn inContext:(JSContextRef)context argumentEncodings:(NSMutableArray*)argumentEncodings objC:(BOOL)objC +{ + if ([argumentEncodings count] == 0) return; + + encodings = argumentEncodings; + [encodings retain]; + jsFunction = fn; + ctx = context; + isObjC = objC; + + int i, argumentCount = [argumentEncodings count]-1; + argTypes = malloc(sizeof(ffi_type*)*argumentCount); + for (i=0; i 0 && size < paddedSize && paddedSize == 4) + { + v = *(long*)returnValue; + v = CFSwapInt32(v); + *(long*)returnValue = v; + } +#endif + } + + if (effectiveArgumentCount) free(args); + if (exception) NSLog(@"%@", [[JSCocoaController sharedController] formatJSException:exception]); +} + + + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.h b/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.h new file mode 100644 index 0000000..48d2813 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.h @@ -0,0 +1,62 @@ +// +// JSCocoaPrivateObject.h +// JSCocoa +// +// Created by Patrick Geiller on 09/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import +#import +#import +#import + +@interface JSCocoaPrivateObject : NSObject { + + NSString* type; + NSString* xml; + NSString* methodName; + NSString* structureName; + + NSString* declaredType; +// void* ptr; + void* rawPointer; + + id object; + + Method method; + + JSValueRef jsValue; + JSContextRef ctx; + + BOOL isAutoCall; + BOOL retainObject; +} + +@property (copy) NSString* type; +@property (copy) NSString* xml; +@property (copy) NSString* methodName; +@property (copy) NSString* structureName; +@property (copy) NSString* declaredType; +@property BOOL isAutoCall; + +//- (void)setPtr:(void*)ptrValue; +//- (void*)ptr; + +- (void)setObject:(id)o; +- (id)object; + +- (void)setMethod:(Method)m; +- (Method)method; + +- (void)setJSValueRef:(JSValueRef)v ctx:(JSContextRef)ctx; +- (JSValueRef)jsValueRef; + +- (void*)rawPointer; +- (void)setRawPointer:(void*)rp; + +- (void)setObjectNoRetain:(id)o; + + +@end diff --git a/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.m b/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.m new file mode 100644 index 0000000..aa1cff3 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoa/JSCocoaPrivateObject.m @@ -0,0 +1,118 @@ +// +// JSCocoaPrivateObject.m +// JSCocoa +// +// Created by Patrick Geiller on 09/07/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "JSCocoaPrivateObject.h" +#import "JSCocoaController.h" + +@implementation JSCocoaPrivateObject + +@synthesize type, xml, declaredType, methodName, structureName, isAutoCall; + + +- (id)init +{ + id r = [super init]; + + type = xml = declaredType = methodName = nil; + object = nil; + isAutoCall = NO; + jsValue = NULL; + retainObject = YES; + rawPointer = NULL; + + + [JSCocoaController upJSCocoaPrivateObjectCount]; + return r; +} + +- (void)dealloc +{ + [JSCocoaController downJSCocoaPrivateObjectCount]; +// if (object) NSLog(@"GO for release (%@) %x %d", [object class], object, [object retainCount]); + if (object && retainObject) + { +// NSLog(@"released !"); + [object release]; + } + if (jsValue) + { + JSValueUnprotect(ctx, jsValue); + [JSCocoaController downJSValueProtectCount]; + } + [super dealloc]; +} + +- (void)setObject:(id)o +{ + object = o; + + + if (object && [object retainCount] == -1) return; + [object retain]; +} + +- (id)object +{ + return object; +} + +- (void)setObjectNoRetain:(id)o +{ + object = o; + retainObject = NO; +} + + + +- (void)setMethod:(Method)m +{ + method = m; +} +- (Method)method +{ + return method; +} + +- (void)setJSValueRef:(JSValueRef)v ctx:(JSContextRef)c; +{ + // While autocalling we'll get a NULL value when boxing a void return type - just skip JSValueProtect + if (!v) + { +// NSLog(@"setJSValueRef: NULL value"); + jsValue = 0; + return; + } + jsValue = v; + ctx = c; + JSValueProtect(ctx, jsValue); + [JSCocoaController upJSValueProtectCount]; +} +- (JSValueRef)jsValueRef +{ + return jsValue; +} + + +- (void*)rawPointer +{ + return rawPointer; +} +- (void)setRawPointer:(void*)rp +{ + rawPointer = rp; +} + +@end + + + + + + + + diff --git a/JSCocoaCodaLoader/JSCocoaLoader.xcodeproj/project.pbxproj b/JSCocoaCodaLoader/JSCocoaLoader.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d82041d --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoaLoader.xcodeproj/project.pbxproj @@ -0,0 +1,320 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; + CC2FDC590EB68AA7005267D1 /* JSCocoaLoaderPlugIn.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC560EB68AA7005267D1 /* JSCocoaLoaderPlugIn.m */; }; + CC2FDC8E0EB68BE7005267D1 /* BridgeSupportController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC850EB68BE7005267D1 /* BridgeSupportController.m */; }; + CC2FDC8F0EB68BE7005267D1 /* JSCocoaController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC870EB68BE7005267D1 /* JSCocoaController.m */; }; + CC2FDC900EB68BE7005267D1 /* JSCocoaFFIArgument.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC890EB68BE7005267D1 /* JSCocoaFFIArgument.m */; }; + CC2FDC910EB68BE7005267D1 /* JSCocoaFFIClosure.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC8B0EB68BE7005267D1 /* JSCocoaFFIClosure.m */; }; + CC2FDC920EB68BE7005267D1 /* JSCocoaPrivateObject.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2FDC8D0EB68BE7005267D1 /* JSCocoaPrivateObject.m */; }; + CC2FDC9F0EB68C40005267D1 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC2FDC9E0EB68C40005267D1 /* JavaScriptCore.framework */; }; + CC2FDCB00EB68CF4005267D1 /* libffi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CC2FDCAF0EB68CF4005267D1 /* libffi.dylib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 8D5B49B6048680CD000E48DA /* JSCocoaLoader.codaplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JSCocoaLoader.codaplugin; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CC2FDC560EB68AA7005267D1 /* JSCocoaLoaderPlugIn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCocoaLoaderPlugIn.m; sourceTree = ""; }; + CC2FDC570EB68AA7005267D1 /* JSCocoaLoaderPlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaLoaderPlugIn.h; sourceTree = ""; }; + CC2FDC580EB68AA7005267D1 /* JSCocoaLoader_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaLoader_Prefix.pch; sourceTree = ""; }; + CC2FDC840EB68BE7005267D1 /* BridgeSupportController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BridgeSupportController.h; sourceTree = ""; }; + CC2FDC850EB68BE7005267D1 /* BridgeSupportController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BridgeSupportController.m; sourceTree = ""; }; + CC2FDC860EB68BE7005267D1 /* JSCocoaController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaController.h; sourceTree = ""; }; + CC2FDC870EB68BE7005267D1 /* JSCocoaController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCocoaController.m; sourceTree = ""; }; + CC2FDC880EB68BE7005267D1 /* JSCocoaFFIArgument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaFFIArgument.h; sourceTree = ""; }; + CC2FDC890EB68BE7005267D1 /* JSCocoaFFIArgument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCocoaFFIArgument.m; sourceTree = ""; }; + CC2FDC8A0EB68BE7005267D1 /* JSCocoaFFIClosure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaFFIClosure.h; sourceTree = ""; }; + CC2FDC8B0EB68BE7005267D1 /* JSCocoaFFIClosure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCocoaFFIClosure.m; sourceTree = ""; }; + CC2FDC8C0EB68BE7005267D1 /* JSCocoaPrivateObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCocoaPrivateObject.h; sourceTree = ""; }; + CC2FDC8D0EB68BE7005267D1 /* JSCocoaPrivateObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSCocoaPrivateObject.m; sourceTree = ""; }; + CC2FDC9E0EB68C40005267D1 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = /System/Library/Frameworks/JavaScriptCore.framework; sourceTree = ""; }; + CC2FDCAF0EB68CF4005267D1 /* libffi.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libffi.dylib; path = usr/lib/libffi.dylib; sourceTree = SDKROOT; }; + CCAD16410EB7D52D0011FE7C /* CodaPlugInsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodaPlugInsController.h; sourceTree = ""; }; + D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D5B49B3048680CD000E48DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, + CC2FDC9F0EB68C40005267D1 /* JavaScriptCore.framework in Frameworks */, + CC2FDCB00EB68CF4005267D1 /* libffi.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* Capitalize */ = { + isa = PBXGroup; + children = ( + CC2FDC830EB68BE7005267D1 /* JSCocoa */, + 08FB77AFFE84173DC02AAC07 /* Classes */, + 32C88E010371C26100C91783 /* Other Sources */, + 089C167CFE841241C02AAC07 /* Resources */, + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, + 19C28FB8FE9D52D311CA2CBB /* Products */, + ); + name = Capitalize; + sourceTree = ""; + }; + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + 089C167CFE841241C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 8D5B49B7048680CD000E48DA /* Info.plist */, + 089C167DFE841241C02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AFFE84173DC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + CCAD16410EB7D52D0011FE7C /* CodaPlugInsController.h */, + CC2FDC560EB68AA7005267D1 /* JSCocoaLoaderPlugIn.m */, + CC2FDC570EB68AA7005267D1 /* JSCocoaLoaderPlugIn.h */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + CC2FDC9E0EB68C40005267D1 /* JavaScriptCore.framework */, + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + CC2FDCAF0EB68CF4005267D1 /* libffi.dylib */, + 089C167FFE841241C02AAC07 /* AppKit.framework */, + D2F7E65807B2D6F200F64583 /* CoreData.framework */, + 089C1672FE841209C02AAC07 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FB8FE9D52D311CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D5B49B6048680CD000E48DA /* JSCocoaLoader.codaplugin */, + ); + name = Products; + sourceTree = ""; + }; + 32C88E010371C26100C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + CC2FDC580EB68AA7005267D1 /* JSCocoaLoader_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + CC2FDC830EB68BE7005267D1 /* JSCocoa */ = { + isa = PBXGroup; + children = ( + CC2FDC840EB68BE7005267D1 /* BridgeSupportController.h */, + CC2FDC850EB68BE7005267D1 /* BridgeSupportController.m */, + CC2FDC860EB68BE7005267D1 /* JSCocoaController.h */, + CC2FDC870EB68BE7005267D1 /* JSCocoaController.m */, + CC2FDC880EB68BE7005267D1 /* JSCocoaFFIArgument.h */, + CC2FDC890EB68BE7005267D1 /* JSCocoaFFIArgument.m */, + CC2FDC8A0EB68BE7005267D1 /* JSCocoaFFIClosure.h */, + CC2FDC8B0EB68BE7005267D1 /* JSCocoaFFIClosure.m */, + CC2FDC8C0EB68BE7005267D1 /* JSCocoaPrivateObject.h */, + CC2FDC8D0EB68BE7005267D1 /* JSCocoaPrivateObject.m */, + ); + path = JSCocoa; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D5B49AC048680CD000E48DA /* JSCocoaLoader */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "JSCocoaLoader" */; + buildPhases = ( + 8D5B49AF048680CD000E48DA /* Resources */, + 8D5B49B1048680CD000E48DA /* Sources */, + 8D5B49B3048680CD000E48DA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = JSCocoaLoader; + productInstallPath = "$(HOME)/Library/Bundles"; + productName = Capitalize; + productReference = 8D5B49B6048680CD000E48DA /* JSCocoaLoader.codaplugin */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "JSCocoaLoader" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 089C166AFE841209C02AAC07 /* Capitalize */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D5B49AC048680CD000E48DA /* JSCocoaLoader */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D5B49AF048680CD000E48DA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D5B49B1048680CD000E48DA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CC2FDC590EB68AA7005267D1 /* JSCocoaLoaderPlugIn.m in Sources */, + CC2FDC8E0EB68BE7005267D1 /* BridgeSupportController.m in Sources */, + CC2FDC8F0EB68BE7005267D1 /* JSCocoaController.m in Sources */, + CC2FDC900EB68BE7005267D1 /* JSCocoaFFIArgument.m in Sources */, + CC2FDC910EB68BE7005267D1 /* JSCocoaFFIClosure.m in Sources */, + CC2FDC920EB68BE7005267D1 /* JSCocoaPrivateObject.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C167EFE841241C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB913B08733D840010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEPLOYMENT_LOCATION = NO; + DSTROOT = "$(HOME)/Library/Application Support/Coda/PlugIns"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = JSCocoaLoader_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = ""; + PRODUCT_NAME = JSCocoaLoader; + SKIP_INSTALL = NO; + WRAPPER_EXTENSION = codaplugin; + ZERO_LINK = NO; + }; + name = Debug; + }; + 1DEB913C08733D840010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + DEPLOYMENT_LOCATION = NO; + DSTROOT = "$(HOME)/Library/Application Support/Coda/PlugIns"; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = JSCocoaLoader_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = ""; + PRODUCT_NAME = JSCocoaLoader; + SKIP_INSTALL = NO; + WRAPPER_EXTENSION = codaplugin; + ZERO_LINK = NO; + }; + name = Release; + }; + 1DEB913F08733D840010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Debug; + }; + 1DEB914008733D840010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "JSCocoaLoader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB913B08733D840010E9CD /* Debug */, + 1DEB913C08733D840010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "JSCocoaLoader" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB913F08733D840010E9CD /* Debug */, + 1DEB914008733D840010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.h b/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.h new file mode 100644 index 0000000..88f9b83 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.h @@ -0,0 +1,14 @@ +#import +#import "CodaPluginsController.h" + +@class CodaPlugInsController; + +@interface JSCocoaLoaderPlugIn : NSObject +{ + CodaPlugInsController* controller; +} + +- (id)initWithPlugInController:(CodaPlugInsController*)controller bundle:(NSBundle*)aBundle; +- (void) findJSCocoaScripts; + +@end diff --git a/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.m b/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.m new file mode 100644 index 0000000..d32a0b8 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoaLoaderPlugIn.m @@ -0,0 +1,70 @@ +#import "JSCocoaLoaderPlugIn.h" +#import "CodaPlugInsController.h" +#import "JSCocoaController.h" + +/// ooooh, a hack! +static CodaPlugInsController *JSCocoaLoaderPlugInCodaPlugInsController; + +@implementation JSCocoaLoaderPlugIn + +- (id)initWithPlugInController:(CodaPlugInsController*)inController bundle:(NSBundle*)aBundle +{ + debug(@"%s:%d", __FUNCTION__, __LINE__); + + if ( (self = [super init]) != nil ) { + controller = inController; + JSCocoaLoaderPlugInCodaPlugInsController = controller; + [self findJSCocoaScripts]; + } + + return self; +} + ++ (CodaPlugInsController *) codaPluginsController { + return JSCocoaLoaderPlugInCodaPlugInsController; +} + +- (NSString*)name { + return @"JSCocoa"; +} + +- (void) execute:(id)sender { + + NSString *path = [sender representedObject]; + + NSError *err = 0x00; + NSString *pathContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&err]; + + id c = [JSCocoaController sharedController]; + + [c evalJSString:pathContents]; + +} + +- (void) findJSCocoaScripts { + + NSString *pluginDir = [@"~/Library/Application Support/Coda/JSPlug-ins/" stringByExpandingTildeInPath]; + NSFileManager *fm = [NSFileManager defaultManager]; + BOOL isDir = NO; + + if (!([fm fileExistsAtPath:pluginDir isDirectory:&isDir] && isDir)) { + return; + } + + for (NSString *fileName in [fm contentsOfDirectoryAtPath:pluginDir error:nil]) { + + if (![fileName hasSuffix:@".js"]) { + continue; + } + + [controller registerActionWithTitle:[fileName stringByDeletingPathExtension] + underSubmenuWithTitle:nil + target:self + selector:@selector(execute:) + representedObject:[pluginDir stringByAppendingPathComponent:fileName] + keyEquivalent:@"^@A" + pluginName:[fileName stringByDeletingPathExtension]]; + } +} + +@end diff --git a/JSCocoaCodaLoader/JSCocoaLoader_Prefix.pch b/JSCocoaCodaLoader/JSCocoaLoader_Prefix.pch new file mode 100644 index 0000000..a804123 --- /dev/null +++ b/JSCocoaCodaLoader/JSCocoaLoader_Prefix.pch @@ -0,0 +1,10 @@ +// +// Prefix header for all source files of the 'Capitalize' target in the 'Capitalize' project. +// + +#ifdef __OBJC__ + #import +#endif + + +#define debug NSLog diff --git a/JSCocoaCodaLoader/Uppercase.js b/JSCocoaCodaLoader/Uppercase.js new file mode 100644 index 0000000..8cf5805 --- /dev/null +++ b/JSCocoaCodaLoader/Uppercase.js @@ -0,0 +1,9 @@ +codaPluginsController = JSCocoaLoaderPlugIn.codaPluginsController(); +var tv = codaPluginsController.focusedTextView(null); +var selectedText = tv.selectedText(); + +if (selectedText != null) { + tv.insertText_(selectedText.uppercaseString()) +} + +// test here. \ No newline at end of file diff --git a/davtest/FMWebDAVRequest.m b/davtest/FMWebDAVRequest.m index 8f3ac9f..277de80 100644 --- a/davtest/FMWebDAVRequest.m +++ b/davtest/FMWebDAVRequest.m @@ -87,7 +87,8 @@ - (FMWebDAVRequest*) createDirectory { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"MKCOL"]; @@ -105,7 +106,8 @@ - (FMWebDAVRequest*) delete { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"DELETE"]; @@ -124,12 +126,12 @@ - (FMWebDAVRequest*) putData:(NSData*)data { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"PUT"]; [req setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"]; - [req setValue:@"FMKit" forHTTPHeaderField:@"User-Agent"]; // this actually speeds things up for some reason. [req setValue:[NSString stringWithFormat:@"%d", [data length]] forHTTPHeaderField:@"Content-Length"]; @@ -149,7 +151,8 @@ - (FMWebDAVRequest*) get { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [self sendRequest:req]; @@ -164,7 +167,8 @@ - (FMWebDAVRequest*) copyToDestinationURL:(NSURL*)dest { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"COPY"]; @@ -183,7 +187,8 @@ - (FMWebDAVRequest*) moveToDestinationURL:(NSURL*)dest { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"MOVE"]; @@ -202,7 +207,8 @@ - (FMWebDAVRequest*) head { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; [req setHTTPMethod:@"HEAD"]; @@ -233,7 +239,8 @@ - (FMWebDAVRequest*) fetchDirectoryListingWithDepth:(NSUInteger)depth { NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:_url]; - [req setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + [req setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + [req setTimeoutInterval:60 * 5]; // the trailing / always gets stripped off for some reason... _uriLength = [[_url path] length] + 1; diff --git a/randomscripts/webkitupdate.py b/randomscripts/webkitupdate.py index ce87362..509364a 100755 --- a/randomscripts/webkitupdate.py +++ b/randomscripts/webkitupdate.py @@ -11,12 +11,14 @@ print("The folder " + appFolder + " does not exist.") sys.exit(1) -s = os.popen("curl http://nightly.webkit.org/").read() +s = os.popen("curl http://nightly.webkit.org/builds/trunk/mac/rss").read() -sloc = s.find('') +eloc = s.find('') -url = "http://nightly.webkit.org/" + s[sloc+10:eloc + 4] +#url = "http://builds.nightly.webkit.org/" + s[sloc+10:eloc + 4] + +url = s[sloc+6: eloc] print url diff --git a/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.mode1v3 b/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.mode1v3 index 589b051..c357ab4 100644 --- a/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.mode1v3 +++ b/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.mode1v3 @@ -230,6 +230,7 @@ active-target-popup active-buildstyle-popup action + active-combo-popup NSToolbarFlexibleSpaceItem buildOrClean build-and-goOrGo @@ -250,6 +251,8 @@ Layout + BecomeActive + ContentConfiguration PBXBottomSmartGroupGIDs @@ -274,10 +277,12 @@ PBXSmartGroupTreeModuleColumnWidthsKey - 186 + 22 + 246 PBXSmartGroupTreeModuleColumnsKey_v4 + TargetStatusColumn MainColumn @@ -286,17 +291,23 @@ PBXSmartGroupTreeModuleOutlineStateExpansionKey 089C166AFE841209C02AAC07 + 08FB77AFFE84173DC02AAC07 + 089C167CFE841241C02AAC07 + 19C28FB8FE9D52D311CA2CBB + 1C37FBAC04509CD000000102 1C37FAAC04509CD000000102 1C37FABC05509CD000000102 PBXSmartGroupTreeModuleOutlineStateSelectionKey + 6 + 1 0 PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {186, 490}} + {{0, 0}, {268, 490}} PBXTopSmartGroupGIDs @@ -308,19 +319,21 @@ GeometryConfiguration Frame - {{0, 0}, {203, 508}} + {{0, 0}, {285, 508}} GroupTreeTableConfiguration + TargetStatusColumn + 22 MainColumn - 186 + 246 RubberWindowFrame - 966 434 698 549 0 0 1920 1178 + 1082 419 698 549 0 0 1920 1178 Module PBXSmartGroupTreeModule Proportion - 203pt + 285pt Dock @@ -350,9 +363,9 @@ GeometryConfiguration Frame - {{0, 0}, {490, 0}} + {{0, 0}, {408, 0}} RubberWindowFrame - 966 434 698 549 0 0 1920 1178 + 1082 419 698 549 0 0 1920 1178 Module PBXNavigatorGroup @@ -360,8 +373,6 @@ 0pt - BecomeActive - ContentConfiguration PBXProjectModuleGUID @@ -372,9 +383,9 @@ GeometryConfiguration Frame - {{0, 5}, {490, 503}} + {{0, 5}, {408, 503}} RubberWindowFrame - 966 434 698 549 0 0 1920 1178 + 1082 419 698 549 0 0 1920 1178 Module XCDetailModule @@ -383,7 +394,7 @@ Proportion - 490pt + 408pt Name @@ -398,9 +409,9 @@ TableOfContents - CC5DE6600D36001E00FE40FA + CCA9D5930E130E2600DCEB2E 1CE0B1FE06471DED0097A5F4 - CC5DE6610D36001E00FE40FA + CCA9D5940E130E2600DCEB2E 1CE0B20306471E060097A5F4 1CE0B20506471E060097A5F4 @@ -519,7 +530,7 @@ StatusbarIsVisible TimeStamp - 221642822.56862599 + 236137669.34318501 ToolbarDisplayMode 2 ToolbarIsVisible @@ -534,13 +545,16 @@ 5 WindowOrderList - /Volumes/srv/Users/gus/Projects/flycode/voodoopadplugins/documentview/DocumentView.xcodeproj - CC01F6D00D35F8F2003E85F4 - 1C78EAAD065D492600B07095 1CD10A99069EF8BA00B06720 + 1C78EAAD065D492600B07095 + CCA9D5B90E1313A100DCEB2E + CC01F6D00D35F8F2003E85F4 + CCA9D5C50E1314C500DCEB2E + /Volumes/srv/Users/gus/Projects/flycode/voodoopadplugins/documentview/DocumentView.xcodeproj + CCA9D5A20E130F0700DCEB2E WindowString - 966 434 698 549 0 0 1920 1178 + 1082 419 698 549 0 0 1920 1178 WindowToolsV3 @@ -561,7 +575,7 @@ PBXProjectModuleGUID 1CD0528F0623707200166675 PBXProjectModuleLabel - + DocumentViewPlugin.m StatusBarVisibility @@ -619,7 +633,7 @@ TableOfContents CC01F6D00D35F8F2003E85F4 - CC5DE6620D36001E00FE40FA + CCA9D5900E130E2100DCEB2E 1CD0528F0623707200166675 XCMainBuildResultsModuleGUID @@ -739,13 +753,13 @@ TableOfContents 1CD10A99069EF8BA00B06720 - CC5DE6630D36001E00FE40FA + CCA9D5950E130E2600DCEB2E 1C162984064C10D400B95A72 - CC5DE6640D36001E00FE40FA - CC5DE6650D36001E00FE40FA - CC5DE6660D36001E00FE40FA - CC5DE6670D36001E00FE40FA - CC5DE6680D36001E00FE40FA + CCA9D5960E130E2600DCEB2E + CCA9D5970E130E2600DCEB2E + CCA9D5980E130E2600DCEB2E + CCA9D5990E130E2600DCEB2E + CCA9D59A0E130E2600DCEB2E ToolbarConfiguration xcode.toolbar.config.debugV3 @@ -754,7 +768,7 @@ WindowToolGUID 1CD10A99069EF8BA00B06720 WindowToolIsVisible - + Identifier @@ -909,7 +923,7 @@ TableOfContents 1C78EAAD065D492600B07095 - CC5DE6690D36001E00FE40FA + CCA9D59B0E130E2600DCEB2E 1C78EAAC065D492600B07095 ToolbarConfiguration diff --git a/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.pbxuser b/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.pbxuser index 8807a09..66337bd 100644 --- a/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.pbxuser +++ b/voodoopadplugins/documentview/DocumentView.xcodeproj/gus.pbxuser @@ -1,8 +1,7 @@ // !$*UTF8*$! { 089C1669FE841209C02AAC07 /* Project object */ = { - activeArchitecture = ppc; - activeBuildConfigurationName = Deployment; + activeBuildConfigurationName = Development; activeExecutable = CCD5A7970BC5AA7C009E02D3 /* VoodooPad Pro */; activeTarget = CC93E18B066ECBC400D09EFE /* DocumentView */; addToTargets = ( @@ -21,7 +20,7 @@ PBXFileTableDataSourceColumnWidthsKey = ( 22, 300, - 156, + 57.58349609375, ); PBXFileTableDataSourceColumnsKey = ( PBXExecutablesDataSource_ActiveFlagID, @@ -34,7 +33,7 @@ PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; PBXFileTableDataSourceColumnWidthsKey = ( 20, - 251, + 169, 20, 48, 43, @@ -51,8 +50,30 @@ PBXFileDataSource_Target_ColumnID, ); }; - PBXPerProjectTemplateStateSaveDate = 221642779; - PBXWorkspaceStateSaveDate = 221642779; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 129, + 60, + 20, + 48.16259765625, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 236129816; + PBXWorkspaceStateSaveDate = 236129816; }; sourceControlManager = CCD5A7670BC5A8FC009E02D3 /* Source Control */; userBuildSettings = { @@ -60,16 +81,17 @@ }; CC29761F07344E7D00F8D6C4 /* DocumentViewPlugin.h */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1247, 976}}"; - sepNavSelRange = "{258, 0}"; + sepNavIntBoundsRect = "{{0, 0}, {1015, 981}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 263}"; sepNavVisRect = "{{0, 0}, {1247, 976}}"; }; }; CC29762007344E7D00F8D6C4 /* DocumentViewPlugin.m */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1350, 1302}}"; - sepNavSelRange = "{1612, 0}"; - sepNavVisRange = "{352, 2429}"; + sepNavIntBoundsRect = "{{0, 0}, {1015, 1680}}"; + sepNavSelRange = "{1249, 1962}"; + sepNavVisRange = "{1446, 2495}"; sepNavVisRect = "{{0, 0}, {1069, 857}}"; }; }; @@ -125,30 +147,33 @@ }; CCD5A7BD0BC5AC21009E02D3 /* DocumentViewWindowController.h */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1069, 834}}"; - sepNavSelRange = "{359, 0}"; + sepNavIntBoundsRect = "{{0, 0}, {1229, 984}}"; + sepNavSelRange = "{360, 57}"; + sepNavVisRange = "{0, 424}"; sepNavVisRect = "{{0, 0}, {1069, 834}}"; }; }; CCD5A7BE0BC5AC21009E02D3 /* DocumentViewWindowController.m */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1201, 2128}}"; - sepNavSelRange = "{0, 0}"; - sepNavVisRange = "{2550, 2948}"; + sepNavIntBoundsRect = "{{0, 0}, {1015, 2128}}"; + sepNavSelRange = "{279, 5213}"; + sepNavVisRange = "{0, 2296}"; sepNavVisRect = "{{0, 0}, {1069, 857}}"; }; }; CCD5A7D70BC5AD86009E02D3 /* MultiplePageView.m */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1069, 3234}}"; - sepNavSelRange = "{6220, 0}"; + sepNavIntBoundsRect = "{{0, 0}, {1015, 3234}}"; + sepNavSelRange = "{2551, 5832}"; + sepNavVisRange = "{5849, 2534}"; sepNavVisRect = "{{0, 2029}, {1069, 857}}"; }; }; CCD5A7D80BC5AD86009E02D3 /* MultiplePageView.h */ = { uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1069, 834}}"; - sepNavSelRange = "{186, 0}"; + sepNavIntBoundsRect = "{{0, 0}, {1015, 981}}"; + sepNavSelRange = "{24, 699}"; + sepNavVisRange = "{0, 723}"; sepNavVisRect = "{{0, 0}, {1069, 834}}"; }; }; diff --git a/voodoopadplugins/documentview/DocumentView.xcodeproj/project.pbxproj b/voodoopadplugins/documentview/DocumentView.xcodeproj/project.pbxproj index 86681e4..c87bbb8 100644 --- a/voodoopadplugins/documentview/DocumentView.xcodeproj/project.pbxproj +++ b/voodoopadplugins/documentview/DocumentView.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ CC01FA8D0BC5C228000D622C /* VPPlugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC01FA8C0BC5C228000D622C /* VPPlugin.framework */; }; - CC136968072D47F300434FEB /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = CC136967072D47F300434FEB /* Info.plist */; }; CC29762107344E7D00F8D6C4 /* DocumentViewPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = CC29761F07344E7D00F8D6C4 /* DocumentViewPlugin.h */; }; CC29762207344E7D00F8D6C4 /* DocumentViewPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = CC29762007344E7D00F8D6C4 /* DocumentViewPlugin.m */; }; CC2976910734512500F8D6C4 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC2976900734512500F8D6C4 /* Carbon.framework */; }; @@ -25,7 +24,7 @@ 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; CC01FA8C0BC5C228000D622C /* VPPlugin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VPPlugin.framework; path = ../../../svn_voodoopad/plugin/build/Deployment/VPPlugin.framework; sourceTree = SOURCE_ROOT; }; CC136959072D473700434FEB /* DocumentView.vpplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DocumentView.vpplugin; sourceTree = BUILT_PRODUCTS_DIR; }; - CC136967072D47F300434FEB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; + CC136967072D47F300434FEB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; CC29761F07344E7D00F8D6C4 /* DocumentViewPlugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DocumentViewPlugin.h; sourceTree = SOURCE_ROOT; }; CC29762007344E7D00F8D6C4 /* DocumentViewPlugin.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = DocumentViewPlugin.m; sourceTree = SOURCE_ROOT; }; CC2976900734512500F8D6C4 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; @@ -175,7 +174,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - CC136968072D47F300434FEB /* Info.plist in Resources */, CCD5A7C50BC5AC4F009E02D3 /* DocumentViewWindow.nib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -259,7 +257,7 @@ GCC_WARN_UNKNOWN_PRAGMAS = NO; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@executable_path/../PlugIns"; - OTHER_CFLAGS = ""; + OTHER_CFLAGS = "-DDEBUG"; OTHER_LDFLAGS = ( "-framework", Foundation, diff --git a/voodoopadplugins/documentview/DocumentViewPlugin.m b/voodoopadplugins/documentview/DocumentViewPlugin.m index 555c421..f4c7a62 100644 --- a/voodoopadplugins/documentview/DocumentViewPlugin.m +++ b/voodoopadplugins/documentview/DocumentViewPlugin.m @@ -21,6 +21,13 @@ - (void) didRegister { keyEquivalent:@"p" keyEquivalentModifierMask:NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask]; + [pluginManager addPluginsMenuTitle:@"Save Document as PDF" + withSuperMenuTitle:@"Miscellaneous" + target:self + action:@selector(pdfDocument:) + keyEquivalent:@"o" + keyEquivalentModifierMask:NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask]; + } @@ -29,22 +36,22 @@ - (void)dealloc { } -- (void) viewDocument:(id)windowController { +- (NSMutableAttributedString *) makeAttributedStringFromDocument:(id)document { - NSMutableAttributedString *as = [[NSMutableAttributedString alloc] init]; + NSMutableAttributedString *as = [[[NSMutableAttributedString alloc] init] autorelease]; NSString *lineBreak = [NSString stringWithFormat: @"\n%C", NSFormFeedCharacter]; #define DocumentViewPagesList @"documentviewpagelist" - NSArray *pagesList = [[windowController document] keys]; + NSArray *pagesList = [document keys]; if ([pagesList containsObject:DocumentViewPagesList]) { - id vpData = [[windowController document] pageForKey:DocumentViewPagesList]; + id vpData = [document pageForKey:DocumentViewPagesList]; if ([vpData type] != VPPageType) { NSBeep(); NSLog(@"Um... DocumentViewPagesList isn't a page."); - return; + return nil; } // ok, "lowercaseString" isn't exactly the algorithm VoodooPad uses for key names... but it's close enough for this. @@ -55,11 +62,13 @@ - (void) viewDocument:(id)windowController { NSEnumerator *e = [pagesList objectEnumerator]; NSString *key = [e nextObject]; + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + while (key) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + id vpData = [document pageForKey:key]; - id vpData = [[windowController document] pageForKey:key]; + key = [e nextObject]; if ([vpData type] == VPPageType) { NSMutableAttributedString *pageAts = [vpData dataAsAttributedString]; @@ -70,18 +79,37 @@ - (void) viewDocument:(id)windowController { [pageAts addAttribute:@"VPPageName" value:[vpData displayName] range:NSMakeRange(0, [pageAts length])]; [as appendAttributedString:pageAts]; + + if (key) { + [[as mutableString] appendString:lineBreak]; + } } - key = [e nextObject]; - - if (key) { - [[as mutableString] appendString:lineBreak]; - } - - [pool release]; + [pool drain]; } + [pool release]; + + return as; +} + +- (void) pdfDocument:(id)windowController { + + NSMutableAttributedString *as = [self makeAttributedStringFromDocument:[windowController document]]; + + DocumentViewWindowController *wc = [[DocumentViewWindowController alloc] initWithWindowNibName:@"DocumentViewWindow"]; + + [wc loadAttributedString:as]; + + [(id)wc printDocument:nil]; + +} + +- (void) viewDocument:(id)windowController { + + NSMutableAttributedString *as = [self makeAttributedStringFromDocument:[windowController document]]; + DocumentViewWindowController *wc = [[DocumentViewWindowController alloc] initWithWindowNibName:@"DocumentViewWindow"]; [wc loadAttributedString:as]; diff --git a/voodoopadplugins/documentview/DocumentViewWindow.nib/info.nib b/voodoopadplugins/documentview/DocumentViewWindow.nib/info.nib index 14d02dd..1e3d8db 100644 --- a/voodoopadplugins/documentview/DocumentViewWindow.nib/info.nib +++ b/voodoopadplugins/documentview/DocumentViewWindow.nib/info.nib @@ -3,13 +3,15 @@ IBFramework Version - 592 + 670 + IBLastKnownRelativeProjectPath + DocumentView.xcodeproj + IBOldestOS + 5 IBOpenObjects - - 2 - + IBSystem Version - 9A377a + 9D34 targetFramework IBCocoaFramework diff --git a/voodoopadplugins/documentview/DocumentViewWindow.nib/keyedobjects.nib b/voodoopadplugins/documentview/DocumentViewWindow.nib/keyedobjects.nib index 104146b2322a857a622b7fda4097b140ae21fc4c..afe5562a6e9c6664cab03ff424f51f746f7a34f3 100644 GIT binary patch delta 2304 zcmYjS3shBA8vgcq>~r=$iU*KTkwEc9BJvQ#NDJ|;fOt^>QAD`{dILffld=0(rnz*r zT3sJ4OHsfkU5@6Xbb6SX*^Et7TDHtIXHDapvN6XTb6PEP_Pt2MUhAIo@AL2df8YN8 z|KIm~-%EX;ul7%egKkJg1~TywMk5E~F%=#xfe(+O7Ax>OJco^F!|$;df4~8}jyG@| z@8JYK#GmmOT)tdPyXJIuq1*i2NhS*(PWvGPL>Tf+JkP#Qf#`DD{bGU;K; zp)8t4AWoT-=Q=btutnSIT072SB(g9TxoybBxWEBBCSd|5Qgj<8VKSzWpkzw<9ATJ- zJmjMQaVW%$z;!!5a~NqPDd{4LlzS#-VK$0Uf>O*u8RlXh$}t}mSb&A7gcplYg=*9U z7U%`CB;S))vBFcc(i=FSkM0#lLum+gi--q6-N~T`T+Qu4i2=jNjyi`r6~7*jp#ekD zh-Fxgra+mIR-=w{AN2@lpuuO=k};Tol@z@b&E$TiHmy?Mp18Ns55kjpN>N&kXRroq z@hsM<*?MfihB^73NuGrbbv~cBp*Y{Obb_yC-U<_%{HijtJT*yO1KcBy$PE`47T7!_!Wa@Y{g4>8QZWOE!d&v zErB)W;1ZYar(Ui>k>NI|C-o+mdUi(M6+BZ5_3RKLD1%=CH7XhQV1oj?kD@4|q&YP$ zD=9uBeN{(B_hEk<_T#lc7>}_J;!qn7;cy_8j|e}4qxd7<#9Me9@8B5T4J_v=B9{8P zoMNa?U_bBWKZ^I2{{x(wo$r}lUDN2LSXZB#lAPHwMxg`bPdL(wBgpQUoyJG)_$Ua9 z?9zNsuCJzkmJ;tvCB(-n6`NEB|Ej2ef=|`4oADVw$2okVtn>I%$zD{^P~~k@M$W>( z6su29S833n;v?^>K~PEVM{Z!HHG0-1e7y@_Zdz2jHK$N z;j@}T`@42qGvb)8{I2q+mM#qyX;9QqZ7|L(%Gjk8EJq7-P^gxLsp?558T6+io%JAt zM$A>3*4O)L7FIOY)YS&r__J694!7cPFn6kRRHUe2q;l2V3KDTDjtqfno&H$Lct!aN zLr8HbS6&&y(kQHR%^Xv2mSw$LteqWX$Jl9ho_)h^Xx+6KEm?a+E7dBs#{#c91O3~zJ=$JvpZ2l#g?3rHs$JK9 z)h)e`o}ee|DSE2@pq``W>qYupy}H0U(PnS6uX(>Y)tqh4H+|*`bB(#t+-$yRZZ)@=EoQ6PZoXo^YVI-jny;Hj z%wy(p^MrZQyk_1q?{dK-c@&T4y?G3eeQ+z#tf$!wI`Eh=ZU*cE!PnKbE%eJJ|%?h)ktp3(;Yos;G%C;t2)2*4-JgdrT zu(nt`tlidO>zH-iI$@o(PFbH=XRObxi`EtEjxFs-`+hsoPPdEgQoGEaXV146*p>Do zyUt!~zhJ*)x7!Eo)Al*P{iFShfQS@*#XylHa>Wd>PctgA^-V^VOGvb1{EWQ<2#Vv7L{49PEcf?(3ODUseZy6(FWt{9M z6J(N1mf3Q&d|2klTsdA&lwP@7`q#-#vQ_SvN9CLHEqPL&k{`-X?ArvoncO ar_Av>i`74`8k*MeW(zvr9Zv&r9{(SI8EjSn delta 2308 zcmYjS3sh9s72Wr}ci(&WJ{<>CG>U@1pb?cn8brkpAt<03MG-WiFv18T5fmGZbDB0L zORd^9`L`=oK3byjZ;VN8G&Xj1)kckJWD&K|Cf0n^7!y}&{q)X|vAney&fL%5`|N$* znsd=@(HkrMGvJ^L5|N5Dj726UVh)Nh7sYD564h9RU*QG3g4gkTG+`_LfIZlYeK?4B z@hAKpAL0Z~;~dW8pX$%SMf?k&CBA6*=Ux*#<5)NWQA-tn}ar1gqb+N=CWcopOv!my@FM-0n;g&vZ#QD(`e!}fhN)z z$|I0T>6GtwXj0%s?X|>*HY1CyPcaEDOvV&s;}PWGQRHGOrXdgcD8O{gz+>P?S4CU~usv>a8 zNb(owdpt{Py-Sw)ybaWadQjI01{$Ku2R~9*$H9k%)N?bc$aPkMEmCVWod6f37Eda5 zb*RTuEJFj9E9g9|z)GxC*g2lEC5x-8y-Vigd+M^QODpQsVqti3UVh*y-gmx3188uB zmPFkmSR@Tn+z$04H@U+Kh{3L`MHfyCas~o>oI4c!x z59+S2xr-irbM*j4Q#`9yonvy ziCx$o_=lMw`cfaaNe@u(fXnLbe*^o~{sFu_JKr-a%_4 zT9MK5?Tx z-*8+-F|%|@g|~iAV6)Xf%B7r$rT*RTsfVD#??N%;3Y@kwOHSfc3r;B!VXkm+MT&ND zO}&ct|Ex9ldBdz{&~~5qtYQiB40N-f2o~5$@-tj$#)Xi7jdt>=j{d&DrAAyr#^Y|c za>Z9uzPMqgcTsJ1X}!1Oz-4^dgfH<`V7%UUkPlaa({UBo@GZsDC`$ZTneZKM;CuXl z7-Zqcz|(qc|B;72HokGV4JGrOwOuWYyMI z`^rk|eT!>?jrm8jUf9xzEy1AGtEZ6q1d$Y7sE@o5VXzA)yg!iAwx~&u>BOaVW(#5r zS7%r}8^K1hQ7nNa(!-QNV=0ry(fFA`*(KS&YHwU!Y(zmrjdw|LCshHjjPaKQllIos z#|F<9cT7O24vTo0(l&+b%ue+LN^AW|+%;vz)u0(Fqe$giipt4SX(>}EF?@HLI?wW0 zJ}Y3;*$nm=^RSt0mWL+O6w0PYD2E=UT$)PL+%hz*YF_WSEw^ynAq4o?JnDI0$rGKF zl!a>bx-u_Y(8v}9DxINm4vSzVtc)#W%h?*Xh3#X9*>Tp+zF;@BZdyMrL7S+RXbS=d zogx00wN2V~ZHKl?JE65{7qoA*>pFCyN9#lM1U*Sl)zkHI{Yib5zD|EhZ`OC~NA)xM z1^tSCO~0<+(0|Zx>9_T}1`N$G4AY1;(v0y&iBV(x+<3-VZ@gyw&UoF}WHcEq##W=% z*kkN74j2cGuZ)|EyY%#Z*t)_pQ`KGzke9Js)o;2IccJr)x*}QJvH1Aoq)!iCv zO|kN=BCE{uS{0VhsW!7@*DeITkI;+{*ZM|cCV4bwuZQbU!WeeM}yV~9C2)nx- zZKv4d?HqfCJ>PDypRw248|@~0yS>*wY#*~v*r)6^yWMY}wa?qv>^s5~F5woDqKAkQ zF=D6~C9=gVu|QOcg`!&2h^1n+cwVd#>%@BTs%R9=VyDC zxG26BSHzFvmbfkMN=QvQva9SPqh)^?D+kKK@`r&$Trz7&&u=iGkH;dE-%T;^18g?n2zm8r;Foq+)kv^ z!-;ZwJAIr%&JZWg8RjH8DNd?0&Y9q3Il0a>C(kK#iq#LfdNi%$KdW$e6yZ;2;r{@( CZg<51