Unzip initialization lists and constructors in JSCell hierarchy (6/7)
[WebKit-https.git] / Source / WebCore / bridge / objc / objc_instance.mm
1 /*
2  * Copyright (C) 2004, 2008, 2009 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 COMPUTER, 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 COMPUTER, 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 #import "config.h"
27 #import "objc_instance.h"
28
29 #import "runtime_method.h"
30 #import <runtime/ObjectPrototype.h>
31 #import "JSDOMBinding.h"
32 #import "ObjCRuntimeObject.h"
33 #import "WebScriptObject.h"
34 #import <objc/objc-auto.h>
35 #import <runtime/Error.h>
36 #import <runtime/JSLock.h>
37 #import "runtime/FunctionPrototype.h"
38 #import <wtf/Assertions.h>
39
40 #ifdef NDEBUG
41 #define OBJC_LOG(formatAndArgs...) ((void)0)
42 #else
43 #define OBJC_LOG(formatAndArgs...) { \
44     fprintf (stderr, "%s:%d -- %s:  ", __FILE__, __LINE__, __FUNCTION__); \
45     fprintf(stderr, formatAndArgs); \
46 }
47 #endif
48
49 using namespace JSC::Bindings;
50 using namespace JSC;
51
52 static NSString *s_exception;
53 static JSGlobalObject* s_exceptionEnvironment; // No need to protect this value, since we just use it for a pointer comparison.
54 static NSMapTable *s_instanceWrapperCache;
55
56 static NSMapTable *createInstanceWrapperCache()
57 {
58     // NSMapTable with zeroing weak pointers is the recommended way to build caches like this under garbage collection.
59     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsZeroingWeakMemory | NSPointerFunctionsOpaquePersonality;
60     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
61     return [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
62 }
63
64 RuntimeObject* ObjcInstance::newRuntimeObject(ExecState* exec)
65 {
66     return ObjCRuntimeObject::create(exec, exec->lexicalGlobalObject(), this);
67 }
68
69 void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment)
70 {
71     NSString *oldException = s_exception;
72     s_exception = [exception copy];
73     [oldException release];
74
75     s_exceptionEnvironment = exceptionEnvironment;
76 }
77
78 void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec)
79 {
80     if (!s_exception) {
81         ASSERT(!s_exceptionEnvironment);
82         return;
83     }
84
85     if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->dynamicGlobalObject()) {
86         JSLock lock(SilenceAssertionsOnly);
87         throwError(exec, s_exception);
88     }
89
90     [s_exception release];
91     s_exception = nil;
92     s_exceptionEnvironment = 0;
93 }
94
95 ObjcInstance::ObjcInstance(id instance, PassRefPtr<RootObject> rootObject) 
96     : Instance(rootObject)
97     , _instance(instance)
98     , _class(0)
99     , _pool(0)
100     , _beginCount(0)
101 {
102 }
103
104 PassRefPtr<ObjcInstance> ObjcInstance::create(id instance, PassRefPtr<RootObject> rootObject)
105 {
106     if (!s_instanceWrapperCache)
107         s_instanceWrapperCache = createInstanceWrapperCache();
108     if (void* existingWrapper = NSMapGet(s_instanceWrapperCache, instance))
109         return static_cast<ObjcInstance*>(existingWrapper);
110     RefPtr<ObjcInstance> wrapper = adoptRef(new ObjcInstance(instance, rootObject));
111     NSMapInsert(s_instanceWrapperCache, instance, wrapper.get());
112     return wrapper.release();
113 }
114
115 ObjcInstance::~ObjcInstance() 
116 {
117     // Both -finalizeForWebScript and -dealloc/-finalize of _instance may require autorelease pools.
118     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
119
120     ASSERT(s_instanceWrapperCache);
121     ASSERT(_instance);
122     NSMapRemove(s_instanceWrapperCache, _instance.get());
123
124     if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
125         [_instance.get() performSelector:@selector(finalizeForWebScript)];
126     _instance = 0;
127
128     [pool drain];
129 }
130
131 static NSAutoreleasePool* allocateAutoReleasePool()
132 {
133 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
134     // If GC is enabled an autorelease pool is unnecessary, and the
135     // pool cannot be protected from GC so may be collected leading
136     // to a crash when we try to drain the release pool.
137     if (objc_collectingEnabled())
138         return nil;
139 #endif
140     return [[NSAutoreleasePool alloc] init];
141 }
142
143 void ObjcInstance::virtualBegin()
144 {
145     if (!_pool)
146         _pool = allocateAutoReleasePool();
147     _beginCount++;
148 }
149
150 void ObjcInstance::virtualEnd()
151 {
152     _beginCount--;
153     ASSERT(_beginCount >= 0);
154     if (!_beginCount) {
155         [_pool drain];
156         _pool = 0;
157     }
158 }
159
160 Bindings::Class* ObjcInstance::getClass() const 
161 {
162     if (!_instance)
163         return 0;
164     if (!_class)
165         _class = ObjcClass::classForIsA(_instance->isa);
166     return static_cast<Bindings::Class*>(_class);
167 }
168
169 bool ObjcInstance::supportsInvokeDefaultMethod() const
170 {
171     return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
172 }
173
174 class ObjCRuntimeMethod : public RuntimeMethod {
175 public:
176     static ObjCRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list)
177     {
178         // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
179         // We need to pass in the right global object for "i".
180         Structure* domStructure = WebCore::deprecatedGetDOMStructure<ObjCRuntimeMethod>(exec);
181         return new (allocateCell<ObjCRuntimeMethod>(*exec->heap())) ObjCRuntimeMethod(exec, globalObject, domStructure, name, list);
182     }
183
184     static Structure* createStructure(JSGlobalData& globalData, JSC::JSGlobalObject* globalObject, JSValue prototype)
185     {
186         return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
187     }
188
189     static const ClassInfo s_info;
190
191 private:
192     typedef RuntimeMethod Base;
193
194     ObjCRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, const Identifier& name, Bindings::MethodList& list)
195         : RuntimeMethod(globalObject, structure, list)
196     {
197         finishCreation(exec->globalData(), name);
198     }
199
200     void finishCreation(JSGlobalData& globalData, const Identifier& name)
201     {
202         Base::finishCreation(globalData, name);
203         ASSERT(inherits(&s_info));
204     }
205 };
206
207 const ClassInfo ObjCRuntimeMethod::s_info = { "ObjCRuntimeMethod", &RuntimeMethod::s_info, 0, 0 };
208
209 JSValue ObjcInstance::getMethod(ExecState* exec, const Identifier& propertyName)
210 {
211     MethodList methodList = getClass()->methodsNamed(propertyName, this);
212     return ObjCRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName, methodList);
213 }
214
215 JSValue ObjcInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
216 {
217     if (!asObject(runtimeMethod)->inherits(&ObjCRuntimeMethod::s_info))
218         return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
219
220     const MethodList& methodList = *runtimeMethod->methods();
221
222     // Overloading methods is not allowed in ObjectiveC.  Should only be one
223     // name match for a particular method.
224     ASSERT(methodList.size() == 1);
225
226     return invokeObjcMethod(exec, static_cast<ObjcMethod*>(methodList[0]));
227 }
228
229 JSValue ObjcInstance::invokeObjcMethod(ExecState* exec, ObjcMethod* method)
230 {
231     JSValue result = jsUndefined();
232     
233     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
234
235     setGlobalException(nil);
236     
237 @try {
238     NSMethodSignature* signature = method->getMethodSignature();
239     NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
240     [invocation setSelector:method->selector()];
241     [invocation setTarget:_instance.get()];
242
243     if (method->isFallbackMethod()) {
244         if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
245             NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
246             return result;
247         }
248
249         // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function
250         // name as first (actually at 2) argument and array of args as second.
251         NSString* jsName = (NSString* )method->javaScriptName();
252         [invocation setArgument:&jsName atIndex:2];
253
254         NSMutableArray* objcArgs = [NSMutableArray array];
255         int count = exec->argumentCount();
256         for (int i = 0; i < count; i++) {
257             ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType);
258             [objcArgs addObject:value.objectValue];
259         }
260         [invocation setArgument:&objcArgs atIndex:3];
261     } else {
262         unsigned count = [signature numberOfArguments];
263         for (unsigned i = 2; i < count ; i++) {
264             const char* type = [signature getArgumentTypeAtIndex:i];
265             ObjcValueType objcValueType = objcValueTypeForType(type);
266
267             // Must have a valid argument type.  This method signature should have
268             // been filtered already to ensure that it has acceptable argument
269             // types.
270             ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
271
272             ObjcValue value = convertValueToObjcValue(exec, exec->argument(i-2), objcValueType);
273
274             switch (objcValueType) {
275                 case ObjcObjectType:
276                     [invocation setArgument:&value.objectValue atIndex:i];
277                     break;
278                 case ObjcCharType:
279                 case ObjcUnsignedCharType:
280                     [invocation setArgument:&value.charValue atIndex:i];
281                     break;
282                 case ObjcShortType:
283                 case ObjcUnsignedShortType:
284                     [invocation setArgument:&value.shortValue atIndex:i];
285                     break;
286                 case ObjcIntType:
287                 case ObjcUnsignedIntType:
288                     [invocation setArgument:&value.intValue atIndex:i];
289                     break;
290                 case ObjcLongType:
291                 case ObjcUnsignedLongType:
292                     [invocation setArgument:&value.longValue atIndex:i];
293                     break;
294                 case ObjcLongLongType:
295                 case ObjcUnsignedLongLongType:
296                     [invocation setArgument:&value.longLongValue atIndex:i];
297                     break;
298                 case ObjcFloatType:
299                     [invocation setArgument:&value.floatValue atIndex:i];
300                     break;
301                 case ObjcDoubleType:
302                     [invocation setArgument:&value.doubleValue atIndex:i];
303                     break;
304                 default:
305                     // Should never get here.  Argument types are filtered (and
306                     // the assert above should have fired in the impossible case
307                     // of an invalid type anyway).
308                     fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType);
309                     ASSERT(false);
310             }
311         }
312     }
313
314     [invocation invoke];
315
316     // Get the return value type.
317     const char* type = [signature methodReturnType];
318     ObjcValueType objcValueType = objcValueTypeForType(type);
319
320     // Must have a valid return type.  This method signature should have
321     // been filtered already to ensure that it have an acceptable return
322     // type.
323     ASSERT(objcValueType != ObjcInvalidType);
324
325     // Get the return value and convert it to a JavaScript value. Length
326     // of return value will never exceed the size of largest scalar
327     // or a pointer.
328     char buffer[1024];
329     ASSERT([signature methodReturnLength] < 1024);
330
331     if (*type != 'v') {
332         [invocation getReturnValue:buffer];
333         result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
334     }
335 } @catch(NSException* localException) {
336 }
337     moveGlobalExceptionToExecState(exec);
338
339     // Work around problem in some versions of GCC where result gets marked volatile and
340     // it can't handle copying from a volatile to non-volatile.
341     return const_cast<JSValue&>(result);
342 }
343
344 JSValue ObjcInstance::invokeDefaultMethod(ExecState* exec)
345 {
346     JSValue result = jsUndefined();
347
348     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
349     setGlobalException(nil);
350     
351 @try {
352     if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
353         return result;
354
355     NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
356     NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
357     [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
358     [invocation setTarget:_instance.get()];
359
360     if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
361         NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
362         return result;
363     }
364
365     NSMutableArray* objcArgs = [NSMutableArray array];
366     unsigned count = exec->argumentCount();
367     for (unsigned i = 0; i < count; i++) {
368         ObjcValue value = convertValueToObjcValue(exec, exec->argument(i), ObjcObjectType);
369         [objcArgs addObject:value.objectValue];
370     }
371     [invocation setArgument:&objcArgs atIndex:2];
372
373     [invocation invoke];
374
375     // Get the return value type, should always be "@" because of
376     // check above.
377     const char* type = [signature methodReturnType];
378     ObjcValueType objcValueType = objcValueTypeForType(type);
379
380     // Get the return value and convert it to a JavaScript value. Length
381     // of return value will never exceed the size of a pointer, so we're
382     // OK with 32 here.
383     char buffer[32];
384     [invocation getReturnValue:buffer];
385     result = convertObjcValueToValue(exec, buffer, objcValueType, m_rootObject.get());
386 } @catch(NSException* localException) {
387 }
388     moveGlobalExceptionToExecState(exec);
389
390     // Work around problem in some versions of GCC where result gets marked volatile and
391     // it can't handle copying from a volatile to non-volatile.
392     return const_cast<JSValue&>(result);
393 }
394
395 bool ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier& property, JSValue aValue)
396 {
397     id targetObject = getObject();
398     if (![targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
399         return false;
400
401     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
402
403     // This check is not really necessary because NSObject implements
404     // setValue:forUndefinedKey:, and unfortunately the default implementation
405     // throws an exception.
406     if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
407         setGlobalException(nil);
408     
409         ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType);
410
411         @try {
412             [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii().data() encoding:NSASCIIStringEncoding]];
413         } @catch(NSException* localException) {
414             // Do nothing.  Class did not override valueForUndefinedKey:.
415         }
416
417         moveGlobalExceptionToExecState(exec);
418     }
419     
420     return true;
421 }
422
423 JSValue ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property) const
424 {
425     JSValue result = jsUndefined();
426     
427     id targetObject = getObject();
428
429     JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly); // Can't put this inside the @try scope because it unwinds incorrectly.
430
431     // This check is not really necessary because NSObject implements
432     // valueForUndefinedKey:, and unfortunately the default implementation
433     // throws an exception.
434     if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
435         setGlobalException(nil);
436     
437         @try {
438             id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii().data() encoding:NSASCIIStringEncoding]];
439             result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, m_rootObject.get());
440         } @catch(NSException* localException) {
441             // Do nothing.  Class did not override valueForUndefinedKey:.
442         }
443
444         moveGlobalExceptionToExecState(exec);
445     }
446
447     // Work around problem in some versions of GCC where result gets marked volatile and
448     // it can't handle copying from a volatile to non-volatile.
449     return const_cast<JSValue&>(result);
450 }
451
452 JSValue ObjcInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
453 {
454     if (hint == PreferString)
455         return stringValue(exec);
456     if (hint == PreferNumber)
457         return numberValue(exec);
458     if ([_instance.get() isKindOfClass:[NSString class]])
459         return stringValue(exec);
460     if ([_instance.get() isKindOfClass:[NSNumber class]])
461         return numberValue(exec);
462     return valueOf(exec);
463 }
464
465 JSValue ObjcInstance::stringValue(ExecState* exec) const
466 {
467     return convertNSStringToString(exec, [getObject() description]);
468 }
469
470 JSValue ObjcInstance::numberValue(ExecState*) const
471 {
472     // FIXME:  Implement something sensible
473     return jsNumber(0);
474 }
475
476 JSValue ObjcInstance::booleanValue() const
477 {
478     // FIXME:  Implement something sensible
479     return jsBoolean(false);
480 }
481
482 JSValue ObjcInstance::valueOf(ExecState* exec) const 
483 {
484     return stringValue(exec);
485 }