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