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