X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/12899fa232562c774004a3a9d7d3149944dec712..cb9aa2694aba0ae4f946ed34b8e0f6c99c1cfe44:/API/JSWrapperMap.mm?ds=sidebyside diff --git a/API/JSWrapperMap.mm b/API/JSWrapperMap.mm index 5ec64d9..f6717b9 100644 --- a/API/JSWrapperMap.mm +++ b/API/JSWrapperMap.mm @@ -29,24 +29,21 @@ #if JSC_OBJC_API_ENABLED #import "APICast.h" -#import "APIShims.h" #import "JSAPIWrapperObject.h" #import "JSCallbackObject.h" #import "JSContextInternal.h" #import "JSWrapperMap.h" #import "ObjCCallbackFunction.h" #import "ObjcRuntimeExtras.h" -#import "Operations.h" +#import "JSCInlines.h" #import "WeakGCMap.h" #import #import #import -#if OS(DARWIN) #include -static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x2193302; // 537.51.2 -#endif +static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x21A0400; // 538.4.0 @class JSObjCClassInfo; @@ -61,7 +58,7 @@ static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x2193302; / static NSString *selectorToPropertyName(const char* start) { // Use 'index' to check for colons, if there are none, this is easy! - const char* firstColon = index(start, ':'); + const char* firstColon = strchr(start, ':'); if (!firstColon) return [NSString stringWithUTF8String:start]; @@ -103,7 +100,7 @@ done: static bool constructorHasInstance(JSContextRef ctx, JSObjectRef constructorRef, JSValueRef possibleInstance, JSValueRef*) { JSC::ExecState* exec = toJS(ctx); - JSC::APIEntryShim entryShim(exec); + JSC::JSLockHolder locker(exec); JSC::JSObject* constructor = toJS(constructorRef); JSC::JSValue instance = toJS(exec, possibleInstance); @@ -113,7 +110,7 @@ static bool constructorHasInstance(JSContextRef ctx, JSObjectRef constructorRef, static JSObjectRef makeWrapper(JSContextRef ctx, JSClassRef jsClass, id wrappedObject) { JSC::ExecState* exec = toJS(ctx); - JSC::APIEntryShim entryShim(exec); + JSC::JSLockHolder locker(exec); ASSERT(jsClass); JSC::JSCallbackObject* object = JSC::JSCallbackObject::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->objcWrapperObjectStructure(), jsClass, 0); @@ -181,6 +178,44 @@ inline void putNonEnumerable(JSValue *base, NSString *propertyName, JSValue *val }]; } +static bool isInitFamilyMethod(NSString *name) +{ + NSUInteger i = 0; + + // Skip over initial underscores. + for (; i < [name length]; ++i) { + if ([name characterAtIndex:i] != '_') + break; + } + + // Match 'init'. + NSUInteger initIndex = 0; + NSString* init = @"init"; + for (; i < [name length] && initIndex < [init length]; ++i, ++initIndex) { + if ([name characterAtIndex:i] != [init characterAtIndex:initIndex]) + return false; + } + + // We didn't match all of 'init'. + if (initIndex < [init length]) + return false; + + // If we're at the end or the next character is a capital letter then this is an init-family selector. + return i == [name length] || [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[name characterAtIndex:i]]; +} + +static bool shouldSkipMethodWithName(NSString *name) +{ + // For clients that don't support init-based constructors just copy + // over the init method as we would have before. + if (!supportsInitMethodConstructors()) + return false; + + // Skip over init family methods because we handle those specially + // for the purposes of hooking up the constructor correctly. + return isInitFamilyMethod(name); +} + // This method will iterate over the set of required methods in the protocol, and: // * Determine a property name (either via a renameMap or default conversion). // * If an accessorMap is provided, and contains this name, store the method in the map. @@ -192,9 +227,8 @@ static void copyMethodsToObject(JSContext *context, Class objcClass, Protocol *p forEachMethodInProtocol(protocol, YES, isInstanceMethod, ^(SEL sel, const char* types){ const char* nameCStr = sel_getName(sel); NSString *name = @(nameCStr); - // Don't copy over init family methods because we handle those specially - // for the purposes of hooking up the constructor correctly. - if ([name hasPrefix:@"init"]) + + if (shouldSkipMethodWithName(name)) return; if (accessorMethods && accessorMethods[name]) { @@ -365,10 +399,9 @@ static void copyPrototypeProperties(JSContext *context, Class objcClass, Protoco static JSValue *allocateConstructorForCustomClass(JSContext *context, const char* className, Class cls) { -#if OS(DARWIN) - if (NSVersionOfLinkTimeLibrary("JavaScriptCore") < webkitFirstVersionWithInitConstructorSupport) + if (!supportsInitMethodConstructors()) return constructorWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls); -#endif + // For each protocol that the class implements, gather all of the init family methods into a hash table. __block HashMap initTable; Protocol *exportProtocol = getJSExportProtocol(); @@ -376,7 +409,7 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char forEachProtocolImplementingProtocol(currentClass, exportProtocol, ^(Protocol *protocol) { forEachMethodInProtocol(protocol, YES, YES, ^(SEL selector, const char*) { const char* name = sel_getName(selector); - if (![@(name) hasPrefix:@"init"]) + if (!isInitFamilyMethod(@(name))) return; initTable.set(name, protocol); }); @@ -431,6 +464,11 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0)); } } else { + // We need to hold a reference to the superclass prototype here on the stack + // to that it won't get GC'ed while we do allocations between now and when we + // set it in this class' prototype below. + JSC::JSObject* superClassPrototype = superClassInfo->m_prototype.get(); + const char* className = class_getName(m_class); // Create or grab the prototype/constructor pair. @@ -460,13 +498,15 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char }); // Set [Prototype]. - JSObjectSetPrototype([m_context JSGlobalContextRef], toRef(m_prototype.get()), toRef(superClassInfo->m_prototype.get())); + JSObjectSetPrototype([m_context JSGlobalContextRef], toRef(m_prototype.get()), toRef(superClassPrototype)); } } - (void)reallocateConstructorAndOrPrototype { [self allocateConstructorAndPrototypeWithSuperClassInfo:[m_context.wrapperMap classInfoForClass:class_getSuperclass(m_class)]]; + // We should not add any code here that can trigger a GC or the prototype and + // constructor that we just created may be collected before they can be used. } - (JSValue *)wrapperForObject:(id)object @@ -486,9 +526,12 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char if (!m_prototype) [self reallocateConstructorAndOrPrototype]; ASSERT(!!m_prototype); + // We need to hold a reference to the prototype here on the stack to that it won't + // get GC'ed while we create the wrapper below. + JSC::JSObject* prototype = m_prototype.get(); JSObjectRef wrapper = makeWrapper([m_context JSGlobalContextRef], m_classRef, object); - JSObjectSetPrototype([m_context JSGlobalContextRef], wrapper, toRef(m_prototype.get())); + JSObjectSetPrototype([m_context JSGlobalContextRef], wrapper, toRef(prototype)); return [JSValue valueWithJSValueRef:wrapper inContext:m_context]; } @@ -497,6 +540,9 @@ static JSValue *allocateConstructorForCustomClass(JSContext *context, const char if (!m_constructor) [self reallocateConstructorAndOrPrototype]; ASSERT(!!m_constructor); + // If we need to add any code here in the future that can trigger a GC, we should + // cache the constructor pointer in a stack local var first so that it is protected + // from the GC until it gets used below. return [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context]; } @@ -591,13 +637,28 @@ id tryUnwrapObjcObject(JSGlobalContextRef context, JSValueRef value) JSValueRef exception = 0; JSObjectRef object = JSValueToObject(context, value, &exception); ASSERT(!exception); - if (toJS(object)->inherits(&JSC::JSCallbackObject::s_info)) + JSC::JSLockHolder locker(toJS(context)); + if (toJS(object)->inherits(JSC::JSCallbackObject::info())) return (id)JSC::jsCast(toJS(object))->wrappedObject(); if (id target = tryUnwrapConstructor(object)) return target; return nil; } +// This class ensures that the JSExport protocol is registered with the runtime. +NS_ROOT_CLASS @interface JSExport +@end +@implementation JSExport +@end + +bool supportsInitMethodConstructors() +{ + static int32_t versionOfLinkTimeLibrary = 0; + if (!versionOfLinkTimeLibrary) + versionOfLinkTimeLibrary = NSVersionOfLinkTimeLibrary("JavaScriptCore"); + return versionOfLinkTimeLibrary >= webkitFirstVersionWithInitConstructorSupport; +} + Protocol *getJSExportProtocol() { static Protocol *protocol = objc_getProtocol("JSExport");