summaryrefslogtreecommitdiffstats
path: root/JavaScriptCore/bindings/objc/objc_instance.mm
diff options
context:
space:
mode:
Diffstat (limited to 'JavaScriptCore/bindings/objc/objc_instance.mm')
-rw-r--r--JavaScriptCore/bindings/objc/objc_instance.mm349
1 files changed, 349 insertions, 0 deletions
diff --git a/JavaScriptCore/bindings/objc/objc_instance.mm b/JavaScriptCore/bindings/objc/objc_instance.mm
new file mode 100644
index 0000000..57ebfe8
--- /dev/null
+++ b/JavaScriptCore/bindings/objc/objc_instance.mm
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "objc_instance.h"
+
+#import "WebScriptObject.h"
+#include <wtf/Assertions.h>
+
+#ifdef NDEBUG
+#define OBJC_LOG(formatAndArgs...) ((void)0)
+#else
+#define OBJC_LOG(formatAndArgs...) { \
+ fprintf (stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \
+ fprintf(stderr, formatAndArgs); \
+}
+#endif
+
+using namespace KJS::Bindings;
+using namespace KJS;
+
+ObjcInstance::ObjcInstance(ObjectStructPtr instance, PassRefPtr<RootObject> rootObject)
+ : Instance(rootObject)
+ , _instance(instance)
+ , _class(0)
+ , _pool(0)
+ , _beginCount(0)
+{
+}
+
+ObjcInstance::~ObjcInstance()
+{
+ begin(); // -finalizeForWebScript and -dealloc/-finalize may require autorelease pools.
+ if ([_instance.get() respondsToSelector:@selector(finalizeForWebScript)])
+ [_instance.get() performSelector:@selector(finalizeForWebScript)];
+ _instance = 0;
+ end();
+}
+
+void ObjcInstance::begin()
+{
+ if (!_pool)
+ _pool = [[NSAutoreleasePool alloc] init];
+ _beginCount++;
+}
+
+void ObjcInstance::end()
+{
+ _beginCount--;
+ ASSERT(_beginCount >= 0);
+ if (!_beginCount) {
+ [_pool drain];
+ _pool = 0;
+ }
+}
+
+Bindings::Class* ObjcInstance::getClass() const
+{
+ if (!_instance)
+ return 0;
+ if (!_class)
+ _class = ObjcClass::classForIsA(_instance->isa);
+ return static_cast<Bindings::Class*>(_class);
+}
+
+bool ObjcInstance::implementsCall() const
+{
+ return [_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)];
+}
+
+JSValue* ObjcInstance::invokeMethod(ExecState* exec, const MethodList &methodList, const List &args)
+{
+ JSValue* result = jsUndefined();
+
+ JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
+
+ // Overloading methods is not allowed in ObjectiveC. Should only be one
+ // name match for a particular method.
+ ASSERT(methodList.size() == 1);
+
+@try {
+ ObjcMethod* method = 0;
+ method = static_cast<ObjcMethod*>(methodList[0]);
+ NSMethodSignature* signature = method->getMethodSignature();
+ NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ [invocation setSelector:sel_registerName(method->name())];
+#else
+ [invocation setSelector:(SEL)method->name()];
+#endif
+ [invocation setTarget:_instance.get()];
+
+ if (method->isFallbackMethod()) {
+ if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
+ NSLog(@"Incorrect signature for invokeUndefinedMethodFromWebScript:withArguments: -- return type must be object.");
+ return result;
+ }
+
+ // Invoke invokeUndefinedMethodFromWebScript:withArguments:, pass JavaScript function
+ // name as first (actually at 2) argument and array of args as second.
+ NSString* jsName = (NSString* )method->javaScriptName();
+ [invocation setArgument:&jsName atIndex:2];
+
+ NSMutableArray* objcArgs = [NSMutableArray array];
+ int count = args.size();
+ for (int i = 0; i < count; i++) {
+ ObjcValue value = convertValueToObjcValue(exec, args.at(i), ObjcObjectType);
+ [objcArgs addObject:value.objectValue];
+ }
+ [invocation setArgument:&objcArgs atIndex:3];
+ } else {
+ unsigned count = [signature numberOfArguments];
+ for (unsigned i = 2; i < count ; i++) {
+ const char* type = [signature getArgumentTypeAtIndex:i];
+ ObjcValueType objcValueType = objcValueTypeForType(type);
+
+ // Must have a valid argument type. This method signature should have
+ // been filtered already to ensure that it has acceptable argument
+ // types.
+ ASSERT(objcValueType != ObjcInvalidType && objcValueType != ObjcVoidType);
+
+ ObjcValue value = convertValueToObjcValue(exec, args.at(i-2), objcValueType);
+
+ switch (objcValueType) {
+ case ObjcObjectType:
+ [invocation setArgument:&value.objectValue atIndex:i];
+ break;
+ case ObjcCharType:
+ case ObjcUnsignedCharType:
+ [invocation setArgument:&value.charValue atIndex:i];
+ break;
+ case ObjcShortType:
+ case ObjcUnsignedShortType:
+ [invocation setArgument:&value.shortValue atIndex:i];
+ break;
+ case ObjcIntType:
+ case ObjcUnsignedIntType:
+ [invocation setArgument:&value.intValue atIndex:i];
+ break;
+ case ObjcLongType:
+ case ObjcUnsignedLongType:
+ [invocation setArgument:&value.longValue atIndex:i];
+ break;
+ case ObjcLongLongType:
+ case ObjcUnsignedLongLongType:
+ [invocation setArgument:&value.longLongValue atIndex:i];
+ break;
+ case ObjcFloatType:
+ [invocation setArgument:&value.floatValue atIndex:i];
+ break;
+ case ObjcDoubleType:
+ [invocation setArgument:&value.doubleValue atIndex:i];
+ break;
+ default:
+ // Should never get here. Argument types are filtered (and
+ // the assert above should have fired in the impossible case
+ // of an invalid type anyway).
+ fprintf(stderr, "%s: invalid type (%d)\n", __PRETTY_FUNCTION__, (int)objcValueType);
+ ASSERT(false);
+ }
+ }
+ }
+
+ [invocation invoke];
+
+ // Get the return value type.
+ const char* type = [signature methodReturnType];
+ ObjcValueType objcValueType = objcValueTypeForType(type);
+
+ // Must have a valid return type. This method signature should have
+ // been filtered already to ensure that it have an acceptable return
+ // type.
+ ASSERT(objcValueType != ObjcInvalidType);
+
+ // Get the return value and convert it to a JavaScript value. Length
+ // of return value will never exceed the size of largest scalar
+ // or a pointer.
+ char buffer[1024];
+ ASSERT([signature methodReturnLength] < 1024);
+
+ if (*type != 'v') {
+ [invocation getReturnValue:buffer];
+ result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
+ }
+} @catch(NSException* localException) {
+}
+ return result;
+}
+
+JSValue* ObjcInstance::invokeDefaultMethod(ExecState* exec, const List &args)
+{
+ JSValue* result = jsUndefined();
+
+ JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
+
+@try {
+ if (![_instance.get() respondsToSelector:@selector(invokeDefaultMethodWithArguments:)])
+ return result;
+
+ NSMethodSignature* signature = [_instance.get() methodSignatureForSelector:@selector(invokeDefaultMethodWithArguments:)];
+ NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
+ [invocation setSelector:@selector(invokeDefaultMethodWithArguments:)];
+ [invocation setTarget:_instance.get()];
+
+ if (objcValueTypeForType([signature methodReturnType]) != ObjcObjectType) {
+ NSLog(@"Incorrect signature for invokeDefaultMethodWithArguments: -- return type must be object.");
+ return result;
+ }
+
+ NSMutableArray* objcArgs = [NSMutableArray array];
+ unsigned count = args.size();
+ for (unsigned i = 0; i < count; i++) {
+ ObjcValue value = convertValueToObjcValue(exec, args.at(i), ObjcObjectType);
+ [objcArgs addObject:value.objectValue];
+ }
+ [invocation setArgument:&objcArgs atIndex:2];
+
+ [invocation invoke];
+
+ // Get the return value type, should always be "@" because of
+ // check above.
+ const char* type = [signature methodReturnType];
+ ObjcValueType objcValueType = objcValueTypeForType(type);
+
+ // Get the return value and convert it to a JavaScript value. Length
+ // of return value will never exceed the size of a pointer, so we're
+ // OK with 32 here.
+ char buffer[32];
+ [invocation getReturnValue:buffer];
+ result = convertObjcValueToValue(exec, buffer, objcValueType, _rootObject.get());
+} @catch(NSException* localException) {
+}
+
+ return result;
+}
+
+bool ObjcInstance::supportsSetValueOfUndefinedField()
+{
+ id targetObject = getObject();
+ if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)])
+ return true;
+ return false;
+}
+
+void ObjcInstance::setValueOfUndefinedField(ExecState* exec, const Identifier &property, JSValue* aValue)
+{
+ id targetObject = getObject();
+
+ JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
+
+ // This check is not really necessary because NSObject implements
+ // setValue:forUndefinedKey:, and unfortnately the default implementation
+ // throws an exception.
+ if ([targetObject respondsToSelector:@selector(setValue:forUndefinedKey:)]){
+ ObjcValue objcValue = convertValueToObjcValue(exec, aValue, ObjcObjectType);
+
+ @try {
+ [targetObject setValue:objcValue.objectValue forUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]];
+ } @catch(NSException* localException) {
+ // Do nothing. Class did not override valueForUndefinedKey:.
+ }
+ }
+}
+
+JSValue* ObjcInstance::getValueOfUndefinedField(ExecState* exec, const Identifier& property, JSType) const
+{
+ JSValue* result = jsUndefined();
+
+ id targetObject = getObject();
+
+ JSLock::DropAllLocks dropAllLocks; // Can't put this inside the @try scope because it unwinds incorrectly.
+
+ // This check is not really necessary because NSObject implements
+ // valueForUndefinedKey:, and unfortnately the default implementation
+ // throws an exception.
+ if ([targetObject respondsToSelector:@selector(valueForUndefinedKey:)]){
+ @try {
+ id objcValue = [targetObject valueForUndefinedKey:[NSString stringWithCString:property.ascii() encoding:NSASCIIStringEncoding]];
+ result = convertObjcValueToValue(exec, &objcValue, ObjcObjectType, _rootObject.get());
+ } @catch(NSException* localException) {
+ // Do nothing. Class did not override valueForUndefinedKey:.
+ }
+ }
+
+ return result;
+}
+
+JSValue* ObjcInstance::defaultValue(JSType hint) const
+{
+ switch (hint) {
+ case StringType:
+ return stringValue();
+ case NumberType:
+ return numberValue();
+ case BooleanType:
+ return booleanValue();
+ case UnspecifiedType:
+ if ([_instance.get() isKindOfClass:[NSString class]])
+ return stringValue();
+ if ([_instance.get() isKindOfClass:[NSNumber class]])
+ return numberValue();
+ default:
+ return valueOf();
+ }
+}
+
+JSValue* ObjcInstance::stringValue() const
+{
+ return convertNSStringToString([getObject() description]);
+}
+
+JSValue* ObjcInstance::numberValue() const
+{
+ // FIXME: Implement something sensible
+ return jsNumber(0);
+}
+
+JSValue* ObjcInstance::booleanValue() const
+{
+ // FIXME: Implement something sensible
+ return jsBoolean(false);
+}
+
+JSValue* ObjcInstance::valueOf() const
+{
+ return stringValue();
+}