#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 <wtf/TCSpinLock.h>
#import <wtf/Vector.h>
#import <wtf/HashSet.h>
-#if OS(DARWIN)
#include <mach-o/dyld.h>
-static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x2193302; // 537.51.2
-#endif
+static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x21A0400; // 538.4.0
@class JSObjCClassInfo;
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];
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);
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<JSC::JSAPIWrapperObject>* object = JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->objcWrapperObjectStructure(), jsClass, 0);
}];
}
+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.
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]) {
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<String, Protocol *> initTable;
Protocol *exportProtocol = getJSExportProtocol();
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);
});
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.
});
// 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
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];
}
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];
}
JSValueRef exception = 0;
JSObjectRef object = JSValueToObject(context, value, &exception);
ASSERT(!exception);
- if (toJS(object)->inherits(&JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::s_info))
+ JSC::JSLockHolder locker(toJS(context));
+ if (toJS(object)->inherits(JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::info()))
return (id)JSC::jsCast<JSC::JSAPIWrapperObject*>(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 <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");