From 5f4cc21291355e0deea9597c3f80f5dbb25bff49 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 30 Apr 2014 11:41:05 +0100 Subject: [PATCH 01/56] Changed NSLog to debug to avoid spamming the console in release builds. --- src/framework/mocha/Utilities/MOUtilities.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/mocha/Utilities/MOUtilities.m b/src/framework/mocha/Utilities/MOUtilities.m index 1204465..4647acb 100644 --- a/src/framework/mocha/Utilities/MOUtilities.m +++ b/src/framework/mocha/Utilities/MOUtilities.m @@ -285,7 +285,7 @@ JSValueRef MOFunctionInvoke(id function, JSContextRef ctx, size_t argumentCount, #pragma message "FIXME: Check to see if function is nil or not." - NSLog(@"function: %@", function); + debug(@"function: %@", function); // Determine the metadata for the function call if ([function isKindOfClass:[MOMethod class]]) { From cd558e5942e5805b105e44314467ee2a1848ee11 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 30 Apr 2014 11:41:05 +0100 Subject: [PATCH 02/56] Changed NSLog to debug to avoid spamming the console in release builds. --- src/framework/mocha/Utilities/MOUtilities.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/mocha/Utilities/MOUtilities.m b/src/framework/mocha/Utilities/MOUtilities.m index 0823520..4647acb 100644 --- a/src/framework/mocha/Utilities/MOUtilities.m +++ b/src/framework/mocha/Utilities/MOUtilities.m @@ -285,7 +285,7 @@ JSValueRef MOFunctionInvoke(id function, JSContextRef ctx, size_t argumentCount, #pragma message "FIXME: Check to see if function is nil or not." - // NSLog(@"function: %@", function); + debug(@"function: %@", function); // Determine the metadata for the function call if ([function isKindOfClass:[MOMethod class]]) { From cd69cbaf2ad8a75f7fe7dda272c7ba9324be53b9 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 13 Jun 2014 12:17:38 +0100 Subject: [PATCH 03/56] XC6 update --- Cocoa Script.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 2ece64e..a3a0f0f 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1225,7 +1225,7 @@ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0500; + LastUpgradeCheck = 0600; }; buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "Cocoa Script" */; compatibilityVersion = "Xcode 3.2"; From aa9d5dbe8ad0bf66b94fa97d00fb51d27d720eb2 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 23 Jun 2014 18:04:46 +0100 Subject: [PATCH 04/56] Fixed debug macro. --- src/framework/CocoaScript_Prefix.pch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/CocoaScript_Prefix.pch b/src/framework/CocoaScript_Prefix.pch index 62bff12..5520853 100644 --- a/src/framework/CocoaScript_Prefix.pch +++ b/src/framework/CocoaScript_Prefix.pch @@ -10,7 +10,7 @@ #ifdef DEBUG extern void COScriptDebug(NSString* format, ...) NS_FORMAT_FUNCTION(1,2); - #define debug(...) COScriptDebug + #define debug COScriptDebug #else #define debug(...) #endif From 9e912f8efbc7282960144d2d2fe4e080de63fa0d Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 23 Jun 2014 18:10:10 +0100 Subject: [PATCH 05/56] Use a name for the debug method which doesn't clash with the debug macro (doh). --- src/framework/COScript.h | 2 +- src/framework/COScript.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/COScript.h b/src/framework/COScript.h index dbe4fc7..11ec8f9 100644 --- a/src/framework/COScript.h +++ b/src/framework/COScript.h @@ -14,7 +14,7 @@ @protocol CODebugController -- (void)debug:(NSString*)format args:(va_list)args; +- (void)output:(NSString*)format args:(va_list)args; @end @interface COScript : NSObject { diff --git a/src/framework/COScript.m b/src/framework/COScript.m index b4e6d7e..2ee8847 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -44,7 +44,7 @@ void COScriptDebug(NSString* format, ...) { if (CODebugController == nil) { NSLogv(format, args); } else { - [CODebugController debug:format args:args]; + [CODebugController output:format args:args]; } va_end(args); From d186c17e8cdf5e85c1d588b7992b9d13449e09ef Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 15 Aug 2014 10:35:31 +0100 Subject: [PATCH 06/56] Fixed use of deprecated API --- src/framework/imagetools/COSCodeSketcher.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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]; } } From f03771600de375cff88cc52cb7cd4f8aff2835f7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 15 Aug 2014 10:36:25 +0100 Subject: [PATCH 07/56] Turned #pragma messages into comments Unfortunately there's no other way to suppress them optionally that I can find, and they were the only warnings in our build. --- src/framework/COScript.m | 2 +- src/framework/imagetools/COSQuickCIFilter.m | 2 +- src/framework/mocha/Utilities/MOUtilities.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 1fcedb2..70fd8f3 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -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." + (COScript*)currentCOScript { return [[[NSThread currentThread] threadDictionary] objectForKey:currentCOScriptThreadIdentifier]; diff --git a/src/framework/imagetools/COSQuickCIFilter.m b/src/framework/imagetools/COSQuickCIFilter.m index 7994b12..ade6d3c 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/Utilities/MOUtilities.m b/src/framework/mocha/Utilities/MOUtilities.m index 519e5eb..6c7eda9 100644 --- a/src/framework/mocha/Utilities/MOUtilities.m +++ b/src/framework/mocha/Utilities/MOUtilities.m @@ -282,7 +282,7 @@ JSValueRef MOFunctionInvoke(id function, JSContextRef ctx, size_t argumentCount, id block = nil; - #pragma message "FIXME: Check to see if function is nil or not." + // FIXME: Check to see if function is nil or not." debug(@"function: %@", function); From c202ef563ceed7cfd347f23034e3a1085e16f69e Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 1 Sep 2014 13:01:50 +0100 Subject: [PATCH 08/56] Minor (and slightly ugly) tweak to avoid a warning in release build. --- src/framework/COScript.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 70fd8f3..6c556d7 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -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; } From 78c081d144829e23640bf55100fd5ac082a764e5 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 1 Sep 2014 13:02:13 +0100 Subject: [PATCH 09/56] Turn on SKIP_INSTALL to avoid a warning when archiving --- Cocoa Script.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 7909f66..c1b4746 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1644,6 +1644,7 @@ "-lexpat", ); PRODUCT_NAME = CocoaScript; + SKIP_INSTALL = YES; }; name = Debug; }; @@ -1673,6 +1674,7 @@ "-lffi", ); PRODUCT_NAME = CocoaScript; + SKIP_INSTALL = YES; }; name = Release; }; From ae7c0bb1c275939c30bd6fa6d80c31b6315e6130 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 18 Nov 2014 13:08:10 +0000 Subject: [PATCH 10/56] XC6.1 update --- Cocoa Script.xcodeproj/project.pbxproj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index c1b4746..1995412 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -23,7 +23,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 */; }; @@ -1232,7 +1232,7 @@ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0600; + LastUpgradeCheck = 0610; }; buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "Cocoa Script" */; compatibilityVersion = "Xcode 3.2"; @@ -1574,8 +1574,10 @@ 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_OPTIMIZATION_LEVEL = 0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; @@ -1600,8 +1602,10 @@ 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_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; From aa1dbc0a902b8785e7eaa80a2342d27f02656e41 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 18 Nov 2014 13:24:37 +0000 Subject: [PATCH 11/56] XC6.1 update - specify the signature for objc_msgSend to prevent a warning --- src/framework/TETextUtils.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/TETextUtils.m b/src/framework/TETextUtils.m index a92dac5..2834c7c 100644 --- a/src/framework/TETextUtils.m +++ b/src/framework/TETextUtils.m @@ -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) { From d857274fe2af15bc9800db872f5c7d54fdaeeb1f Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Fri, 12 Dec 2014 12:01:08 +0000 Subject: [PATCH 12/56] Import: Add support for absolute and ~ paths Import paths can be in the format: relative/to/script.js /absolute/path/to/script.js ~/relative/to/home/script.js --- src/framework/COSPreprocessor.m | 36 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/framework/COSPreprocessor.m b/src/framework/COSPreprocessor.m index 227b803..3282e33 100644 --- a/src/framework/COSPreprocessor.m +++ b/src/framework/COSPreprocessor.m @@ -217,34 +217,28 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { [tokenizer nextToken]; // the space NSString *pathInQuotes = [[tokenizer nextToken] stringValue]; - NSString *path = [pathInQuotes substringWithRange:NSMakeRange(1, [pathInQuotes length]-2)]; + NSString *path = [[pathInQuotes substringWithRange:NSMakeRange(1, [pathInQuotes length]-2)] stringByExpandingTildeInPath]; + NSURL *importURL = nil; - if (base) { - - NSURL *importURL = [[base URLByDeletingLastPathComponent] URLByAppendingPathComponent:path]; - - NSError *outErr = nil; - NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; - - if (s) { - [buffer appendFormat:@"// imported from %@", [importURL path]]; - [buffer appendString:s]; - } - else { - [buffer appendFormat:@"'Unable to import %@ becase %@'", path, [outErr localizedFailureReason]]; - } - - - //debug(@"importURL: '%@'", importURL); - + if (base && path.length && ![[path substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"/"]) { + importURL = [[base URLByDeletingLastPathComponent] URLByAppendingPathComponent:path]; + } else { + importURL = [NSURL fileURLWithPath:path]; + } + + NSError *outErr = nil; + NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; + + if (s) { + [buffer appendFormat:@"// imported from %@", [importURL path]]; + [buffer appendString:s]; } else { - [buffer appendFormat:@"'Unable to import %@ becase we have no base url to import from'", path]; + [buffer appendFormat:@"'Unable to import %@ because %@'", path, [outErr localizedFailureReason]]; } debug(@"[tok stringValue]: '%@'", path); - continue; } else { From 3ba543da55f997254f77a3c89a9ebdbd047cfb9c Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Fri, 12 Dec 2014 13:51:48 +0000 Subject: [PATCH 13/56] import: reinstate warning for empty base in non-absolute URLs --- src/framework/COSPreprocessor.m | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/framework/COSPreprocessor.m b/src/framework/COSPreprocessor.m index 3282e33..5157199 100644 --- a/src/framework/COSPreprocessor.m +++ b/src/framework/COSPreprocessor.m @@ -220,21 +220,25 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { NSString *path = [[pathInQuotes substringWithRange:NSMakeRange(1, [pathInQuotes length]-2)] stringByExpandingTildeInPath]; NSURL *importURL = nil; - if (base && path.length && ![[path substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"/"]) { + if (path.length && ![[path substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"/"]) { importURL = [[base URLByDeletingLastPathComponent] URLByAppendingPathComponent:path]; - } else { + } else if (base) { importURL = [NSURL fileURLWithPath:path]; + } else { + [buffer appendFormat:@"'Unable to import %@ becase we have no base url to import from'", path]; } - NSError *outErr = nil; - NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; - - if (s) { - [buffer appendFormat:@"// imported from %@", [importURL path]]; - [buffer appendString:s]; - } - else { - [buffer appendFormat:@"'Unable to import %@ because %@'", path, [outErr localizedFailureReason]]; + if (importURL) { + NSError *outErr = nil; + NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; + + if (s) { + [buffer appendFormat:@"// imported from %@", [importURL path]]; + [buffer appendString:s]; + } + else { + [buffer appendFormat:@"'Unable to import %@ because %@'", path, [outErr localizedFailureReason]]; + } } debug(@"[tok stringValue]: '%@'", path); From 67e609f58d6e062c95906d85b89bd423e5934271 Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Tue, 16 Dec 2014 11:58:06 +0000 Subject: [PATCH 14/56] Add support for nested @import statements Also guards against multiple imports of the same URL, and therefore circular imports Ref BohemianCoding/Sketch#3319 --- src/framework/COSPreprocessor.m | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/framework/COSPreprocessor.m b/src/framework/COSPreprocessor.m index 5157199..1633e2e 100644 --- a/src/framework/COSPreprocessor.m +++ b/src/framework/COSPreprocessor.m @@ -181,7 +181,7 @@ + (NSString*)preprocessForObjCMessagesToJS:(NSString*)sourceString { return buffer; } -+ (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { ++ (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base importedURLs:(NSMutableArray *)importedURLs { /* @@ -200,7 +200,6 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { BOOL lastWasAtSym = NO; - while ((tok = [tokenizer nextToken]) != eof) { if ([tok isSymbol] && [[tok stringValue] isEqualToString:@"@"]) { @@ -229,16 +228,23 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { } if (importURL) { - NSError *outErr = nil; - NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; - - if (s) { - [buffer appendFormat:@"// imported from %@", [importURL path]]; - [buffer appendString:s]; + if ([importedURLs containsObject:importURL]) { + [buffer appendFormat:@"// skipping already imported file from %@", [importURL path]]; + } else { + NSError *outErr = nil; + NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; + + if (s) { + [importedURLs addObject:importURL]; + s = [self processImports:s withBaseURL:base importedURLs:importedURLs]; + + [buffer appendFormat:@"// imported from %@", [importURL path]]; + [buffer appendString:s]; + } + else { + [buffer appendFormat:@"'Unable to import %@ because %@'", path, [outErr localizedFailureReason]]; + } } - else { - [buffer appendFormat:@"'Unable to import %@ because %@'", path, [outErr localizedFailureReason]]; - } } debug(@"[tok stringValue]: '%@'", path); @@ -263,7 +269,9 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base { + (NSString*)preprocessCode:(NSString*)sourceString withBaseURL:(NSURL*)base { - sourceString = [self processImports:sourceString withBaseURL:(NSURL*)base]; + NSMutableArray *importedURLs = (base) ? [NSMutableArray arrayWithObject:base] : [NSMutableArray new]; + + sourceString = [self processImports:sourceString withBaseURL:(NSURL*)base importedURLs:importedURLs]; sourceString = [self processMultilineStrings:sourceString]; sourceString = [self preprocessForObjCStrings:sourceString]; sourceString = [self preprocessForObjCMessagesToJS:sourceString]; From fd78d579d26d2fd9c0f924a9df35ab25d6034654 Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Thu, 18 Dec 2014 15:32:37 +0000 Subject: [PATCH 15/56] Fix a bug which ignored first line of @import-ed scripts --- src/framework/COSPreprocessor.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/COSPreprocessor.m b/src/framework/COSPreprocessor.m index 1633e2e..961c654 100644 --- a/src/framework/COSPreprocessor.m +++ b/src/framework/COSPreprocessor.m @@ -229,7 +229,7 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base imp if (importURL) { if ([importedURLs containsObject:importURL]) { - [buffer appendFormat:@"// skipping already imported file from %@", [importURL path]]; + [buffer appendFormat:@"// skipping already imported file from %@\n", [importURL path]]; } else { NSError *outErr = nil; NSString *s = [NSString stringWithContentsOfURL:importURL encoding:NSUTF8StringEncoding error:&outErr]; @@ -238,7 +238,7 @@ + (NSString*)processImports:(NSString*)sourceString withBaseURL:(NSURL*)base imp [importedURLs addObject:importURL]; s = [self processImports:s withBaseURL:base importedURLs:importedURLs]; - [buffer appendFormat:@"// imported from %@", [importURL path]]; + [buffer appendFormat:@"// imported from %@\n", [importURL path]]; [buffer appendString:s]; } else { From 31b4e73e853062df0080cbd8e159efd511a142a7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 16:24:22 +0000 Subject: [PATCH 16/56] Added a test script that exhibits the crash --- Cocoa Script.xcodeproj/project.pbxproj | 2 ++ test.js | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test.js diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 1995412..641bcd8 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -352,6 +352,7 @@ /* 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 = ""; }; + 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 = ""; }; @@ -687,6 +688,7 @@ children = ( CC66D826181A2A810039A0A5 /* CocoaScript_Prefix.pch */, CC66D8D7181A2FC10039A0A5 /* cocoascript_tool.m */, + 22E281E41A8BB99D006650D2 /* test.js */, ); name = "Other Sources"; sourceTree = ""; diff --git a/test.js b/test.js new file mode 100644 index 0000000..58e935f --- /dev/null +++ b/test.js @@ -0,0 +1,11 @@ +framework("AppKit"); +framework("CoreGraphics"); + +for (var n = 0; n < 20000; 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 From fa6871d4aca29550ca85bf557d9d022b7486c28b Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 16:26:20 +0000 Subject: [PATCH 17/56] Toned down the debug output for the command line tool whilst debugging the crash. --- src/cocoascript_tool.m | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cocoascript_tool.m b/src/cocoascript_tool.m index 4044444..980023a 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,6 +105,8 @@ int main(int argc, char *argv[]) { } } + [COScript setDebugController:[NullDebugController new]]; + id o = [t executeString:source]; if (o) { From e505ce8e3b54c81c0f5f556b145827f897df789f Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 16:56:24 +0000 Subject: [PATCH 18/56] reduced loop as script should crash --- test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.js b/test.js index 58e935f..4d2d35b 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,7 @@ framework("AppKit"); framework("CoreGraphics"); -for (var n = 0; n < 20000; n++) { +for (var n = 0; n < 3000; n++) { print(n); var path=NSBezierPath.bezierPath(); path.moveToPoint(NSMakePoint(0,0)); From a812a1bdf6499cf04f5d9c541946fbb703a676a4 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 16:57:15 +0000 Subject: [PATCH 19/56] Slight tweaks to MOBox - tracking down crash --- src/framework/mocha/MochaRuntime.m | 54 ++++++++++++++--------------- src/framework/mocha/Objects/MOBox.h | 16 +++++---- src/framework/mocha/Objects/MOBox.m | 29 ++++++++++++++-- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 46ef895..411b61f 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -462,30 +462,27 @@ - (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 = [_objectsToBoxes objectForKey:object]; + if (box != nil) { + jsObject = [box JSObject]; + } else { + box = [[MOBox alloc] initWithRuntime:self]; + + 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)); + } + + [box associateObject:object jsObject:jsObject context:_ctx]; + + [_objectsToBoxes setObject:box forKey:object]; } - box.JSObject = jsObject; - - [_objectsToBoxes setObject:box forKey:object]; - return jsObject; } @@ -499,7 +496,11 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { - (void)removeBoxAssociationForObject:(id)object { if (object != nil) { - [_objectsToBoxes removeObjectForKey:object]; + MOBox* box = [_objectsToBoxes objectForKey:object]; + if (box) { + [box disassociateObjectInContext:_ctx]; + [_objectsToBoxes removeObjectForKey:object]; + } } } @@ -1182,12 +1183,9 @@ static void MOObject_initialize(JSContextRef ctx, JSObjectRef object) { static void MOObject_finalize(JSObjectRef object) { MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(object)); id o = [private representedObject]; - - //debug(@"finalizing %@ o: %p", o, object); - -// if (class_isMetaClass(object_getClass(o))) { -// debug(@"Finalizing local class: %@ %p", o, object); -// } + if (o == [NSNumber class]) { + NSLog(@"about to finalize NSNumber %p", object); + } // Give the object a chance to finalize itself if ([o respondsToSelector:@selector(finalizeForMochaScript)]) { diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index 8d5063d..bace812 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -19,28 +19,32 @@ */ @interface MOBox : NSObject +- (id)initWithRuntime:(Mocha*)runtime; +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context; +- (void)disassociateObjectInContext:(JSContextRef)context; + /*! * @property representedObject * @abstract The boxed Objective-C object - * + * * @result An object */ -@property (strong) id representedObject; +@property (strong, readonly) id representedObject; /*! * @property JSObject * @abstract The JSObject representation of the box - * + * * @result A JSObjectRef value */ -@property JSObjectRef JSObject; +@property (assign, readonly) JSObjectRef JSObject; /*! * @property runtime * @abstract The runtime for the object - * + * * @result A Mocha object */ -@property (weak) Mocha *runtime; +@property (weak, readonly) Mocha *runtime; @end diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 2341b46..fc1c381 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -7,7 +7,10 @@ // #import "MOBox.h" - +#import "MOMethod.h" +#import "MOBridgeSupportSymbol.h" +#import "MOFunctionArgument.h" +#import "MOClosure.h" @implementation MOBox @@ -15,9 +18,29 @@ @implementation MOBox @synthesize JSObject=_JSObject; @synthesize runtime=_runtime; -- (void)dealloc { - // debug(@"MOBox dealloc releasing: '%@'", _representedObject); +- (id)initWithRuntime:(Mocha *)runtime { + self = [super init]; + if (self) { + _runtime = runtime; + } + + return self; +} + +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context { + _representedObject = object; + _JSObject = jsObject; + //JSValueProtect(context, jsObject); } +- (void)disassociateObjectInContext:(JSContextRef)context { + NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); + //JSValueUnprotect(context, self.JSObject); + _JSObject = nil; +} + +- (void)dealloc { + NSAssert(_JSObject == nil, @"should have been disassociated"); +} @end From 3bd263e06a27a76d6e992bf13963edf316fd1d81 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 17:15:38 +0000 Subject: [PATCH 20/56] Added Andrey's temporary workaround to the crash --- src/framework/mocha/Objects/MOBox.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index fc1c381..dc580e7 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -30,12 +30,12 @@ - (id)initWithRuntime:(Mocha *)runtime { - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context { _representedObject = object; _JSObject = jsObject; - //JSValueProtect(context, jsObject); + JSValueProtect(context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... } - (void)disassociateObjectInContext:(JSContextRef)context { - NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); - //JSValueUnprotect(context, self.JSObject); +// NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); + JSValueUnprotect(context, self.JSObject); // TODO: also a hack _JSObject = nil; } From 0aac538cd82eea0993878acf3b85cc9bae5abfb8 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 17:57:23 +0000 Subject: [PATCH 21/56] Perform cleanup before exiting - easier to test if it's working... --- src/cocoascript_tool.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cocoascript_tool.m b/src/cocoascript_tool.m index 980023a..b674cb9 100644 --- a/src/cocoascript_tool.m +++ b/src/cocoascript_tool.m @@ -113,5 +113,7 @@ int main(int argc, char *argv[]) { printf("%s\n", [[o description] UTF8String]); } + [t cleanup]; + return 0; } From d5ee507570cc737b8ebf5b8088eb5a22dfcae406 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 11 Feb 2015 17:58:13 +0000 Subject: [PATCH 22/56] Can't call unprotect from finalize - it wouldn't make much difference here anyway... --- src/framework/mocha/Objects/MOBox.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index dc580e7..14b17d6 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -35,7 +35,7 @@ - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSCon - (void)disassociateObjectInContext:(JSContextRef)context { // NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); - JSValueUnprotect(context, self.JSObject); // TODO: also a hack +// JSValueUnprotect(context, self.JSObject); // TODO: also a hack _JSObject = nil; } From 0e3c556903da9794bbc3dfdac9b83b3241c1b9a7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 22:54:53 +0100 Subject: [PATCH 23/56] Fixed leak of CGColorSpaceRef --- src/framework/imagetools/COSOpenCLProgram.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/imagetools/COSOpenCLProgram.m b/src/framework/imagetools/COSOpenCLProgram.m index 7b2ebc6..572063f 100644 --- a/src/framework/imagetools/COSOpenCLProgram.m +++ b/src/framework/imagetools/COSOpenCLProgram.m @@ -271,8 +271,8 @@ - (id)initWithContext:(COSOpenCLContext*)theContext usingImageAtPath:(NSString*) CFRelease(imageSourceRef); CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); - CGContextRef bcontext = CGBitmapContextCreate(_bitmapData, _width, _height, 8, _bytesPerRow, cs, kCGBitmapFloatComponents | kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Host); + CGColorSpaceRelease(cs); if (!bcontext) { NSLog(@"Could not create a context for drawing"); From 821b49d7b3745115cfabd087589e2f507f78a708 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 22:55:21 +0100 Subject: [PATCH 24/56] Keep the analyzer happy with a check for a nil encoding. --- .../mocha/Utilities/MOFunctionArgument.m | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/framework/mocha/Utilities/MOFunctionArgument.m b/src/framework/mocha/Utilities/MOFunctionArgument.m index b523da3..085186d 100644 --- a/src/framework/mocha/Utilities/MOFunctionArgument.m +++ b/src/framework/mocha/Utilities/MOFunctionArgument.m @@ -492,16 +492,20 @@ + (NSString *)descriptionOfTypeEncoding:(char)typeEncoding fullTypeEncoding:(NSS + (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++; + NSString* result = nil; + if (encoding) { + char *c = (char *)[encoding UTF8String] + 1; + // skip '_' if it's there + if (*c == '_') { + c++; + } + char *c2 = c; + while (*c2 && *c2 != '=') { + c2++; + } + result = [[NSString alloc] initWithBytes:c length:(c2-c) encoding:NSUTF8StringEncoding]; } - return [[NSString alloc] initWithBytes:c length:(c2-c) encoding:NSUTF8StringEncoding]; + return result; } + (NSString *)structureFullTypeEncodingFromStructureTypeEncoding:(NSString *)encoding { From e3d2d6dc0e93b7722228153b19685b23c7dd32c8 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 22:59:25 +0100 Subject: [PATCH 25/56] Fix for a nice leak. --- src/framework/todparsekit/TDTokenizer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/todparsekit/TDTokenizer.m b/src/framework/todparsekit/TDTokenizer.m index 38bea9a..43c0ee3 100644 --- a/src/framework/todparsekit/TDTokenizer.m +++ b/src/framework/todparsekit/TDTokenizer.m @@ -162,7 +162,7 @@ - (NSString *)string { - (void)setString:(NSString *)s { if (string != s) { - [string retain]; + [string release]; string = [s retain]; } reader.string = string; From 7724039795e3aef3d002624f24e93eecaf95bed5 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:05:45 +0100 Subject: [PATCH 26/56] Disabled the analyzer for a particularly bonkers piece of code. --- src/framework/todparsekit/TDToken.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/todparsekit/TDToken.m b/src/framework/todparsekit/TDToken.m index f6b3cf2..fbc6725 100644 --- a/src/framework/todparsekit/TDToken.m +++ b/src/framework/todparsekit/TDToken.m @@ -16,6 +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... + (TDTokenEOF *)instance { @synchronized(self) { if (!EOFToken) { @@ -24,7 +25,7 @@ + (TDTokenEOF *)instance { } return EOFToken; } - +#endif + (id)allocWithZone:(NSZone *)zone { @synchronized(self) { From d48dd440d4434a622a5e35b0e6ce28d9f904dc01 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:14:05 +0100 Subject: [PATCH 27/56] Another analyzer fix. Not sure about this one - looks like the wrong size was being used, and the wrong parameter, but I may have misinterpreted things... --- src/framework/imagetools/COSOpenCLContext.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/imagetools/COSOpenCLContext.m b/src/framework/imagetools/COSOpenCLContext.m index 80b7020..5134531 100644 --- a/src/framework/imagetools/COSOpenCLContext.m +++ b/src/framework/imagetools/COSOpenCLContext.m @@ -145,9 +145,9 @@ - (NSUInteger)maximum2DImageHeight { - (NSArray*)maximumWorkItemSizes { NSUInteger numberOfDimensions = [self maximumWorkItemDimensions]; - size_t *sizes = malloc(numberOfDimensions * sizeof(cl_uint)); + size_t *sizes = malloc(numberOfDimensions * sizeof(size_t)); - clGetDeviceInfo(computeDeviceId, CL_DEVICE_MAX_WORK_GROUP_SIZE, numberOfDimensions * sizeof( size_t ), sizes, NULL); + clGetDeviceInfo(computeDeviceId, CL_DEVICE_MAX_WORK_ITEM_SIZES, numberOfDimensions * sizeof( size_t ), sizes, NULL); NSMutableArray *sizeArray = [NSMutableArray array]; for ( NSInteger i = 0; i < numberOfDimensions; i++ ) { From f46097218e851b3b26b8a9ac770fababf10439e7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:14:23 +0100 Subject: [PATCH 28/56] Disabled the analzyer for some particularly gnarly code. --- src/framework/todparsekit/TDToken.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From 0df6e10163942d651cadf3c664a6410da7286fb1 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:16:28 +0100 Subject: [PATCH 29/56] Fixed leak of CGColorSpaceRef --- src/framework/COSGifAnimator.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/COSGifAnimator.m b/src/framework/COSGifAnimator.m index 389e88c..4ca1aa2 100644 --- a/src/framework/COSGifAnimator.m +++ b/src/framework/COSGifAnimator.m @@ -43,8 +43,8 @@ - (void)drawInBlock:(MOJavaScriptObject*)drawFunction { CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - CGContextRef context = CGBitmapContextCreate(nil, _size.width, _size.height, 8, 0, cs, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(cs); NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]; [NSGraphicsContext saveGraphicsState]; From bd2700757c1812c70a7c72f65cf58a563e29f7dc Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:21:51 +0100 Subject: [PATCH 30/56] Fixed leak of CGColorSpaceRef. Annotated createImageRefFromBuffer correctly to tell the analyzer that it returns a new thing... --- src/framework/imagetools/COSImageTools.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/imagetools/COSImageTools.m b/src/framework/imagetools/COSImageTools.m index c0d7e6e..e7bc74b 100644 --- a/src/framework/imagetools/COSImageTools.m +++ b/src/framework/imagetools/COSImageTools.m @@ -22,7 +22,7 @@ + (id)imageWithSize:(NSSize)s { @implementation COSImageTools -+ (CGImageRef)createImageRefFromBuffer:(COSOpenCLImageBuffer*)imgBuffer { ++ (CGImageRef)createImageRefFromBuffer:(COSOpenCLImageBuffer*)imgBuffer CF_RETURNS_RETAINED { CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); @@ -33,6 +33,7 @@ + (CGImageRef)createImageRefFromBuffer:(COSOpenCLImageBuffer*)imgBuffer { [imgBuffer bytesPerRow], cs, kCGBitmapFloatComponents | kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Host); + CGColorSpaceRelease(cs); CGImageRef img = CGBitmapContextCreateImage(context); CGContextRelease(context); From 945aab1134a62e3a345bfce1c53d4856e15766b4 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:27:52 +0100 Subject: [PATCH 31/56] Fixed warning. --- src/framework/imagetools/COSImageTools.m | 1 - 1 file changed, 1 deletion(-) diff --git a/src/framework/imagetools/COSImageTools.m b/src/framework/imagetools/COSImageTools.m index e7bc74b..e1f5de1 100644 --- a/src/framework/imagetools/COSImageTools.m +++ b/src/framework/imagetools/COSImageTools.m @@ -243,7 +243,6 @@ + (NSString*)pathOfImageNamed:(NSString*)imageName { + (void)setMainDisplayTransferUsingRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b { CGDisplayErr err; - CGDisplayCount dspyCnt; float red[256]; float green[256]; From dd6e52985c8bfaa20cd03ba6b2883ef2fe7a9118 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 27 May 2015 23:28:12 +0100 Subject: [PATCH 32/56] Changed #pragma to comment to stop it generating a message. --- src/framework/COSR.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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]; From 166c0d48753b476a782cba94b35badff8b5368c4 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 10 Jun 2015 21:56:45 +0100 Subject: [PATCH 33/56] XC7 updates. --- Cocoa Script.xcodeproj/project.pbxproj | 9 +++++++- res/Info.plist | 26 ++++++++++------------ res/JSTalkFramework-Info.plist | 2 +- src/framework/COSExtras.m | 6 +++++ src/framework/todparsekit/TDAny.m | 2 +- src/framework/todparsekit/TDChar.m | 2 +- src/framework/todparsekit/TDComment.m | 4 ++-- src/framework/todparsekit/TDDigit.m | 2 +- src/framework/todparsekit/TDLetter.m | 2 +- src/framework/todparsekit/TDNum.m | 2 +- src/framework/todparsekit/TDQuotedString.m | 2 +- src/framework/todparsekit/TDSymbol.m | 2 +- src/framework/todparsekit/TDTerminal.h | 6 ++--- src/framework/todparsekit/TDWord.m | 2 +- 14 files changed, 40 insertions(+), 29 deletions(-) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index b380fbe..ad8535e 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1334,7 +1334,7 @@ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0700; }; buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "Cocoa Script" */; compatibilityVersion = "Xcode 3.2"; @@ -1661,6 +1661,7 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-DDEBUG"; OTHER_LDFLAGS = "-lffi"; + PRODUCT_BUNDLE_IDENTIFIER = com.cocoascript.CocoaScript; PRODUCT_NAME = "Cocoa Script Editor"; }; name = Debug; @@ -1676,6 +1677,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; @@ -1693,7 +1695,9 @@ 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; @@ -1722,6 +1726,7 @@ 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; @@ -1765,6 +1770,7 @@ AppKit, "-lexpat", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.cocoascript.${PRODUCT_NAME:identifier}"; PRODUCT_NAME = CocoaScript; SKIP_INSTALL = YES; }; @@ -1799,6 +1805,7 @@ "-lexpat", "-lffi", ); + PRODUCT_BUNDLE_IDENTIFIER = "com.cocoascript.${PRODUCT_NAME:identifier}"; PRODUCT_NAME = CocoaScript; SKIP_INSTALL = YES; }; 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/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/todparsekit/TDAny.m b/src/framework/todparsekit/TDAny.m index bbb746d..c45923a 100644 --- a/src/framework/todparsekit/TDAny.m +++ b/src/framework/todparsekit/TDAny.m @@ -12,7 +12,7 @@ @implementation TDAny + (id)any { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDAny*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDChar.m b/src/framework/todparsekit/TDChar.m index 9b552a0..4390572 100644 --- a/src/framework/todparsekit/TDChar.m +++ b/src/framework/todparsekit/TDChar.m @@ -11,7 +11,7 @@ @implementation TDChar + (id)char { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDChar*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDComment.m b/src/framework/todparsekit/TDComment.m index a8e7816..9ad91f3 100644 --- a/src/framework/todparsekit/TDComment.m +++ b/src/framework/todparsekit/TDComment.m @@ -11,8 +11,8 @@ @implementation TDComment -+ (id)comment { - return [[[self alloc] initWithString:nil] autorelease]; ++ (instancetype)comment { + return [[(TDComment*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDDigit.m b/src/framework/todparsekit/TDDigit.m index c4ffc40..4b5cfa0 100644 --- a/src/framework/todparsekit/TDDigit.m +++ b/src/framework/todparsekit/TDDigit.m @@ -11,7 +11,7 @@ @implementation TDDigit + (id)digit { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDDigit*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDLetter.m b/src/framework/todparsekit/TDLetter.m index 1abc5b8..015a3f6 100644 --- a/src/framework/todparsekit/TDLetter.m +++ b/src/framework/todparsekit/TDLetter.m @@ -11,7 +11,7 @@ @implementation TDLetter + (id)letter { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDLetter*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDNum.m b/src/framework/todparsekit/TDNum.m index 0906ead..fb507fa 100644 --- a/src/framework/todparsekit/TDNum.m +++ b/src/framework/todparsekit/TDNum.m @@ -12,7 +12,7 @@ @implementation TDNum + (id)num { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDNum*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDQuotedString.m b/src/framework/todparsekit/TDQuotedString.m index 7416989..b9c2510 100644 --- a/src/framework/todparsekit/TDQuotedString.m +++ b/src/framework/todparsekit/TDQuotedString.m @@ -12,7 +12,7 @@ @implementation TDQuotedString + (id)quotedString { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDQuotedString*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDSymbol.m b/src/framework/todparsekit/TDSymbol.m index 88e8b50..6bb4815 100644 --- a/src/framework/todparsekit/TDSymbol.m +++ b/src/framework/todparsekit/TDSymbol.m @@ -16,7 +16,7 @@ @interface TDSymbol () @implementation TDSymbol + (id)symbol { - return [[[self alloc] initWithString:nil] autorelease]; + return [[(TDSymbol*)[self alloc] initWithString:nil] autorelease]; } diff --git a/src/framework/todparsekit/TDTerminal.h b/src/framework/todparsekit/TDTerminal.h index be99833..06bb907 100644 --- a/src/framework/todparsekit/TDTerminal.h +++ b/src/framework/todparsekit/TDTerminal.h @@ -26,18 +26,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/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]; } From ef25daead95c3c04c495c5fbc705652d8507d6a9 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 30 Jun 2015 17:24:04 +0100 Subject: [PATCH 34/56] Don't use nullable on old versions of Xcode that don't know about it... --- src/framework/todparsekit/TDTerminal.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/framework/todparsekit/TDTerminal.h b/src/framework/todparsekit/TDTerminal.h index 06bb907..ed6c781 100644 --- a/src/framework/todparsekit/TDTerminal.h +++ b/src/framework/todparsekit/TDTerminal.h @@ -6,6 +6,10 @@ // Copyright 2008 Todd Ditchendorf. All rights reserved. // +#ifndef __MAC_10_10_3 +#define nullable +#endif + #import #import "TDParser.h" From 7f42d6ec2db1abaf08be7ff6c7d6e1a4c823eea6 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 30 Jun 2015 17:27:14 +0100 Subject: [PATCH 35/56] and another that XC6.2 doesn't know about --- src/framework/todparsekit/TDTerminal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/todparsekit/TDTerminal.h b/src/framework/todparsekit/TDTerminal.h index ed6c781..feaa295 100644 --- a/src/framework/todparsekit/TDTerminal.h +++ b/src/framework/todparsekit/TDTerminal.h @@ -8,6 +8,7 @@ #ifndef __MAC_10_10_3 #define nullable +#define nonnull #endif #import From b993aea55cac429a1ae8a13ca7756fb687efac14 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 30 Jun 2015 18:02:34 +0100 Subject: [PATCH 36/56] Slightly cleaner XC6.2 fix. --- src/framework/todparsekit/TDTerminal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/todparsekit/TDTerminal.h b/src/framework/todparsekit/TDTerminal.h index feaa295..2a0b1bd 100644 --- a/src/framework/todparsekit/TDTerminal.h +++ b/src/framework/todparsekit/TDTerminal.h @@ -6,7 +6,7 @@ // Copyright 2008 Todd Ditchendorf. All rights reserved. // -#ifndef __MAC_10_10_3 +#ifndef __MAC_10_10_3 // TEMPORARY SUPPORT FOR XC6.2, which doesn't know about some of these #define nullable #define nonnull #endif From 34fd6d9dfc8b2d497dbe281cd73a1c9b065f2f12 Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Thu, 20 Aug 2015 16:52:15 +0100 Subject: [PATCH 37/56] Replace NSMapTable with NSDictionary & associated objects Ref BohemianCoding/Sketch#5799 --- src/framework/mocha/MochaRuntime.m | 58 +++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index cda5d9b..d6b497e 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -72,12 +72,42 @@ #pragma mark - #pragma mark Runtime +@interface Mocha () +- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue; +@end + +static void * MochaUnboxerKey = &MochaUnboxerKey; + +@interface MochaUnboxer : NSObject { + __weak Mocha *_mocha; + NSValue *_objectPointerValue; +} +- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue; +@end + +@implementation MochaUnboxer + +- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue +{ + self = [super init]; + if (self) { + _mocha = runtime; + _objectPointerValue = objectPointerValue; + } + return self; +} + +-(void)dealloc { + [_mocha removeBoxForObjectPointerValue:_objectPointerValue]; +} + +@end @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; NSMutableDictionary *_exportedObjects; - NSMapTable *_objectsToBoxes; + NSMutableDictionary *_objectsToBoxes; NSMutableArray *_frameworkSearchPaths; } @@ -204,9 +234,7 @@ - (id)initWithGlobalContext:(JSGlobalContextRef)ctx { if (self) { _ctx = ctx; _exportedObjects = [[NSMutableDictionary alloc] init]; - _objectsToBoxes = [NSMapTable - mapTableWithKeyOptions:NSMapTableWeakMemory | NSMapTableObjectPointerPersonality - valueOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality]; + _objectsToBoxes = [NSMutableDictionary new]; _frameworkSearchPaths = [[NSMutableArray alloc] initWithObjects: @"/System/Library/Frameworks", @"/Library/Frameworks", @@ -464,12 +492,16 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } JSObjectRef jsObject = NULL; - MOBox* box = [_objectsToBoxes objectForKey:object]; + NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; + MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; if (box != nil) { jsObject = [box JSObject]; } else { box = [[MOBox alloc] initWithRuntime:self]; + MochaUnboxer *unboxer = [[MochaUnboxer alloc] initWithRuntime:self objectPointerValue:objectPointerValue]; + objc_setAssociatedObject(box, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); + if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] || [object isKindOfClass:[MOBridgeSupportFunction class]]) { @@ -480,8 +512,7 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } [box associateObject:object jsObject:jsObject context:_ctx]; - - [_objectsToBoxes setObject:box forKey: object]; + [_objectsToBoxes setObject:box forKey: objectPointerValue]; } return jsObject; @@ -497,14 +528,17 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { - (void)removeBoxAssociationForObject:(id)object { if (object != nil) { - MOBox* box = [_objectsToBoxes objectForKey:object]; - if (box) { - [box disassociateObjectInContext:_ctx]; - [_objectsToBoxes removeObjectForKey:object]; - } + [self removeBoxForObjectPointerValue:[NSValue valueWithPointer:(__bridge const void *)(object)]]; } } +- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue { + MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; + if (box) { + [box disassociateObjectInContext:_ctx]; + [_objectsToBoxes removeObjectForKey:objectPointerValue]; + } +} #pragma mark - #pragma mark Object Storage From 7b5a81c8f22be6847a10e292f99d6e12c94a37db Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Thu, 20 Aug 2015 17:00:14 +0100 Subject: [PATCH 38/56] Associate the unboxer with the object, not the box. --- src/framework/mocha/MochaRuntime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index d6b497e..c6344e9 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -500,7 +500,7 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { box = [[MOBox alloc] initWithRuntime:self]; MochaUnboxer *unboxer = [[MochaUnboxer alloc] initWithRuntime:self objectPointerValue:objectPointerValue]; - objc_setAssociatedObject(box, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); + objc_setAssociatedObject(object, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] From bfda6393b265e2dd76856cf0568318cc6b72a73a Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Thu, 20 Aug 2015 17:12:39 +0100 Subject: [PATCH 39/56] Remove objects to box map in favour of directly associated objects --- src/framework/mocha/MochaRuntime.m | 56 +++++------------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index c6344e9..91f67ed 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -72,42 +72,12 @@ #pragma mark - #pragma mark Runtime -@interface Mocha () -- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue; -@end - -static void * MochaUnboxerKey = &MochaUnboxerKey; - -@interface MochaUnboxer : NSObject { - __weak Mocha *_mocha; - NSValue *_objectPointerValue; -} -- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue; -@end - -@implementation MochaUnboxer - -- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue -{ - self = [super init]; - if (self) { - _mocha = runtime; - _objectPointerValue = objectPointerValue; - } - return self; -} - --(void)dealloc { - [_mocha removeBoxForObjectPointerValue:_objectPointerValue]; -} - -@end +static void * MochaObjectToBoxKey = &MochaObjectToBoxKey; @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; NSMutableDictionary *_exportedObjects; - NSMutableDictionary *_objectsToBoxes; NSMutableArray *_frameworkSearchPaths; } @@ -234,7 +204,6 @@ - (id)initWithGlobalContext:(JSGlobalContextRef)ctx { if (self) { _ctx = ctx; _exportedObjects = [[NSMutableDictionary alloc] init]; - _objectsToBoxes = [NSMutableDictionary new]; _frameworkSearchPaths = [[NSMutableArray alloc] initWithObjects: @"/System/Library/Frameworks", @"/Library/Frameworks", @@ -492,16 +461,12 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } JSObjectRef jsObject = NULL; - NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; - MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; + MOBox* box = objc_getAssociatedObject(object, MochaObjectToBoxKey); if (box != nil) { jsObject = [box JSObject]; } else { box = [[MOBox alloc] initWithRuntime:self]; - MochaUnboxer *unboxer = [[MochaUnboxer alloc] initWithRuntime:self objectPointerValue:objectPointerValue]; - objc_setAssociatedObject(object, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); - if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] || [object isKindOfClass:[MOBridgeSupportFunction class]]) { @@ -512,7 +477,8 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } [box associateObject:object jsObject:jsObject context:_ctx]; - [_objectsToBoxes setObject:box forKey: objectPointerValue]; + + objc_setAssociatedObject(object, MochaObjectToBoxKey, box, OBJC_ASSOCIATION_RETAIN); } return jsObject; @@ -528,15 +494,11 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { - (void)removeBoxAssociationForObject:(id)object { if (object != nil) { - [self removeBoxForObjectPointerValue:[NSValue valueWithPointer:(__bridge const void *)(object)]]; - } -} - -- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue { - MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; - if (box) { - [box disassociateObjectInContext:_ctx]; - [_objectsToBoxes removeObjectForKey:objectPointerValue]; + MOBox* box = objc_getAssociatedObject(object, MochaObjectToBoxKey); + if (box) { + [box disassociateObjectInContext:_ctx]; + objc_setAssociatedObject(object, MochaObjectToBoxKey, nil, OBJC_ASSOCIATION_RETAIN); + } } } From 28d54ff942fd5ad500435cc44e28152c3137a23d Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Fri, 21 Aug 2015 10:57:25 +0100 Subject: [PATCH 40/56] Refactor object to box mapping into MOBox Ref BohemianCoding/Sketch#5799 --- src/framework/mocha/MochaRuntime.m | 13 +++---------- src/framework/mocha/Objects/MOBox.h | 8 ++++++++ src/framework/mocha/Objects/MOBox.m | 13 +++++++++++++ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 91f67ed..2e77f61 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -72,8 +72,6 @@ #pragma mark - #pragma mark Runtime -static void * MochaObjectToBoxKey = &MochaObjectToBoxKey; - @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; @@ -461,7 +459,7 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } JSObjectRef jsObject = NULL; - MOBox* box = objc_getAssociatedObject(object, MochaObjectToBoxKey); + MOBox *box = [MOBox boxForObject:object]; if (box != nil) { jsObject = [box JSObject]; } else { @@ -477,8 +475,6 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } [box associateObject:object jsObject:jsObject context:_ctx]; - - objc_setAssociatedObject(object, MochaObjectToBoxKey, box, OBJC_ASSOCIATION_RETAIN); } return jsObject; @@ -494,11 +490,8 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { - (void)removeBoxAssociationForObject:(id)object { if (object != nil) { - MOBox* box = objc_getAssociatedObject(object, MochaObjectToBoxKey); - if (box) { - [box disassociateObjectInContext:_ctx]; - objc_setAssociatedObject(object, MochaObjectToBoxKey, nil, OBJC_ASSOCIATION_RETAIN); - } + MOBox *box = [MOBox boxForObject:object]; + [box disassociateObjectInContext:_ctx]; } } diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index bace812..f0cbee9 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -19,6 +19,14 @@ */ @interface MOBox : NSObject +/*! + * @method boxForObject + * @param object + * + * @result The box associated with object, or nil if no box exists + */ ++ (instancetype)boxForObject:(id)object; + - (id)initWithRuntime:(Mocha*)runtime; - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context; - (void)disassociateObjectInContext:(JSContextRef)context; diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 14b17d6..64fbb13 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -12,12 +12,21 @@ #import "MOFunctionArgument.h" #import "MOClosure.h" +#import + @implementation MOBox @synthesize representedObject=_representedObject; @synthesize JSObject=_JSObject; @synthesize runtime=_runtime; +static void * MochaObjectToBoxKey = &MochaObjectToBoxKey; + ++ (instancetype)boxForObject:(id)object { + if (! object) return nil; + return objc_getAssociatedObject(object, MochaObjectToBoxKey); +} + - (id)initWithRuntime:(Mocha *)runtime { self = [super init]; if (self) { @@ -31,12 +40,16 @@ - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSCon _representedObject = object; _JSObject = jsObject; JSValueProtect(context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... + + objc_setAssociatedObject(_representedObject, MochaObjectToBoxKey, self, OBJC_ASSOCIATION_RETAIN); } - (void)disassociateObjectInContext:(JSContextRef)context { // NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); // JSValueUnprotect(context, self.JSObject); // TODO: also a hack _JSObject = nil; + + objc_setAssociatedObject(_representedObject, MochaObjectToBoxKey, nil, OBJC_ASSOCIATION_RETAIN); } - (void)dealloc { From 5b8085de3035632f79430cca8e839557786732ff Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Wed, 28 Oct 2015 16:46:48 +0000 Subject: [PATCH 41/56] Revert to 34fd6d9 Ref BohemianCoding/Sketch#6742 --- src/framework/mocha/MochaRuntime.m | 49 +++++++++++++++++++++++++++-- src/framework/mocha/Objects/MOBox.h | 8 ----- src/framework/mocha/Objects/MOBox.m | 13 -------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 2e77f61..d6b497e 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -72,10 +72,42 @@ #pragma mark - #pragma mark Runtime +@interface Mocha () +- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue; +@end + +static void * MochaUnboxerKey = &MochaUnboxerKey; + +@interface MochaUnboxer : NSObject { + __weak Mocha *_mocha; + NSValue *_objectPointerValue; +} +- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue; +@end + +@implementation MochaUnboxer + +- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue +{ + self = [super init]; + if (self) { + _mocha = runtime; + _objectPointerValue = objectPointerValue; + } + return self; +} + +-(void)dealloc { + [_mocha removeBoxForObjectPointerValue:_objectPointerValue]; +} + +@end + @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; NSMutableDictionary *_exportedObjects; + NSMutableDictionary *_objectsToBoxes; NSMutableArray *_frameworkSearchPaths; } @@ -202,6 +234,7 @@ - (id)initWithGlobalContext:(JSGlobalContextRef)ctx { if (self) { _ctx = ctx; _exportedObjects = [[NSMutableDictionary alloc] init]; + _objectsToBoxes = [NSMutableDictionary new]; _frameworkSearchPaths = [[NSMutableArray alloc] initWithObjects: @"/System/Library/Frameworks", @"/Library/Frameworks", @@ -459,12 +492,16 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } JSObjectRef jsObject = NULL; - MOBox *box = [MOBox boxForObject:object]; + NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; + MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; if (box != nil) { jsObject = [box JSObject]; } else { box = [[MOBox alloc] initWithRuntime:self]; + MochaUnboxer *unboxer = [[MochaUnboxer alloc] initWithRuntime:self objectPointerValue:objectPointerValue]; + objc_setAssociatedObject(box, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); + if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] || [object isKindOfClass:[MOBridgeSupportFunction class]]) { @@ -475,6 +512,7 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } [box associateObject:object jsObject:jsObject context:_ctx]; + [_objectsToBoxes setObject:box forKey: objectPointerValue]; } return jsObject; @@ -490,8 +528,15 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { - (void)removeBoxAssociationForObject:(id)object { if (object != nil) { - MOBox *box = [MOBox boxForObject:object]; + [self removeBoxForObjectPointerValue:[NSValue valueWithPointer:(__bridge const void *)(object)]]; + } +} + +- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue { + MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; + if (box) { [box disassociateObjectInContext:_ctx]; + [_objectsToBoxes removeObjectForKey:objectPointerValue]; } } diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index f0cbee9..bace812 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -19,14 +19,6 @@ */ @interface MOBox : NSObject -/*! - * @method boxForObject - * @param object - * - * @result The box associated with object, or nil if no box exists - */ -+ (instancetype)boxForObject:(id)object; - - (id)initWithRuntime:(Mocha*)runtime; - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context; - (void)disassociateObjectInContext:(JSContextRef)context; diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 64fbb13..14b17d6 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -12,21 +12,12 @@ #import "MOFunctionArgument.h" #import "MOClosure.h" -#import - @implementation MOBox @synthesize representedObject=_representedObject; @synthesize JSObject=_JSObject; @synthesize runtime=_runtime; -static void * MochaObjectToBoxKey = &MochaObjectToBoxKey; - -+ (instancetype)boxForObject:(id)object { - if (! object) return nil; - return objc_getAssociatedObject(object, MochaObjectToBoxKey); -} - - (id)initWithRuntime:(Mocha *)runtime { self = [super init]; if (self) { @@ -40,16 +31,12 @@ - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSCon _representedObject = object; _JSObject = jsObject; JSValueProtect(context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... - - objc_setAssociatedObject(_representedObject, MochaObjectToBoxKey, self, OBJC_ASSOCIATION_RETAIN); } - (void)disassociateObjectInContext:(JSContextRef)context { // NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); // JSValueUnprotect(context, self.JSObject); // TODO: also a hack _JSObject = nil; - - objc_setAssociatedObject(_representedObject, MochaObjectToBoxKey, nil, OBJC_ASSOCIATION_RETAIN); } - (void)dealloc { From 5fb80ef4293d539295b934eff63e0236e434e5ce Mon Sep 17 00:00:00 2001 From: Johnnie Walker Date: Wed, 28 Oct 2015 17:37:24 +0000 Subject: [PATCH 42/56] Remove MochaUnboxer, which wouldn't have unboxed anything anyway Ref BohemianCoding/Sketch#6742 --- src/framework/mocha/MochaRuntime.m | 34 ------------------------------ 1 file changed, 34 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index d6b497e..7729fce 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -72,37 +72,6 @@ #pragma mark - #pragma mark Runtime -@interface Mocha () -- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue; -@end - -static void * MochaUnboxerKey = &MochaUnboxerKey; - -@interface MochaUnboxer : NSObject { - __weak Mocha *_mocha; - NSValue *_objectPointerValue; -} -- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue; -@end - -@implementation MochaUnboxer - -- (instancetype)initWithRuntime:(Mocha *)runtime objectPointerValue:(NSValue *)objectPointerValue -{ - self = [super init]; - if (self) { - _mocha = runtime; - _objectPointerValue = objectPointerValue; - } - return self; -} - --(void)dealloc { - [_mocha removeBoxForObjectPointerValue:_objectPointerValue]; -} - -@end - @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; @@ -499,9 +468,6 @@ - (JSObjectRef)boxedJSObjectForObject:(id)object { } else { box = [[MOBox alloc] initWithRuntime:self]; - MochaUnboxer *unboxer = [[MochaUnboxer alloc] initWithRuntime:self objectPointerValue:objectPointerValue]; - objc_setAssociatedObject(box, MochaUnboxerKey, unboxer, OBJC_ASSOCIATION_RETAIN); - if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] || [object isKindOfClass:[MOBridgeSupportFunction class]]) { From f817640464f8982eddba87998396774656e62f0a Mon Sep 17 00:00:00 2001 From: Gavin MacLean Date: Tue, 10 Nov 2015 11:53:10 +0000 Subject: [PATCH 43/56] Fixed an analyser issue --- src/framework/TETextUtils.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/TETextUtils.m b/src/framework/TETextUtils.m index 2834c7c..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; } From 56bb64add1859063369925ec3ee68ee403b05539 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 16 Dec 2015 15:50:24 +0000 Subject: [PATCH 44/56] Added MOBoxManager class to clean up access to boxes a bit. Added explicit unprotection and removal of boxes in an attempt to prevent reference loops when session is cleaned up. --- Cocoa Script.xcodeproj/project.pbxproj | 10 ++++ src/framework/mocha/MochaRuntime.m | 68 ++++++++++------------ src/framework/mocha/Objects/MOBox.h | 13 +++-- src/framework/mocha/Objects/MOBox.m | 14 ++--- src/framework/mocha/Objects/MOBoxManager.h | 20 +++++++ src/framework/mocha/Objects/MOBoxManager.m | 65 +++++++++++++++++++++ 6 files changed, 137 insertions(+), 53 deletions(-) create mode 100644 src/framework/mocha/Objects/MOBoxManager.h create mode 100644 src/framework/mocha/Objects/MOBoxManager.m diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index ad8535e..e40c0d4 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 */; }; @@ -387,6 +390,8 @@ /* 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 = ""; }; @@ -1090,6 +1095,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 */, @@ -1232,6 +1239,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 */, @@ -1426,6 +1434,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 */, @@ -1592,6 +1601,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 */, diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 7729fce..a8aa6ba 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" @@ -76,7 +77,7 @@ @implementation Mocha { JSGlobalContextRef _ctx; BOOL _ownsContext; NSMutableDictionary *_exportedObjects; - NSMutableDictionary *_objectsToBoxes; + MOBoxManager *_boxManager; NSMutableArray *_frameworkSearchPaths; } @@ -203,7 +204,7 @@ - (id)initWithGlobalContext:(JSGlobalContextRef)ctx { if (self) { _ctx = ctx; _exportedObjects = [[NSMutableDictionary alloc] init]; - _objectsToBoxes = [NSMutableDictionary new]; + _boxManager = [[MOBoxManager alloc] initWithContext:ctx]; _frameworkSearchPaths = [[NSMutableArray alloc] initWithObjects: @"/System/Library/Frameworks", @"/Library/Frameworks", @@ -455,30 +456,32 @@ - (id)objectForJSValue:(JSValueRef)value unboxObjects:(BOOL)unboxObjects { return [Mocha objectForJSValue:value inContext:_ctx unboxObjects:unboxObjects]; } +- (void)cleanupBoxes { + [_boxManager cleanup]; + _boxManager = nil; +} + - (JSObjectRef)boxedJSObjectForObject:(id)object { if (object == nil) { return NULL; } JSObjectRef jsObject = NULL; - NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; - MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; + MOBox* box = [_boxManager boxForObject:object]; if (box != nil) { jsObject = [box JSObject]; } else { - box = [[MOBox alloc] initWithRuntime:self]; - + JSClassRef jsClass; if ([object isKindOfClass:[MOMethod class]] || [object isKindOfClass:[MOClosure class]] || [object isKindOfClass:[MOBridgeSupportFunction class]]) { - jsObject = JSObjectMake(_ctx, MOFunctionClass, (__bridge void *)(box)); + jsClass = MOFunctionClass; } else { - jsObject = JSObjectMake(_ctx, MOBoxedObjectClass, (__bridge void *)(box)); + jsClass = MOBoxedObjectClass; } - - [box associateObject:object jsObject:jsObject context:_ctx]; - [_objectsToBoxes setObject:box forKey: objectPointerValue]; + + jsObject = [_boxManager makeBoxForObject:object jsClass:jsClass]; } return jsObject; @@ -493,17 +496,7 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { } - (void)removeBoxAssociationForObject:(id)object { - if (object != nil) { - [self removeBoxForObjectPointerValue:[NSValue valueWithPointer:(__bridge const void *)(object)]]; - } -} - -- (void)removeBoxForObjectPointerValue:(NSValue *)objectPointerValue { - MOBox* box = [_objectsToBoxes objectForKey:objectPointerValue]; - if (box) { - [box disassociateObjectInContext:_ctx]; - [_objectsToBoxes removeObjectForKey:objectPointerValue]; - } + [_boxManager removeBoxForObject:object]; } #pragma mark - @@ -917,7 +910,9 @@ - (void)shutdown { [self setNilValueForKey:@"print"]; [self removeObjectWithName:@"__mocha__"]; - + + [self cleanupBoxes]; + JSGlobalContextRelease(_ctx); _ctx = nil; @@ -1169,37 +1164,36 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p #pragma mark - #pragma mark Mocha Objects -static void MOObject_initialize(JSContextRef ctx, JSObjectRef object) { - MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(object)); - CFRetain((__bridge CFTypeRef)private); +static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { + MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + CFRetain((__bridge CFTypeRef)box); - if (class_isMetaClass(object_getClass([private representedObject]))) { + if (class_isMetaClass(object_getClass([box representedObject]))) { //debug(@"inited a local class object %@ - going to keep it protected %p", [private representedObject], object); // JSValueProtect(ctx, [private JSObject]); } } -static void MOObject_finalize(JSObjectRef object) { - MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(object)); - id o = [private representedObject]; +static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { + MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + id boxedObject = [box representedObject]; // if (class_isMetaClass(object_getClass(o))) { // debug(@"Finalizing local class: %@ %p", o, object); // } // Give the object a chance to finalize itself - if ([o respondsToSelector:@selector(finalizeForMochaScript)]) { - [o finalizeForMochaScript]; + if ([boxedObject respondsToSelector:@selector(finalizeForMochaScript)]) { + [boxedObject finalizeForMochaScript]; } // Remove the object association - Mocha *runtime = [private runtime]; - [runtime removeBoxAssociationForObject:o]; - - JSObjectSetPrivate(object, NULL); + MOBoxManager *manager = [box manager]; + [manager removeBoxForObject:boxedObject]; - CFRelease((__bridge CFTypeRef)private); + JSObjectSetPrivate(jsObjectRepresentingBox, NULL); + CFRelease((__bridge CFTypeRef)box); } diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index bace812..f378736 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -11,6 +11,7 @@ @class Mocha; +@class MOBoxManager; /*! @@ -19,8 +20,8 @@ */ @interface MOBox : NSObject -- (id)initWithRuntime:(Mocha*)runtime; -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context; +- (id)initWithManager:(MOBoxManager*)manager; +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; - (void)disassociateObjectInContext:(JSContextRef)context; /*! @@ -40,11 +41,11 @@ @property (assign, readonly) JSObjectRef JSObject; /*! - * @property runtime - * @abstract The runtime for the object + * @property manager + * @abstract The manager for the object * - * @result A Mocha object + * @result A MOBoxManager object */ -@property (weak, readonly) Mocha *runtime; +@property (weak, readonly) MOBoxManager *manager; @end diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 14b17d6..b5795c6 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -14,29 +14,23 @@ @implementation MOBox -@synthesize representedObject=_representedObject; -@synthesize JSObject=_JSObject; -@synthesize runtime=_runtime; - -- (id)initWithRuntime:(Mocha *)runtime { +- (id)initWithManager:(MOBoxManager *)manager { self = [super init]; if (self) { - _runtime = runtime; + _manager = manager; } return self; } -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject context:(JSContextRef)context { +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { _representedObject = object; _JSObject = jsObject; - JSValueProtect(context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... } - (void)disassociateObjectInContext:(JSContextRef)context { -// NSLog(@"disassociated box %p for %p js:%p", self, self.representedObject, self.JSObject); -// JSValueUnprotect(context, self.JSObject); // TODO: also a hack _JSObject = nil; + _representedObject = nil; } - (void)dealloc { diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h new file mode 100644 index 0000000..6fbb667 --- /dev/null +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -0,0 +1,20 @@ +// +// MOBoxManager.h +// +// +// Created by Sam Deane on 16/12/2015. +// +// + +#import +#import + +@class MOBox; + +@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..87cef79 --- /dev/null +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -0,0 +1,65 @@ +// +// MOBoxManager.m +// +// +// Created by Sam Deane on 16/12/2015. +// +// + +#import "MOBoxManager.h" +#import "MOBox.h" + +@implementation MOBoxManager { + JSGlobalContextRef _context; + NSMutableDictionary *_index; +} + +- (instancetype)initWithContext:(JSGlobalContextRef)context { + self = [super init]; + if (self) { + _index = [NSMutableDictionary new]; + _context = context; + } + + return self; +} + +- (void)cleanup { + [_index enumerateKeysAndObjectsUsingBlock:^(id key, MOBox *box, BOOL *stop) { + JSValueUnprotect(_context, box.JSObject); + [box disassociateObjectInContext:_context]; + }]; + _index = nil; + _context = nil; +} + +- (id)keyForObject:(id)object { + NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; + return objectPointerValue; +} + +- (MOBox*)boxForObject:(id)object { + id key = [self keyForObject:object]; + MOBox* box = [_index objectForKey:key]; + return box; +} + +- (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { + MOBox* box = [[MOBox alloc] initWithManager:self]; + JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); + [box associateObject:object jsObject:jsObject]; + JSValueProtect(_context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... + [_index setObject:box forKey:[self keyForObject:object]]; + return jsObject; +} + +- (void)removeBoxForObject:(id)object { + id key = [self keyForObject:object]; + MOBox* box = [_index objectForKey:key]; + if (box) { + [box disassociateObjectInContext:_context]; + [_index removeObjectForKey:key]; + } +} + +@end From 26e51276ed8d98fbf69dc38e1ef29d53024879ab Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 16 Dec 2015 17:19:20 +0000 Subject: [PATCH 45/56] Cleaned up boxing a bit, removed the JSProtect hack. --- src/framework/mocha/Objects/MOBox.h | 2 +- src/framework/mocha/Objects/MOBox.m | 3 ++- src/framework/mocha/Objects/MOBoxManager.m | 13 +++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index f378736..8c310f5 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -22,7 +22,7 @@ - (id)initWithManager:(MOBoxManager*)manager; - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; -- (void)disassociateObjectInContext:(JSContextRef)context; +- (void)disassociateObject; /*! * @property representedObject diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index b5795c6..2c4b6c6 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -28,7 +28,8 @@ - (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { _JSObject = jsObject; } -- (void)disassociateObjectInContext:(JSContextRef)context { +- (void)disassociateObject { + JSObjectSetPrivate(_JSObject, nil); _JSObject = nil; _representedObject = nil; } diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 87cef79..e91d1cc 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -25,9 +25,11 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { } - (void)cleanup { + NSAssert([NSThread isMainThread], @"should be main thread"); [_index enumerateKeysAndObjectsUsingBlock:^(id key, MOBox *box, BOOL *stop) { - JSValueUnprotect(_context, box.JSObject); - [box disassociateObjectInContext:_context]; + NSLog(@"cleaned up box %@ for %@", box, [box.representedObject class]); +// JSValueUnprotect(_context, box.JSObject); + [box disassociateObject]; }]; _index = nil; _context = nil; @@ -39,25 +41,28 @@ - (id)keyForObject:(id)object { } - (MOBox*)boxForObject:(id)object { + NSAssert([NSThread isMainThread], @"should be main thread"); id key = [self keyForObject:object]; MOBox* box = [_index objectForKey:key]; return box; } - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { + NSAssert([NSThread isMainThread], @"should be main thread"); MOBox* box = [[MOBox alloc] initWithManager:self]; JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); [box associateObject:object jsObject:jsObject]; - JSValueProtect(_context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... +// JSValueProtect(_context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... [_index setObject:box forKey:[self keyForObject:object]]; return jsObject; } - (void)removeBoxForObject:(id)object { + NSAssert([NSThread isMainThread], @"should be main thread"); id key = [self keyForObject:object]; MOBox* box = [_index objectForKey:key]; if (box) { - [box disassociateObjectInContext:_context]; + [box disassociateObject]; [_index removeObjectForKey:key]; } } From b29a1a14a2814f451bec8887b5ee49c5897cd933 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 16 Dec 2015 17:21:16 +0000 Subject: [PATCH 46/56] Protect function/selector arguments around calls. The theory here is that JS references to objects may be in the ffi arguments as raw pointers, so it's a good idea to protect everything. May be spurious... --- src/framework/mocha/MochaRuntime.m | 16 ++++++---- src/framework/mocha/Utilities/MOUtilities.m | 34 +++++++++++++++++++-- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index a8aa6ba..823d7f5 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -665,6 +665,7 @@ - (JSValueRef)callJSFunction:(JSObjectRef)jsFunction withArgumentsInArray:(NSArr for (NSUInteger i=0; i Date: Wed, 16 Dec 2015 17:22:28 +0000 Subject: [PATCH 47/56] Protect all the JS objects representing members of the struct. These get created but aren't referenced from JS anywhere at the point the struct is made, so I suspect that they might be destroyed again immediately. --- src/framework/mocha/Utilities/MOFunctionArgument.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/mocha/Utilities/MOFunctionArgument.m b/src/framework/mocha/Utilities/MOFunctionArgument.m index 085186d..455a872 100644 --- a/src/framework/mocha/Utilities/MOFunctionArgument.m +++ b/src/framework/mocha/Utilities/MOFunctionArgument.m @@ -1068,7 +1068,7 @@ + (NSInteger)structureToJSValue:(JSValueRef *)value inContext:(JSContextRef)ctx *convertedValueCount = *convertedValueCount+1; } } - + id objValue = [runtime objectForJSValue:valueJS]; [memberNames addObject:propertyName]; [memberValues setObject:objValue forKey:propertyName]; @@ -1079,9 +1079,14 @@ + (NSInteger)structureToJSValue:(JSValueRef *)value inContext:(JSContextRef)ctx for (NSString *name in memberNames) { id memberValue = [memberValues objectForKey:name]; [structure setObject:memberValue forMemberName:name]; + JSValueRef memberJS = [runtime JSValueForObject:memberValue]; + if (memberJS) { + JSValueProtect(ctx, memberJS); + } } JSValueRef jsValue = [runtime JSValueForObject:structure]; +// JSValueProtect(ctx, jsValue); JSObjectRef jsObject = JSValueToObject(ctx, jsValue, NULL); if (!*value) { From a48761dccab7c5432661473bc928d0c0d329d08e Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 09:34:55 +0000 Subject: [PATCH 48/56] Make MOStruct unprotect values when it's shut down. --- src/framework/mocha/Objects/MOStruct.h | 5 +++-- src/framework/mocha/Objects/MOStruct.m | 19 +++++++++++++++---- .../mocha/Utilities/MOFunctionArgument.m | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) 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 Date: Thu, 17 Dec 2015 09:57:06 +0000 Subject: [PATCH 49/56] Use strong map again instead of dictionary. We will "leak" any object in the map until the JS reference is garbage collected, or until the end of the script session - which is not ideal, but better than crashing. --- src/framework/mocha/Objects/MOBoxManager.m | 31 +++++++++------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index e91d1cc..1e27cea 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -11,13 +11,15 @@ @implementation MOBoxManager { JSGlobalContextRef _context; - NSMutableDictionary *_index; + NSMapTable *_index; } - (instancetype)initWithContext:(JSGlobalContextRef)context { self = [super init]; if (self) { - _index = [NSMutableDictionary new]; + _index = [NSMapTable + mapTableWithKeyOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality + valueOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality]; _context = context; } @@ -26,24 +28,18 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { - (void)cleanup { NSAssert([NSThread isMainThread], @"should be main thread"); - [_index enumerateKeysAndObjectsUsingBlock:^(id key, MOBox *box, BOOL *stop) { - NSLog(@"cleaned up box %@ for %@", box, [box.representedObject class]); -// JSValueUnprotect(_context, box.JSObject); + for (NSValue* key in _index) { + MOBox* box = [_index objectForKey:key]; +// NSLog(@"cleaned up box %@ for %@", box, [box.representedObject class]); [box disassociateObject]; - }]; + } _index = nil; _context = nil; } -- (id)keyForObject:(id)object { - NSValue *objectPointerValue = [NSValue valueWithPointer:(__bridge const void *)(object)]; - return objectPointerValue; -} - - (MOBox*)boxForObject:(id)object { NSAssert([NSThread isMainThread], @"should be main thread"); - id key = [self keyForObject:object]; - MOBox* box = [_index objectForKey:key]; + MOBox* box = [_index objectForKey:object]; return box; } @@ -52,18 +48,17 @@ - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { MOBox* box = [[MOBox alloc] initWithManager:self]; JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); [box associateObject:object jsObject:jsObject]; -// JSValueProtect(_context, jsObject); // TODO: this is a temporary hack. It will fix the script crash, but only at the expense of leaking all JS objects during a script run. Which is not good... - [_index setObject:box forKey:[self keyForObject:object]]; + 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"); - id key = [self keyForObject:object]; - MOBox* box = [_index objectForKey:key]; + MOBox* box = [_index objectForKey:object]; if (box) { [box disassociateObject]; - [_index removeObjectForKey:key]; + [_index removeObjectForKey:object]; } } From 5246cd584fbf0150f4b260bc2751586545edea9d Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 09:58:00 +0000 Subject: [PATCH 50/56] Simplified slightly. --- src/framework/mocha/Objects/MOBoxManager.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 1e27cea..cebf852 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -17,9 +17,7 @@ @implementation MOBoxManager { - (instancetype)initWithContext:(JSGlobalContextRef)context { self = [super init]; if (self) { - _index = [NSMapTable - mapTableWithKeyOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality - valueOptions:NSMapTableStrongMemory | NSMapTableObjectPointerPersonality]; + _index = [NSMapTable strongToStrongObjectsMapTable]; _context = context; } From f0e69e80c62a4256350a3f4b3fcc916cb66a55b7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 10:03:41 +0000 Subject: [PATCH 51/56] Commented MOBoxManager --- src/framework/mocha/Objects/MOBoxManager.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h index 6fbb667..b4fc246 100644 --- a/src/framework/mocha/Objects/MOBoxManager.h +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -11,6 +11,26 @@ @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; From 12b81e54ca674bcd8d0cfb9abcbe625bc67dcd80 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 11:43:00 +0000 Subject: [PATCH 52/56] Cleaned up and remove some obsolete stuff. --- src/framework/mocha/MochaRuntime.m | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 823d7f5..4ee82d6 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -456,11 +456,6 @@ - (id)objectForJSValue:(JSValueRef)value unboxObjects:(BOOL)unboxObjects { return [Mocha objectForJSValue:value inContext:_ctx unboxObjects:unboxObjects]; } -- (void)cleanupBoxes { - [_boxManager cleanup]; - _boxManager = nil; -} - - (JSObjectRef)boxedJSObjectForObject:(id)object { if (object == nil) { return NULL; @@ -495,10 +490,6 @@ - (id)unboxedObjectForJSObject:(JSObjectRef)jsObject { return nil; } -- (void)removeBoxAssociationForObject:(id)object { - [_boxManager removeBoxForObject:object]; -} - #pragma mark - #pragma mark Object Storage @@ -917,14 +908,11 @@ - (void)shutdown { [self removeObjectWithName:@"__mocha__"]; - [self cleanupBoxes]; + [_boxManager cleanup]; + _boxManager = nil; JSGlobalContextRelease(_ctx); - _ctx = nil; - - //[_mochaRuntime garbageCollect]; - } - (void)print:(id)o { @@ -1181,14 +1169,9 @@ static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresenti } static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { + // Give the object a chance to finalize itself MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); id boxedObject = [box representedObject]; - -// if (class_isMetaClass(object_getClass(o))) { -// debug(@"Finalizing local class: %@ %p", o, object); -// } - - // Give the object a chance to finalize itself if ([boxedObject respondsToSelector:@selector(finalizeForMochaScript)]) { [boxedObject finalizeForMochaScript]; } @@ -1196,8 +1179,6 @@ static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { // Remove the object association MOBoxManager *manager = [box manager]; [manager removeBoxForObject:boxedObject]; - - JSObjectSetPrivate(jsObjectRepresentingBox, NULL); } From 9d028f1e2b3a432a2031597bd54e6345f9c26f67 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 11:43:26 +0000 Subject: [PATCH 53/56] Clean out runtime when shutting down COScript, to break retain cycle and ensure everything gets disposed of. --- src/framework/COScript.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 25f4d79..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 { From ebc6f2a9606cbe35a443d785f8fbcc54a62a29be Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 11:45:12 +0000 Subject: [PATCH 54/56] Strengthened assertion. --- src/framework/mocha/Objects/MOBox.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 2c4b6c6..8d04365 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -35,7 +35,7 @@ - (void)disassociateObject { } - (void)dealloc { - NSAssert(_JSObject == nil, @"should have been disassociated"); + NSAssert((_JSObject == nil) && (_representedObject == nil), @"should have been disassociated"); } @end From 27b6784b48215115b62f042bd105199f0cbcf8a4 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 13:00:29 +0000 Subject: [PATCH 55/56] A bit more cleanup. --- src/framework/mocha/MochaRuntime.m | 37 ++----------------- src/framework/mocha/Objects/MOBoxManager.m | 3 +- .../mocha/Utilities/MOFunctionArgument.m | 1 - 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 07b8360..e6d8468 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -987,34 +987,6 @@ - (void)finalizeForMochaScript { #pragma mark - #pragma mark Global Object -//static void Mocha_initialize(JSContextRef ctx, JSObjectRef object) { -// MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(object)); -// -// if (private) { -// -// CFRetain((__bridge CFTypeRef)private); -// -//// if (class_isMetaClass(object_getClass([private representedObject]))) { -//// debug(@"inited a global class object %@ - going to keep it protected", [private representedObject]); -//// JSValueProtect(ctx, [private JSObject]); -//// } -// } -// -// -//} -// -//static void Mocha_finalize(JSObjectRef object) { -// MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(object)); -// id o = [private representedObject]; -// -// //debug(@"finalizing %@ o: %p", o, object); -// -// if (class_isMetaClass(object_getClass(o))) { -// debug(@"Finalizing global class: %@ %p", o, object); -// } -//} - - JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyNameJS, JSValueRef *exception) { NSString *propertyName = (NSString *)CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, propertyNameJS)); @@ -1159,17 +1131,14 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p #pragma mark Mocha Objects static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { - MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); - - if (class_isMetaClass(object_getClass([box representedObject]))) { - //debug(@"inited a local class object %@ - going to keep it protected %p", [private representedObject], object); -// JSValueProtect(ctx, [private JSObject]); - } + NSCAssert([((__bridge MOBox *)JSObjectGetPrivate(jsObjectRepresentingBox)) isKindOfClass:[MOBox class]], @"should have an associated box object"); } static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { // Give the object a chance to finalize itself MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + NSCAssert(!box || [box isKindOfClass:[MOBox class]], @"if we're shutting down, the private object may have been cleaned out already, but otherwise, it should be an MOBox"); + id boxedObject = [box representedObject]; if ([boxedObject respondsToSelector:@selector(finalizeForMochaScript)]) { [boxedObject finalizeForMochaScript]; diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index cebf852..7978354 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -19,6 +19,7 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { if (self) { _index = [NSMapTable strongToStrongObjectsMapTable]; _context = context; + JSGlobalContextRetain(context); } return self; @@ -28,10 +29,10 @@ - (void)cleanup { NSAssert([NSThread isMainThread], @"should be main thread"); for (NSValue* key in _index) { MOBox* box = [_index objectForKey:key]; -// NSLog(@"cleaned up box %@ for %@", box, [box.representedObject class]); [box disassociateObject]; } _index = nil; + JSGlobalContextRelease(_context); _context = nil; } diff --git a/src/framework/mocha/Utilities/MOFunctionArgument.m b/src/framework/mocha/Utilities/MOFunctionArgument.m index f06a41e..1fec2e2 100644 --- a/src/framework/mocha/Utilities/MOFunctionArgument.m +++ b/src/framework/mocha/Utilities/MOFunctionArgument.m @@ -1086,7 +1086,6 @@ + (NSInteger)structureToJSValue:(JSValueRef *)value inContext:(JSContextRef)ctx } JSValueRef jsValue = [runtime JSValueForObject:structure]; -// JSValueProtect(ctx, jsValue); JSObjectRef jsObject = JSValueToObject(ctx, jsValue, NULL); if (!*value) { From d3273a3dade6b718ff5388cdbe990aa55edaef40 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 17 Dec 2015 13:04:51 +0000 Subject: [PATCH 56/56] Added some more assertions. --- src/framework/mocha/Objects/MOBoxManager.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 7978354..2a6aaf8 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -38,12 +38,14 @@ - (void)cleanup { - (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]; @@ -55,6 +57,7 @@ - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { - (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];