APIEntryShims need some love
[WebKit-https.git] / Source / JavaScriptCore / API / JSWrapperMap.mm
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #import "JavaScriptCore.h"
28
29 #if JSC_OBJC_API_ENABLED
30
31 #import "APICast.h"
32 #import "APIShims.h"
33 #import "JSAPIWrapperObject.h"
34 #import "JSCallbackObject.h"
35 #import "JSContextInternal.h"
36 #import "JSWrapperMap.h"
37 #import "ObjCCallbackFunction.h"
38 #import "ObjcRuntimeExtras.h"
39 #import "Operations.h"
40 #import "WeakGCMap.h"
41 #import <wtf/TCSpinLock.h>
42 #import <wtf/Vector.h>
43 #import <wtf/HashSet.h>
44
45 #include <mach-o/dyld.h>
46
47 static const int32_t webkitFirstVersionWithInitConstructorSupport = 0x21A0400; // 538.4.0
48
49 @class JSObjCClassInfo;
50
51 @interface JSWrapperMap () 
52
53 - (JSObjCClassInfo*)classInfoForClass:(Class)cls;
54
55 @end
56
57 // Default conversion of selectors to property names.
58 // All semicolons are removed, lowercase letters following a semicolon are capitalized.
59 static NSString *selectorToPropertyName(const char* start)
60 {
61     // Use 'index' to check for colons, if there are none, this is easy!
62     const char* firstColon = index(start, ':');
63     if (!firstColon)
64         return [NSString stringWithUTF8String:start];
65
66     // 'header' is the length of string up to the first colon.
67     size_t header = firstColon - start;
68     // The new string needs to be long enough to hold 'header', plus the remainder of the string, excluding
69     // at least one ':', but including a '\0'. (This is conservative if there are more than one ':').
70     char* buffer = static_cast<char*>(malloc(header + strlen(firstColon + 1) + 1));
71     // Copy 'header' characters, set output to point to the end of this & input to point past the first ':'.
72     memcpy(buffer, start, header);
73     char* output = buffer + header;
74     const char* input = start + header + 1;
75
76     // On entry to the loop, we have already skipped over a ':' from the input.
77     while (true) {
78         char c;
79         // Skip over any additional ':'s. We'll leave c holding the next character after the
80         // last ':', and input pointing past c.
81         while ((c = *(input++)) == ':');
82         // Copy the character, converting to upper case if necessary.
83         // If the character we copy is '\0', then we're done!
84         if (!(*(output++) = toupper(c)))
85             goto done;
86         // Loop over characters other than ':'.
87         while ((c = *(input++)) != ':') {
88             // Copy the character.
89             // If the character we copy is '\0', then we're done!
90             if (!(*(output++) = c))
91                 goto done;
92         }
93         // If we get here, we've consumed a ':' - wash, rinse, repeat.
94     }
95 done:
96     NSString *result = [NSString stringWithUTF8String:buffer];
97     free(buffer);
98     return result;
99 }
100
101 static JSObjectRef makeWrapper(JSContextRef ctx, JSClassRef jsClass, id wrappedObject)
102 {
103     JSC::ExecState* exec = toJS(ctx);
104     JSC::APIEntryShim entryShim(exec);
105
106     ASSERT(jsClass);
107     JSC::JSCallbackObject<JSC::JSAPIWrapperObject>* object = JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->objcWrapperObjectStructure(), jsClass, 0);
108     object->setWrappedObject(wrappedObject);
109     if (JSC::JSObject* prototype = jsClass->prototype(exec))
110         object->setPrototype(exec->vm(), prototype);
111
112     return toRef(object);
113 }
114
115 // Make an object that is in all ways a completely vanilla JavaScript object,
116 // other than that it has a native brand set that will be displayed by the default
117 // Object.prototype.toString conversion.
118 static JSValue *objectWithCustomBrand(JSContext *context, NSString *brand, Class cls = 0)
119 {
120     JSClassDefinition definition;
121     definition = kJSClassDefinitionEmpty;
122     definition.className = [brand UTF8String];
123     JSClassRef classRef = JSClassCreate(&definition);
124     JSObjectRef result = makeWrapper([context JSGlobalContextRef], classRef, cls);
125     JSClassRelease(classRef);
126     return [JSValue valueWithJSValueRef:result inContext:context];
127 }
128
129 // Look for @optional properties in the prototype containing a selector to property
130 // name mapping, separated by a __JS_EXPORT_AS__ delimiter.
131 static NSMutableDictionary *createRenameMap(Protocol *protocol, BOOL isInstanceMethod)
132 {
133     NSMutableDictionary *renameMap = [[NSMutableDictionary alloc] init];
134
135     forEachMethodInProtocol(protocol, NO, isInstanceMethod, ^(SEL sel, const char*){
136         NSString *rename = @(sel_getName(sel));
137         NSRange range = [rename rangeOfString:@"__JS_EXPORT_AS__"];
138         if (range.location == NSNotFound)
139             return;
140         NSString *selector = [rename substringToIndex:range.location];
141         NSUInteger begin = range.location + range.length;
142         NSUInteger length = [rename length] - begin - 1;
143         NSString *name = [rename substringWithRange:(NSRange){ begin, length }];
144         renameMap[selector] = name;
145     });
146
147     return renameMap;
148 }
149
150 inline void putNonEnumerable(JSValue *base, NSString *propertyName, JSValue *value)
151 {
152     [base defineProperty:propertyName descriptor:@{
153         JSPropertyDescriptorValueKey: value,
154         JSPropertyDescriptorWritableKey: @YES,
155         JSPropertyDescriptorEnumerableKey: @NO,
156         JSPropertyDescriptorConfigurableKey: @YES
157     }];
158 }
159
160 static bool isInitFamilyMethod(NSString *name)
161 {
162     NSUInteger i = 0;
163
164     // Skip over initial underscores.
165     for (; i < [name length]; ++i) {
166         if ([name characterAtIndex:i] != '_')
167             break;
168     }
169
170     // Match 'init'.
171     NSUInteger initIndex = 0;
172     NSString* init = @"init";
173     for (; i < [name length] && initIndex < [init length]; ++i, ++initIndex) {
174         if ([name characterAtIndex:i] != [init characterAtIndex:initIndex])
175             return false;
176     }
177
178     // We didn't match all of 'init'.
179     if (initIndex < [init length])
180         return false;
181
182     // If we're at the end or the next character is a capital letter then this is an init-family selector.
183     return i == [name length] || [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[name characterAtIndex:i]]; 
184 }
185
186 static bool shouldSkipMethodWithName(NSString *name)
187 {
188     // For clients that don't support init-based constructors just copy 
189     // over the init method as we would have before.
190     if (!supportsInitMethodConstructors())
191         return false;
192
193     // Skip over init family methods because we handle those specially 
194     // for the purposes of hooking up the constructor correctly.
195     return isInitFamilyMethod(name);
196 }
197
198 // This method will iterate over the set of required methods in the protocol, and:
199 //  * Determine a property name (either via a renameMap or default conversion).
200 //  * If an accessorMap is provided, and contains this name, store the method in the map.
201 //  * Otherwise, if the object doesn't already contain a property with name, create it.
202 static void copyMethodsToObject(JSContext *context, Class objcClass, Protocol *protocol, BOOL isInstanceMethod, JSValue *object, NSMutableDictionary *accessorMethods = nil)
203 {
204     NSMutableDictionary *renameMap = createRenameMap(protocol, isInstanceMethod);
205
206     forEachMethodInProtocol(protocol, YES, isInstanceMethod, ^(SEL sel, const char* types){
207         const char* nameCStr = sel_getName(sel);
208         NSString *name = @(nameCStr);
209
210         if (shouldSkipMethodWithName(name))
211             return;
212
213         if (accessorMethods && accessorMethods[name]) {
214             JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
215             if (!method)
216                 return;
217             accessorMethods[name] = [JSValue valueWithJSValueRef:method inContext:context];
218         } else {
219             name = renameMap[name];
220             if (!name)
221                 name = selectorToPropertyName(nameCStr);
222             if ([object hasProperty:name])
223                 return;
224             JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
225             if (method)
226                 putNonEnumerable(object, name, [JSValue valueWithJSValueRef:method inContext:context]);
227         }
228     });
229
230     [renameMap release];
231 }
232
233 static bool parsePropertyAttributes(objc_property_t property, char*& getterName, char*& setterName)
234 {
235     bool readonly = false;
236     unsigned attributeCount;
237     objc_property_attribute_t* attributes = property_copyAttributeList(property, &attributeCount);
238     if (attributeCount) {
239         for (unsigned i = 0; i < attributeCount; ++i) {
240             switch (*(attributes[i].name)) {
241             case 'G':
242                 getterName = strdup(attributes[i].value);
243                 break;
244             case 'S':
245                 setterName = strdup(attributes[i].value);
246                 break;
247             case 'R':
248                 readonly = true;
249                 break;
250             default:
251                 break;
252             }
253         }
254         free(attributes);
255     }
256     return readonly;
257 }
258
259 static char* makeSetterName(const char* name)
260 {
261     size_t nameLength = strlen(name);
262     char* setterName = (char*)malloc(nameLength + 5); // "set" Name ":\0"
263     setterName[0] = 's';
264     setterName[1] = 'e';
265     setterName[2] = 't';
266     setterName[3] = toupper(*name);
267     memcpy(setterName + 4, name + 1, nameLength - 1);
268     setterName[nameLength + 3] = ':';
269     setterName[nameLength + 4] = '\0';
270     return setterName;
271 }
272
273 static void copyPrototypeProperties(JSContext *context, Class objcClass, Protocol *protocol, JSValue *prototypeValue)
274 {
275     // First gather propreties into this list, then handle the methods (capturing the accessor methods).
276     struct Property {
277         const char* name;
278         char* getterName;
279         char* setterName;
280     };
281     __block Vector<Property> propertyList;
282
283     // Map recording the methods used as getters/setters.
284     NSMutableDictionary *accessorMethods = [NSMutableDictionary dictionary];
285
286     // Useful value.
287     JSValue *undefined = [JSValue valueWithUndefinedInContext:context];
288
289     forEachPropertyInProtocol(protocol, ^(objc_property_t property){
290         char* getterName = 0;
291         char* setterName = 0;
292         bool readonly = parsePropertyAttributes(property, getterName, setterName);
293         const char* name = property_getName(property);
294
295         // Add the names of the getter & setter methods to 
296         if (!getterName)
297             getterName = strdup(name);
298         accessorMethods[@(getterName)] = undefined;
299         if (!readonly) {
300             if (!setterName)
301                 setterName = makeSetterName(name);
302             accessorMethods[@(setterName)] = undefined;
303         }
304
305         // Add the properties to a list.
306         propertyList.append((Property){ name, getterName, setterName });
307     });
308
309     // Copy methods to the prototype, capturing accessors in the accessorMethods map.
310     copyMethodsToObject(context, objcClass, protocol, YES, prototypeValue, accessorMethods);
311
312     // Iterate the propertyList & generate accessor properties.
313     for (size_t i = 0; i < propertyList.size(); ++i) {
314         Property& property = propertyList[i];
315
316         JSValue *getter = accessorMethods[@(property.getterName)];
317         free(property.getterName);
318         ASSERT(![getter isUndefined]);
319
320         JSValue *setter = undefined;
321         if (property.setterName) {
322             setter = accessorMethods[@(property.setterName)];
323             free(property.setterName);
324             ASSERT(![setter isUndefined]);
325         }
326         
327         [prototypeValue defineProperty:@(property.name) descriptor:@{
328             JSPropertyDescriptorGetKey: getter,
329             JSPropertyDescriptorSetKey: setter,
330             JSPropertyDescriptorEnumerableKey: @NO,
331             JSPropertyDescriptorConfigurableKey: @YES
332         }];
333     }
334 }
335
336 @interface JSObjCClassInfo : NSObject {
337     JSContext *m_context;
338     Class m_class;
339     bool m_block;
340     JSClassRef m_classRef;
341     JSC::Weak<JSC::JSObject> m_prototype;
342     JSC::Weak<JSC::JSObject> m_constructor;
343 }
344
345 - (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo;
346 - (JSValue *)wrapperForObject:(id)object;
347 - (JSValue *)constructor;
348
349 @end
350
351 @implementation JSObjCClassInfo
352
353 - (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo
354 {
355     self = [super init];
356     if (!self)
357         return nil;
358
359     const char* className = class_getName(cls);
360     m_context = context;
361     m_class = cls;
362     m_block = [cls isSubclassOfClass:getNSBlockClass()];
363     JSClassDefinition definition;
364     definition = kJSClassDefinitionEmpty;
365     definition.className = className;
366     m_classRef = JSClassCreate(&definition);
367
368     [self allocateConstructorAndPrototypeWithSuperClassInfo:superClassInfo];
369
370     return self;
371 }
372
373 - (void)dealloc
374 {
375     JSClassRelease(m_classRef);
376     [super dealloc];
377 }
378
379 static JSValue *allocateConstructorForCustomClass(JSContext *context, const char* className, Class cls)
380 {
381     if (!supportsInitMethodConstructors())
382         return objectWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls);
383
384     // For each protocol that the class implements, gather all of the init family methods into a hash table.
385     __block HashMap<String, Protocol *> initTable;
386     Protocol *exportProtocol = getJSExportProtocol();
387     for (Class currentClass = cls; currentClass; currentClass = class_getSuperclass(currentClass)) {
388         forEachProtocolImplementingProtocol(currentClass, exportProtocol, ^(Protocol *protocol) {
389             forEachMethodInProtocol(protocol, YES, YES, ^(SEL selector, const char*) {
390                 const char* name = sel_getName(selector);
391                 if (!isInitFamilyMethod(@(name)))
392                     return;
393                 initTable.set(name, protocol);
394             });
395         });
396     }
397
398     for (Class currentClass = cls; currentClass; currentClass = class_getSuperclass(currentClass)) {
399         __block unsigned numberOfInitsFound = 0;
400         __block SEL initMethod = 0;
401         __block Protocol *initProtocol = 0;
402         __block const char* types = 0;
403         forEachMethodInClass(currentClass, ^(Method method) {
404             SEL selector = method_getName(method);
405             const char* name = sel_getName(selector);
406             auto iter = initTable.find(name);
407
408             if (iter == initTable.end())
409                 return;
410
411             numberOfInitsFound++;
412             initMethod = selector;
413             initProtocol = iter->value;
414             types = method_getTypeEncoding(method);
415         });
416
417         if (!numberOfInitsFound)
418             continue;
419
420         if (numberOfInitsFound > 1) {
421             NSLog(@"ERROR: Class %@ exported more than one init family method via JSExport. Class %@ will not have a callable JavaScript constructor function.", cls, cls);
422             break;
423         }
424
425         JSObjectRef method = objCCallbackFunctionForInit(context, cls, initProtocol, initMethod, types);
426         return [JSValue valueWithJSValueRef:method inContext:context];
427     }
428     return objectWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], cls);
429 }
430
431 - (void)allocateConstructorAndPrototypeWithSuperClassInfo:(JSObjCClassInfo*)superClassInfo
432 {
433     ASSERT(!m_constructor || !m_prototype);
434     ASSERT((m_class == [NSObject class]) == !superClassInfo);
435     if (!superClassInfo) {
436         JSContextRef cContext = [m_context JSGlobalContextRef];
437         JSValue *constructor = m_context[@"Object"];
438         if (!m_constructor)
439             m_constructor = toJS(JSValueToObject(cContext, valueInternalValue(constructor), 0));
440
441         if (!m_prototype) {
442             JSValue *prototype = constructor[@"prototype"];
443             m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0));
444         }
445     } else {
446         const char* className = class_getName(m_class);
447
448         // Create or grab the prototype/constructor pair.
449         JSValue *prototype;
450         JSValue *constructor;
451         if (m_prototype)
452             prototype = [JSValue valueWithJSValueRef:toRef(m_prototype.get()) inContext:m_context];
453         else
454             prototype = objectWithCustomBrand(m_context, [NSString stringWithFormat:@"%sPrototype", className]);
455
456         if (m_constructor)
457             constructor = [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context];
458         else
459             constructor = allocateConstructorForCustomClass(m_context, className, m_class);
460
461         JSContextRef cContext = [m_context JSGlobalContextRef];
462         m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0));
463         m_constructor = toJS(JSValueToObject(cContext, valueInternalValue(constructor), 0));
464
465         putNonEnumerable(prototype, @"constructor", constructor);
466         putNonEnumerable(constructor, @"prototype", prototype);
467
468         Protocol *exportProtocol = getJSExportProtocol();
469         forEachProtocolImplementingProtocol(m_class, exportProtocol, ^(Protocol *protocol){
470             copyPrototypeProperties(m_context, m_class, protocol, prototype);
471             copyMethodsToObject(m_context, m_class, protocol, NO, constructor);
472         });
473
474         // Set [Prototype].
475         JSObjectSetPrototype([m_context JSGlobalContextRef], toRef(m_prototype.get()), toRef(superClassInfo->m_prototype.get()));
476     }
477 }
478
479 - (void)reallocateConstructorAndOrPrototype
480 {
481     [self allocateConstructorAndPrototypeWithSuperClassInfo:[m_context.wrapperMap classInfoForClass:class_getSuperclass(m_class)]];
482 }
483
484 - (JSValue *)wrapperForObject:(id)object
485 {
486     ASSERT([object isKindOfClass:m_class]);
487     ASSERT(m_block == [object isKindOfClass:getNSBlockClass()]);
488     if (m_block) {
489         if (JSObjectRef method = objCCallbackFunctionForBlock(m_context, object)) {
490             JSValue *constructor = [JSValue valueWithJSValueRef:method inContext:m_context];
491             JSValue *prototype = [JSValue valueWithNewObjectInContext:m_context];
492             putNonEnumerable(constructor, @"prototype", prototype);
493             putNonEnumerable(prototype, @"constructor", constructor);
494             return constructor;
495         }
496     }
497
498     if (!m_prototype)
499         [self reallocateConstructorAndOrPrototype];
500     ASSERT(!!m_prototype);
501
502     JSObjectRef wrapper = makeWrapper([m_context JSGlobalContextRef], m_classRef, object);
503     JSObjectSetPrototype([m_context JSGlobalContextRef], wrapper, toRef(m_prototype.get()));
504     return [JSValue valueWithJSValueRef:wrapper inContext:m_context];
505 }
506
507 - (JSValue *)constructor
508 {
509     if (!m_constructor)
510         [self reallocateConstructorAndOrPrototype];
511     ASSERT(!!m_constructor);
512     return [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context];
513 }
514
515 @end
516
517 @implementation JSWrapperMap {
518     JSContext *m_context;
519     NSMutableDictionary *m_classMap;
520     JSC::WeakGCMap<id, JSC::JSObject> m_cachedJSWrappers;
521     NSMapTable *m_cachedObjCWrappers;
522 }
523
524 - (id)initWithContext:(JSContext *)context
525 {
526     self = [super init];
527     if (!self)
528         return nil;
529
530     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
531     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
532     m_cachedObjCWrappers = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
533     
534     m_context = context;
535     m_classMap = [[NSMutableDictionary alloc] init];
536     return self;
537 }
538
539 - (void)dealloc
540 {
541     [m_cachedObjCWrappers release];
542     [m_classMap release];
543     [super dealloc];
544 }
545
546 - (JSObjCClassInfo*)classInfoForClass:(Class)cls
547 {
548     if (!cls)
549         return nil;
550
551     // Check if we've already created a JSObjCClassInfo for this Class.
552     if (JSObjCClassInfo* classInfo = (JSObjCClassInfo*)m_classMap[cls])
553         return classInfo;
554
555     // Skip internal classes beginning with '_' - just copy link to the parent class's info.
556     if ('_' == *class_getName(cls))
557         return m_classMap[cls] = [self classInfoForClass:class_getSuperclass(cls)];
558
559     return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls superClassInfo:[self classInfoForClass:class_getSuperclass(cls)]] autorelease];
560 }
561
562 - (JSValue *)jsWrapperForObject:(id)object
563 {
564     JSC::JSObject* jsWrapper = m_cachedJSWrappers.get(object);
565     if (jsWrapper)
566         return [JSValue valueWithJSValueRef:toRef(jsWrapper) inContext:m_context];
567
568     JSValue *wrapper;
569     if (class_isMetaClass(object_getClass(object)))
570         wrapper = [[self classInfoForClass:(Class)object] constructor];
571     else {
572         JSObjCClassInfo* classInfo = [self classInfoForClass:[object class]];
573         wrapper = [classInfo wrapperForObject:object];
574     }
575
576     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105891
577     // This general approach to wrapper caching is pretty effective, but there are a couple of problems:
578     // (1) For immortal objects JSValues will effectively leak and this results in error output being logged - we should avoid adding associated objects to immortal objects.
579     // (2) A long lived object may rack up many JSValues. When the contexts are released these will unprotect the associated JavaScript objects,
580     //     but still, would probably nicer if we made it so that only one associated object was required, broadcasting object dealloc.
581     JSC::ExecState* exec = toJS([m_context JSGlobalContextRef]);
582     jsWrapper = toJS(exec, valueInternalValue(wrapper)).toObject(exec);
583     m_cachedJSWrappers.set(object, jsWrapper);
584     return wrapper;
585 }
586
587 - (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value
588 {
589     JSValue *wrapper = static_cast<JSValue *>(NSMapGet(m_cachedObjCWrappers, value));
590     if (!wrapper) {
591         wrapper = [[[JSValue alloc] initWithValue:value inContext:m_context] autorelease];
592         NSMapInsert(m_cachedObjCWrappers, value, wrapper);
593     }
594     return wrapper;
595 }
596
597 @end
598
599 id tryUnwrapObjcObject(JSGlobalContextRef context, JSValueRef value)
600 {
601     if (!JSValueIsObject(context, value))
602         return nil;
603     JSValueRef exception = 0;
604     JSObjectRef object = JSValueToObject(context, value, &exception);
605     ASSERT(!exception);
606     JSC::APIEntryShim entryShim(toJS(context));
607     if (toJS(object)->inherits(JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::info()))
608         return (id)JSC::jsCast<JSC::JSAPIWrapperObject*>(toJS(object))->wrappedObject();
609     if (id target = tryUnwrapConstructor(object))
610         return target;
611     return nil;
612 }
613
614 // This class ensures that the JSExport protocol is registered with the runtime.
615 NS_ROOT_CLASS @interface JSExport <JSExport>
616 @end
617 @implementation JSExport
618 @end
619
620 bool supportsInitMethodConstructors()
621 {
622     static int32_t versionOfLinkTimeLibrary = 0;
623     if (!versionOfLinkTimeLibrary)
624         versionOfLinkTimeLibrary = NSVersionOfLinkTimeLibrary("JavaScriptCore");
625     return versionOfLinkTimeLibrary >= webkitFirstVersionWithInitConstructorSupport;
626 }
627
628 Protocol *getJSExportProtocol()
629 {
630     static Protocol *protocol = objc_getProtocol("JSExport");
631     return protocol;
632 }
633
634 Class getNSBlockClass()
635 {
636     static Class cls = objc_getClass("NSBlock");
637     return cls;
638 }
639
640 #endif