summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/bridge/objc/objc_class.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/bridge/objc/objc_class.mm')
-rw-r--r--Source/WebCore/bridge/objc/objc_class.mm253
1 files changed, 253 insertions, 0 deletions
diff --git a/Source/WebCore/bridge/objc/objc_class.mm b/Source/WebCore/bridge/objc/objc_class.mm
new file mode 100644
index 0000000..2d29499
--- /dev/null
+++ b/Source/WebCore/bridge/objc/objc_class.mm
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "objc_class.h"
+
+#include "objc_instance.h"
+#include "WebScriptObject.h"
+
+namespace JSC {
+namespace Bindings {
+
+static void deleteMethod(CFAllocatorRef, const void* value)
+{
+ delete static_cast<const Method*>(value);
+}
+
+static void deleteField(CFAllocatorRef, const void* value)
+{
+ delete static_cast<const Field*>(value);
+}
+
+const CFDictionaryValueCallBacks MethodDictionaryValueCallBacks = { 0, 0, &deleteMethod, 0 , 0 };
+const CFDictionaryValueCallBacks FieldDictionaryValueCallBacks = { 0, 0, &deleteField, 0 , 0 };
+
+ObjcClass::ObjcClass(ClassStructPtr aClass)
+ : _isa(aClass)
+ , _methods(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &MethodDictionaryValueCallBacks))
+ , _fields(AdoptCF, CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &FieldDictionaryValueCallBacks))
+{
+}
+
+static CFMutableDictionaryRef classesByIsA = 0;
+
+static void _createClassesByIsAIfNecessary()
+{
+ if (!classesByIsA)
+ classesByIsA = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
+}
+
+ObjcClass* ObjcClass::classForIsA(ClassStructPtr isa)
+{
+ _createClassesByIsAIfNecessary();
+
+ ObjcClass* aClass = (ObjcClass*)CFDictionaryGetValue(classesByIsA, isa);
+ if (!aClass) {
+ aClass = new ObjcClass(isa);
+ CFDictionaryAddValue(classesByIsA, isa, aClass);
+ }
+
+ return aClass;
+}
+
+MethodList ObjcClass::methodsNamed(const Identifier& identifier, Instance*) const
+{
+ MethodList methodList;
+ char fixedSizeBuffer[1024];
+ char* buffer = fixedSizeBuffer;
+ CString jsName = identifier.ascii();
+ if (!convertJSMethodNameToObjc(jsName.data(), buffer, sizeof(fixedSizeBuffer))) {
+ int length = jsName.length() + 1;
+ buffer = new char[length];
+ if (!buffer || !convertJSMethodNameToObjc(jsName.data(), buffer, length))
+ return methodList;
+ }
+
+
+ RetainPtr<CFStringRef> methodName(AdoptCF, CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII));
+ Method* method = (Method*)CFDictionaryGetValue(_methods.get(), methodName.get());
+ if (method) {
+ methodList.append(method);
+ return methodList;
+ }
+
+ ClassStructPtr thisClass = _isa;
+ while (thisClass && methodList.isEmpty()) {
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ unsigned numMethodsInClass = 0;
+ MethodStructPtr* objcMethodList = class_copyMethodList(thisClass, &numMethodsInClass);
+#else
+ void* iterator = 0;
+ struct objc_method_list* objcMethodList;
+ while ((objcMethodList = class_nextMethodList(thisClass, &iterator))) {
+ unsigned numMethodsInClass = objcMethodList->method_count;
+#endif
+ for (unsigned i = 0; i < numMethodsInClass; i++) {
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ MethodStructPtr objcMethod = objcMethodList[i];
+ SEL objcMethodSelector = method_getName(objcMethod);
+#else
+ struct objc_method* objcMethod = &objcMethodList->method_list[i];
+ SEL objcMethodSelector = objcMethod->method_name;
+#endif
+ const char* objcMethodSelectorName = sel_getName(objcMethodSelector);
+ NSString* mappedName = nil;
+
+ // See if the class wants to exclude the selector from visibility in JavaScript.
+ if ([thisClass respondsToSelector:@selector(isSelectorExcludedFromWebScript:)])
+ if ([thisClass isSelectorExcludedFromWebScript:objcMethodSelector])
+ continue;
+
+ // See if the class want to provide a different name for the selector in JavaScript.
+ // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
+ // of the class.
+ if ([thisClass respondsToSelector:@selector(webScriptNameForSelector:)])
+ mappedName = [thisClass webScriptNameForSelector:objcMethodSelector];
+
+ if ((mappedName && [mappedName isEqual:(NSString*)methodName.get()]) || strcmp(objcMethodSelectorName, buffer) == 0) {
+ Method* aMethod = new ObjcMethod(thisClass, objcMethodSelector); // deleted when the dictionary is destroyed
+ CFDictionaryAddValue(_methods.get(), methodName.get(), aMethod);
+ methodList.append(aMethod);
+ break;
+ }
+ }
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ thisClass = class_getSuperclass(thisClass);
+ free(objcMethodList);
+#else
+ }
+ thisClass = thisClass->super_class;
+#endif
+ }
+
+ if (buffer != fixedSizeBuffer)
+ delete [] buffer;
+
+ return methodList;
+}
+
+Field* ObjcClass::fieldNamed(const Identifier& identifier, Instance* instance) const
+{
+ ClassStructPtr thisClass = _isa;
+
+ CString jsName = identifier.ascii();
+ RetainPtr<CFStringRef> fieldName(AdoptCF, CFStringCreateWithCString(NULL, jsName.data(), kCFStringEncodingASCII));
+ Field* aField = (Field*)CFDictionaryGetValue(_fields.get(), fieldName.get());
+ if (aField)
+ return aField;
+
+ id targetObject = (static_cast<ObjcInstance*>(instance))->getObject();
+ id attributes = [targetObject attributeKeys];
+ if (attributes) {
+ // Class overrides attributeKeys, use that array of key names.
+ unsigned count = [attributes count];
+ for (unsigned i = 0; i < count; i++) {
+ NSString* keyName = [attributes objectAtIndex:i];
+ const char* UTF8KeyName = [keyName UTF8String]; // ObjC actually only supports ASCII names.
+
+ // See if the class wants to exclude the selector from visibility in JavaScript.
+ if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
+ if ([thisClass isKeyExcludedFromWebScript:UTF8KeyName])
+ continue;
+
+ // See if the class want to provide a different name for the selector in JavaScript.
+ // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
+ // of the class.
+ NSString* mappedName = nil;
+ if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
+ mappedName = [thisClass webScriptNameForKey:UTF8KeyName];
+
+ if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || [keyName isEqual:(NSString*)fieldName.get()]) {
+ aField = new ObjcField((CFStringRef)keyName); // deleted when the dictionary is destroyed
+ CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
+ break;
+ }
+ }
+ } else {
+ // Class doesn't override attributeKeys, so fall back on class runtime
+ // introspection.
+
+ while (thisClass) {
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ unsigned numFieldsInClass = 0;
+ IvarStructPtr* ivarsInClass = class_copyIvarList(thisClass, &numFieldsInClass);
+#else
+ struct objc_ivar_list* fieldsInClass = thisClass->ivars;
+ if (fieldsInClass) {
+ unsigned numFieldsInClass = fieldsInClass->ivar_count;
+#endif
+ for (unsigned i = 0; i < numFieldsInClass; i++) {
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ IvarStructPtr objcIVar = ivarsInClass[i];
+ const char* objcIvarName = ivar_getName(objcIVar);
+#else
+ IvarStructPtr objcIVar = &fieldsInClass->ivar_list[i];
+ const char* objcIvarName = objcIVar->ivar_name;
+#endif
+ NSString* mappedName = 0;
+
+ // See if the class wants to exclude the selector from visibility in JavaScript.
+ if ([thisClass respondsToSelector:@selector(isKeyExcludedFromWebScript:)])
+ if ([thisClass isKeyExcludedFromWebScript:objcIvarName])
+ continue;
+
+ // See if the class want to provide a different name for the selector in JavaScript.
+ // Note that we do not do any checks to guarantee uniqueness. That's the responsiblity
+ // of the class.
+ if ([thisClass respondsToSelector:@selector(webScriptNameForKey:)])
+ mappedName = [thisClass webScriptNameForKey:objcIvarName];
+
+ if ((mappedName && [mappedName isEqual:(NSString*)fieldName.get()]) || strcmp(objcIvarName, jsName.data()) == 0) {
+ aField = new ObjcField(objcIVar); // deleted when the dictionary is destroyed
+ CFDictionaryAddValue(_fields.get(), fieldName.get(), aField);
+ break;
+ }
+ }
+#if defined(OBJC_API_VERSION) && OBJC_API_VERSION >= 2
+ thisClass = class_getSuperclass(thisClass);
+ free(ivarsInClass);
+#else
+ }
+ thisClass = thisClass->super_class;
+#endif
+ }
+ }
+
+ return aField;
+}
+
+JSValue ObjcClass::fallbackObject(ExecState* exec, Instance* instance, const Identifier &propertyName)
+{
+ ObjcInstance* objcInstance = static_cast<ObjcInstance*>(instance);
+ id targetObject = objcInstance->getObject();
+
+ if (![targetObject respondsToSelector:@selector(invokeUndefinedMethodFromWebScript:withArguments:)])
+ return jsUndefined();
+ return new (exec) ObjcFallbackObjectImp(exec, exec->lexicalGlobalObject(), objcInstance, propertyName);
+}
+
+}
+}