JavaScriptCore:
[WebKit-https.git] / WebCore / bridge / objc / objc_instance.mm
1 /*
2  * Copyright (C) 2004, 2008 Apple Computer, 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 "FoundationExtras.h"
30 #import "WebScriptObject.h"
31 #include <kjs/Error.h>
32 #include <kjs/JSLock.h>
33 #include <wtf/Assertions.h>
34
35 #ifdef NDEBUG
36 #define OBJC_LOG(formatAndArgs...) ((void)0)
37 #else
38 #define OBJC_LOG(formatAndArgs...) { \
39     fprintf (stderr, "%s:%d -- %s:  ", __FILE__, __LINE__, __FUNCTION__); \
40     fprintf(stderr, formatAndArgs); \
41 }
42 #endif
43
44 using namespace KJS::Bindings;
45 using namespace KJS;
46
47 static NSString* s_exception;
48 static JSGlobalObject* s_exceptionEnvironment; // No need to protect this value, since we just use it for a pointer comparison.
49
50 void ObjcInstance::setGlobalException(NSString* exception, JSGlobalObject* exceptionEnvironment)
51 {
52     HardRelease(s_exception);
53     HardRetain(exception);
54     s_exception = exception;
55
56     s_exceptionEnvironment = exceptionEnvironment;
57 }
58
59 void ObjcInstance::moveGlobalExceptionToExecState(ExecState* exec)
60 {
61     if (!s_exception) {
62         ASSERT(!s_exceptionEnvironment);
63         return;
64     }
65
66     if (!s_exceptionEnvironment || s_exceptionEnvironment == exec->dynamicGlobalObject()) {
67         JSLock lock(false);
68         throwError(exec, GeneralError, s_exception);
69     }
70
71     HardRelease(s_exception);
72     s_exception = 0;
73
74     s_exceptionEnvironment = 0;
75 }
76
77 ObjcInstance::ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject> rootObject) 
78     : Instance(rootObject)
79     , _instance(instance)
80     , _class(0)
81     , _pool(0)
82     , _beginCount(0)
83 {
84 }
85
86 ObjcInstance::~ObjcInstance() 
87 {
88     // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools.
89     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
90     if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
91         [_instance.get() performSelector:@selector(finalizeForWebScript)];
92     _instance = 0;
93     [pool drain];
94 }
95
96 void ObjcInstance::virtualBegin()
97 {
98     if (!_pool)
99         _pool = [[NSAutoreleasePool alloc] init];
100     _beginCount++;
101 }
102
103 void ObjcInstance::virtualEnd()
104 {
105     _beginCount--;
106     ASSERT(_beginCount >= 0);
107     if (!_beginCount) {
108         [_pool drain];
109         _pool = 0;
110     }
111 }
112
113 Bindings::Class* ObjcInstance::getClass() const 
114 {
115     if (!_instance)
116         return 0;
117     if (!_class)
118         _class = ObjcClass::classForIsA(_instance->isa);
119     return static_cast<Bindings::Class*>(_class);
120 }
121
122 bool ObjcInstance::supportsInvokeDefaultMethod() const
123 {
124     return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
125 }
126
127 JSValue* ObjcInstance::invokeMethod(ExecState* exec, const MethodList &methodList, const ArgList &args)
128 {
129     JSValue* result = jsUndefined();
130     
131     JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly.
132
133     setGlobalException(nil);
134     
135     // Overloading methods is not allowed in ObjectiveC.  Should only be one
136     // name match for a particular method.
137     ASSERT(methodList.size() == 1);
138
139 @try {
140     ObjcMethod* method = 0;
141     method = static_cast<ObjcMethod*>(methodList[0]);
142     NSMethodSignature* signature = method->getMethodSignature();
143     NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
144 #if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
145     [invocation setSelector:sel_registerName(method->name())];
146 #else
147     [invocation setSelector:(SEL)method->name()];
148 #endif
149     [invocation setTarget:_instance.get()];
150
151     if (method->isFallbackMethod()) {
152         if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
153             NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
154             return result;
155         }
156
157         // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function
158         // name as first (actually at 2) argument and array of args as second.
159         NSString* jsName = (NSString* )method->javaScriptName();
160         [invocation setArgument:&jsName atIndex:2];
161
162         NSMutableArray* objcArgs = [NSMutableArray array];
163         int count = args.size();
164         for (int i = 0; i < count; i++) {
165             ObjcValue value = convertValueToObjcValue(exec, args.at(exec, i), ObjcObjectType);
166             [objcArgs addObject:value.objectValue];
167         }
168         [invocation setArgument:&objcArgs atIndex:3];
169     } else {
170         unsigned count = [signature numberOfArguments];
171         for (unsigned i = 2; i < count ; i++) {
172             const char* type = [signature getArgumentTypeAtIndex:i];
173             ObjcValueType objcValueType = objcValueTypeForType(type);
174
175             // Must have a valid argument type.  This method signature should have
176             // been filtered already to ensure that it has acceptable argument
177             // types.
178             ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
179
180             ObjcValue value = convertValueToObjcValue(exec, args.at(exec, i-2), objcValueType);
181
182             switch (objcValueType) {
183                 case ObjcObjectType:
184                     [invocation setArgument:&value.objectValue atIndex:i];
185                     break;
186                 case ObjcCharType:
187                 case ObjcUnsignedCharType:
188                     [invocation setArgument:&value.charValue atIndex:i];
189                     break;
190                 case ObjcShortType:
191                 case ObjcUnsignedShortType:
192                     [invocation setArgument:&value.shortValue atIndex:i];
193                     break;
194                 case ObjcIntType:
195                 case ObjcUnsignedIntType:
196                     [invocation setArgument:&value.intValue atIndex:i];
197                     break;
198                 case ObjcLongType:
199                 case ObjcUnsignedLongType:
200                     [invocation setArgument:&value.longValue atIndex:i];
201                     break;
202                 case ObjcLongLongType:
203                 case ObjcUnsignedLongLongType:
204                     [invocation setArgument:&value.longLongValue atIndex:i];
205                     break;
206                 case ObjcFloatType:
207                     [invocation setArgument:&value.floatValue atIndex:i];
208                     break;
209                 case ObjcDoubleType:
210                     [invocation setArgument:&value.doubleValue atIndex:i];
211                     break;
212                 default:
213                     // Should never get here.  Argument types are filtered (and
214                     // the assert above should have fired in the impossible case
215                     // of an invalid type anyway).
216                     fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType);
217                     ASSERT(false);
218             }
219         }
220     }
221
222     [invocation invoke];
223
224     // Get the return value type.
225     const char* type = [signature methodReturnType];
226     ObjcValueType objcValueType = objcValueTypeForType(type);
227
228     // Must have a valid return type.  This method signature should have
229     // been filtered already to ensure that it have an acceptable return
230     // type.
231     ASSERT(objcValueType != ObjcInvalidType);
232
233     // Get the return value and convert it to a JavaScript value. Length
234     // of return value will never exceed the size of largest scalar
235     // or a pointer.
236     char buffer[1024];
237     ASSERT([signature methodReturnLength] < 1024);
238
239     if (*type != 'v') {
240         [invocation getReturnValue:buffer];
241         result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
242     }
243 } @catch(NSException* localException) {
244 }
245     moveGlobalExceptionToExecState(exec);
246     return result;
247 }
248
249 JSValue* ObjcInstance::invokeDefaultMethod(ExecState* exec, const ArgList &args)
250 {
251     JSValue* result = jsUndefined();
252
253     JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly.
254     setGlobalException(nil);
255     
256 @try {
257     if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
258         return result;
259
260     NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
261     NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
262     [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
263     [invocation setTarget:_instance.get()];
264
265     if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
266         NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
267         return result;
268     }
269
270     NSMutableArray* objcArgs = [NSMutableArray array];
271     unsigned count = args.size();
272     for (unsigned i = 0; i < count; i++) {
273         ObjcValue value = convertValueToObjcValue(exec, args.at(exec, i), ObjcObjectType);
274         [objcArgs addObject:value.objectValue];
275     }
276     [invocation setArgument:&objcArgs atIndex:2];
277
278     [invocation invoke];
279
280     // Get the return value type, should always be "@" because of
281     // check above.
282     const char* type = [signature methodReturnType];
283     ObjcValueType objcValueType = objcValueTypeForType(type);
284
285     // Get the return value and convert it to a JavaScript value. Length
286     // of return value will never exceed the size of a pointer, so we're
287     // OK with 32 here.
288     char buffer[32];
289     [invocation getReturnValue:buffer];
290     result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
291 } @catch(NSException* localException) {
292 }
293     moveGlobalExceptionToExecState(exec);
294     return result;
295 }
296
297 bool ObjcInstance::supportsSetValueOfUndefinedField()
298 {
299     id targetObject = getObject();
300     if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
301         return true;
302     return false;
303 }
304
305 void ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier &property, JSValue* aValue)
306 {
307     id targetObject = getObject();
308
309     JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly.
310
311     // This check is not really necessary because NSObject implements
312     // setValue:forUndefinedKey:, and unfortnately the default implementation
313     // throws an exception.
314     if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
315         setGlobalException(nil);
316     
317         ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType);
318
319         @try {
320             [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]];
321         } @catch(NSException* localException) {
322             // Do nothing.  Class did not override valueForUndefinedKey:.
323         }
324
325         moveGlobalExceptionToExecState(exec);
326     }
327 }
328
329 JSValue* ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property, JSType) const
330 {
331     JSValue* result = jsUndefined();
332     
333     id targetObject = getObject();
334
335     JSLock::DropAllLocks dropAllLocks(false); // Can't put this inside the @try scope because it unwinds incorrectly.
336
337     // This check is not really necessary because NSObject implements
338     // valueForUndefinedKey:, and unfortnately the default implementation
339     // throws an exception.
340     if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
341         setGlobalException(nil);
342     
343         @try {
344             id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]];
345             result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, _rootObject.get());
346         } @catch(NSException* localException) {
347             // Do nothing.  Class did not override valueForUndefinedKey:.
348         }
349
350         moveGlobalExceptionToExecState(exec);
351     }
352
353     return result;
354 }
355
356 JSValue* ObjcInstance::defaultValue(ExecState* exec, JSType hint) const
357 {
358     switch (hint) {
359     case StringType:
360         return stringValue(exec);
361     case NumberType:
362         return numberValue(exec);
363     case BooleanType:
364         return booleanValue();
365     case UnspecifiedType:
366         if ([_instance.get() isKindOfClass:[NSString class]])
367             return stringValue(exec);
368         if ([_instance.get() isKindOfClass:[NSNumber class]])
369             return numberValue(exec);
370     default:
371         return valueOf(exec);
372     }
373 }
374
375 JSValue* ObjcInstance::stringValue(ExecState* exec) const
376 {
377     return convertNSStringToString(exec, [getObject() description]);
378 }
379
380 JSValue* ObjcInstance::numberValue(ExecState* exec) const
381 {
382     // FIXME:  Implement something sensible
383     return jsNumber(exec, 0);
384 }
385
386 JSValue* ObjcInstance::booleanValue() const
387 {
388     // FIXME:  Implement something sensible
389     return jsBoolean(false);
390 }
391
392 JSValue* ObjcInstance::valueOf(ExecState* exec) const 
393 {
394     return stringValue(exec);
395 }