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