Skip to content

Commit b28cc4c

Browse files
Fix crash when redefine class.
1 parent b3860bf commit b28cc4c

10 files changed

Lines changed: 162 additions & 67 deletions

File tree

Classes/JSBScriptingSupport.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
@protocol JSBScriptingSupport <JSExport>
1515

1616
@required
17-
+ (JSContext *)globalContext;
18-
1917
JSExportAs(defineClass,
2018
+ (id)defineClass:(NSString *)declaration instanceMembers:(JSValue *)instanceMembers staticMembers:(JSValue *)staticMembers);
2119

Classes/JSBScriptingSupport.m

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
#import "JSBScriptingSupport.h"
10+
#import "JSBScriptingSupport+Private.h"
1011
#import "JSBNSObject.h"
1112
#import "JSBMessageForwarding.h"
1213
#import "JSContext+JavaScriptBridge.h"
@@ -30,50 +31,7 @@ + (void)initialize
3031
[globalContext addScriptingSupport:@"UIKit"];
3132
[globalContext addScriptingSupport:@"QuartzCore"];
3233

33-
globalContext[@"__JSB_JSBScriptingSupport"] = [JSBScriptingSupport class];
34-
[globalContext evaluateScript:
35-
@"JSB = (function() {\n"
36-
@" var namespace = {\n"
37-
@" defineClass: function(declaration, instanceMembers, staticMembers) {\n"
38-
@" return __JSB_JSBScriptingSupport.defineClass(declaration,"
39-
@" instanceMembers == null ? {} : instanceMembers,"
40-
@" staticMembers == null ? {} : staticMembers);\n"
41-
@" },\n"
42-
@" define: function(declaration, instanceMembers, staticMembers) {\n"
43-
@" JSB.dump('`define` is deprecated, use `defineClass` instead.');\n"
44-
@" return __JSB_JSBScriptingSupport.defineClass(declaration, instanceMembers, staticMembers);\n"
45-
@" },\n"
46-
@" require: function(name) {\n"
47-
@" return __JSB_JSBScriptingSupport.require(name);\n"
48-
@" },\n"
49-
@" exports: {},\n"
50-
@" selector: function(str) {\n"
51-
@" return __JSB_JSBScriptingSupport.selectorFromString(str);\n"
52-
@" },\n"
53-
@" dispatch_async: function(queue, block) {\n"
54-
@" return __JSB_JSBScriptingSupport.dispatch_async(queue, block);\n"
55-
@" },\n"
56-
@" dispatch_get_global_queue: function(priority, flags) {\n"
57-
@" return __JSB_JSBScriptingSupport.dispatch_get_global_queue(priority, flags);\n"
58-
@" },\n"
59-
@" dispatch_get_main_queue: function() {\n"
60-
@" return __JSB_JSBScriptingSupport.dispatch_get_main_queue();\n"
61-
@" },\n"
62-
@" log: function(format) {\n"
63-
@" var args = [];\n"
64-
@" for (var i = 1; i < arguments.length; i++) {\n"
65-
@" args.push(arguments[i]);\n"
66-
@" }\n"
67-
@" return __JSB_JSBScriptingSupport.log(format, args);\n"
68-
@" },\n"
69-
@" dump: function(obj) {\n"
70-
@" return __JSB_JSBScriptingSupport.dump(obj);\n"
71-
@" }\n"
72-
@" };\n"
73-
@"\n"
74-
@" return namespace;\n"
75-
@"})();\n"
76-
];
34+
[self setupSupportFunctionsToContext:globalContext];
7735
});
7836
}
7937

@@ -109,7 +67,11 @@ + (id)defineClass:(NSString *)declaration instanceMembers:(JSValue *)instanceMem
10967
parentClassName = @"NSObject";
11068
}
11169

112-
Class cls = objc_allocateClassPair(NSClassFromString(parentClassName), className.UTF8String, 0);
70+
Class cls = objc_getClass(className.UTF8String);
71+
if (cls) {
72+
objc_disposeClassPair(cls);
73+
}
74+
cls = objc_allocateClassPair(NSClassFromString(parentClassName), className.UTF8String, 0);
11375
objc_registerClassPair(cls);
11476

11577
Class superClass = class_getSuperclass(cls);
@@ -120,7 +82,7 @@ + (id)defineClass:(NSString *)declaration instanceMembers:(JSValue *)instanceMem
12082
NSString *types;
12183
BOOL result;
12284

123-
Class metaClass = objc_getMetaClass(className.UTF8String);
85+
Class metaClass = objc_getClass(className.UTF8String);
12486

12587
types = [NSString stringWithFormat:@"%s%s%s%s", @encode(NSMethodSignature), @encode(id), @encode(SEL), @encode(SEL)];
12688
result = class_addMethod(cls, @selector(methodSignatureForSelector:), (IMP)methodSignatureForSelector, types.UTF8String);
@@ -140,10 +102,12 @@ + (id)defineClass:(NSString *)declaration instanceMembers:(JSValue *)instanceMem
140102

141103
class_addProtocol(cls, @protocol(JSBNSObject));
142104

105+
JSContext *context = [self currentContext];
106+
143107
NSString *key = mangledNameFromClass(cls);
144-
globalContext[key] = cls;
145-
globalContext[key][JSBInstanceMembersKey] = instanceMembers;
146-
globalContext[key][JSBStaticMembersKey] = staticMembers;
108+
context[key] = cls;
109+
context[key][JSBInstanceMembersKey] = instanceMembers;
110+
context[key][JSBStaticMembersKey] = staticMembers;
147111

148112
return cls;
149113
}
@@ -160,10 +124,12 @@ + (id)require:(NSString *)name
160124
script = [NSString stringWithContentsOfFile:[name stringByAppendingPathExtension:@"js"] encoding:NSUTF8StringEncoding error:nil];
161125
}
162126
if (script) {
163-
JSValue *function = globalContext[@"Function"];
127+
JSContext *context = [self currentContext];
128+
129+
JSValue *function = context[@"Function"];
164130
JSValue *value = [function constructWithArguments:@[script]];
165131
[value callWithArguments:nil];
166-
module = globalContext[@"JSB"][@"exports"];
132+
module = context[@"JSB"][@"exports"];
167133
}
168134

169135
return module;

Classes/JSContext+JavaScriptBridge.m

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,20 @@ @implementation JSContext (JavaScriptBridge)
1212

1313
- (void)addScriptingSupport:(NSString *)framework
1414
{
15+
static NSMapTable *hashTables;
16+
if (!hashTables) {
17+
hashTables = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsWeakMemory valueOptions:NSPointerFunctionsWeakMemory];
18+
}
19+
20+
NSHashTable *frameworks = [hashTables objectForKey:self];
21+
if (!frameworks) {
22+
frameworks = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
23+
[hashTables setObject:frameworks forKey:self];
24+
}
25+
1526
NSString *prefix = @"JSB";
1627
id classObject = NSClassFromString([NSString stringWithFormat:@"%@%@", prefix, framework]);
17-
if (classObject) {
28+
if (classObject && ![frameworks containsObject:framework]) {
1829
SEL selector = NSSelectorFromString(@"addScriptingSupportToContext:");
1930

2031
NSMethodSignature *methodSignature = [classObject methodSignatureForSelector:selector];
@@ -26,6 +37,8 @@ - (void)addScriptingSupport:(NSString *)framework
2637
[invocation setArgument:&context atIndex:2];
2738

2839
[invocation invoke];
40+
41+
[frameworks addObject:framework];
2942
}
3043
}
3144

Classes/Private/JSBMessageForwarding.m

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//
88

99
#import "JSBMessageForwarding.h"
10-
#import "JSBScriptingSupport.h"
10+
#import "JSBScriptingSupport+Private.h"
1111

1212
NSString * const JSBInstanceMembersKey = @"instanceMembers";
1313
NSString * const JSBStaticMembersKey = @"staticMembers";
@@ -41,7 +41,7 @@
4141

4242
JSValue *propertyForObject(id obj, NSString *propertyName)
4343
{
44-
JSContext *context = [JSBScriptingSupport globalContext];
44+
JSContext *context = [JSBScriptingSupport currentContext];
4545

4646
JSValue *properties = nil;
4747

@@ -71,7 +71,7 @@ void invokeSuper(NSInvocation *inv)
7171

7272
void dispatchFunction(id self, JSValue *function, NSArray *parameters)
7373
{
74-
JSContext *context = [JSBScriptingSupport globalContext];
74+
JSContext *context = [JSBScriptingSupport currentContext];
7575
context[@"self"] = self;
7676

7777
[function callWithArguments:parameters];
@@ -212,7 +212,7 @@ void setReturnValue(JSValue *value, NSInvocation *invocation)
212212

213213
CGFloat tableViewHeightForRowAtIndexPath(id self, SEL _cmd, UITableView *tableView, NSIndexPath *indexPath)
214214
{
215-
JSContext *context = [JSBScriptingSupport globalContext];
215+
JSContext *context = [JSBScriptingSupport currentContext];
216216

217217
NSString *propertyName = propertyNameFromSelector(_cmd);
218218
JSValue *function = context[mangledNameFromClass(object_getClass(self))][JSBInstanceMembersKey][propertyName];
@@ -233,7 +233,7 @@ CGFloat tableViewHeightForRowAtIndexPath(id self, SEL _cmd, UITableView *tableVi
233233

234234
CGFloat tableViewHeightForHeaderInSection(id self, SEL _cmd, UITableView *tableView, NSInteger section)
235235
{
236-
JSContext *context = [JSBScriptingSupport globalContext];
236+
JSContext *context = [JSBScriptingSupport currentContext];
237237

238238
NSString *propertyName = propertyNameFromSelector(_cmd);
239239
JSValue *function = context[mangledNameFromClass(object_getClass(self))][JSBInstanceMembersKey][propertyName];
@@ -254,7 +254,7 @@ CGFloat tableViewHeightForHeaderInSection(id self, SEL _cmd, UITableView *tableV
254254

255255
CGFloat tableViewHeightForFooterInSection(id self, SEL _cmd, UITableView *tableView, NSInteger section)
256256
{
257-
JSContext *context = [JSBScriptingSupport globalContext];
257+
JSContext *context = [JSBScriptingSupport currentContext];
258258

259259
NSString *propertyName = propertyNameFromSelector(_cmd);
260260
JSValue *function = context[mangledNameFromClass(object_getClass(self))][JSBInstanceMembersKey][propertyName];
@@ -343,7 +343,7 @@ void setupForwardingImplementations(Class targetClass, Class cls, JSValue *insta
343343

344344
void forwardInvocation(id self, SEL _cmd, NSInvocation *invocation)
345345
{
346-
JSContext *context = [JSBScriptingSupport globalContext];
346+
JSContext *context = [JSBScriptingSupport currentContext];
347347

348348
if ([[self superclass] instancesRespondToSelector:invocation.selector]) {
349349
invokeSuper(invocation);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// JSBScriptingSupport+Private.h
3+
// UICatalog
4+
//
5+
// Created by kishikawa katsumi on 2014/02/14.
6+
// Copyright (c) 2014 kishikawa katsumi. All rights reserved.
7+
//
8+
9+
#import "JSBScriptingSupport.h"
10+
11+
@interface JSBScriptingSupport (Private)
12+
13+
+ (JSContext *)currentContext;
14+
15+
+ (void)setupSupportFunctionsToContext:(JSContext *)context;
16+
17+
@end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// JSBScriptingSupport+Private.m
3+
// UICatalog
4+
//
5+
// Created by kishikawa katsumi on 2014/02/14.
6+
// Copyright (c) 2014 kishikawa katsumi. All rights reserved.
7+
//
8+
9+
#import "JSBScriptingSupport+Private.h"
10+
11+
@implementation JSBScriptingSupport (Private)
12+
13+
+ (JSContext *)currentContext
14+
{
15+
static JSContext *currentContext;
16+
JSContext *context = [JSContext currentContext];
17+
if (!context) {
18+
context = currentContext;
19+
} else {
20+
currentContext = context;
21+
}
22+
23+
return context;
24+
}
25+
26+
+ (void)setupSupportFunctionsToContext:(JSContext *)context
27+
{
28+
static NSHashTable *initializedContext;
29+
if (!initializedContext) {
30+
context[@"__JSB_JSBScriptingSupport"] = self;
31+
[context evaluateScript:
32+
@"JSB = (function() {\n"
33+
@" var namespace = {\n"
34+
@" defineClass: function(declaration, instanceMembers, staticMembers) {\n"
35+
@" return __JSB_JSBScriptingSupport.defineClass(declaration,"
36+
@" instanceMembers == null ? {} : instanceMembers,"
37+
@" staticMembers == null ? {} : staticMembers);\n"
38+
@" },\n"
39+
@" define: function(declaration, instanceMembers, staticMembers) {\n"
40+
@" JSB.dump('`define` is deprecated, use `defineClass` instead.');\n"
41+
@" return __JSB_JSBScriptingSupport.defineClass(declaration, instanceMembers, staticMembers);\n"
42+
@" },\n"
43+
@" require: function(name) {\n"
44+
@" return __JSB_JSBScriptingSupport.require(name);\n"
45+
@" },\n"
46+
@" exports: {},\n"
47+
@" selector: function(str) {\n"
48+
@" return __JSB_JSBScriptingSupport.selectorFromString(str);\n"
49+
@" },\n"
50+
@" dispatch_async: function(queue, block) {\n"
51+
@" return __JSB_JSBScriptingSupport.dispatch_async(queue, block);\n"
52+
@" },\n"
53+
@" dispatch_get_global_queue: function(priority, flags) {\n"
54+
@" return __JSB_JSBScriptingSupport.dispatch_get_global_queue(priority, flags);\n"
55+
@" },\n"
56+
@" dispatch_get_main_queue: function() {\n"
57+
@" return __JSB_JSBScriptingSupport.dispatch_get_main_queue();\n"
58+
@" },\n"
59+
@" log: function(format) {\n"
60+
@" var args = [];\n"
61+
@" for (var i = 1; i < arguments.length; i++) {\n"
62+
@" args.push(arguments[i]);\n"
63+
@" }\n"
64+
@" return __JSB_JSBScriptingSupport.log(format, args);\n"
65+
@" },\n"
66+
@" dump: function(obj) {\n"
67+
@" return __JSB_JSBScriptingSupport.dump(obj);\n"
68+
@" }\n"
69+
@" };\n"
70+
@"\n"
71+
@" return namespace;\n"
72+
@"})();\n"
73+
];
74+
75+
[initializedContext addObject:context];
76+
}
77+
}
78+
79+
@end

Examples/HelloWorld/HelloWorld.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
14A04848187829CB004831E8 /* JSBiAd.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A04829187829CB004831E8 /* JSBiAd.m */; };
6767
14A98447189BF01F0033D110 /* UIGestureRecognizer+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A98444189BF01F0033D110 /* UIGestureRecognizer+JavaScriptBridge.m */; };
6868
14A98448189BF01F0033D110 /* UIMenuItem+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 14A98446189BF01F0033D110 /* UIMenuItem+JavaScriptBridge.m */; };
69+
14E28D8E18AD1E2F0084F84A /* JSBScriptingSupport+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E28D8D18AD1E2F0084F84A /* JSBScriptingSupport+Private.m */; };
6970
/* End PBXBuildFile section */
7071

7172
/* Begin PBXFileReference section */
@@ -797,6 +798,8 @@
797798
14A98444189BF01F0033D110 /* UIGestureRecognizer+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIGestureRecognizer+JavaScriptBridge.m"; sourceTree = "<group>"; };
798799
14A98445189BF01F0033D110 /* UIMenuItem+JavaScriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIMenuItem+JavaScriptBridge.h"; sourceTree = "<group>"; };
799800
14A98446189BF01F0033D110 /* UIMenuItem+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIMenuItem+JavaScriptBridge.m"; sourceTree = "<group>"; };
801+
14E28D8C18AD1E2F0084F84A /* JSBScriptingSupport+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSBScriptingSupport+Private.h"; sourceTree = "<group>"; };
802+
14E28D8D18AD1E2F0084F84A /* JSBScriptingSupport+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSBScriptingSupport+Private.m"; sourceTree = "<group>"; };
800803
/* End PBXFileReference section */
801804

802805
/* Begin PBXFrameworksBuildPhase section */
@@ -891,6 +894,8 @@
891894
14A0455D187829B4004831E8 /* Private */ = {
892895
isa = PBXGroup;
893896
children = (
897+
14E28D8C18AD1E2F0084F84A /* JSBScriptingSupport+Private.h */,
898+
14E28D8D18AD1E2F0084F84A /* JSBScriptingSupport+Private.m */,
894899
14A0455E187829B4004831E8 /* JSBMessageForwarding.h */,
895900
149A45EF18796C5D007C2CA7 /* JSBMessageForwarding.m */,
896901
1448F2D3189EAAFA005BC527 /* NSObject+JavaScriptBridge.h */,
@@ -1952,6 +1957,7 @@
19521957
140E224618A2B1AE00A9F9EF /* NSMutableString+JavaScriptBridge.m in Sources */,
19531958
1448F2D8189EAAFA005BC527 /* UIBarButtonItem+JavaScriptCore.m in Sources */,
19541959
14A04834187829CB004831E8 /* JSBCoreMotion.m in Sources */,
1960+
14E28D8E18AD1E2F0084F84A /* JSBScriptingSupport+Private.m in Sources */,
19551961
149A460C1879730A007C2CA7 /* SLComposeViewController+JavaScriptBridge.m in Sources */,
19561962
14A0453918782920004831E8 /* main.m in Sources */,
19571963
140E224918A2B1AE00A9F9EF /* UIAlertView+JavaScriptBridge.m in Sources */,

Examples/UICatalog/UICatalog.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
14B5CD301875C3740019A135 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14B5CD2F1875C3740019A135 /* AppDelegate.m */; };
7676
14B5CD321875C3740019A135 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14B5CD311875C3740019A135 /* Images.xcassets */; };
7777
14D6C30C18793F12005C7018 /* collectionViewController.js in Resources */ = {isa = PBXBuildFile; fileRef = 14D6C30B18793F12005C7018 /* collectionViewController.js */; };
78+
14E28D8718AD1BF30084F84A /* JSBScriptingSupport+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E28D8618AD1BF30084F84A /* JSBScriptingSupport+Private.m */; };
7879
14F55CFB18A1882E008B798E /* gradientViewController.js in Resources */ = {isa = PBXBuildFile; fileRef = 14F55CFA18A1882E008B798E /* gradientViewController.js */; };
7980
/* End PBXBuildFile section */
8081

@@ -816,6 +817,8 @@
816817
14B5CD311875C3740019A135 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
817818
14B5CD381875C3750019A135 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
818819
14D6C30B18793F12005C7018 /* collectionViewController.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = collectionViewController.js; path = js/collectionViewController.js; sourceTree = "<group>"; };
820+
14E28D8518AD1BF30084F84A /* JSBScriptingSupport+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "JSBScriptingSupport+Private.h"; path = "../../../Classes/Private/JSBScriptingSupport+Private.h"; sourceTree = "<group>"; };
821+
14E28D8618AD1BF30084F84A /* JSBScriptingSupport+Private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "JSBScriptingSupport+Private.m"; path = "../../../Classes/Private/JSBScriptingSupport+Private.m"; sourceTree = "<group>"; };
819822
14F55CFA18A1882E008B798E /* gradientViewController.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = gradientViewController.js; path = js/gradientViewController.js; sourceTree = "<group>"; };
820823
/* End PBXFileReference section */
821824

@@ -1804,6 +1807,8 @@
18041807
14A0451F18781837004831E8 /* Private */ = {
18051808
isa = PBXGroup;
18061809
children = (
1810+
14E28D8518AD1BF30084F84A /* JSBScriptingSupport+Private.h */,
1811+
14E28D8618AD1BF30084F84A /* JSBScriptingSupport+Private.m */,
18071812
14A0451B18781832004831E8 /* JSBMessageForwarding.h */,
18081813
149A45E0187961AB007C2CA7 /* JSBMessageForwarding.m */,
18091814
1448F2CD189EAAC7005BC527 /* NSObject+JavaScriptBridge.h */,
@@ -1999,6 +2004,7 @@
19992004
140E223A18A2B19300A9F9EF /* NSMutableString+JavaScriptBridge.m in Sources */,
20002005
1448F2D2189EAAC7005BC527 /* UIBarButtonItem+JavaScriptCore.m in Sources */,
20012006
149C56731875C5BB0003EE16 /* JSBCoreMotion.m in Sources */,
2007+
14E28D8718AD1BF30084F84A /* JSBScriptingSupport+Private.m in Sources */,
20022008
149A4606187972FF007C2CA7 /* SLComposeViewController+JavaScriptBridge.m in Sources */,
20032009
149C566C1875C5BB0003EE16 /* JSBAddressBookUI.m in Sources */,
20042010
140E223D18A2B19300A9F9EF /* UIAlertView+JavaScriptBridge.m in Sources */,

0 commit comments

Comments
 (0)