UNPKG

objj-runtime

Version:

JavaScript (ECMAScript) and Objective-J runtime

1,163 lines (888 loc) 32.2 kB
/* * Runtime.js * Objective-J * * Created by Francisco Tolmasky. * Copyright 2008-2010, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ var CLS_CLASS = 0x1, CLS_META = 0x2, CLS_INITIALIZED = 0x4, CLS_INITIALIZING = 0x8; #define CHANGEINFO(aClass, aSetInfoMask, aClearInfoMask) aClass.info = (aClass.info | (aSetInfoMask)) & ~(aClearInfoMask) #define GETINFO(aClass, anInfoMask) (aClass.info & (anInfoMask)) #define SETINFO(aClass, anInfoMask) CHANGEINFO(aClass, anInfoMask, 0) #define CLEARINFO(aClass, anInfoMask) CHANGEINFO(aClass, 0, anInfoMask) #define ISMETA(aClass) (GETINFO(aClass, CLS_META)) #define GETMETA(aClass) (ISMETA(aClass) ? aClass : aClass.isa) #define ISINITIALIZED(aClass) GETINFO(GETMETA(aClass), CLS_INITIALIZED) // MAXIMUM_RECURSION_CHECKS // If defined, objj_msgSend will check for recursion deeper than MAXIMUM_RECURSION_DEPTH and // throw an error if found. While crude, this can be helpful when your JavaScript debugger // crashes on recursion errors (e.g. Safari) or ignores them (e.g. Chrome). // #define MAXIMUM_RECURSION_CHECKS #define MAXIMUM_RECURSION_DEPTH 80 GLOBAL(objj_ivar) = function(/*String*/ aName, /*String*/ aType) { this.name = aName; this.type = aType; } GLOBAL(objj_method) = function(/*String*/ aName, /*IMP*/ anImplementation, /*Array<String>*/ types) { var method = anImplementation || function(/*id*/ aReceiver, /*SEL*/ aSelector) {CPException.isa.objj_msgSend2(CPException, "raise:reason:", CPInternalInconsistencyException, aReceiver.isa.method_msgSend0(self, "className") + " does not have an implementation for selector '" + aSelector + "'")}; method.method_name = aName; method.method_imp = anImplementation; method.method_types = types; return method; } GLOBAL(objj_class) = function(displayName) { this.isa = NULL; this.version = 0; this.super_class = NULL; this.name = NULL; this.info = 0; this.ivar_list = []; this.ivar_store = function() { }; this.ivar_dtable = this.ivar_store.prototype; this.method_list = []; this.method_store = function() { }; this.method_dtable = this.method_store.prototype; this.protocol_list = []; #if DEBUG // Naming the allocator allows the WebKit heap snapshot tool to display object class names correctly // HACK: displayName property is not respected so we must eval a function to name it eval("this.allocator = function " + (displayName || "OBJJ_OBJECT").replace(/\W/g, "_") + "() { }"); #else this.allocator = function() { }; #endif this._UID = -1; } GLOBAL(objj_protocol) = function(/*String*/ aName) { this.name = aName; this.instance_methods = { }; this.class_methods = { }; } GLOBAL(objj_object) = function() { this.isa = NULL; this._UID = -1; } GLOBAL(objj_typeDef) = function(/*String*/ aName) { this.name = aName; } // Working with Classes GLOBAL(class_getName) = function(/*Class*/ aClass) { if (aClass == Nil) return ""; return aClass.name; } GLOBAL(class_isMetaClass) = function(/*Class*/ aClass) { if (!aClass) return NO; return ISMETA(aClass); } GLOBAL(class_getSuperclass) = function(/*Class*/ aClass) { if (aClass == Nil) return Nil; return aClass.super_class; } GLOBAL(class_setSuperclass) = function(/*Class*/ aClass, /*Class*/ aSuperClass) { // Set up the actual class hierarchy. aClass.super_class = aSuperClass; aClass.isa.super_class = aSuperClass.isa; } GLOBAL(class_addIvar) = function(/*Class*/ aClass, /*String*/ aName, /*String*/ aType) { var thePrototype = aClass.allocator.prototype; // FIXME: Use getInstanceVariable if (typeof thePrototype[aName] != "undefined") return NO; var ivar = new objj_ivar(aName, aType); aClass.ivar_list.push(ivar); aClass.ivar_dtable[aName] = ivar; thePrototype[aName] = NULL; return YES; } GLOBAL(class_addIvars) = function(/*Class*/ aClass, /*Array*/ivars) { var index = 0, count = ivars.length, thePrototype = aClass.allocator.prototype; for (; index < count; ++index) { var ivar = ivars[index], name = ivar.name; // FIXME: Use getInstanceVariable if (typeof thePrototype[name] === "undefined") { aClass.ivar_list.push(ivar); aClass.ivar_dtable[name] = ivar; thePrototype[name] = NULL; } } } GLOBAL(class_copyIvarList) = function(/*Class*/ aClass) { return aClass.ivar_list.slice(0); } //#define class_copyIvarList(aClass) (aClass.ivar_list.slice(0)) #define METHOD_DISPLAY_NAME(aClass, aMethod) (ISMETA(aClass) ? '+' : '-') + " [" + class_getName(aClass) + ' ' + method_getName(aMethod) + ']' GLOBAL(class_addMethod) = function(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anImplementation, /*Array<String>*/ types) { // FIXME: return NO if it exists? var method = new objj_method(aName, anImplementation, types); aClass.method_list.push(method); aClass.method_dtable[aName] = method; #if DEBUG // Give this function a "pretty" name for the console. method.displayName = METHOD_DISPLAY_NAME(aClass, method); #endif // FIXME: Should this be done here? // If this is a root class... if (!ISMETA(aClass) && GETMETA(aClass).isa === GETMETA(aClass)) class_addMethod(GETMETA(aClass), aName, anImplementation, types); return YES; } GLOBAL(class_addMethods) = function(/*Class*/ aClass, /*Array*/ methods) { var index = 0, count = methods.length, method_list = aClass.method_list, method_dtable = aClass.method_dtable; for (; index < count; ++index) { var method = methods[index]; // FIXME: Don't do it if it exists? method_list.push(method); method_dtable[method.method_name] = method; #if DEBUG // Give this function a "pretty" name for the console. method.displayName = METHOD_DISPLAY_NAME(aClass, method); #endif } // If this is a root class... if (!ISMETA(aClass) && GETMETA(aClass).isa === GETMETA(aClass)) class_addMethods(GETMETA(aClass), methods); } GLOBAL(class_getInstanceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector) { if (!aClass || !aSelector) return NULL; var method = aClass.method_dtable[aSelector]; return method ? method : NULL; } GLOBAL(class_getInstanceVariable) = function(/*Class*/ aClass, /*String*/ aName) { if (!aClass || !aName) return NULL; // FIXME: this doesn't appropriately deal with Object's properties. var variable = aClass.ivar_dtable[aName]; return variable; } GLOBAL(class_getClassMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector) { if (!aClass || !aSelector) return NULL; var method = GETMETA(aClass).method_dtable[aSelector]; return method ? method : NULL; } GLOBAL(class_respondsToSelector) = function(/*Class*/ aClass, /*SEL*/ aSelector) { return class_getClassMethod(aClass, aSelector) != NULL; } GLOBAL(class_copyMethodList) = function(/*Class*/ aClass) { return aClass.method_list.slice(0); } GLOBAL(class_getVersion) = function(/*Class*/ aClass) { return aClass.version; } GLOBAL(class_setVersion) = function(/*Class*/ aClass, /*Integer*/ aVersion) { aClass.version = parseInt(aVersion, 10); } GLOBAL(class_replaceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector, /*IMP*/ aMethodImplementation) { if (!aClass || !aSelector) return NULL; var method = aClass.method_dtable[aSelector], method_imp = method.method_imp, new_method = new objj_method(method.method_name, aMethodImplementation, method.method_types); new_method.displayName = method.displayName; aClass.method_dtable[aSelector] = new_method; var index = aClass.method_list.indexOf(method); if (index !== -1) { aClass.method_list[index] = new_method; } else { aClass.method_list.push(new_method); } return method_imp; } GLOBAL(class_addProtocol) = function(/*Class*/ aClass, /*Protocol*/ aProtocol) { if (!aProtocol || class_conformsToProtocol(aClass, aProtocol)) { return; } (aClass.protocol_list || (aClass.protocol_list = [])).push(aProtocol); return true; } GLOBAL(class_conformsToProtocol) = function(/*Class*/ aClass, /*Protocol*/ aProtocol) { if (!aProtocol) return false; while (aClass) { var protocols = aClass.protocol_list, size = protocols ? protocols.length : 0; for (var i = 0; i < size; i++) { var p = protocols[i]; if (p.name === aProtocol.name) { return true; } if (protocol_conformsToProtocol(p, aProtocol)) { return true; } } aClass = class_getSuperclass(aClass); } return false; } GLOBAL(class_copyProtocolList) = function(/*Class*/ aClass) { var protocols = aClass.protocol_list; return protocols ? protocols.slice(0) : []; } GLOBAL(protocol_conformsToProtocol) = function(/*Protocol*/ p1, /*Protocol*/ p2) { if (!p1 || !p2) return false; if (p1.name === p2.name) return true; var protocols = p1.protocol_list, size = protocols ? protocols.length : 0; for (var i = 0; i < size; i++) { var p = protocols[i]; if (p.name === p2.name) { return true; } if (protocol_conformsToProtocol(p, p2)) { return true; } } return false; } var REGISTERED_PROTOCOLS = Object.create(null); GLOBAL(objj_allocateProtocol) = function(/*String*/ aName) { var protocol = new objj_protocol(aName); return protocol; } GLOBAL(objj_registerProtocol) = function(/*Protocol*/ proto) { REGISTERED_PROTOCOLS[proto.name] = proto; } GLOBAL(protocol_getName) = function(/*Protocol*/ proto) { return proto.name; } // Right now we only register required methods. THis might need to change in the future GLOBAL(protocol_addMethodDescription) = function(/*Protocol*/ proto, /*SEL*/ selector, /*Array*/ types, /*BOOL*/ isRequiredMethod, /*BOOL*/ isInstanceMethod) { if (!proto || !selector) return; if (isRequiredMethod) (isInstanceMethod ? proto.instance_methods : proto.class_methods)[selector] = new objj_method(selector, null, types); } GLOBAL(protocol_addMethodDescriptions) = function(/*Protocol*/ proto, /*Array*/ methods, /*BOOL*/ isRequiredMethod, /*BOOL*/ isInstanceMethod) { if (!isRequiredMethod) return; var index = 0, count = methods.length, method_dtable = isInstanceMethod ? proto.instance_methods : proto.class_methods; for (; index < count; ++index) { var method = methods[index]; method_dtable[method.method_name] = method; } } GLOBAL(protocol_copyMethodDescriptionList) = function(/*Protocol*/ proto, /*BOOL*/ isRequiredMethod, /*BOOL*/ isInstanceMethod) { if (!isRequiredMethod) return []; var method_dtable = isInstanceMethod ? proto.instance_methods : proto.class_methods, methodList = []; for (var selector in method_dtable) if (method_dtable.hasOwnProperty(selector)) methodList.push(method_dtable[selector]); return methodList; } GLOBAL(protocol_addProtocol) = function(/*Protocol*/ proto, /*Protocol*/ addition) { if (!proto || !addition) return; (proto.protocol_list || (proto.protocol_list = [])).push(addition); } var REGISTERED_TYPEDEFS = Object.create(null); GLOBAL(objj_allocateTypeDef) = function(/*String*/ aName) { var typeDef = new objj_typeDef(aName); return typeDef; } GLOBAL(objj_registerTypeDef) = function(/*TypeDef*/ typeDef) { REGISTERED_TYPEDEFS[typeDef.name] = typeDef; } GLOBAL(typeDef_getName) = function(/*TypeDef*/ typeDef) { return typeDef.name; } var _class_initialize = function(/*Class*/ aClass) { var meta = GETMETA(aClass); if (GETINFO(aClass, CLS_META)) aClass = objj_getClass(aClass.name); if (aClass.super_class && !ISINITIALIZED(aClass.super_class)) _class_initialize(aClass.super_class); if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING)) { SETINFO(meta, CLS_INITIALIZING); // We don't need to initialize any more. aClass.objj_msgSend = objj_msgSendFast; aClass.objj_msgSend0 = objj_msgSendFast0; aClass.objj_msgSend1 = objj_msgSendFast1; aClass.objj_msgSend2 = objj_msgSendFast2; aClass.objj_msgSend3 = objj_msgSendFast3; meta.objj_msgSend = objj_msgSendFast; meta.objj_msgSend0 = objj_msgSendFast0; meta.objj_msgSend1 = objj_msgSendFast1; meta.objj_msgSend2 = objj_msgSendFast2; meta.objj_msgSend3 = objj_msgSendFast3; aClass.method_msgSend = aClass.method_dtable; var metaMethodDTable = meta.method_msgSend = meta.method_dtable, initializeImp = metaMethodDTable["initialize"]; if (initializeImp) initializeImp(aClass, "initialize") CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING); } } GLOBAL(_objj_forward) = function(self, _cmd) { var isa = self.isa, meta = GETMETA(isa); if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING)) { _class_initialize(isa); } var implementation = isa.method_msgSend[_cmd]; if (implementation) { return implementation.apply(isa, arguments); } implementation = isa.method_dtable[SEL_forwardingTargetForSelector_]; if (implementation) { var target = implementation(self, SEL_forwardingTargetForSelector_, _cmd); if (target && target !== self) { arguments[0] = target; return target.isa.objj_msgSend.apply(target.isa, arguments); } } implementation = isa.method_dtable[SEL_methodSignatureForSelector_]; if (implementation) { var forwardInvocationImplementation = isa.method_dtable[SEL_forwardInvocation_]; if (forwardInvocationImplementation) { var signature = implementation(self, SEL_methodSignatureForSelector_, _cmd); if (signature) { var invocationClass = objj_lookUpClass("CPInvocation"); if (invocationClass) { var invocation = invocationClass.isa.objj_msgSend1(invocationClass, SEL_invocationWithMethodSignature_, signature), index = 0, count = arguments.length; if (invocation != null) { var invocationIsa = invocation.isa; for (; index < count; ++index) invocationIsa.objj_msgSend2(invocation, SEL_setArgument_atIndex_, arguments[index], index); } forwardInvocationImplementation(self, SEL_forwardInvocation_, invocation); return invocation == null ? null : invocationIsa.objj_msgSend0(invocation, SEL_returnValue); } } } } implementation = isa.method_dtable[SEL_doesNotRecognizeSelector_]; if (implementation) return implementation(self, SEL_doesNotRecognizeSelector_, _cmd); throw class_getName(isa) + " does not implement doesNotRecognizeSelector:. Did you forget a superclass for " + class_getName(isa) + "?"; }; // I think this forward:: may need to be a common method, instead of defined in CPObject. #define CLASS_GET_METHOD_IMPLEMENTATION(aMethodImplementation, aClass, aSelector)\ if (!ISINITIALIZED(aClass))\ _class_initialize(aClass);\ \ aMethodImplementation = aClass.method_dtable[aSelector] || _objj_forward; GLOBAL(class_getMethodImplementation) = function(/*Class*/ aClass, /*SEL*/ aSelector) { CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aClass, aSelector); return implementation; } // Adding Classes var REGISTERED_CLASSES = Object.create(null); GLOBAL(objj_enumerateClassesUsingBlock) = function(/* function(aClass) */aBlock) { for (var key in REGISTERED_CLASSES) { aBlock(REGISTERED_CLASSES[key]); } } GLOBAL(objj_allocateClassPair) = function(/*Class*/ superclass, /*String*/ aName) { var classObject = new objj_class(aName), metaClassObject = new objj_class(aName), rootClassObject = classObject; // If we don't have a superclass, we are the root class. if (superclass) { rootClassObject = superclass; while (rootClassObject.superclass) rootClassObject = rootClassObject.superclass; // Give our current allocator all the instance variables of our super class' allocator. classObject.allocator.prototype = new superclass.allocator; // "Inherit" parent properties. classObject.ivar_dtable = classObject.ivar_store.prototype = new superclass.ivar_store; classObject.method_dtable = classObject.method_store.prototype = new superclass.method_store; metaClassObject.method_dtable = metaClassObject.method_store.prototype = new superclass.isa.method_store; // Set up the actual class hierarchy. classObject.super_class = superclass; metaClassObject.super_class = superclass.isa; } else classObject.allocator.prototype = new objj_object(); classObject.isa = metaClassObject; classObject.name = aName; classObject.info = CLS_CLASS; classObject._UID = objj_generateObjectUID(); // It needs initialize classObject.init = true; metaClassObject.isa = rootClassObject.isa; metaClassObject.name = aName; metaClassObject.info = CLS_META; metaClassObject._UID = objj_generateObjectUID(); // It needs initialize metaClassObject.init = true; return classObject; } var CONTEXT_BUNDLE = nil; GLOBAL(objj_registerClassPair) = function(/*Class*/ aClass) { global[aClass.name] = aClass; REGISTERED_CLASSES[aClass.name] = aClass; addClassToBundle(aClass, CONTEXT_BUNDLE); } GLOBAL(objj_resetRegisterClasses) = function() { for (var key in REGISTERED_CLASSES) delete global[key]; REGISTERED_CLASSES = Object.create(null); REGISTERED_PROTOCOLS = Object.create(null); REGISTERED_TYPEDEFS = Object.create(null); resetBundle(); } // Instantiating Classes GLOBAL(class_createInstance) = function(/*Class*/ aClass) { if (!aClass) throw new Error("*** Attempting to create object with Nil class."); var object = new aClass.allocator(); object.isa = aClass; object._UID = objj_generateObjectUID(); return object; } // Opera 9.5.1 has a bug where prototypes "inheret" members from instances when "with" is used. // Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go // ahead and do that. var prototype_bug = function() { } prototype_bug.prototype.member = false; with (new prototype_bug()) member = true; // If the bug exists, go down the slow path. if (new prototype_bug().member) { var fast_class_createInstance = class_createInstance; class_createInstance = function(/*Class*/ aClass) { var object = fast_class_createInstance(aClass); if (object) { var theClass = object.isa, actualClass = theClass; while (theClass) { var ivars = theClass.ivar_list, count = ivars.length; while (count--) object[ivars[count].name] = NULL; theClass = theClass.super_class; } object.isa = actualClass; } return object; } } // Working with Instances GLOBAL(object_getClassName) = function(/*id*/ anObject) { if (!anObject) return ""; var theClass = anObject.isa; return theClass ? class_getName(theClass) : ""; } //objc_getClassList GLOBAL(objj_lookUpClass) = function(/*String*/ aName) { var theClass = REGISTERED_CLASSES[aName]; return theClass ? theClass : Nil; } GLOBAL(objj_getClass) = function(/*String*/ aName) { var theClass = REGISTERED_CLASSES[aName]; /*if (!theClass) { for (var key in REGISTERED_CLASSES) { print("regClass: " + key + ", regClass.isa: " + REGISTERED_CLASSES[key].isa); } print(""); }*/ if (!theClass) { // class handler callback??? } return theClass ? theClass : Nil; } GLOBAL(objj_getClassList) = function(/*CPArray*/ buffer, /*int*/ bufferLen) { for (var aName in REGISTERED_CLASSES) { buffer.push(REGISTERED_CLASSES[aName]); if (bufferLen && --bufferLen === 0) break; } return buffer.length; } //objc_getRequiredClass GLOBAL(objj_getMetaClass) = function(/*String*/ aName) { var theClass = objj_getClass(aName); return GETMETA(theClass); } // Working with Protocol GLOBAL(objj_getProtocol) = function(/*String*/ aName) { return REGISTERED_PROTOCOLS[aName]; } // Working with typeDef GLOBAL(objj_getTypeDef) = function(/*String*/ aName) { return REGISTERED_TYPEDEFS[aName]; } // Working with Instance Variables GLOBAL(ivar_getName) = function(anIvar) { return anIvar.name; } GLOBAL(ivar_getTypeEncoding) = function(anIvar) { return anIvar.type; } // Sending Messages #ifdef MAXIMUM_RECURSION_CHECKS var __objj_msgSend__StackDepth = 0; #endif GLOBAL(objj_msgSend) = function(/*id*/ aReceiver, /*SEL*/ aSelector) { if (aReceiver == nil) return nil; var isa = aReceiver.isa; // Here we should do the following line: CLASS_GET_METHOD_IMPLEMENTATION(var implementation, isa, aSelector); // But set the 'init' attribute to 'true' when register the class pair and just check for that here is around 20% faster depending // on environment. The '_class_initialize' function sets the 'init' attribute to 'false' when the class is initialized if (isa.init) _class_initialize(isa); var method = isa.method_dtable[aSelector]; var implementation = method ? method.method_imp : _objj_forward; #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif switch(arguments.length) { case 2: return implementation(aReceiver, aSelector); case 3: return implementation(aReceiver, aSelector, arguments[2]); case 4: return implementation(aReceiver, aSelector, arguments[2], arguments[3]); case 5: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4]); case 6: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4], arguments[5]); case 7: return implementation(aReceiver, aSelector, arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]); } return implementation.apply(aReceiver, arguments); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } GLOBAL(objj_msgSendSuper) = function(/*id*/ aSuper, /*SEL*/ aSelector) { var super_class = aSuper.super_class; arguments[0] = aSuper.receiver; CLASS_GET_METHOD_IMPLEMENTATION(var implementation, super_class, aSelector); return implementation.apply(aSuper.receiver, arguments); } GLOBAL(objj_msgSendSuper0) = function(/*id*/ aSuper, /*SEL*/ aSelector) { return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector); } GLOBAL(objj_msgSendSuper1) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0) { return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0); } GLOBAL(objj_msgSendSuper2) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0, arg1) { return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0, arg1); } GLOBAL(objj_msgSendSuper3) = function(/*id*/ aSuper, /*SEL*/ aSelector, arg0, arg1, arg2) { return (aSuper.super_class.method_dtable[aSelector] || _objj_forward)(aSuper.receiver, aSelector, arg0, arg1, arg2); } GLOBAL(objj_msgSendFast) = function(/*id*/ aReceiver, /*SEL*/ aSelector) { #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif return (this.method_dtable[aSelector] || _objj_forward).apply(aReceiver, arguments); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } var objj_msgSendFastInitialize = function(/*id*/ aReceiver, /*SEL*/ aSelector) { _class_initialize(this); return this.objj_msgSend.apply(this, arguments); } GLOBAL(objj_msgSendFast0) = function(/*id*/ aReceiver, /*SEL*/ aSelector) { #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } var objj_msgSendFast0Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector) { _class_initialize(this); return this.objj_msgSend0(aReceiver, aSelector); } GLOBAL(objj_msgSendFast1) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0) { #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } var objj_msgSendFast1Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0) { _class_initialize(this); return this.objj_msgSend1(aReceiver, aSelector, arg0); } GLOBAL(objj_msgSendFast2) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1) { #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0, arg1); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } var objj_msgSendFast2Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1) { _class_initialize(this); return this.objj_msgSend2(aReceiver, aSelector, arg0, arg1); } GLOBAL(objj_msgSendFast3) = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1, arg2) { #ifdef MAXIMUM_RECURSION_CHECKS if (__objj_msgSend__StackDepth++ > MAXIMUM_RECURSION_DEPTH) throw new Error("Maximum call stack depth exceeded."); try { #endif return (this.method_dtable[aSelector] || _objj_forward)(aReceiver, aSelector, arg0, arg1, arg2); #ifdef MAXIMUM_RECURSION_CHECKS } finally { __objj_msgSend__StackDepth--; } #endif } var objj_msgSendFast3Initialize = function(/*id*/ aReceiver, /*SEL*/ aSelector, arg0, arg1, arg2) { _class_initialize(this); return this.objj_msgSend3(aReceiver, aSelector, arg0, arg1, arg2); } // Working with Methods GLOBAL(method_getName) = function(/*Method*/ aMethod) { return aMethod.method_name; } // This will not return correct values if the compiler does not have the option 'IncludeTypeSignatures' GLOBAL(method_copyReturnType) = function(/*Method*/ aMethod) { var types = aMethod.method_types; if (types) { var argType = types[0]; return argType != NULL ? argType : NULL; } else return NULL; } // This will not return correct values for index > 1 if the compiler does not have the option 'IncludeTypeSignatures' GLOBAL(method_copyArgumentType) = function(/*Method*/ aMethod, /*unsigned int*/ index) { switch (index) { case 0: return "id"; case 1: return "SEL"; default: var types = aMethod.method_types; if (types) { var argType = types[index - 1]; return argType != NULL ? argType : NULL; } else return NULL; } } // Returns number of arguments for a method. The first argument is 'self' and the second is the selector. // Those are followed by the method arguments. So for example it will return 2 for a method with no arguments. GLOBAL(method_getNumberOfArguments) = function(/*Method*/ aMethod) { var types = aMethod.method_types; return types ? types.length + 1 : ((aMethod.method_name.match(/:/g) || []).length + 2); } GLOBAL(method_getImplementation) = function(/*Method*/ aMethod) { return aMethod.method_imp; } GLOBAL(method_setImplementation) = function(/*Method*/ aMethod, /*IMP*/ anImplementation) { var oldImplementation = aMethod.method_imp; aMethod.method_imp = anImplementation; return oldImplementation; } GLOBAL(method_exchangeImplementations) = function(/*Method*/ lhs, /*Method*/ rhs) { var lhs_imp = method_getImplementation(lhs), rhs_imp = method_getImplementation(rhs); method_setImplementation(lhs, rhs_imp); method_setImplementation(rhs, lhs_imp); } // Working with Selectors GLOBAL(sel_getName) = function(aSelector) { return aSelector ? aSelector : "<null selector>"; } GLOBAL(sel_getUid) = function(/*String*/ aName) { return aName; } GLOBAL(sel_isEqual) = function(/*SEL*/ lhs, /*SEL*/ rhs) { return lhs === rhs; } GLOBAL(sel_registerName) = function(/*String*/ aName) { return aName; } objj_class.prototype.toString = objj_object.prototype.toString = function() { var isa = this.isa; if (class_getInstanceMethod(isa, SEL_description)) return isa.objj_msgSend0(this, SEL_description); if (class_isMetaClass(isa)) return this.name; return "[" + isa.name + " Object](-description not implemented)"; } objj_class.prototype.objj_msgSend = objj_msgSendFastInitialize; objj_class.prototype.objj_msgSend0 = objj_msgSendFast0Initialize; objj_class.prototype.objj_msgSend1 = objj_msgSendFast1Initialize; objj_class.prototype.objj_msgSend2 = objj_msgSendFast2Initialize; objj_class.prototype.objj_msgSend3 = objj_msgSendFast3Initialize; objj_class.prototype.method_msgSend = Object.create(null); var SEL_description = sel_getUid("description"), SEL_forwardingTargetForSelector_ = sel_getUid("forwardingTargetForSelector:"), SEL_methodSignatureForSelector_ = sel_getUid("methodSignatureForSelector:"), SEL_forwardInvocation_ = sel_getUid("forwardInvocation:"), SEL_doesNotRecognizeSelector_ = sel_getUid("doesNotRecognizeSelector:"), SEL_invocationWithMethodSignature_ = sel_getUid("invocationWithMethodSignature:"), SEL_setTarget_ = sel_getUid("setTarget:"), SEL_setSelector_ = sel_getUid("setSelector:"), SEL_setArgument_atIndex_ = sel_getUid("setArgument:atIndex:"), SEL_returnValue = sel_getUid("returnValue");