diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 39399bc..5f4613a 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 229C1FF61C21B38D004C5B3B /* MOBoxManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 229C1FF41C21B38D004C5B3B /* MOBoxManager.h */; }; + 229C1FF71C21B38D004C5B3B /* MOBoxManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 229C1FF51C21B38D004C5B3B /* MOBoxManager.m */; }; + 229C1FF81C21B38D004C5B3B /* MOBoxManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 229C1FF51C21B38D004C5B3B /* MOBoxManager.m */; }; 384830E21832D48500B34168 /* COSTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 384830E01832D48500B34168 /* COSTarget.h */; }; 384830E31832D48500B34168 /* COSTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 384830E11832D48500B34168 /* COSTarget.m */; }; 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; @@ -29,7 +32,7 @@ CC4143520F25261200E46669 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC5B8A40F1EFA6D00126722 /* JavaScriptCore.framework */; }; CC4143530F25261300E46669 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; CC4143620F2527CD00E46669 /* CocoaScript.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC41431A0F25254200E46669 /* CocoaScript.framework */; }; - CC4143660F2527E400E46669 /* CocoaScript.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CC41431A0F25254200E46669 /* CocoaScript.framework */; }; + CC4143660F2527E400E46669 /* CocoaScript.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = CC41431A0F25254200E46669 /* CocoaScript.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; CC5EDD4D1237F6DF00E0D965 /* JSTalkStatusIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CC5EDD4C1237F6DF00E0D965 /* JSTalkStatusIcon.png */; }; CC5EDD531237F71300E0D965 /* JSTalkStatusIconAlt.png in Resources */ = {isa = PBXBuildFile; fileRef = CC5EDD521237F71300E0D965 /* JSTalkStatusIconAlt.png */; }; CC5FB7DF0F1FDE3800F4ECC2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; @@ -397,6 +400,9 @@ /* Begin PBXFileReference section */ 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 13E42FBA07B3F13500E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + 229C1FF41C21B38D004C5B3B /* MOBoxManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOBoxManager.h; path = src/framework/mocha/Objects/MOBoxManager.h; sourceTree = SOURCE_ROOT; }; + 229C1FF51C21B38D004C5B3B /* MOBoxManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOBoxManager.m; path = src/framework/mocha/Objects/MOBoxManager.m; sourceTree = SOURCE_ROOT; }; + 22E281E41A8BB99D006650D2 /* test.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = test.js; sourceTree = ""; }; 2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 384830E01832D48500B34168 /* COSTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = COSTarget.h; path = src/framework/COSTarget.h; sourceTree = ""; }; @@ -768,6 +774,7 @@ children = ( CC66D826181A2A810039A0A5 /* CocoaScript_Prefix.pch */, CC66D8D7181A2FC10039A0A5 /* cocoascript_tool.m */, + 22E281E41A8BB99D006650D2 /* test.js */, ); name = "Other Sources"; sourceTree = ""; @@ -1130,6 +1137,8 @@ CC66D879181A2B0A0039A0A5 /* MOAllocator.m */, CC66D87A181A2B0A0039A0A5 /* MOBox.h */, CC66D87B181A2B0A0039A0A5 /* MOBox.m */, + 229C1FF41C21B38D004C5B3B /* MOBoxManager.h */, + 229C1FF51C21B38D004C5B3B /* MOBoxManager.m */, CC66D87C181A2B0A0039A0A5 /* MOClassDescription.h */, CC66D87D181A2B0A0039A0A5 /* MOClassDescription.m */, CC66D87E181A2B0A0039A0A5 /* MOClosure_Private.h */, @@ -1272,6 +1281,7 @@ CC66D7CD181A2A470039A0A5 /* TDEmpty.h in Headers */, CC66D8A3181A2B0A0039A0A5 /* MOClosure.h in Headers */, CC66D7CF181A2A470039A0A5 /* TDLetter.h in Headers */, + 229C1FF61C21B38D004C5B3B /* MOBoxManager.h in Headers */, CC66D7E6181A2A470039A0A5 /* TDRepetition.h in Headers */, CC66D834181A2A9B0039A0A5 /* COSCIImageAdditions.h in Headers */, CC66D83E181A2A9B0039A0A5 /* COSQuickCIFilter.h in Headers */, @@ -1392,7 +1402,7 @@ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0500; + LastUpgradeCheck = 0700; TargetAttributes = { 9FC343C61AF7D2FE00B53759 = { CreatedOnToolsVersion = 6.3.1; @@ -1506,6 +1516,7 @@ CC66D7DF181A2A470039A0A5 /* TDParser.m in Sources */, 384830E31832D48500B34168 /* COSTarget.m in Sources */, CC66D86B181A2AE10039A0A5 /* MochaRuntime.m in Sources */, + 229C1FF81C21B38D004C5B3B /* MOBoxManager.m in Sources */, CC66D821181A2A710039A0A5 /* COSExtras.m in Sources */, CC66D83F181A2A9B0039A0A5 /* COSQuickCIFilter.m in Sources */, CC66D81F181A2A710039A0A5 /* COScript.m in Sources */, @@ -1672,6 +1683,7 @@ CC9FF01118296A2B009DB0F9 /* TDToken.m in Sources */, CC9FF01318296A2B009DB0F9 /* TDTokenArraySource.m in Sources */, CC9FF01518296A2B009DB0F9 /* TDTokenAssembly.m in Sources */, + 229C1FF71C21B38D004C5B3B /* MOBoxManager.m in Sources */, CC9FF01718296A2B009DB0F9 /* TDTokenizer.m in Sources */, CCD3834A195A454F00C436B9 /* COSDatabaseQueue.m in Sources */, CC9FF01918296A2B009DB0F9 /* TDTokenizerState.m in Sources */, @@ -1812,6 +1824,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-DDEBUG"; OTHER_LDFLAGS = "-lffi"; + PRODUCT_BUNDLE_IDENTIFIER = com.cocoascript.CocoaScript; PRODUCT_NAME = "Cocoa Script Editor"; }; name = Debug; @@ -1827,6 +1840,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-lffi"; + PRODUCT_BUNDLE_IDENTIFIER = com.cocoascript.CocoaScript; PRODUCT_NAME = "Cocoa Script Editor"; }; name = Release; @@ -1840,9 +1854,13 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -1866,9 +1884,12 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -1912,7 +1933,9 @@ AppKit, "-lexpat", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.cocoascript.${PRODUCT_NAME:identifier}"; PRODUCT_NAME = CocoaScript; + SKIP_INSTALL = YES; }; name = Debug; }; @@ -1945,7 +1968,9 @@ "-lexpat", "-lffi", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.cocoascript.${PRODUCT_NAME:identifier}"; PRODUCT_NAME = CocoaScript; + SKIP_INSTALL = YES; }; name = Release; }; diff --git a/res/Info.plist b/res/Info.plist index aad5694..f4748cb 100644 --- a/res/Info.plist +++ b/res/Info.plist @@ -51,25 +51,12 @@ JSTPluginMover - - CFBundleURLTypes - - - CFBundleURLName - Cocoa Script Whatever - CFBundleURLSchemes - - x-coscript - - - - CFBundleExecutable Cocoa Script Editor CFBundleIconFile JSTalk.icns CFBundleIdentifier - com.cocoascript.CocoaScript + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -80,6 +67,17 @@ 3 CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleURLName + Cocoa Script Whatever + CFBundleURLSchemes + + x-coscript + + + CFBundleVersion 1 LSApplicationCategoryType diff --git a/res/JSTalkFramework-Info.plist b/res/JSTalkFramework-Info.plist index 602679a..de31ba4 100644 --- a/res/JSTalkFramework-Info.plist +++ b/res/JSTalkFramework-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.cocoascript.${PRODUCT_NAME:identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/src/cocoascript_tool.m b/src/cocoascript_tool.m index 4044444..b674cb9 100644 --- a/src/cocoascript_tool.m +++ b/src/cocoascript_tool.m @@ -4,6 +4,19 @@ BOOL JSCErrorHandlerExitOnError = YES; +@interface NullDebugController : NSObject + +@end + +@implementation NullDebugController + +- (void)output:(NSString*)format args:(va_list)args +{ + +} + +@end + @interface JSCErrorHandler : NSObject { } @@ -92,11 +105,15 @@ int main(int argc, char *argv[]) { } } + [COScript setDebugController:[NullDebugController new]]; + id o = [t executeString:source]; if (o) { printf("%s\n", [[o description] UTF8String]); } + [t cleanup]; + return 0; } diff --git a/src/framework/COSExtras.m b/src/framework/COSExtras.m index 6fee319..c1f527e 100644 --- a/src/framework/COSExtras.m +++ b/src/framework/COSExtras.m @@ -102,12 +102,16 @@ - (void)system:(NSString*)s { @implementation NSApplication (COSExtras) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + - (id)open:(NSString*)pathToFile { NSError *err = nil; NSURL *url = [NSURL fileURLWithPath:pathToFile]; + // TODO - this call needs replacing as it is deprecated, but the new API is async so it requires a little bit of work id doc = [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:YES error:&err]; @@ -120,6 +124,8 @@ - (id)open:(NSString*)pathToFile { return doc; } +#pragma clang diagnostic pop + - (void)activate { ProcessSerialNumber xpsn = { 0, kCurrentProcess }; SetFrontProcess( &xpsn ); diff --git a/src/framework/COSR.m b/src/framework/COSR.m index 80bd73f..960d5c8 100644 --- a/src/framework/COSR.m +++ b/src/framework/COSR.m @@ -96,7 +96,7 @@ - (instancetype)makeTable:(NSString*)tableName { COSR *sub = [self subTableWithName:tableName]; - #pragma message "FIXME: make sure to return an existing one if it's already around" + // FIXME: make sure to return an existing one if it's already around [sub setTableID:[NSString stringWithUUID]]; [self setObject:sub forKeyedSubscript:tableName]; diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 26afb37..06ed725 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -102,7 +102,7 @@ - (void)cleanup { [self deleteObjectWithName:@"log"]; [_mochaRuntime shutdown]; - + _mochaRuntime = nil; } - (void)garbageCollect { @@ -111,7 +111,7 @@ - (void)garbageCollect { [_mochaRuntime garbageCollect]; - debug(@"gc took %f seconds", [NSDate timeIntervalSinceReferenceDate] - start); + debug(@"gc took %f seconds", [NSDate timeIntervalSinceReferenceDate] - start); (void)start; } @@ -268,7 +268,7 @@ + (void)loadBridgeSupportFileAtURL:(NSURL*)url { NSString *currentCOScriptThreadIdentifier = @"org.jstalk.currentCOScriptHack"; -#pragma message "FIXME: Change currentCOScript and friends to use a stack in the thread dictionary, instead of just overwriting what might already be there." +// FIXME: Change currentCOScript and friends to use a stack in the thread dictionary, instead of just overwriting what might already be there." + (NSMutableArray*)currentCOSThreadStack { diff --git a/src/framework/TETextUtils.m b/src/framework/TETextUtils.m index a92dac5..edf7488 100644 --- a/src/framework/TETextUtils.m +++ b/src/framework/TETextUtils.m @@ -45,7 +45,7 @@ unsigned TE_numberOfLeadingSpacesFromRangeInString(NSString *string, NSRange *ra unsigned tabW = tabWidth; NSUInteger endOfWhiteSpaceIndex = NSNotFound; - if (range->length == 0) { + if (range && range->length == 0) { return 0; } @@ -521,7 +521,7 @@ unsigned TE_expandVariablesInString(NSMutableString *input, NSString *variableSt replacement = nil; [invocation getReturnValue:&replacement]; #else - replacement = objc_msgSend(modalDelegate, callbackSelector, varName, input, varRange, replacementRange, context); + replacement = ((NSString* (*)(id, SEL, id, id, NSRange, NSRange, void*))objc_msgSend)(modalDelegate, callbackSelector, varName, input, varRange, replacementRange, context); #endif if (replacement) { diff --git a/src/framework/imagetools/COSCodeSketcher.m b/src/framework/imagetools/COSCodeSketcher.m index 60ddcee..f42fd61 100644 --- a/src/framework/imagetools/COSCodeSketcher.m +++ b/src/framework/imagetools/COSCodeSketcher.m @@ -195,7 +195,7 @@ - (void)setupWindow { NSPoint p = [NSEvent mouseLocation]; - p = [_mwindow convertScreenToBase:p]; + p = [_mwindow convertRectFromScreen:NSMakeRect(p.x, p.y, 0, 0)].origin; _mouseLocation = [self convertPoint:p fromView:nil]; } } diff --git a/src/framework/imagetools/COSQuickCIFilter.m b/src/framework/imagetools/COSQuickCIFilter.m index 0e8f3b5..74d847e 100644 --- a/src/framework/imagetools/COSQuickCIFilter.m +++ b/src/framework/imagetools/COSQuickCIFilter.m @@ -8,7 +8,7 @@ #import "COSQuickCIFilter.h" -#pragma message "FIXME: Gus- you can get CI kernel errors like so: http://stackoverflow.com/questions/13754997/how-do-you-debug-syntax-errors-in-a-core-image-kernel" +// FIXME: Gus- you can get CI kernel errors like so: http://stackoverflow.com/questions/13754997/how-do-you-debug-syntax-errors-in-a-core-image-kernel" /* If you use introspection on the CIKernel class, you will find a kernelsWithString:messageLog: method. There is no public interface to it, but don't let that stop you… diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 8078fe2..e6d8468 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -10,6 +10,7 @@ #import "MochaRuntime_Private.h" #import "MOBox.h" +#import "MOBoxManager.h" #import "MOUndefined.h" #import "MOMethod_Private.h" #import "MOClosure_Private.h" @@ -72,12 +73,11 @@ #pragma mark - #pragma mark Runtime - @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; NSMutableDictionary *_exportedObjects; - NSMapTable *_objectsToBoxes; + MOBoxManager *_boxManager; NSMutableArray *_frameworkSearchPaths; } @@ -204,9 +204,7 @@ - (id)initWithGlobalContext:(JSGlobalContextRef)ctx { if (self) { _ctx = ctx; _exportedObjects = [[NSMutableDictionary alloc] init]; - _objectsToBoxes = [NSMapTable - mapTableWithKeyOptions:NSMapTableWeakMemory | NSMapTableObjectPointerPersonality - valueOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality]; + _boxManager = [[MOBoxManager alloc] initWithContext:ctx]; _frameworkSearchPaths = [[NSMutableArray alloc] initWithObjects: @"/System/Library/Frameworks", @"/Library/Frameworks", @@ -463,30 +461,24 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { return NULL; } - MOBox *box = [_objectsToBoxes objectForKey: object]; - if (box != nil) { - return [box JSObject]; - } - - box = [[MOBox alloc] init]; - box.runtime = self; - box.representedObject = object; - JSObjectRef jsObject = NULL; - - if ([object isKindOfClass:[MOMethod class]] - || [object isKindOfClass:[MOClosure class]] - || [object isKindOfClass:[MOBridgeSupportFunction class]]) { - jsObject = JSObjectMake(_ctx, MOFunctionClass, (__bridge void *)(box)); - } - else { - jsObject = JSObjectMake(_ctx, MOBoxedObjectClass, (__bridge void *)(box)); + MOBox* box = [_boxManager boxForObject:object]; + if (box != nil) { + jsObject = [box JSObject]; + } else { + JSClassRef jsClass; + if ([object isKindOfClass:[MOMethod class]] + || [object isKindOfClass:[MOClosure class]] + || [object isKindOfClass:[MOBridgeSupportFunction class]]) { + jsClass = MOFunctionClass; + } + else { + jsClass = MOBoxedObjectClass; + } + + jsObject = [_boxManager makeBoxForObject:object jsClass:jsClass]; } - box.JSObject = jsObject; - - [_objectsToBoxes setObject:box forKey: object]; - return jsObject; } @@ -498,13 +490,6 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { return nil; } -- (void)removeBoxAssociationForObject:(id)object { - if (object != nil) { - [_objectsToBoxes removeObjectForKey: object]; - } -} - - #pragma mark - #pragma mark Object Storage @@ -671,6 +656,7 @@ - (JSValueRef)callJSFunction:(JSObjectRef)jsFunction withArgumentsInArray:(NSArr for (NSUInteger i=0; i +#import + +@class MOBox; + +/** + Manages the boxing and un-boxing of non-JS objects. + + For each object, we keep a "box", which ties together a JS object reference and an Objective C object. + + The JS object's private data pointer points to an MOBox instance. This allows us to take a JS object ref + and look up the corresponding Obj-C object. It also ensures that the Obj-C object lives as long as the JS ref. + + The MOBox instances are stored in a strong map, keyed with the Obj-C object, which allows us to go in + the other direction and look up a JS object ref from the Obj-C object. + + The MOBox has a strong reference to the Obj-C object, and also an unprotected reference to the JS object. + + When a JS reference is no longer needed, it should be garbage collected, at which point our finalize callback + will be called and we will remove the corresponding box (thus potentially releasing the Obj-C object). + + When an Obj-C object is longer referenced externally, we will continue to retain it, until such time as there + are no more JS references to it. + */ + +@interface MOBoxManager : NSObject +- (instancetype)initWithContext:(JSGlobalContextRef)context; +- (void)cleanup; +- (MOBox*)boxForObject:(id)object; +- (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass; +- (void)removeBoxForObject:(id)object; +@end diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m new file mode 100644 index 0000000..2a6aaf8 --- /dev/null +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -0,0 +1,67 @@ +// +// MOBoxManager.m +// +// +// Created by Sam Deane on 16/12/2015. +// +// + +#import "MOBoxManager.h" +#import "MOBox.h" + +@implementation MOBoxManager { + JSGlobalContextRef _context; + NSMapTable *_index; +} + +- (instancetype)initWithContext:(JSGlobalContextRef)context { + self = [super init]; + if (self) { + _index = [NSMapTable strongToStrongObjectsMapTable]; + _context = context; + JSGlobalContextRetain(context); + } + + return self; +} + +- (void)cleanup { + NSAssert([NSThread isMainThread], @"should be main thread"); + for (NSValue* key in _index) { + MOBox* box = [_index objectForKey:key]; + [box disassociateObject]; + } + _index = nil; + JSGlobalContextRelease(_context); + _context = nil; +} + +- (MOBox*)boxForObject:(id)object { + NSAssert([NSThread isMainThread], @"should be main thread"); + NSAssert(![object isKindOfClass:[MOBox class]], @"shouldn't box a box"); + MOBox* box = [_index objectForKey:object]; + return box; +} + +- (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { + NSAssert([NSThread isMainThread], @"should be main thread"); + NSAssert(![object isKindOfClass:[MOBox class]], @"shouldn't box a box"); + MOBox* box = [[MOBox alloc] initWithManager:self]; + JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); + [box associateObject:object jsObject:jsObject]; + NSAssert([_index objectForKey:object] == nil, @"shouldn't already have an entry for the object"); + [_index setObject:box forKey:object]; + return jsObject; +} + +- (void)removeBoxForObject:(id)object { + NSAssert([NSThread isMainThread], @"should be main thread"); + MOBox* box = [_index objectForKey:object]; + NSAssert(box != nil, @"shouldn't be asked to unbox something that has no box"); + if (box) { + [box disassociateObject]; + [_index removeObjectForKey:object]; + } +} + +@end diff --git a/src/framework/mocha/Objects/MOStruct.h b/src/framework/mocha/Objects/MOStruct.h index f667bde..f597b0d 100644 --- a/src/framework/mocha/Objects/MOStruct.h +++ b/src/framework/mocha/Objects/MOStruct.h @@ -8,6 +8,7 @@ #import +@class Mocha; /*! * @class MOStruct @@ -27,7 +28,7 @@ * * @result An MOStruct object */ -+ (MOStruct *)structureWithName:(NSString *)name memberNames:(NSArray *)memberNames; ++ (MOStruct *)structureWithName:(NSString *)name memberNames:(NSArray *)memberNames runtime:(Mocha*)runtime; /*! * @method initWithName:memberNames: @@ -41,7 +42,7 @@ * * @result An MOStruct object */ -- (id)initWithName:(NSString *)name memberNames:(NSArray *)memberNames; +- (id)initWithName:(NSString *)name memberNames:(NSArray *)memberNames runtime:(Mocha*)runtime; /*! diff --git a/src/framework/mocha/Objects/MOStruct.m b/src/framework/mocha/Objects/MOStruct.m index 3b61826..6ded3bc 100644 --- a/src/framework/mocha/Objects/MOStruct.m +++ b/src/framework/mocha/Objects/MOStruct.m @@ -13,29 +13,40 @@ @implementation MOStruct { NSArray *_memberNames; NSMutableDictionary *_memberValues; + __weak Mocha *_runtime; } @synthesize name=_name; @synthesize memberNames=_memberNames; -+ (MOStruct *)structureWithName:(NSString *)name memberNames:(NSArray *)memberNames { - return [[self alloc] initWithName:name memberNames:memberNames]; ++ (MOStruct *)structureWithName:(NSString *)name memberNames:(NSArray *)memberNames runtime:(Mocha*)runtime { + return [[self alloc] initWithName:name memberNames:memberNames runtime:runtime]; } -- (id)initWithName:(NSString *)name memberNames:(NSArray *)memberNames { +- (id)initWithName:(NSString *)name memberNames:(NSArray *)memberNames runtime:(Mocha*)runtime { self = [super init]; if (self) { _name = [name copy]; _memberNames = [memberNames copy]; _memberValues = [[NSMutableDictionary alloc] init]; + _runtime = runtime; } return self; } - (id)init { - return [self initWithName:nil memberNames:nil]; + return [self initWithName:nil memberNames:nil runtime:nil]; } +- (void)dealloc { + for (NSString *name in _memberNames) { + id memberValue = [_memberValues objectForKey:name]; + JSValueRef memberJS = [_runtime JSValueForObject:memberValue]; + if (memberJS) { + JSValueUnprotect(_runtime.context, memberJS); + } + } +} - (NSString *)descriptionWithIndent:(NSUInteger)indent { NSMutableString *indentString = [NSMutableString string]; for (NSUInteger i=0; i #import "TDParser.h" @@ -26,18 +31,18 @@ @param s the string matched by this parser @result an initialized TDTerminal subclass object */ -- (id)initWithString:(NSString *)s; +- (nullable instancetype)initWithString:(nullable NSString *)s; /*! @brief By default, terminals push themselves upon a assembly's stack, after a successful match. This method will turn off that behavior. @details This method returns this parser as a convenience for chainging-style usage. @result this parser, returned for chaining/convenience */ -- (TDTerminal *)discard; +- (nonnull TDTerminal *)discard; /*! @property string @brief the string matched by this parser. */ -@property (nonatomic, readonly, copy) NSString *string; +@property (nonatomic, readonly, copy, nullable) NSString *string; @end diff --git a/src/framework/todparsekit/TDToken.m b/src/framework/todparsekit/TDToken.m index fbc6725..e73b668 100644 --- a/src/framework/todparsekit/TDToken.m +++ b/src/framework/todparsekit/TDToken.m @@ -16,7 +16,7 @@ @implementation TDTokenEOF static TDTokenEOF *EOFToken = nil; -#ifndef __clang_analyzer__ // SD: disabled analyzer for this somewhat crazy code; not sure quite what would be wrong with just doing a dispatch_once here... +#ifndef __clang_analyzer__ // SD: disabled analyzer for this somewhat crazy code to stop a warning about a leak; not sure quite what would be wrong with just doing a dispatch_once here... + (TDTokenEOF *)instance { @synchronized(self) { if (!EOFToken) { diff --git a/src/framework/todparsekit/TDWord.m b/src/framework/todparsekit/TDWord.m index c772d7a..28009f0 100644 --- a/src/framework/todparsekit/TDWord.m +++ b/src/framework/todparsekit/TDWord.m @@ -12,7 +12,7 @@ @implementation TDWord + (id)word { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDWord*)[self alloc] initWithString:nil] autorelease]; } diff --git a/test.js b/test.js new file mode 100644 index 0000000..4d2d35b --- /dev/null +++ b/test.js @@ -0,0 +1,11 @@ +framework("AppKit"); +framework("CoreGraphics"); + +for (var n = 0; n < 3000; n++) { + print(n); + var path=NSBezierPath.bezierPath(); + path.moveToPoint(NSMakePoint(0,0)); + path.lineToPoint(NSMakePoint(10,10)); + path.lineToPoint(NSMakePoint(150,200)); + path.closePath(); +} \ No newline at end of file