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