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