objj-runtime
Version:
JavaScript (ECMAScript) and Objective-J runtime
1,163 lines (888 loc) • 32.2 kB
JavaScript
/*
* 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");