2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "objc_instance.h"
29 #import "WebScriptObject.h"
30 #include <wtf/Assertions.h>
33 #define OBJC_LOG(formatAndArgs...) ((void)0)
35 #define OBJC_LOG(formatAndArgs...) { \
36 fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \
37 fprintf(stderr, formatAndArgs); \
41 using namespace KJS::Bindings;
44 ObjcInstance::ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject> rootObject)
45 : Instance(rootObject)
53 ObjcInstance::~ObjcInstance()
55 begin(); // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools.
56 if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
57 [_instance.get() performSelector:@selector(finalizeForWebScript)];
62 void ObjcInstance::begin()
65 _pool = [[NSAutoreleasePool alloc] init];
69 void ObjcInstance::end()
72 ASSERT(_beginCount >= 0);
79 Bindings::Class* ObjcInstance::getClass() const
84 _class = ObjcClass::classForIsA(_instance->isa);
85 return static_cast<Bindings::Class*>(_class);
88 bool ObjcInstance::implementsCall() const
90 return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
93 JSValue* ObjcInstance::invokeMethod(ExecState* exec, const MethodList &methodList, const List &args)
95 JSValue* result = jsUndefined();
97 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
99 // Overloading methods is not allowed in ObjectiveC. Should only be one
100 // name match for a particular method.
101 ASSERT(methodList.size() == 1);
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())];
111 [invocation setSelector:(SEL)method->name()];
113 [invocation setTarget:_instance.get()];
115 if (method->isFallbackMethod()) {
116 if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
117 NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
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];
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];
132 [invocation setArgument:&objcArgs atIndex:3];
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);
139 // Must have a valid argument type. This method signature should have
140 // been filtered already to ensure that it has acceptable argument
142 ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
144 ObjcValue value = convertValueToObjcValue(exec, args.at(i-2), objcValueType);
146 switch (objcValueType) {
148 [invocation setArgument:&value.objectValue atIndex:i];
151 case ObjcUnsignedCharType:
152 [invocation setArgument:&value.charValue atIndex:i];
155 case ObjcUnsignedShortType:
156 [invocation setArgument:&value.shortValue atIndex:i];
159 case ObjcUnsignedIntType:
160 [invocation setArgument:&value.intValue atIndex:i];
163 case ObjcUnsignedLongType:
164 [invocation setArgument:&value.longValue atIndex:i];
166 case ObjcLongLongType:
167 case ObjcUnsignedLongLongType:
168 [invocation setArgument:&value.longLongValue atIndex:i];
171 [invocation setArgument:&value.floatValue atIndex:i];
174 [invocation setArgument:&value.doubleValue atIndex:i];
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);
188 // Get the return value type.
189 const char* type = [signature methodReturnType];
190 ObjcValueType objcValueType = objcValueTypeForType(type);
192 // Must have a valid return type. This method signature should have
193 // been filtered already to ensure that it have an acceptable return
195 ASSERT(objcValueType != ObjcInvalidType);
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
201 ASSERT([signature methodReturnLength] < 1024);
204 [invocation getReturnValue:buffer];
205 result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
207 } @catch(NSException* localException) {
212 JSValue* ObjcInstance::invokeDefaultMethod(ExecState* exec, const List &args)
214 JSValue* result = jsUndefined();
216 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
219 if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
222 NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
223 NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
224 [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
225 [invocation setTarget:_instance.get()];
227 if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
228 NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
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];
238 [invocation setArgument:&objcArgs atIndex:2];
242 // Get the return value type, should always be "@" because of
244 const char* type = [signature methodReturnType];
245 ObjcValueType objcValueType = objcValueTypeForType(type);
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
251 [invocation getReturnValue:buffer];
252 result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
253 } @catch(NSException* localException) {
259 bool ObjcInstance::supportsSetValueOfUndefinedField()
261 id targetObject = getObject();
262 if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
267 void ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier &property, JSValue* aValue)
269 id targetObject = getObject();
271 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
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);
280 [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]];
281 } @catch(NSException* localException) {
282 // Do nothing. Class did not override valueForUndefinedKey:.
287 JSValue* ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property, JSType) const
289 JSValue* result = jsUndefined();
291 id targetObject = getObject();
293 JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
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:)]){
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:.
310 JSValue* ObjcInstance::defaultValue(JSType hint) const
314 return stringValue();
316 return numberValue();
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();
329 JSValue* ObjcInstance::stringValue() const
331 return convertNSStringToString([getObject() description]);
334 JSValue* ObjcInstance::numberValue() const
336 // FIXME: Implement something sensible
340 JSValue* ObjcInstance::booleanValue() const
342 // FIXME: Implement something sensible
343 return jsBoolean(false);
346 JSValue* ObjcInstance::valueOf() const
348 return stringValue();