From 5f4cc21291355e0deea9597c3f80f5dbb25bff49 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Wed, 30 Apr 2014 11:41:05 +0100 Subject: [PATCH 01/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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]; From 59207254d85e1acb2bbb7e2a3aff18685edbdb8c Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 10:08:03 +0000 Subject: [PATCH 57/80] Only add box to the index if the JS initialiser actually gets called. --- src/framework/mocha/MochaRuntime.m | 7 ++++++- src/framework/mocha/Objects/MOBox.h | 4 ++-- src/framework/mocha/Objects/MOBox.m | 7 ++++--- src/framework/mocha/Objects/MOBoxManager.h | 1 + src/framework/mocha/Objects/MOBoxManager.m | 21 +++++++++++++++++---- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index e6d8468..8454b00 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -1131,7 +1131,10 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p #pragma mark Mocha Objects static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { - NSCAssert([((__bridge MOBox *)JSObjectGetPrivate(jsObjectRepresentingBox)) isKindOfClass:[MOBox class]], @"should have an associated box object"); + MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + NSCAssert([box isKindOfClass:[MOBox class]], @"should have an associated box object"); + MOBoxManager *manager = [box manager]; + [manager associateObject:jsObjectRepresentingBox withBox:box]; } static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { @@ -1640,6 +1643,8 @@ static JSValueRef MOFunction_callAsFunction(JSContextRef ctx, JSObjectRef functi value = MOFunctionInvoke(function, ctx, argumentCount, arguments, exception); } @catch (NSException *e) { + debug(@"caught exception whilst invoking function %@: %@", function, e); + // Catch ObjC exceptions and propogate them up as JS exceptions if (exception != nil) { *exception = [runtime JSValueForObject:e]; diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index ceca565..d09b44a 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -20,8 +20,8 @@ */ @interface MOBox : NSObject -- (id)initWithManager:(MOBoxManager*)manager; -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; +- (id)initWithManager:(MOBoxManager*)manager object:(id)object; +- (void)associateObject:(JSObjectRef)jsObject; - (void)disassociateObject; /*! diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 8d04365..3e319d5 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -14,17 +14,18 @@ @implementation MOBox -- (id)initWithManager:(MOBoxManager *)manager { +- (id)initWithManager:(MOBoxManager *)manager object:(id)object { self = [super init]; if (self) { _manager = manager; + _representedObject = object; } return self; } -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { - _representedObject = object; +- (void)associateObject:(JSObjectRef)jsObject { + NSAssert(JSObjectGetPrivate(jsObject) == (__bridge void *)self, @"object should already be connected to this box"); _JSObject = jsObject; } diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h index b4fc246..a39e238 100644 --- a/src/framework/mocha/Objects/MOBoxManager.h +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -36,5 +36,6 @@ - (void)cleanup; - (MOBox*)boxForObject:(id)object; - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass; +- (void)associateObject:(JSObjectRef)jsObject withBox:(MOBox*)box; - (void)removeBoxForObject:(id)object; @end diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 2a6aaf8..a1b7de1 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -37,6 +37,7 @@ - (void)cleanup { } - (MOBox*)boxForObject:(id)object { + debug(@"added box for %p %@", object, object); NSAssert([NSThread isMainThread], @"should be main thread"); NSAssert(![object isKindOfClass:[MOBox class]], @"shouldn't box a box"); MOBox* box = [_index objectForKey:object]; @@ -46,21 +47,33 @@ - (MOBox*)boxForObject:(id)object { - (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]; + MOBox* box = [[MOBox alloc] initWithManager:self object:object]; JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); - [box associateObject:object jsObject:jsObject]; + return jsObject; +} + +- (void)associateObject:(JSObjectRef)jsObject withBox:(MOBox*)box { + id object = box.representedObject; NSAssert([_index objectForKey:object] == nil, @"shouldn't already have an entry for the object"); + [box associateObject:jsObject]; [_index setObject:box forKey:object]; - return jsObject; } - (void)removeBoxForObject:(id)object { + debug(@"removing box for %p %@", object, 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]; + } else { + debug(@"shouldn't be asked to unbox something that has no box (the object was %p %@)", object, object); + for (id key in _index) { + box = [_index objectForKey:key]; + if (box.representedObject == object) { + NSLog(@"found orphaned box"); + } + } } } From 67508c8d2f307e9cfdbdeedc2a0bf262a4797c39 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 10:23:16 +0000 Subject: [PATCH 58/80] More debugging. --- src/framework/mocha/Objects/MOBox.m | 2 ++ src/framework/mocha/Objects/MOBoxManager.m | 1 + 2 files changed, 3 insertions(+) diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 3e319d5..f2e9294 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -19,6 +19,8 @@ - (id)initWithManager:(MOBoxManager *)manager object:(id)object { if (self) { _manager = manager; _representedObject = object; + _representedObjectCanary = object; + _representedObjectCanaryDesc = [object description]; } return self; diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index a1b7de1..120e5cd 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -15,6 +15,7 @@ @implementation MOBoxManager { } - (instancetype)initWithContext:(JSGlobalContextRef)context { + NSAssert([NSThread isMainThread], @"should be main thread"); self = [super init]; if (self) { _index = [NSMapTable strongToStrongObjectsMapTable]; From f5a781f59118ca83f313c7d17e487452b12dac44 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 11:48:58 +0000 Subject: [PATCH 59/80] Don't make a box at all until the JS object has been initialized. Make sure that boxes get dissassociated and clean out the JS object's private pointer, before they are dealloced. --- src/framework/mocha/MochaRuntime.m | 13 ++++++++----- src/framework/mocha/Objects/MOBox.h | 4 +--- src/framework/mocha/Objects/MOBox.m | 20 ++++++++++---------- src/framework/mocha/Objects/MOBoxManager.h | 3 ++- src/framework/mocha/Objects/MOBoxManager.m | 15 ++++++++------- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 8454b00..aaaa04f 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -1131,15 +1131,18 @@ 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)); - NSCAssert([box isKindOfClass:[MOBox class]], @"should have an associated box object"); - MOBoxManager *manager = [box manager]; - [manager associateObject:jsObjectRepresentingBox withBox:box]; + NSDictionary* context = (__bridge NSDictionary *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + NSCAssert([context isKindOfClass:[NSDictionary class]], @"should have an dictionary with context"); + + MOBoxManager* manager = context[@"manager"]; + id object = context[@"object"]; + [manager associateObject:object jsObject:jsObjectRepresentingBox]; } static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { // Give the object a chance to finalize itself - MOBox *box = (__bridge MOBox *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + void* private = JSObjectGetPrivate(jsObjectRepresentingBox); + MOBox *box = (__bridge MOBox *)private; 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]; diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index d09b44a..d8597b7 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -20,8 +20,7 @@ */ @interface MOBox : NSObject -- (id)initWithManager:(MOBoxManager*)manager object:(id)object; -- (void)associateObject:(JSObjectRef)jsObject; +- (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObjectRef)jsObject; - (void)disassociateObject; /*! @@ -32,7 +31,6 @@ */ @property (strong, readonly) id representedObject; -@property (weak) id representedObjectCanary; @property (strong) NSString *representedObjectCanaryDesc; /*! diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index f2e9294..9c79505 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -14,31 +14,31 @@ @implementation MOBox -- (id)initWithManager:(MOBoxManager *)manager object:(id)object { +- (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObjectRef)jsObject { self = [super init]; if (self) { _manager = manager; _representedObject = object; - _representedObjectCanary = object; - _representedObjectCanaryDesc = [object description]; + _representedObjectCanaryDesc = [NSString stringWithFormat:@"%@ %@", [NSDate date], object]; + _JSObject = jsObject; + JSObjectSetPrivate(jsObject, (__bridge void*)self); } return self; } -- (void)associateObject:(JSObjectRef)jsObject { - NSAssert(JSObjectGetPrivate(jsObject) == (__bridge void *)self, @"object should already be connected to this box"); - _JSObject = jsObject; -} - - (void)disassociateObject { + NSAssert(_manager != nil, @"shouldn't have been disassociated already"); JSObjectSetPrivate(_JSObject, nil); - _JSObject = nil; _representedObject = nil; + _manager = nil; } - (void)dealloc { - NSAssert((_JSObject == nil) && (_representedObject == nil), @"should have been disassociated"); + if (_manager) { + debug(@"box should have been disassociated for %@", _representedObjectCanaryDesc); + [self disassociateObject]; + } } @end diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h index a39e238..684324e 100644 --- a/src/framework/mocha/Objects/MOBoxManager.h +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -32,10 +32,11 @@ */ @interface MOBoxManager : NSObject + - (instancetype)initWithContext:(JSGlobalContextRef)context; - (void)cleanup; - (MOBox*)boxForObject:(id)object; - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass; -- (void)associateObject:(JSObjectRef)jsObject withBox:(MOBox*)box; +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; - (void)removeBoxForObject:(id)object; @end diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 120e5cd..2f0edd7 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -28,6 +28,8 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { - (void)cleanup { NSAssert([NSThread isMainThread], @"should be main thread"); + + // break any retain cycles between the boxed objects and the things that they are boxing for (NSValue* key in _index) { MOBox* box = [_index objectForKey:key]; [box disassociateObject]; @@ -38,7 +40,7 @@ - (void)cleanup { } - (MOBox*)boxForObject:(id)object { - debug(@"added box for %p %@", object, object); + debug(@"added box for %p %@", object, [object className]); NSAssert([NSThread isMainThread], @"should be main thread"); NSAssert(![object isKindOfClass:[MOBox class]], @"shouldn't box a box"); MOBox* box = [_index objectForKey:object]; @@ -48,15 +50,14 @@ - (MOBox*)boxForObject:(id)object { - (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 object:object]; - JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(box)); + NSDictionary* context = @{ @"manager" : self, @"object" : object }; + JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(context)); return jsObject; } -- (void)associateObject:(JSObjectRef)jsObject withBox:(MOBox*)box { - id object = box.representedObject; +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { NSAssert([_index objectForKey:object] == nil, @"shouldn't already have an entry for the object"); - [box associateObject:jsObject]; + MOBox* box = [[MOBox alloc] initWithManager:self object:object jsObject:jsObject]; [_index setObject:box forKey:object]; } @@ -68,7 +69,7 @@ - (void)removeBoxForObject:(id)object { [box disassociateObject]; [_index removeObjectForKey:object]; } else { - debug(@"shouldn't be asked to unbox something that has no box (the object was %p %@)", object, object); + debug(@"shouldn't be asked to unbox something that has no box (the object was %p %@)", object, [object className]); for (id key in _index) { box = [_index objectForKey:key]; if (box.representedObject == object) { From 093e56c2063b540495df06428e73d5a8db948718 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 11:52:42 +0000 Subject: [PATCH 60/80] improved comments and removed some more debug --- src/framework/mocha/Objects/MOBoxManager.m | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 2f0edd7..c604467 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -34,7 +34,12 @@ - (void)cleanup { MOBox* box = [_index objectForKey:key]; [box disassociateObject]; } + + // throw away the index, which will release any boxes still in it, which in turn should release the objects they were boxing _index = nil; + + // throw the context away, which should clean up any remaining JS objects + // (these should all have had their boxes removed by now, and their private pointers cleaned out) JSGlobalContextRelease(_context); _context = nil; } @@ -70,12 +75,6 @@ - (void)removeBoxForObject:(id)object { [_index removeObjectForKey:object]; } else { debug(@"shouldn't be asked to unbox something that has no box (the object was %p %@)", object, [object className]); - for (id key in _index) { - box = [_index objectForKey:key]; - if (box.representedObject == object) { - NSLog(@"found orphaned box"); - } - } } } From 4c565c02ab03fbd0798df39b510ad9fb6829b058 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 14:50:00 +0000 Subject: [PATCH 61/80] Cleaner implementation of the box creation - pass parameters in a custom class instead of a dictionary. --- Cocoa Script.xcodeproj/project.pbxproj | 11 +++++ src/framework/mocha/MochaRuntime.m | 9 ++--- src/framework/mocha/Objects/MOBoxManager.h | 2 +- src/framework/mocha/Objects/MOBoxManager.m | 20 ++++++---- .../mocha/Objects/MOBoxManagerBoxContext.h | 27 +++++++++++++ .../mocha/Objects/MOBoxManagerBoxContext.m | 40 +++++++++++++++++++ 6 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 src/framework/mocha/Objects/MOBoxManagerBoxContext.h create mode 100644 src/framework/mocha/Objects/MOBoxManagerBoxContext.m diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 5f4613a..03ce39a 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 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 */; }; + 22FBD9951C47EA55004CF31E /* MOBoxManagerBoxContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 22FBD9931C47EA55004CF31E /* MOBoxManagerBoxContext.h */; }; + 22FBD9961C47EA55004CF31E /* MOBoxManagerBoxContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 22FBD9941C47EA55004CF31E /* MOBoxManagerBoxContext.m */; }; + 22FBD9971C47EA55004CF31E /* MOBoxManagerBoxContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 22FBD9941C47EA55004CF31E /* MOBoxManagerBoxContext.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 */; }; @@ -403,6 +406,8 @@ 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 = ""; }; + 22FBD9931C47EA55004CF31E /* MOBoxManagerBoxContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MOBoxManagerBoxContext.h; path = src/framework/mocha/Objects/MOBoxManagerBoxContext.h; sourceTree = SOURCE_ROOT; }; + 22FBD9941C47EA55004CF31E /* MOBoxManagerBoxContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MOBoxManagerBoxContext.m; path = src/framework/mocha/Objects/MOBoxManagerBoxContext.m; sourceTree = SOURCE_ROOT; }; 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 = ""; }; @@ -1139,6 +1144,8 @@ CC66D87B181A2B0A0039A0A5 /* MOBox.m */, 229C1FF41C21B38D004C5B3B /* MOBoxManager.h */, 229C1FF51C21B38D004C5B3B /* MOBoxManager.m */, + 22FBD9931C47EA55004CF31E /* MOBoxManagerBoxContext.h */, + 22FBD9941C47EA55004CF31E /* MOBoxManagerBoxContext.m */, CC66D87C181A2B0A0039A0A5 /* MOClassDescription.h */, CC66D87D181A2B0A0039A0A5 /* MOClassDescription.m */, CC66D87E181A2B0A0039A0A5 /* MOClosure_Private.h */, @@ -1270,6 +1277,7 @@ CC66D869181A2AE10039A0A5 /* MochaRuntime_Private.h in Headers */, CC66D8B9181A2B0A0039A0A5 /* MOProtocolDescription_Private.h in Headers */, CC66D824181A2A710039A0A5 /* COSPreprocessor.h in Headers */, + 22FBD9951C47EA55004CF31E /* MOBoxManagerBoxContext.h in Headers */, CC66D836181A2A9B0039A0A5 /* COSCodeSketcher.h in Headers */, CC66D84E181A2AB10039A0A5 /* MOBridgeSupportSymbol.h in Headers */, CCD38343195A454F00C436B9 /* COSDatabase.h in Headers */, @@ -1569,6 +1577,7 @@ CC66D813181A2A470039A0A5 /* TDWordOrReservedState.m in Sources */, CC66D7CA181A2A470039A0A5 /* TDCommentState.m in Sources */, CC66D7E9181A2A470039A0A5 /* TDReservedWord.m in Sources */, + 22FBD9971C47EA55004CF31E /* MOBoxManagerBoxContext.m in Sources */, CC66D7D0181A2A470039A0A5 /* TDLetter.m in Sources */, CC66D75B181A2A210039A0A5 /* TETextUtils.m in Sources */, CC66D84D181A2AB10039A0A5 /* MOBridgeSupportParser.m in Sources */, @@ -1658,6 +1667,7 @@ CC9FEFE218296A2A009DB0F9 /* TDEmpty.m in Sources */, CC9FEFE418296A2A009DB0F9 /* TDLetter.m in Sources */, CC9FEFE618296A2A009DB0F9 /* TDLiteral.m in Sources */, + 22FBD9961C47EA55004CF31E /* MOBoxManagerBoxContext.m in Sources */, CC9FEFE818296A2A009DB0F9 /* TDLowercaseWord.m in Sources */, CC9FEFEA18296A2A009DB0F9 /* TDMultiLineCommentState.m in Sources */, CCD38344195A454F00C436B9 /* COSDatabase.m in Sources */, @@ -2027,6 +2037,7 @@ 9FC343D21AF7D2FE00B53759 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; C05733C708A9546B00998B17 /* Build configuration list for PBXNativeTarget "Cocoa Script Editor" */ = { isa = XCConfigurationList; diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index aaaa04f..19be823 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -11,6 +11,7 @@ #import "MOBox.h" #import "MOBoxManager.h" +#import "MOBoxManagerBoxContext.h" #import "MOUndefined.h" #import "MOMethod_Private.h" #import "MOClosure_Private.h" @@ -1131,12 +1132,10 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p #pragma mark Mocha Objects static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { - NSDictionary* context = (__bridge NSDictionary *)(JSObjectGetPrivate(jsObjectRepresentingBox)); - NSCAssert([context isKindOfClass:[NSDictionary class]], @"should have an dictionary with context"); + MOBoxManagerBoxContext* context = (__bridge MOBoxManagerBoxContext *)(JSObjectGetPrivate(jsObjectRepresentingBox)); + NSCAssert([context isKindOfClass:[MOBoxManagerBoxContext class]], @"should have an dictionary with context"); - MOBoxManager* manager = context[@"manager"]; - id object = context[@"object"]; - [manager associateObject:object jsObject:jsObjectRepresentingBox]; + [context finishMakingBoxForObject:jsObjectRepresentingBox]; } static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h index 684324e..9a34b45 100644 --- a/src/framework/mocha/Objects/MOBoxManager.h +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -11,6 +11,7 @@ @class MOBox; + /** Manages the boxing and un-boxing of non-JS objects. @@ -37,6 +38,5 @@ - (void)cleanup; - (MOBox*)boxForObject:(id)object; - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass; -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; - (void)removeBoxForObject:(id)object; @end diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index c604467..0b03b07 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -7,6 +7,7 @@ // #import "MOBoxManager.h" +#import "MOBoxManagerBoxContext.h" #import "MOBox.h" @implementation MOBoxManager { @@ -55,17 +56,11 @@ - (MOBox*)boxForObject:(id)object { - (JSObjectRef)makeBoxForObject:(id)object jsClass:(JSClassRef)jsClass { NSAssert([NSThread isMainThread], @"should be main thread"); NSAssert(![object isKindOfClass:[MOBox class]], @"shouldn't box a box"); - NSDictionary* context = @{ @"manager" : self, @"object" : object }; + MOBoxManagerBoxContext* context = [[MOBoxManagerBoxContext alloc] initWithManager:self object:object]; JSObjectRef jsObject = JSObjectMake(_context, jsClass, (__bridge void *)(context)); return jsObject; } -- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { - NSAssert([_index objectForKey:object] == nil, @"shouldn't already have an entry for the object"); - MOBox* box = [[MOBox alloc] initWithManager:self object:object jsObject:jsObject]; - [_index setObject:box forKey:object]; -} - - (void)removeBoxForObject:(id)object { debug(@"removing box for %p %@", object, object); NSAssert([NSThread isMainThread], @"should be main thread"); @@ -79,3 +74,14 @@ - (void)removeBoxForObject:(id)object { } @end + + +@implementation MOBoxManager(MOBoxManagerBoxContextSupport) + +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject { + NSAssert([_index objectForKey:object] == nil, @"shouldn't already have an entry for the object"); + MOBox* box = [[MOBox alloc] initWithManager:self object:object jsObject:jsObject]; + [_index setObject:box forKey:object]; +} + +@end \ No newline at end of file diff --git a/src/framework/mocha/Objects/MOBoxManagerBoxContext.h b/src/framework/mocha/Objects/MOBoxManagerBoxContext.h new file mode 100644 index 0000000..504da79 --- /dev/null +++ b/src/framework/mocha/Objects/MOBoxManagerBoxContext.h @@ -0,0 +1,27 @@ +// +// MOBoxManagerBoxContext.h +// +// +// Created by Sam Deane on 14/01/2016. +// +// + +#import +#import + +@class MOBoxManager; + +/** + Represents the setup informaton for a box. + + An instance of this class is initially assigned as the private data + for a new javascript object that is going to be boxed. When the + javascript object's initialize callback is called, the context is used + to finish making the box object. + + */ + +@interface MOBoxManagerBoxContext : NSObject +- (id)initWithManager:(MOBoxManager*)manager object:(id)object; +- (void)finishMakingBoxForObject:(JSObjectRef)jsObject; +@end diff --git a/src/framework/mocha/Objects/MOBoxManagerBoxContext.m b/src/framework/mocha/Objects/MOBoxManagerBoxContext.m new file mode 100644 index 0000000..c60d835 --- /dev/null +++ b/src/framework/mocha/Objects/MOBoxManagerBoxContext.m @@ -0,0 +1,40 @@ +// +// MOBoxManagerBoxContext.m +// +// +// Created by Sam Deane on 14/01/2016. +// +// + +#import "MOBoxManagerBoxContext.h" +#import "MOBoxManager.h" + +@interface MOBoxManager(MOBoxManagerBoxContextSupport) +- (void)associateObject:(id)object jsObject:(JSObjectRef)jsObject; +@end + +@interface MOBoxManagerBoxContext() +@property (strong, nonatomic) MOBoxManager* manager; +@property (strong, nonatomic) id object; +@end + +@implementation MOBoxManagerBoxContext + +- (id)initWithManager:(MOBoxManager*)manager object:(id)object { + NSParameterAssert(manager); + NSParameterAssert(object); + + self = [super init]; + if (self) { + _manager = manager; + _object = object; + } + + return self; +} + +- (void)finishMakingBoxForObject:(JSObjectRef)jsObject { + [_manager associateObject:_object jsObject:jsObject]; +} + +@end From c73b5fa656033c08cc8449c95d9390fa8cfeb6a9 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 14:53:06 +0000 Subject: [PATCH 62/80] Fixed out of date comment. --- 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 19be823..fa23815 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -1133,7 +1133,7 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { MOBoxManagerBoxContext* context = (__bridge MOBoxManagerBoxContext *)(JSObjectGetPrivate(jsObjectRepresentingBox)); - NSCAssert([context isKindOfClass:[MOBoxManagerBoxContext class]], @"should have an dictionary with context"); + NSCAssert([context isKindOfClass:[MOBoxManagerBoxContext class]], @"should have a context object"); [context finishMakingBoxForObject:jsObjectRepresentingBox]; } From d214f184a990099d1b2193bb46b5d30b4a2a244c Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 14 Jan 2016 14:59:47 +0000 Subject: [PATCH 63/80] Disabled some additional debugging. --- src/framework/mocha/Objects/MOBox.h | 7 +++++-- src/framework/mocha/Objects/MOBox.m | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index d8597b7..d81cfec 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -13,6 +13,7 @@ @class Mocha; @class MOBoxManager; +#define DEBUG_CRASHES 0 /*! * @class MOBox @@ -31,8 +32,6 @@ */ @property (strong, readonly) id representedObject; -@property (strong) NSString *representedObjectCanaryDesc; - /*! * @property JSObject * @abstract The JSObject representation of the box @@ -49,4 +48,8 @@ */ @property (weak, readonly) MOBoxManager *manager; +#if DEBUG_CRASHES +@property (strong) NSString *representedObjectCanaryDesc; +#endif + @end diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 9c79505..69e5681 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -19,7 +19,9 @@ - (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObje if (self) { _manager = manager; _representedObject = object; +#if DEBUG_CRASHES _representedObjectCanaryDesc = [NSString stringWithFormat:@"%@ %@", [NSDate date], object]; +#endif _JSObject = jsObject; JSObjectSetPrivate(jsObject, (__bridge void*)self); } @@ -36,7 +38,11 @@ - (void)disassociateObject { - (void)dealloc { if (_manager) { +#if DEBUG_CRASHES debug(@"box should have been disassociated for %@", _representedObjectCanaryDesc); +#else + debug(@"box should have been disassociated for %@", _representedObject); +#endif [self disassociateObject]; } } From 81e15f9bdd1c00358df68a9e3d6ef98e156bb042 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 15 Jan 2016 15:32:23 +0000 Subject: [PATCH 64/80] improved comment --- src/framework/mocha/Objects/MOBoxManager.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/framework/mocha/Objects/MOBoxManager.h b/src/framework/mocha/Objects/MOBoxManager.h index 9a34b45..2a393fc 100644 --- a/src/framework/mocha/Objects/MOBoxManager.h +++ b/src/framework/mocha/Objects/MOBoxManager.h @@ -30,6 +30,10 @@ 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. + + Note that the JS object does not increment the retain count for the MOBox object. + We ensure that there's a retain cycle MOBox -> object -> index -> MOBox, which keeps the box and its object + alive whilst the JS object is alive. */ @interface MOBoxManager : NSObject From 363a1ebd3baa76d710ddc59783e899e8cae2a395 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 18 Jan 2016 13:08:35 +0000 Subject: [PATCH 65/80] Disable assertions in release builds. --- Cocoa Script.xcodeproj/project.pbxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 03ce39a..6366438 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1957,6 +1957,7 @@ COPY_PHASE_STRIP = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; FRAMEWORK_VERSION = A; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = src/framework/CocoaScript_Prefix.pch; From 897f31b961a481b7b7dc20b4552d459511a057a3 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 19 Jan 2016 09:36:15 +0000 Subject: [PATCH 66/80] Downgraded an assertion to logging instead. --- src/framework/mocha/Objects/MOBox.h | 4 +++- src/framework/mocha/Objects/MOBox.m | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index d81cfec..b2f93ef 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -13,7 +13,9 @@ @class Mocha; @class MOBoxManager; -#define DEBUG_CRASHES 0 +#if DEBUG + #define DEBUG_CRASHES 1 +#endif /*! * @class MOBox diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 69e5681..e2fb2a5 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -20,7 +20,7 @@ - (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObje _manager = manager; _representedObject = object; #if DEBUG_CRASHES - _representedObjectCanaryDesc = [NSString stringWithFormat:@"%@ %@", [NSDate date], object]; + _representedObjectCanaryDesc = [NSString stringWithFormat:@"box: %p\nobject: %p %@\njs object: %p\nboxed at: %@\n", self, object, object, jsObject, [NSDate date]]; #endif _JSObject = jsObject; JSObjectSetPrivate(jsObject, (__bridge void*)self); @@ -30,10 +30,21 @@ - (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObje } - (void)disassociateObject { - NSAssert(_manager != nil, @"shouldn't have been disassociated already"); - JSObjectSetPrivate(_JSObject, nil); - _representedObject = nil; - _manager = nil; + if (_JSObject) { + JSObjectSetPrivate(_JSObject, nil); + _JSObject = nil; + } + + if (_manager) { + _representedObject = nil; + _manager = nil; + } else { +#if DEBUG_CRASHES + debug(@"shouldn't have been disassociated already %@", _representedObjectCanaryDesc); +#else + debug(@"shouldn't have been disassociated already %@", _representedObject); +#endif + } } - (void)dealloc { From 64e8b511c889a789ff1cd34143af372a0f60905f Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 16 Feb 2016 16:48:50 +0000 Subject: [PATCH 67/80] Analyzer warning fixes for situations where a parameter might be nil. --- src/framework/COSPreprocessor.m | 6 ++++-- src/framework/fmdb/COSDatabase.m | 27 ++++++++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/framework/COSPreprocessor.m b/src/framework/COSPreprocessor.m index c8f08bc..9fd4bed 100644 --- a/src/framework/COSPreprocessor.m +++ b/src/framework/COSPreprocessor.m @@ -391,8 +391,10 @@ - (NSString*)description { [methodArgs addObject:[currentArgs componentsJoinedByString:@" "]]; [currentArgs removeAllObjects]; } - - [selector appendString:lastWord]; + + if (lastWord) { + [selector appendString:lastWord]; + } [selector appendString:value]; } else { diff --git a/src/framework/fmdb/COSDatabase.m b/src/framework/fmdb/COSDatabase.m index 3c1af2e..d59be7d 100644 --- a/src/framework/fmdb/COSDatabase.m +++ b/src/framework/fmdb/COSDatabase.m @@ -325,20 +325,21 @@ - (COSStatement*)cachedStatementForQuery:(NSString*)query { - (void)setCachedStatement:(COSStatement*)statement forQuery:(NSString*)query { - - query = [query copy]; // in case we got handed in a mutable string... - [statement setQuery:query]; - - NSMutableSet* statements = [_cachedStatements objectForKey:query]; - if (!statements) { - statements = [NSMutableSet set]; + if (query) { + query = [query copy]; // in case we got handed in a mutable string... + [statement setQuery:query]; + + NSMutableSet* statements = [_cachedStatements objectForKey:query]; + if (!statements) { + statements = [NSMutableSet set]; + } + + [statements addObject:statement]; + + [_cachedStatements setObject:statements forKey:query]; + + FMDBRelease(query); } - - [statements addObject:statement]; - - [_cachedStatements setObject:statements forKey:query]; - - FMDBRelease(query); } #pragma mark Key routines From 1272fb3794419d683c00a144075693587f449bea Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 22 Feb 2016 15:15:28 +0000 Subject: [PATCH 68/80] Supply a name for the context when creating a COScript object. --- src/framework/COScript.h | 1 + src/framework/COScript.m | 7 +++++- src/framework/mocha/MochaRuntime.h | 12 ++++++++++ src/framework/mocha/MochaRuntime.m | 26 +++++++++++----------- src/framework/mocha/MochaRuntime_Private.h | 2 +- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/framework/COScript.h b/src/framework/COScript.h index 4a11409..2181113 100644 --- a/src/framework/COScript.h +++ b/src/framework/COScript.h @@ -30,6 +30,7 @@ @property (assign) BOOL shouldPreprocess; @property (assign) BOOL shouldKeepAround; +- (instancetype)initWithName:(NSString*)name; - (void)cleanup; - (void)garbageCollect; - (id)executeString:(NSString*) str; diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 25f4d79..1814821 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -70,10 +70,15 @@ + (void)setShouldLoadJSTPlugins:(BOOL)b { JSTalkShouldLoadJSTPlugins = b; } + - (id)init { + return [self initWithName:@"Untitled"]; +} + +- (instancetype)initWithName:(NSString*)name { self = [super init]; if ((self != nil)) { - _mochaRuntime = [[Mocha alloc] init]; + _mochaRuntime = [[Mocha alloc] initWithName:name]; [self setEnv:[NSMutableDictionary dictionary]]; [self setShouldPreprocess:YES]; diff --git a/src/framework/mocha/MochaRuntime.h b/src/framework/mocha/MochaRuntime.h index 298488c..0b589a3 100644 --- a/src/framework/mocha/MochaRuntime.h +++ b/src/framework/mocha/MochaRuntime.h @@ -32,6 +32,7 @@ + (Mocha *)sharedRuntime; + /*! * @property delegate * @abstract Gets the runtime delegate @@ -41,6 +42,17 @@ @property (unsafe_unretained) id delegate; +/*! + * @method initWithName: + * @abstract Create a new runtime, with a given name. + * + * @param name + * The name to use for the context (for use with interactive debugging). + * + * @result An object, or nil + */ +- (instancetype)initWithName:(NSString*)name; + /*! * @method evalString: * @abstract Evalutates the specified JavaScript expression, returning the result diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 7729fce..6c0605f 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -32,7 +32,6 @@ #import #import - // Class types static JSClassRef MochaClass = NULL; static JSClassRef MOObjectClass = NULL; @@ -180,12 +179,22 @@ + (Mocha *)runtimeWithContext:(JSContextRef)ctx { } - (id)init { - return [self initWithGlobalContext:NULL]; + return [self initWithGlobalContext:NULL name:nil]; +} + +- (id)initWithName:(NSString *)name { + return [self initWithGlobalContext:NULL name:name]; } -- (id)initWithGlobalContext:(JSGlobalContextRef)ctx { + +- (id)initWithGlobalContext:(JSGlobalContextRef)ctx name:(NSString*)name { if (ctx == NULL) { ctx = JSGlobalContextCreate(MochaClass); + if (name) { + JSStringRef jsName = JSStringCreateWithCFString((__bridge CFStringRef)name); + JSGlobalContextSetName(ctx, jsName); + JSStringRelease(jsName); + } _ownsContext = YES; } else { @@ -571,15 +580,6 @@ - (id)evalString:(NSString *)string { } - (id)evalString:(NSString *)string atURL:(NSURL *)url { -#ifdef MAC_OS_X_VERSION_10_10 - if (JSGlobalContextSetName != NULL) - { - NSString* name = url ? [[url lastPathComponent] stringByDeletingPathExtension] : @"Untitled Script"; - JSStringRef jsName = JSStringCreateWithUTF8CString([name UTF8String]); - JSGlobalContextSetName(_ctx, jsName); - JSStringRelease(jsName); - } -#endif JSValueRef jsValue = [self evalJSString:string scriptPath:[url path]]; return [self objectForJSValue:jsValue]; } @@ -610,7 +610,7 @@ - (JSValueRef)evalJSString:(NSString *)string scriptPath:(NSString *)scriptPath [self throwJSException:exception]; return NULL; } - + return result; } diff --git a/src/framework/mocha/MochaRuntime_Private.h b/src/framework/mocha/MochaRuntime_Private.h index 2f14245..e613501 100644 --- a/src/framework/mocha/MochaRuntime_Private.h +++ b/src/framework/mocha/MochaRuntime_Private.h @@ -14,7 +14,7 @@ + (Mocha *)runtimeWithContext:(JSContextRef)ctx; -- (id)initWithGlobalContext:(JSGlobalContextRef)ctx; +- (id)initWithGlobalContext:(JSGlobalContextRef)ctx name:(NSString*)name; @property (readonly) JSGlobalContextRef context; From d1f3e123f5df12f32e4162183b22bf40ea8e4236 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 11 Mar 2016 12:54:58 +0100 Subject: [PATCH 69/80] made JSTTextView part of Cocoascript so that we can use it in Sketch --- Cocoa Script.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index ad8535e..c412343 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 220D93E71C92E69C001EF05C /* JSTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = CC66D8CD181A2E2F0039A0A5 /* JSTTextView.m */; }; + 220D93E81C92E75A001EF05C /* JSTTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = CC66D8CC181A2E2F0039A0A5 /* JSTTextView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; @@ -1207,6 +1209,7 @@ CC66D8B2181A2B0A0039A0A5 /* MOPointer_Private.h in Headers */, CCA1A535183EA86E000159B3 /* COSInterval.h in Headers */, CC66D8BC181A2B0A0039A0A5 /* MOStruct.h in Headers */, + 220D93E81C92E75A001EF05C /* JSTTextView.h in Headers */, CC66D7D5181A2A470039A0A5 /* TDMultiLineCommentState.h in Headers */, CC66D7BD181A2A470039A0A5 /* TDAssembly.h in Headers */, CCE675E8182ED9C900DB1932 /* COSAlertWindow.h in Headers */, @@ -1486,6 +1489,7 @@ CC66D7E5181A2A470039A0A5 /* TDReader.m in Sources */, CC66D7D6181A2A470039A0A5 /* TDMultiLineCommentState.m in Sources */, CC66D8B1181A2B0A0039A0A5 /* MOObjCRuntime.m in Sources */, + 220D93E71C92E69C001EF05C /* JSTTextView.m in Sources */, CC07AF4D1982C310001CCA93 /* COSMarkdown.m in Sources */, CC66D807181A2A470039A0A5 /* TDTokenizerState.m in Sources */, CC66D84F181A2AB10039A0A5 /* MOBridgeSupportSymbol.m in Sources */, From e2abc7c678cf43ba3ab65a3a3c729c7b4400cb3d Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 11 Mar 2016 14:29:29 +0100 Subject: [PATCH 70/80] Removed some deprecated API usage. --- src/editor/JSTTextView.m | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/editor/JSTTextView.m b/src/editor/JSTTextView.m index e0bd8ca..e5ad5f9 100644 --- a/src/editor/JSTTextView.m +++ b/src/editor/JSTTextView.m @@ -168,8 +168,7 @@ - (void)parseCode:(id)sender { } - -- (void) textStorageDidProcessEditing:(NSNotification *)note { +- (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta { [self parseCode:nil]; } @@ -188,22 +187,22 @@ - (void)autoInsertText:(NSString*)text { } -- (void)insertText:(id)insertString { - +- (void)insertText:(id)insertString replacementRange:(NSRange)replacementRange { + if (!([JSTPrefs boolForKey:@"codeCompletionEnabled"])) { - [super insertText:insertString]; + [super insertText:insertString replacementRange:replacementRange]; return; } // make sure we're not doing anything fance in a quoted string. - if (NSMaxRange([self selectedRange]) < [[self textStorage] length] && [[[self textStorage] attributesAtIndex:[self selectedRange].location effectiveRange:nil] objectForKey:JSTQuotedStringAttributeName]) { - [super insertText:insertString]; + if (NSMaxRange(replacementRange) < [[self textStorage] length] && [[[self textStorage] attributesAtIndex:replacementRange.location effectiveRange:nil] objectForKey:JSTQuotedStringAttributeName]) { + [super insertText:insertString replacementRange:replacementRange]; return; } if ([@")" isEqualToString:insertString] && [_lastAutoInsert isEqualToString:@")"]) { - NSRange nextRange = [self selectedRange]; + NSRange nextRange = replacementRange; nextRange.length = 1; if (NSMaxRange(nextRange) <= [[self textStorage] length]) { @@ -222,8 +221,8 @@ - (void)insertText:(id)insertString { [self setLastAutoInsert:nil]; - [super insertText:insertString]; - + [super insertText:insertString replacementRange:replacementRange]; + NSRange currentRange = [self selectedRange]; NSRange r = [self selectionRangeForProposedRange:currentRange granularity:NSSelectByParagraph]; BOOL atEndOfLine = (NSMaxRange(r) - 1 == NSMaxRange(currentRange)); From eac11a067eafca5d3d95ff2b259da64574f8e6cf Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Tue, 15 Mar 2016 17:16:42 +0000 Subject: [PATCH 71/80] Pass back the script URL to the error handler if we have it. --- 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 1814821..36b1f08 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -352,7 +352,7 @@ - (id)executeString:(NSString*)str baseURL:(NSURL*)base { NSDictionary *d = [e userInfo]; if ([d objectForKey:@"line"]) { if ([_errorController respondsToSelector:@selector(coscript:hadError:onLineNumber:atSourceURL:)]) { - [_errorController coscript:self hadError:[e reason] onLineNumber:[[d objectForKey:@"line"] integerValue] atSourceURL:nil]; + [_errorController coscript:self hadError:[e reason] onLineNumber:[[d objectForKey:@"line"] integerValue] atSourceURL:base]; } } From 76bb178960183be612c6c5b03727540ce75d9dda Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 18 Mar 2016 12:36:49 +0000 Subject: [PATCH 72/80] Defer re-parsing of text to avoid messing up the selection. --- src/editor/JSTTextView.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/editor/JSTTextView.m b/src/editor/JSTTextView.m index e5ad5f9..de8daf4 100644 --- a/src/editor/JSTTextView.m +++ b/src/editor/JSTTextView.m @@ -169,7 +169,9 @@ - (void)parseCode:(id)sender { - (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta { - [self parseCode:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self parseCode:nil]; + }); } - (NSArray *)writablePasteboardTypes { From 9e5a8cd5746b7d5beb71aa4062737ed7a84b587a Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Mon, 21 Mar 2016 15:19:03 +0000 Subject: [PATCH 73/80] Fix for endless re-parsing problem. --- src/editor/JSTTextView.m | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/editor/JSTTextView.m b/src/editor/JSTTextView.m index de8daf4..6d66130 100644 --- a/src/editor/JSTTextView.m +++ b/src/editor/JSTTextView.m @@ -23,6 +23,7 @@ @interface JSTTextView () @property (assign) CGPoint initialDragPoint; @property (strong) NSNumber *initialNumber; @property (strong) NSMutableDictionary *numberRanges; +@property (assign) BOOL parsingInResponseToEdit; @end @@ -169,9 +170,16 @@ - (void)parseCode:(id)sender { - (void)textStorage:(NSTextStorage *)textStorage didProcessEditing:(NSTextStorageEditActions)editedMask range:(NSRange)editedRange changeInLength:(NSInteger)delta { - dispatch_async(dispatch_get_main_queue(), ^{ - [self parseCode:nil]; - }); + // SD: calling parseCode directly from here was causing the selection to be messed up when deleting characters - seemingly some sort of timing issue. + // deferring the parse has fixed that, but at the expense of causing potential recursion since the parse then seems to register as another edit. + // to avoid this, I've added the parsingInResponseToEdit flag, but it's all a bit clumsy; a better fix might be to sort out the original deletion problem + if (!self.parsingInResponseToEdit) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.parsingInResponseToEdit = YES; + [self parseCode:nil]; + self.parsingInResponseToEdit = NO; + }); + } } - (NSArray *)writablePasteboardTypes { From 6b5bc1a97f4e302589343a9beb4b78827aa6da1a Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 30 Jun 2016 16:21:10 +0100 Subject: [PATCH 74/80] Updated for XC8. --- Cocoa Script.xcodeproj/project.pbxproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index c412343..779cc14 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1337,7 +1337,7 @@ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; }; buildConfigurationList = C05733CB08A9546B00998B17 /* Build configuration list for PBXProject "Cocoa Script" */; compatibilityVersion = "Xcode 3.2"; @@ -1689,6 +1689,7 @@ C05733CC08A9546B00998B17 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; @@ -1719,6 +1720,7 @@ C05733CD08A9546B00998B17 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; From 430f1f388e01f9bfcc4acb3f5d00421062ba684f Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 1 Jul 2016 16:17:10 +0100 Subject: [PATCH 75/80] Turned off the localisation warning that XC8 enabled. --- Cocoa Script.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cocoa Script.xcodeproj/project.pbxproj b/Cocoa Script.xcodeproj/project.pbxproj index 779cc14..044ac2e 100644 --- a/Cocoa Script.xcodeproj/project.pbxproj +++ b/Cocoa Script.xcodeproj/project.pbxproj @@ -1689,7 +1689,6 @@ C05733CC08A9546B00998B17 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; @@ -1720,7 +1719,6 @@ C05733CD08A9546B00998B17 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; From b82ee868b8b0cc148aeed3653a794ba1ad7ffc26 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Thu, 7 Jul 2016 10:33:43 +0100 Subject: [PATCH 76/80] Ensure that runtime is thrown away when we cleanup - breaks retain cycle which would otherwise occur if the runtime contains a global object that owns a reference to the COScript object (which happens in Sketch). --- src/framework/COScript.m | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/COScript.m b/src/framework/COScript.m index 36b1f08..cf9215f 100644 --- a/src/framework/COScript.m +++ b/src/framework/COScript.m @@ -107,6 +107,7 @@ - (void)cleanup { [self deleteObjectWithName:@"log"]; [_mochaRuntime shutdown]; + _mochaRuntime = nil; } From 629c08e52e28146c554cb018a092801d4755b827 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 8 Jul 2016 15:38:37 +0100 Subject: [PATCH 77/80] Factored out the code that calls JSObjectGetPrivate, and that returns the boxed object, so that we can annotate it with extra debug if necessary. --- src/framework/mocha/MochaRuntime.m | 50 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/framework/mocha/MochaRuntime.m b/src/framework/mocha/MochaRuntime.m index 263721a..b5d14b0 100644 --- a/src/framework/mocha/MochaRuntime.m +++ b/src/framework/mocha/MochaRuntime.m @@ -1131,6 +1131,19 @@ JSValueRef Mocha_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef p #pragma mark - #pragma mark Mocha Objects +static inline MOBox* boxForJSObject(JSObjectRef jsObject) { + void* private = JSObjectGetPrivate(jsObject); + MOBox *box = (__bridge MOBox *)private; + 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"); + return box; +} + +static inline id objectForJSObject(JSObjectRef jsObject) { + MOBox* box = boxForJSObject(jsObject); + id object = [box representedObject]; + return object; +} + static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresentingBox) { MOBoxManagerBoxContext* context = (__bridge MOBoxManagerBoxContext *)(JSObjectGetPrivate(jsObjectRepresentingBox)); NSCAssert([context isKindOfClass:[MOBoxManagerBoxContext class]], @"should have a context object"); @@ -1140,9 +1153,7 @@ static void MOObject_initialize(JSContextRef ctx, JSObjectRef jsObjectRepresenti static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { // Give the object a chance to finalize itself - void* private = JSObjectGetPrivate(jsObjectRepresentingBox); - MOBox *box = (__bridge MOBox *)private; - 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"); + MOBox* box = boxForJSObject(jsObjectRepresentingBox); id boxedObject = [box representedObject]; if ([boxedObject respondsToSelector:@selector(finalizeForMochaScript)]) { @@ -1161,10 +1172,7 @@ static void MOObject_finalize(JSObjectRef jsObjectRepresentingBox) { static bool MOBoxedObject_hasProperty(JSContextRef ctx, JSObjectRef objectJS, JSStringRef propertyNameJS) { NSString *propertyName = (NSString *)CFBridgingRelease(JSStringCopyCFString(NULL, propertyNameJS)); -// Mocha *runtime = [Mocha runtimeWithContext:ctx]; - - id private = (__bridge id)(JSObjectGetPrivate(objectJS)); - id object = [private representedObject]; + id object = objectForJSObject(objectJS); Class objectClass = [object class]; // String conversion @@ -1293,8 +1301,7 @@ static JSValueRef MOBoxedObject_getProperty(JSContextRef ctx, JSObjectRef object Mocha *runtime = [Mocha runtimeWithContext:ctx]; - id private = (__bridge id)(JSObjectGetPrivate(objectJS)); - id object = [private representedObject]; + id object = objectForJSObject(objectJS); Class objectClass = [object class]; // Perform the lookup @@ -1465,8 +1472,7 @@ static bool MOBoxedObject_setProperty(JSContextRef ctx, JSObjectRef objectJS, JS Mocha *runtime = [Mocha runtimeWithContext:ctx]; - id private = (__bridge id)(JSObjectGetPrivate(objectJS)); - id object = [private representedObject]; + id object = objectForJSObject(objectJS); Class objectClass = [object class]; id value = [runtime objectForJSValue:valueJS]; @@ -1524,9 +1530,8 @@ static bool MOBoxedObject_deleteProperty(JSContextRef ctx, JSObjectRef objectJS, Mocha *runtime = [Mocha runtimeWithContext:ctx]; - id private = (__bridge id)(JSObjectGetPrivate(objectJS)); - id object = [private representedObject]; - + id object = objectForJSObject(objectJS); + // Perform the lookup @try { // Indexed subscript @@ -1556,11 +1561,7 @@ static bool MOBoxedObject_deleteProperty(JSContextRef ctx, JSObjectRef objectJS, } static void MOBoxedObject_getPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) { - MOBox *privateObject = (__bridge MOBox *)(JSObjectGetPrivate(object)); - - // If we have a dictionary, add keys from allKeys - id o = [privateObject representedObject]; - + id o = objectForJSObject(object); if ([o isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = o; NSArray *keys = [dictionary allKeys]; @@ -1579,9 +1580,8 @@ static JSValueRef MOBoxedObject_convertToType(JSContextRef ctx, JSObjectRef obje static bool MOBoxedObject_hasInstance(JSContextRef ctx, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef *exception) { Mocha *runtime = [Mocha runtimeWithContext:ctx]; - MOBox *privateObject = (__bridge MOBox *)(JSObjectGetPrivate(constructor)); - id representedObject = [privateObject representedObject]; - + id representedObject = objectForJSObject(constructor); + if (!JSValueIsObject(ctx, possibleInstance)) { return false; } @@ -1590,8 +1590,7 @@ static bool MOBoxedObject_hasInstance(JSContextRef ctx, JSObjectRef constructor, if (instanceObj == nil) { return NO; } - MOBox *instancePrivateObj = (__bridge MOBox *)(JSObjectGetPrivate(instanceObj)); - id instanceRepresentedObj = [instancePrivateObj representedObject]; + id instanceRepresentedObj = objectForJSObject(instanceObj); // Check to see if the object's class matches the passed-in class @try { @@ -1623,8 +1622,7 @@ static JSObjectRef MOConstructor_callAsConstructor(JSContextRef ctx, JSObjectRef static JSValueRef MOFunction_callAsFunction(JSContextRef ctx, JSObjectRef functionJS, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { Mocha *runtime = [Mocha runtimeWithContext:ctx]; - MOBox *private = (__bridge MOBox *)(JSObjectGetPrivate(functionJS)); - id function = [private representedObject]; + id function = objectForJSObject(functionJS); JSValueRef value = NULL; // if ([function isKindOfClass:[MOMethod class]]) { From 884b1fa4e10dbd6ee46bf93f2254723752adc9df Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 8 Jul 2016 15:40:27 +0100 Subject: [PATCH 78/80] added a generation counter, and some more debugging --- src/framework/mocha/Objects/MOBox.h | 1 + src/framework/mocha/Objects/MOBox.m | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/framework/mocha/Objects/MOBox.h b/src/framework/mocha/Objects/MOBox.h index b2f93ef..c18ac7a 100644 --- a/src/framework/mocha/Objects/MOBox.h +++ b/src/framework/mocha/Objects/MOBox.h @@ -52,6 +52,7 @@ #if DEBUG_CRASHES @property (strong) NSString *representedObjectCanaryDesc; +@property (assign) NSUInteger count; #endif @end diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index e2fb2a5..903579a 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -14,24 +14,35 @@ @implementation MOBox +static NSUInteger initCount = 0; + - (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObjectRef)jsObject { self = [super init]; if (self) { + NSAssert(manager != nil, @"valid manager"); _manager = manager; _representedObject = object; #if DEBUG_CRASHES _representedObjectCanaryDesc = [NSString stringWithFormat:@"box: %p\nobject: %p %@\njs object: %p\nboxed at: %@\n", self, object, object, jsObject, [NSDate date]]; + _count = initCount++; #endif _JSObject = jsObject; JSObjectSetPrivate(jsObject, (__bridge void*)self); + debug(@"set private for %p to %p (%@)", jsObject, self, [_representedObject className]); } return self; } - (void)disassociateObject { +#if DEBUG_CRASHES + debug(@"dissassociated %p %ld", self, _count); +#else + debug(@"dissassociated %p", self); +#endif if (_JSObject) { JSObjectSetPrivate(_JSObject, nil); + debug(@"cleared private for %p", _JSObject); _JSObject = nil; } @@ -48,7 +59,12 @@ - (void)disassociateObject { } - (void)dealloc { - if (_manager) { +#if DEBUG_CRASHES + debug(@"dealloced %p %ld", self, _count); +#else + debug(@"dealloced %p", self); +#endif + if (_manager || _JSObject) { #if DEBUG_CRASHES debug(@"box should have been disassociated for %@", _representedObjectCanaryDesc); #else From e412d2e651d8f02275a0f4675da5ade75cae506f Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 8 Jul 2016 15:49:44 +0100 Subject: [PATCH 79/80] Cleaned up some index behaviour. We were sometimes missing out some boxes when performing cleanup, because the keys returned to us by (for NSValue* key in _index) were failing to return a value when [_index objectForKey:key] was called! I presume that this is because the key lookup was using isEqual: and somehow was failing to match somehow. To avoid this we turn on NSMapTableObjectPointerPersonality for the keys. For extra paranoia, we iterate the collection's objects, rather than its keys, so that we can be certain we're getting them all. --- src/framework/mocha/Objects/MOBoxManager.m | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/framework/mocha/Objects/MOBoxManager.m b/src/framework/mocha/Objects/MOBoxManager.m index 0b03b07..e4deff6 100644 --- a/src/framework/mocha/Objects/MOBoxManager.m +++ b/src/framework/mocha/Objects/MOBoxManager.m @@ -19,7 +19,9 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { NSAssert([NSThread isMainThread], @"should be main thread"); self = [super init]; if (self) { - _index = [NSMapTable strongToStrongObjectsMapTable]; + // we want to use NSMapTableObjectPointerPersonality for the keys, since we are associating boxes with specific objects + // and not with the underlying values of them (if two objects are equal according to isEqual:, we still want a box for both) + _index = [[NSMapTable alloc] initWithKeyOptions:NSMapTableObjectPointerPersonality | NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:0]; _context = context; JSGlobalContextRetain(context); } @@ -27,12 +29,18 @@ - (instancetype)initWithContext:(JSGlobalContextRef)context { return self; } +- (void)dealloc { + debug(@"dealloced manager"); +} + - (void)cleanup { NSAssert([NSThread isMainThread], @"should be main thread"); // break any retain cycles between the boxed objects and the things that they are boxing - for (NSValue* key in _index) { - MOBox* box = [_index objectForKey:key]; + NSEnumerator* enumerator = [_index objectEnumerator]; + MOBox* box = nil; + while (box = [enumerator nextObject]) { + debug(@"cleaned %p %ld", box, box.count); [box disassociateObject]; } From 1580a9e8a87f3eefe47581f39411b46c0dbcab52 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 8 Jul 2016 16:04:17 +0100 Subject: [PATCH 80/80] Fixed analyser warning. --- src/framework/mocha/Objects/MOBox.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/framework/mocha/Objects/MOBox.m b/src/framework/mocha/Objects/MOBox.m index 903579a..0dcdc58 100644 --- a/src/framework/mocha/Objects/MOBox.m +++ b/src/framework/mocha/Objects/MOBox.m @@ -14,7 +14,10 @@ @implementation MOBox +#if DEBUG_CRASHES static NSUInteger initCount = 0; +#endif + - (id)initWithManager:(MOBoxManager *)manager object:(id)object jsObject:(JSObjectRef)jsObject { self = [super init];