mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
New: Added Objective-J protocol support
The Objective-J parser and compiler now handles protocol syntax. The Objective-J runtime has new functions to handle protocols. The method 'conformsToProtocol:' has been added to CPObject as an instance and a class method.
This commit is contained in:
@@ -254,6 +254,26 @@ CPLog(@"Got some class: %@", inst);
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*!
|
||||
Test whether instances of this class conforms to the provided protocol.
|
||||
@param aProtocol the protocol for which to test the class
|
||||
@return \c YES if instances of the class conforms to the protocol
|
||||
*/
|
||||
+ (BOOL)conformsToProtocol:(Protocol)aProtocol
|
||||
{
|
||||
return class_conformsToProtocol(self, aProtocol);
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests whether the receiver conforms to the provided protocol.
|
||||
@param protocol the protocol for which to test the class
|
||||
@return \c YES if instances of the class conforms to the protocol
|
||||
*/
|
||||
- (BOOL)conformsToProtocol:(Protocol)aProtocol
|
||||
{
|
||||
return class_conformsToProtocol(isa, aProtocol);
|
||||
}
|
||||
|
||||
// Obtaining method information
|
||||
|
||||
/*!
|
||||
|
||||
@@ -45,7 +45,12 @@ Scope.prototype.isRootScope = function()
|
||||
|
||||
Scope.prototype.currentClassName = function()
|
||||
{
|
||||
return this.classDef ? this.classDef.className : this.prev ? this.prev.currentClassName() : null;
|
||||
return this.classDef ? this.classDef.name : this.prev ? this.prev.currentClassName() : null;
|
||||
}
|
||||
|
||||
Scope.prototype.currentProtocolName = function()
|
||||
{
|
||||
return this.protocolDef ? this.protocolDef.name : this.prev ? this.prev.currentProtocolName() : null;
|
||||
}
|
||||
|
||||
Scope.prototype.getIvarForCurrentClass = function(/* String */ ivarName)
|
||||
@@ -144,6 +149,215 @@ StringBuffer.prototype.isEmpty = function()
|
||||
return this.atoms.length !== 0;
|
||||
}
|
||||
|
||||
// Both the ClassDef and ProtocolDef conforms to a 'protocol' (That we can't declare in Javascript).
|
||||
// Both Objects have the attribute 'protocols': Array of ProtocolDef that they conform to
|
||||
// Both also have the functions: addInstanceMethod, addClassMethod, getInstanceMethod and getClassMethod
|
||||
// classDef = {"className": aClassName, "superClass": superClass , "ivars": myIvars, "instanceMethods": instanceMethodDefs, "classMethods": classMethodDefs, "protocols": myProtocols};
|
||||
var ClassDef = function(isImplementationDeclaration, name, superClass, ivars, instanceMethods, classMethods, protocols)
|
||||
{
|
||||
this.name = name;
|
||||
if (superClass)
|
||||
this.superClass = superClass;
|
||||
if (ivars)
|
||||
this.ivars = ivars;
|
||||
if (isImplementationDeclaration) {
|
||||
this.instanceMethods = instanceMethods || Object.create(null);
|
||||
this.classMethods = classMethods || Object.create(null);
|
||||
}
|
||||
if (protocols)
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
ClassDef.prototype.addInstanceMethod = function(methodDef) {
|
||||
this.instanceMethods[methodDef.name] = methodDef;
|
||||
}
|
||||
|
||||
ClassDef.prototype.addClassMethod = function(methodDef) {
|
||||
this.classMethods[methodDef.name] = methodDef;
|
||||
}
|
||||
|
||||
ClassDef.prototype.listOfNotImplementedMethodsForProtocols = function(protocolDefs) {
|
||||
var resultList = [],
|
||||
instanceMethods = this.getInstanceMethods(),
|
||||
classMethods = this.getClassMethods();
|
||||
|
||||
for (var i = 0, size = protocolDefs.length; i < size; i++)
|
||||
{
|
||||
var protocolDef = protocolDefs[i],
|
||||
protocolInstanceMethods = protocolDef.requiredInstanceMethods,
|
||||
protocolClassMethods = protocolDef.requiredClassMethods,
|
||||
inheritFromProtocols = protocolDef.protocols;
|
||||
|
||||
if (protocolInstanceMethods) for (var methodName in protocolInstanceMethods) {
|
||||
var methodDef = protocolInstanceMethods[methodName];
|
||||
|
||||
if (!instanceMethods[methodName])
|
||||
resultList.push({"methodDef": methodDef, "protocolDef": protocolDef});
|
||||
}
|
||||
|
||||
if (protocolClassMethods) for (var methodName in protocolClassMethods) {
|
||||
var methodDef = protocolClassMethods[methodName];
|
||||
|
||||
if (!classMethods[methodName])
|
||||
resultList.push({"methodDef": methodDef, "protocolDef": protocolDef});
|
||||
}
|
||||
|
||||
if (inheritFromProtocols)
|
||||
resultList = resultList.concat(this.listOfNotImplementedMethodsForProtocols(inheritFromProtocols));
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
ClassDef.prototype.getInstanceMethod = function(name) {
|
||||
var instanceMethods = this.instanceMethods;
|
||||
|
||||
if (instanceMethods) {
|
||||
var method = instanceMethods[name];
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
var superClass = this.superClass;
|
||||
|
||||
if (superClass)
|
||||
return superClass.getInstanceMethod(name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassDef.prototype.getClassMethod = function(name) {
|
||||
var classMethods = this.classMethods;
|
||||
if (classMethods) {
|
||||
var method = classMethods[name];
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
var superClass = this.superClass;
|
||||
|
||||
if (superClass)
|
||||
return superClass.getClassMethod(name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return a new Array with all instance methods
|
||||
ClassDef.prototype.getInstanceMethods = function() {
|
||||
var instanceMethods = this.instanceMethods;
|
||||
if (instanceMethods) {
|
||||
var superClass = this.superClass,
|
||||
returnObject = Object.create(null);
|
||||
if (superClass) {
|
||||
var superClassMethods = superClass.getInstanceMethods();
|
||||
for (var methodName in superClassMethods)
|
||||
returnObject[methodName] = superClassMethods[methodName];
|
||||
}
|
||||
|
||||
for (var methodName in instanceMethods)
|
||||
returnObject[methodName] = instanceMethods[methodName];
|
||||
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// Return a new Array with all class methods
|
||||
ClassDef.prototype.getClassMethods = function() {
|
||||
var classMethods = this.classMethods;
|
||||
if (classMethods) {
|
||||
var superClass = this.superClass,
|
||||
returnObject = Object.create(null);
|
||||
if (superClass) {
|
||||
var superClassMethods = superClass.getClassMethods();
|
||||
for (var methodName in superClassMethods)
|
||||
returnObject[methodName] = superClassMethods[methodName];
|
||||
}
|
||||
|
||||
for (var methodName in classMethods)
|
||||
returnObject[methodName] = classMethods[methodName];
|
||||
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// protocolDef = {"name": aProtocolName, "protocols": inheritFromProtocols, "requiredInstanceMethods": requiredInstanceMethodDefs, "requiredClassMethods": requiredClassMethodDefs};
|
||||
var ProtocolDef = function(name, protocols, requiredInstanceMethodDefs, requiredClassMethodDefs)
|
||||
{
|
||||
this.name = name;
|
||||
this.protocols = protocols;
|
||||
if (requiredInstanceMethodDefs)
|
||||
this.requiredInstanceMethods = requiredInstanceMethodDefs;
|
||||
if (requiredClassMethodDefs)
|
||||
this.requiredClassMethods = requiredClassMethodDefs;
|
||||
}
|
||||
|
||||
ProtocolDef.prototype.addInstanceMethod = function(methodDef) {
|
||||
(this.requiredInstanceMethods || (this.requiredInstanceMethods = Object.create(null)))[methodDef.name] = methodDef;
|
||||
}
|
||||
|
||||
ProtocolDef.prototype.addClassMethod = function(methodDef) {
|
||||
(this.requiredClassMethods || (this.requiredClassMethods = Object.create(null)))[methodDef.name] = methodDef;
|
||||
}
|
||||
|
||||
ProtocolDef.prototype.getInstanceMethod = function(name) {
|
||||
var instanceMethods = this.requiredInstanceMethods;
|
||||
|
||||
if (instanceMethods) {
|
||||
var method = instanceMethods[name];
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
var protocols = this.protocols;
|
||||
|
||||
for (var i = 0, size = protocols.length; i < size; i++) {
|
||||
var protocol = protocols[i],
|
||||
method = protocol.getInstanceMethod(name);
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ProtocolDef.prototype.getClassMethod = function(name) {
|
||||
var classMethods = this.requiredClassMethods;
|
||||
|
||||
if (classMethods) {
|
||||
var method = classMethods[name];
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
var protocols = this.protocols;
|
||||
|
||||
for (var i = 0, size = protocols.length; i < size; i++) {
|
||||
var protocol = protocols[i],
|
||||
method = protocol.getInstanceMethod(name);
|
||||
|
||||
if (method)
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// methodDef = {"types": types, "name": selector}
|
||||
var MethodDef = function(name, types)
|
||||
{
|
||||
this.name = name;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
var currentCompilerFlags = "";
|
||||
|
||||
var reservedIdentifiers = exports.acorn.makePredicate("self _cmd undefined localStorage arguments");
|
||||
@@ -153,7 +367,7 @@ var wordPrefixOperators = exports.acorn.makePredicate("delete in instanceof new
|
||||
var isLogicalBinary = exports.acorn.makePredicate("LogicalExpression BinaryExpression");
|
||||
var isInInstanceof = exports.acorn.makePredicate("in instanceof");
|
||||
|
||||
var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, /*unsigned*/ pass, /* Dictionary */ classDefs)
|
||||
var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, /*unsigned*/ pass, /* Dictionary */ classDefs, /* Dictionary */ protocolDefs)
|
||||
{
|
||||
this.source = aString;
|
||||
this.URL = new CFURL(aURL);
|
||||
@@ -182,6 +396,7 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*
|
||||
this.dependencies = [];
|
||||
this.flags = flags | ObjJAcornCompiler.Flags.IncludeDebugSymbols;
|
||||
this.classDefs = classDefs ? classDefs : Object.create(null);
|
||||
this.protocolDefs = protocolDefs ? protocolDefs : Object.create(null);
|
||||
this.lastPos = 0;
|
||||
if (currentCompilerFlags & ObjJAcornCompiler.Flags.Generate)
|
||||
this.generate = true;
|
||||
@@ -215,8 +430,8 @@ ObjJAcornCompiler.prototype.compilePass2 = function()
|
||||
this.pass = 2;
|
||||
this.jsBuffer = new StringBuffer();
|
||||
this.warnings = [];
|
||||
//print(this.URL + ": Compiling");
|
||||
compile(this.tokens, new Scope(null ,{ compiler: this }), pass2);
|
||||
|
||||
for (var i = 0; i < this.warnings.length; i++)
|
||||
{
|
||||
var message = this.prettifyMessage(this.warnings[i], "WARNING");
|
||||
@@ -227,6 +442,7 @@ ObjJAcornCompiler.prototype.compilePass2 = function()
|
||||
#endif
|
||||
}
|
||||
|
||||
//print(this.URL + ": " + this.jsBuffer.toString());
|
||||
return this.jsBuffer.toString();
|
||||
}
|
||||
|
||||
@@ -271,7 +487,7 @@ ObjJAcornCompiler.prototype.getIvarForClass = function(/* String */ ivarName, /*
|
||||
if (ivarDef)
|
||||
return ivarDef;
|
||||
}
|
||||
c = this.getClassDef(c.superClassName);
|
||||
c = c.superClass;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,37 +495,100 @@ ObjJAcornCompiler.prototype.getClassDef = function(/* String */ aClassName)
|
||||
{
|
||||
if (!aClassName) return null;
|
||||
|
||||
var c = this.classDefs[aClassName];
|
||||
var c = this.classDefs[aClassName];
|
||||
|
||||
if (c) return c;
|
||||
if (c) return c;
|
||||
|
||||
if (objj_getClass)
|
||||
{
|
||||
var aClass = objj_getClass(aClassName);
|
||||
if (aClass)
|
||||
{
|
||||
var ivars = class_copyIvarList(aClass),
|
||||
ivarSize = ivars.length,
|
||||
myIvars = Object.create(null),
|
||||
superClass = aClass.super_class;
|
||||
if (objj_getClass)
|
||||
{
|
||||
var aClass = objj_getClass(aClassName);
|
||||
if (aClass)
|
||||
{
|
||||
var ivars = class_copyIvarList(aClass),
|
||||
ivarSize = ivars.length,
|
||||
myIvars = Object.create(null),
|
||||
protocols = class_copyProtocolList(aClass),
|
||||
protocolSize = protocols.length,
|
||||
myProtocols = Object.create(null),
|
||||
instanceMethodDefs = ObjJAcornCompiler.methodDefsFromMethodList(class_copyMethodList(aClass)),
|
||||
classMethodDefs = ObjJAcornCompiler.methodDefsFromMethodList(class_copyMethodList(aClass.isa)),
|
||||
superClass = class_getSuperclass(aClass);
|
||||
|
||||
for (var i = 0; i < ivarSize; i++)
|
||||
{
|
||||
var ivar = ivars[i];
|
||||
for (var i = 0; i < ivarSize; i++)
|
||||
{
|
||||
var ivar = ivars[i];
|
||||
|
||||
myIvars[ivar.name] = {"type": ivar.type, "name": ivar.name};
|
||||
}
|
||||
c = {"className": aClassName, "ivars": myIvars};
|
||||
myIvars[ivar.name] = {"type": ivar.type, "name": ivar.name};
|
||||
}
|
||||
|
||||
if (superClass)
|
||||
c.superClassName = superClass.name;
|
||||
this.classDefs[aClassName] = c;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < protocolSize; i++)
|
||||
{
|
||||
var protocol = protocols[i],
|
||||
protocolName = protocol_getName(protocol),
|
||||
protocolDef = this.getProtocolDef(protocolName);
|
||||
|
||||
return null;
|
||||
// classDef = {"className": className, "superClassName": superClassName, "ivars": Object.create(null), "methods": Object.create(null)};
|
||||
myProtocols[protocolName] = protocolDef;
|
||||
}
|
||||
|
||||
c = new ClassDef(true, aClassName, superClass ? this.getClassDef(superClass.name) : null, myIvars, instanceMethodDefs, classMethodDefs, myProtocols);
|
||||
this.classDefs[aClassName] = c;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ObjJAcornCompiler.prototype.getProtocolDef = function(/* String */ aProtocolName)
|
||||
{
|
||||
if (!aProtocolName) return null;
|
||||
|
||||
var p = this.protocolDefs[aProtocolName];
|
||||
|
||||
if (p) return p;
|
||||
|
||||
if (objj_getProtocol)
|
||||
{
|
||||
var aProtocol = objj_getProtocol(aProtocolName);
|
||||
if (aProtocol)
|
||||
{
|
||||
var protocol = protocols[i],
|
||||
protocolName = protocol_getName(protocol),
|
||||
requiredInstanceMethods = protocol_copyMethodDescriptionList(protocol, true, true),
|
||||
requiredInstanceMethodDefs = ObjJAcornCompiler.methodDefsFromMethodList(requiredInstanceMethods),
|
||||
requiredClassMethods = protocol_copyMethodDescriptionList(protocol, true, false),
|
||||
requiredClassMethodDefs = ObjJAcornCompiler.methodDefsFromMethodList(requiredClassMethods),
|
||||
protocols = protocol.protocols,
|
||||
inheritFromProtocols = [];
|
||||
|
||||
for (var i = 0, size = protocols.length; i < size; i++)
|
||||
inheritFromProtocols.push(compiler.getProtocolDef(protocols[i].name));
|
||||
|
||||
p = new ProtocolDef(protocolName, inheritFromProtocols, requiredInstanceMethodDefs, requiredClassMethodDefs);
|
||||
|
||||
this.protocolDefs[aProtocolName] = p;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// protocolDef = {"name": protocolName, "protocols": Object.create(null), "required": Object.create(null), "optional": Object.create(null)};
|
||||
}
|
||||
|
||||
ObjJAcornCompiler.methodDefsFromMethodList = function(/* Array */ methodList)
|
||||
{
|
||||
var methodSize = methodList.length,
|
||||
myMethods = Object.create(null);
|
||||
|
||||
for (var i = 0; i < methodSize; i++)
|
||||
{
|
||||
var method = methodList[i],
|
||||
methodName = method_getName(method);
|
||||
|
||||
myMethods[methodName] = new MethodDef(methodName, method.types);
|
||||
}
|
||||
|
||||
return myMethods;
|
||||
}
|
||||
|
||||
ObjJAcornCompiler.prototype.executable = function()
|
||||
@@ -372,7 +651,9 @@ function createMessage(/* String */ aMessage, /* SpiderMonkey AST node */ node,
|
||||
|
||||
function compile(node, state, visitor) {
|
||||
function c(node, st, override) {
|
||||
//print("c: " + (override ? override + ", " : "") + node.type + ", " + exports.acorn.getLineInfo(st.compiler.source, node.start).line);
|
||||
visitor[override || node.type](node, st, c);
|
||||
//print("cc: " + (override ? override + ", " : "") + node.type + ", " + exports.acorn.getLineInfo(st.compiler.source, node.end).line);
|
||||
}
|
||||
c(node, state);
|
||||
};
|
||||
@@ -1300,10 +1581,12 @@ ImportStatement: function(node, st, c) {
|
||||
ClassDeclarationStatement: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
generate = compiler.generate,
|
||||
classDef,
|
||||
saveJSBuffer = compiler.jsBuffer,
|
||||
className = node.classname.name,
|
||||
classScope = new Scope(st);
|
||||
classDef = compiler.getClassDef(className),
|
||||
classScope = new Scope(st),
|
||||
isInterfaceDeclaration = node.type === "InterfaceDeclarationStatement",
|
||||
protocols = node.protocols;
|
||||
|
||||
compiler.imBuffer = new StringBuffer();
|
||||
compiler.cmBuffer = new StringBuffer();
|
||||
@@ -1314,10 +1597,19 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
// First we declare the class
|
||||
if (node.superclassname)
|
||||
{
|
||||
classDef = compiler.getClassDef(className);
|
||||
if (classDef && classDef.ivars) // Must have ivars dictionary to be a real declaration. Without it is a "@class" declaration
|
||||
// Must have methods dictionaries and ivars dictionary to be a real implementaion declaration.
|
||||
// Without it is a "@class" declaration (without both ivars dictionary and method dictionaries) or
|
||||
// "interface" declaration (without ivars dictionary)
|
||||
// TODO: Create a ClassDef object and add this logic to it
|
||||
if (classDef && classDef.ivars)
|
||||
// It has a real implementation declaration already
|
||||
throw compiler.error_message("Duplicate class " + className, node.classname);
|
||||
if (!compiler.getClassDef(node.superclassname.name))
|
||||
|
||||
if (isInterfaceDeclaration && classDef && classDef.instanceMethods && classDef.classMethods)
|
||||
// It has a interface declaration already
|
||||
throw compiler.error_message("Duplicate interface definition for class " + className, node.classname);
|
||||
var superClassDef = compiler.getClassDef(node.superclassname.name);
|
||||
if (!superClassDef)
|
||||
{
|
||||
var errorMessage = "Can't find superclass " + node.superclassname.name;
|
||||
for (var i = ObjJAcornCompiler.importStack.length; --i >= 0;)
|
||||
@@ -1325,7 +1617,7 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
throw compiler.error_message(errorMessage, node.superclassname);
|
||||
}
|
||||
|
||||
classDef = {"className": className, "superClassName": node.superclassname.name, "ivars": Object.create(null), "methods": Object.create(null)};
|
||||
classDef = new ClassDef(!isInterfaceDeclaration, className, superClassDef, Object.create(null));
|
||||
|
||||
saveJSBuffer.concat("{var the_class = objj_allocateClassPair(" + node.superclassname.name + ", \"" + className + "\"),\nmeta_class = the_class.isa;");
|
||||
}
|
||||
@@ -1341,11 +1633,21 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
}
|
||||
else
|
||||
{
|
||||
classDef = {"className": className, "superClassName": null, "ivars": Object.create(null), "methods": Object.create(null)};
|
||||
classDef = new ClassDef(!isInterfaceDeclaration, className, null, Object.create(null));
|
||||
|
||||
saveJSBuffer.concat("{var the_class = objj_allocateClassPair(Nil, \"" + className + "\"),\nmeta_class = the_class.isa;");
|
||||
}
|
||||
|
||||
if (protocols) for (var i = 0, size = protocols.length; i < size; i++)
|
||||
{
|
||||
saveJSBuffer.concat("\nvar aProtocol = objj_getProtocol(\"" + protocols[i].name + "\");");
|
||||
saveJSBuffer.concat("\nif (!aProtocol) throw new SyntaxError(\"*** Could not find definition for protocol \\\"" + protocols[i].name + "\\\"\");");
|
||||
saveJSBuffer.concat("\nclass_addProtocol(the_class, aProtocol);");
|
||||
}
|
||||
/*
|
||||
if (isInterfaceDeclaration)
|
||||
classDef.interfaceDeclaration = true;
|
||||
*/
|
||||
classScope.classDef = classDef;
|
||||
compiler.currentSuperClass = "objj_getClass(\"" + className + "\").super_class";
|
||||
compiler.currentSuperMetaClass = "objj_getMetaClass(\"" + className + "\").super_class";
|
||||
@@ -1359,8 +1661,12 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
var ivarDecl = node.ivardeclarations[i],
|
||||
ivarType = ivarDecl.ivartype ? ivarDecl.ivartype.name : null,
|
||||
ivarName = ivarDecl.id.name,
|
||||
ivars = classDef.ivars,
|
||||
ivar = {"type": ivarType, "name": ivarName};
|
||||
|
||||
if (ivars[ivarName])
|
||||
throw compiler.error_message("Instance variable '" + ivarName + "'is already declared for class " + className, ivarDecl.id);
|
||||
|
||||
if (firstIvarDeclaration)
|
||||
{
|
||||
firstIvarDeclaration = false;
|
||||
@@ -1376,7 +1682,7 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
|
||||
if (ivarDecl.outlet)
|
||||
ivar.outlet = true;
|
||||
classDef.ivars[ivarName] = ivar;
|
||||
ivars[ivarName] = ivar;
|
||||
if (!classScope.ivars)
|
||||
classScope.ivars = Object.create(null);
|
||||
classScope.ivars[ivarName] = {type: "ivar", name: ivarName, node: ivarDecl.id, ivar: ivar};
|
||||
@@ -1389,7 +1695,7 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
saveJSBuffer.concat("]);");
|
||||
|
||||
// If we have accessors add get and set methods for them
|
||||
if (hasAccessors)
|
||||
if (!isInterfaceDeclaration && hasAccessors)
|
||||
{
|
||||
var getterSetterBuffer = new StringBuffer();
|
||||
|
||||
@@ -1449,20 +1755,22 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
// We will store the classDef first after accessors are done so we don't get a duplicate class error
|
||||
compiler.classDefs[className] = classDef;
|
||||
|
||||
if (node.body.length > 0)
|
||||
var bodies = node.body,
|
||||
bodyLength = bodies.length;
|
||||
|
||||
if (bodyLength > 0)
|
||||
{
|
||||
if (!generate) compiler.lastPos = node.body[0].start;
|
||||
if (!generate) compiler.lastPos = bodies[0].start;
|
||||
|
||||
// And last add methods and other statements
|
||||
for (var i = 0; i < node.body.length; ++i) {
|
||||
var body = node.body[i];
|
||||
for (var i = 0; i < bodyLength; ++i) {
|
||||
var body = bodies[i];
|
||||
c(body, classScope, "Statement");
|
||||
}
|
||||
if (!generate) saveJSBuffer.concat(compiler.source.substring(compiler.lastPos, body.end));
|
||||
}
|
||||
|
||||
// We must make a new class object for our class definition if it's not a category
|
||||
if (!node.categoryname) {
|
||||
if (!isInterfaceDeclaration && !node.categoryname) {
|
||||
saveJSBuffer.concat("objj_registerClassPair(the_class);\n");
|
||||
}
|
||||
|
||||
@@ -1488,23 +1796,146 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
|
||||
// Skip the "@end"
|
||||
if (!generate) compiler.lastPos = node.end;
|
||||
|
||||
// If the class conforms to protocols check that all required methods are implemented
|
||||
if (protocols)
|
||||
{
|
||||
// Lookup the protocolDefs for the protocols
|
||||
var protocolDefs = [];
|
||||
|
||||
for (var i = 0, size = protocols.length; i < size; i++)
|
||||
protocolDefs.push(compiler.getProtocolDef(protocols[i].name));
|
||||
|
||||
var unimplementedMethods = classDef.listOfNotImplementedMethodsForProtocols(protocolDefs);
|
||||
|
||||
if (unimplementedMethods && unimplementedMethods.length > 0)
|
||||
for (var i = 0, size = unimplementedMethods.length; i < size; i++) {
|
||||
var unimplementedMethod = unimplementedMethods[i],
|
||||
methodDef = unimplementedMethod.methodDef,
|
||||
protocolDef = unimplementedMethod.protocolDef;
|
||||
|
||||
compiler.addWarning(createMessage("Method '" + methodDef.name + "' in protocol '" + protocolDef.name + "' is not implemented", node.classname, compiler.source));
|
||||
}
|
||||
}
|
||||
},
|
||||
ProtocolDeclarationStatement: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
generate = compiler.generate,
|
||||
buffer = compiler.jsBuffer,
|
||||
protocolName = node.protocolname.name,
|
||||
protocolDef = compiler.getProtocolDef(protocolName),
|
||||
protocols = node.protocols,
|
||||
protocolScope = new Scope(st),
|
||||
inheritFromProtocols = [];
|
||||
|
||||
if (protocolDef)
|
||||
throw compiler.error_message("Duplicate protocol " + protocolName, node.protocolName);
|
||||
|
||||
compiler.imBuffer = new StringBuffer();
|
||||
compiler.cmBuffer = new StringBuffer();
|
||||
|
||||
if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, node.start));
|
||||
|
||||
buffer.concat("{var the_protocol = objc_allocateProtocol(\"" + protocolName + "\");");
|
||||
|
||||
if (protocols) for (var i = 0, size = protocols.length; i < size; i++)
|
||||
{
|
||||
var protocol = protocols[i],
|
||||
inheritFromProtocolName = protocol.name;
|
||||
inheritProtocolDef = compiler.getProtocolDef(inheritFromProtocolName);
|
||||
|
||||
if (!inheritProtocolDef)
|
||||
throw compiler.error_message("Can't find protocol " + inheritFromProtocolName, protocol);
|
||||
|
||||
buffer.concat("\nvar aProtocol = objj_getProtocol(\"" + inheritFromProtocolName + "\");");
|
||||
buffer.concat("\nif (!aProtocol) throw new SyntaxError(\"*** Could not find definition for protocol \\\"" + protocolName + "\\\"\");");
|
||||
buffer.concat("\nprotocol_addProtocol(the_protocol, aProtocol);");
|
||||
inheritFromProtocols.push(inheritProtocolDef);
|
||||
}
|
||||
|
||||
protocolDef = new ProtocolDef(protocolName, inheritFromProtocols);
|
||||
compiler.protocolDefs[protocolName] = protocolDef;
|
||||
protocolScope.protocolDef = protocolDef;
|
||||
|
||||
var someRequired = node.required,
|
||||
requiredLength = someRequired.length;
|
||||
|
||||
if (requiredLength > 0)
|
||||
{
|
||||
// We only add the required methods
|
||||
for (var i = 0; i < requiredLength; ++i) {
|
||||
var required = someRequired[i];
|
||||
if (!generate) compiler.lastPos = required.start;
|
||||
c(required, protocolScope, "Statement");
|
||||
}
|
||||
if (!generate) buffer.concat(compiler.source.substring(compiler.lastPos, required.end));
|
||||
}
|
||||
|
||||
buffer.concat("\nobjc_registerProtocol(the_protocol);\n");
|
||||
|
||||
// Add instance methods
|
||||
if (compiler.imBuffer.isEmpty())
|
||||
{
|
||||
buffer.concat("protocol_addMethodDescriptions(the_protocol, [");
|
||||
buffer.atoms.push.apply(buffer.atoms, compiler.imBuffer.atoms); // FIXME: Move this append to StringBuffer
|
||||
buffer.concat("], true, false);\n");
|
||||
}
|
||||
|
||||
// Add class methods
|
||||
if (compiler.cmBuffer.isEmpty())
|
||||
{
|
||||
buffer.concat("protocol_addMethodDescriptions(the_protocol, [");
|
||||
buffer.atoms.push.apply(buffer.atoms, compiler.cmBuffer.atoms); // FIXME: Move this append to StringBuffer
|
||||
buffer.concat("], true, true);\n");
|
||||
}
|
||||
|
||||
buffer.concat("}");
|
||||
|
||||
compiler.jsBuffer = buffer;
|
||||
|
||||
// Skip the "@end"
|
||||
if (!generate) compiler.lastPos = node.end;
|
||||
},
|
||||
MethodDeclarationStatement: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
generate = compiler.generate,
|
||||
saveJSBuffer = compiler.jsBuffer,
|
||||
methodScope = new Scope(st),
|
||||
isInstanceMethodType = node.methodtype === '-';
|
||||
selectors = node.selectors,
|
||||
arguments = node.arguments,
|
||||
types = [node.returntype ? node.returntype.name : "id"],
|
||||
returnType = node.returntype,
|
||||
types = [returnType ? returnType.name : "id"],
|
||||
returnTypeProtocols = returnType ? returnType.protocols : null;
|
||||
selector = selectors[0].name; // There is always at least one selector
|
||||
|
||||
if (returnTypeProtocols) for (var i = 0, size = returnTypeProtocols.length; i < size; i++) {
|
||||
var returnTypeProtocol = returnTypeProtocols[i];
|
||||
if (!compiler.getProtocolDef(returnTypeProtocol.name)) {
|
||||
compiler.addWarning(createMessage("Cannot find protocol declaration for '" + returnTypeProtocol.name + "'", returnTypeProtocol, compiler.source));
|
||||
}
|
||||
}
|
||||
|
||||
if (!generate) saveJSBuffer.concat(compiler.source.substring(compiler.lastPos, node.start));
|
||||
|
||||
compiler.jsBuffer = node.methodtype === '-' ? compiler.imBuffer : compiler.cmBuffer;
|
||||
compiler.jsBuffer = isInstanceMethodType ? compiler.imBuffer : compiler.cmBuffer;
|
||||
|
||||
// Put together the selector. Maybe this should be done in the parser...
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var argument = arguments[i],
|
||||
argumentType = argument.type,
|
||||
argumentTypeName = argumentType ? argumentType.name : "id",
|
||||
argumentProtocols = argumentType ? argumentType.protocols : null;
|
||||
|
||||
types.push(argumentType ? argumentType.name : "id");
|
||||
|
||||
if (argumentProtocols) for (var i = 0, size = argumentProtocols.length; i < size; i++) {
|
||||
var argumentProtocol = argumentProtocols[i];
|
||||
if (!compiler.getProtocolDef(argumentProtocol.name)) {
|
||||
compiler.addWarning(createMessage("Cannot find protocol declaration for '" + argumentProtocol.name + "'", argumentProtocol, compiler.source));
|
||||
}
|
||||
}
|
||||
|
||||
if (i === 0)
|
||||
selector += ":";
|
||||
else
|
||||
@@ -1513,45 +1944,111 @@ MethodDeclarationStatement: function(node, st, c) {
|
||||
|
||||
if (compiler.jsBuffer.isEmpty()) // Add comma separator if this is not first method in this buffer
|
||||
compiler.jsBuffer.concat(", ");
|
||||
|
||||
compiler.jsBuffer.concat("new objj_method(sel_getUid(\"");
|
||||
compiler.jsBuffer.concat(selector);
|
||||
compiler.jsBuffer.concat("\"), function");
|
||||
compiler.jsBuffer.concat("\"), ");
|
||||
|
||||
// this.currentSelector = selector;
|
||||
if (node.body) {
|
||||
compiler.jsBuffer.concat("function");
|
||||
|
||||
if (compiler.flags & ObjJAcornCompiler.Flags.IncludeDebugSymbols)
|
||||
{
|
||||
compiler.jsBuffer.concat(" $" + st.currentClassName() + "__" + selector.replace(/:/g, "_"));
|
||||
if (compiler.flags & ObjJAcornCompiler.Flags.IncludeDebugSymbols)
|
||||
{
|
||||
compiler.jsBuffer.concat(" $" + st.currentClassName() + "__" + selector.replace(/:/g, "_"));
|
||||
}
|
||||
|
||||
compiler.jsBuffer.concat("(self, _cmd");
|
||||
|
||||
methodScope.methodType = node.methodtype;
|
||||
if (arguments) for (var i = 0; i < arguments.length; i++)
|
||||
{
|
||||
var argument = arguments[i],
|
||||
argumentName = argument.identifier.name;
|
||||
|
||||
compiler.jsBuffer.concat(", ");
|
||||
compiler.jsBuffer.concat(argumentName);
|
||||
methodScope.vars[argumentName] = {type: "method argument", node: argument};
|
||||
}
|
||||
|
||||
compiler.jsBuffer.concat(")\n");
|
||||
|
||||
if (!generate) compiler.lastPos = node.startOfBody;
|
||||
indentation += indentStep;
|
||||
c(node.body, methodScope, "Statement");
|
||||
indentation = indentation.substring(indentationSpaces);
|
||||
if (!generate) compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, node.body.end));
|
||||
|
||||
compiler.jsBuffer.concat("\n");
|
||||
} else { // It is a interface or protocol declatartion and we don't have a method implementation
|
||||
compiler.jsBuffer.concat("Nil\n");
|
||||
}
|
||||
|
||||
compiler.jsBuffer.concat("(self, _cmd");
|
||||
|
||||
methodScope.methodType = node.methodtype;
|
||||
if (arguments) for (var i = 0; i < arguments.length; i++)
|
||||
{
|
||||
var argument = arguments[i],
|
||||
argumentName = argument.identifier.name;
|
||||
|
||||
compiler.jsBuffer.concat(", ");
|
||||
compiler.jsBuffer.concat(argumentName);
|
||||
types.push(argument.type ? argument.type.name : null);
|
||||
methodScope.vars[argumentName] = {type: "method argument", node: argument};
|
||||
}
|
||||
|
||||
compiler.jsBuffer.concat(")\n");
|
||||
|
||||
if (!generate) compiler.lastPos = node.startOfBody;
|
||||
indentation += indentStep;
|
||||
c(node.body, methodScope, "Statement");
|
||||
indentation = indentation.substring(indentationSpaces);
|
||||
if (!generate) compiler.jsBuffer.concat(compiler.source.substring(compiler.lastPos, node.body.end));
|
||||
|
||||
compiler.jsBuffer.concat("\n");
|
||||
if (compiler.flags & ObjJAcornCompiler.Flags.IncludeDebugSymbols)
|
||||
compiler.jsBuffer.concat(","+JSON.stringify(types));
|
||||
|
||||
compiler.jsBuffer.concat(")");
|
||||
compiler.jsBuffer = saveJSBuffer;
|
||||
|
||||
if (!generate) compiler.lastPos = node.end;
|
||||
|
||||
// Add the method to the class or protocol definition
|
||||
var def = st.classDef,
|
||||
alreadyDeclared;
|
||||
|
||||
// But first, if it is a class definition check if it is declared in superclass or interface declaration
|
||||
if (def)
|
||||
alreadyDeclared = isInstanceMethodType ? def.getInstanceMethod(selector) : def.getClassMethod(selector);
|
||||
else
|
||||
def = st.protocolDef;
|
||||
|
||||
if (!def)
|
||||
throw "InternalError: MethodDeclaration without ClassDeclaration or ProtocolDeclaration at line: " + exports.acorn.getLineInfo(compiler.source, node.start).line;
|
||||
|
||||
// Create warnings if types does not corresponds to method declaration in superclass or interface declarations
|
||||
// If we don't find the method in superclass or interface declarations above or if it is a protocol
|
||||
// declaration, try to find it in any of the conforming protocols
|
||||
if (!alreadyDeclared) {
|
||||
var protocols = def.protocols;
|
||||
|
||||
if (protocols) for (var i = 0, size = protocols.length; i < size; i++) {
|
||||
var protocol = protocols[i],
|
||||
alreadyDeclared = isInstanceMethodType ? protocol.getInstanceMethod(selector) : protocol.getClassMethod(selector);
|
||||
|
||||
if (alreadyDeclared)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyDeclared) {
|
||||
var declaredTypes = alreadyDeclared.types;
|
||||
|
||||
if (declaredTypes) {
|
||||
var typeSize = declaredTypes.length;
|
||||
if (typeSize > 0) {
|
||||
// First type is return type
|
||||
var returnType = declaredTypes[0];
|
||||
|
||||
if (returnType !== types[0])
|
||||
compiler.addWarning(createMessage("Conflicting return type in implementation of '" + selector + "': '" + returnType + "' vs '" + types[0] + "'", node.returntype || node, compiler.source));
|
||||
|
||||
// Check the parameter types. The size of the two type arrays should be the same
|
||||
for (var i = 1; i < typeSize; i++) {
|
||||
var parameterType = declaredTypes[i];
|
||||
|
||||
if (parameterType !== types[i])
|
||||
compiler.addWarning(createMessage("Conflicting parameter types in implementation of '" + selector + "': '" + parameterType + "' vs '" + types[i] + "'", node.arguments[i - 1].type || node.arguments[i - 1].identifier, compiler.source));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we add it
|
||||
var methodDef = new MethodDef(selector, types);
|
||||
|
||||
if (isInstanceMethodType)
|
||||
def.addInstanceMethod(methodDef);
|
||||
else
|
||||
def.addClassMethod(methodDef);
|
||||
},
|
||||
MessageSendExpression: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
@@ -1636,6 +2133,19 @@ SelectorLiteralExpression: function(node, st, c) {
|
||||
buffer.concat("\")");
|
||||
if (!generate) compiler.lastPos = node.end;
|
||||
},
|
||||
ProtocolLiteralExpression: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
buffer = compiler.jsBuffer,
|
||||
generate = compiler.generate;
|
||||
if (!generate) {
|
||||
buffer.concat(compiler.source.substring(compiler.lastPos, node.start));
|
||||
buffer.concat(" "); // Add an extra space if it looks something like this: "return(@protocol(a))". No space between return and expression.
|
||||
}
|
||||
buffer.concat("objj_getProtocol(\"");
|
||||
buffer.concat(node.id.name);
|
||||
buffer.concat("\")");
|
||||
if (!generate) compiler.lastPos = node.end;
|
||||
},
|
||||
Reference: function(node, st, c) {
|
||||
var compiler = st.compiler,
|
||||
buffer = compiler.jsBuffer,
|
||||
@@ -1677,7 +2187,7 @@ ClassStatement: function(node, st, c) {
|
||||
}
|
||||
var className = node.id.name;
|
||||
if (!compiler.getClassDef(className)) {
|
||||
classDef = {"className": className};
|
||||
classDef = new ClassDef(false, className);
|
||||
compiler.classDefs[className] = classDef;
|
||||
}
|
||||
st.vars[node.id.name] = {type: "class", node: node.id};
|
||||
|
||||
+150
-1
@@ -47,7 +47,7 @@ GLOBAL(objj_ivar) = function(/*String*/ aName, /*String*/ aType)
|
||||
this.type = aType;
|
||||
}
|
||||
|
||||
GLOBAL(objj_method) = function(/*String*/ aName, /*IMP*/ anImplementation, /*String*/ types)
|
||||
GLOBAL(objj_method) = function(/*String*/ aName, /*IMP*/ anImplementation, /*Array<String>*/ types)
|
||||
{
|
||||
this.name = aName;
|
||||
this.method_imp = anImplementation;
|
||||
@@ -74,6 +74,8 @@ GLOBAL(objj_class) = function(displayName)
|
||||
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
|
||||
@@ -85,6 +87,13 @@ GLOBAL(objj_class) = function(displayName)
|
||||
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;
|
||||
@@ -288,6 +297,139 @@ GLOBAL(class_replaceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector, /*IM
|
||||
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 = { };
|
||||
|
||||
GLOBAL(objc_allocateProtocol) = function(/*String*/ aName)
|
||||
{
|
||||
var protocol = new objj_protocol(aName);
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
GLOBAL(objc_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.name] = method;
|
||||
}
|
||||
}
|
||||
|
||||
GLOBAL(protocol_copyMethodDescriptionList) = function(/*Protocol*/ proto, /*BOOL*/ isRequiredMethod, /*BOOL*/ isInstanceMethod)
|
||||
{
|
||||
return isRequiredMethod ? (isInstanceMethod ? proto.instance_methods : proto.class_methods).slice(0) : [];
|
||||
}
|
||||
|
||||
GLOBAL(protocol_addProtocol) = function(/*Protocol*/ proto, /*Protocol*/ addition)
|
||||
{
|
||||
if (!proto || !addition) return;
|
||||
|
||||
(proto.protocol_list || (proto.protocol_list = [])).push(addition);
|
||||
}
|
||||
|
||||
var _class_initialize = function(/*Class*/ aClass)
|
||||
{
|
||||
var meta = GETMETA(aClass);
|
||||
@@ -567,6 +709,13 @@ GLOBAL(objj_getMetaClass) = function(/*String*/ aName)
|
||||
return GETMETA(theClass);
|
||||
}
|
||||
|
||||
// Working with Protocol
|
||||
|
||||
GLOBAL(objj_getProtocol) = function(/*String*/ aName)
|
||||
{
|
||||
return REGISTERED_PROTOCOLS[aName];
|
||||
}
|
||||
|
||||
// Working with Instance Variables
|
||||
|
||||
GLOBAL(ivar_getName) = function(anIvar)
|
||||
|
||||
+201
-75
@@ -373,6 +373,8 @@ if (typeof exports != "undefined" && !exports.acorn) {
|
||||
var _action = {keyword: "action"}, _selector = {keyword: "selector"}, _class = {keyword: "class"}, _global = {keyword: "global"};
|
||||
var _dictionaryLiteral = {keyword: "{"}, _arrayLiteral = {keyword: "["};
|
||||
var _ref = {keyword: "ref"}, _deref = {keyword: "deref"};
|
||||
var _protocol = {keyword: "protocol"}, _optional = {keyword: "optional"}, _required = {keyword: "required"};
|
||||
var _interface = {keyword: "interface"};
|
||||
|
||||
// Objective-J keywords
|
||||
|
||||
@@ -418,7 +420,8 @@ if (typeof exports != "undefined" && !exports.acorn) {
|
||||
|
||||
var objJAtKeywordTypes = {"implementation": _implementation, "outlet": _outlet, "accessors": _accessors, "end": _end,
|
||||
"import": _import, "action": _action, "selector": _selector, "class": _class, "global": _global,
|
||||
"ref": _ref, "deref": _deref};
|
||||
"ref": _ref, "deref": _deref, "protocol": _protocol, "optional": _optional, "required": _required,
|
||||
"interface": _interface};
|
||||
|
||||
// Map Preprocessor keyword names to token types.
|
||||
|
||||
@@ -2103,7 +2106,7 @@ var preIfLevel = 0;
|
||||
return finishNode(node, "EmptyStatement");
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _implementation:
|
||||
case _interface:
|
||||
if (options.objj) {
|
||||
next();
|
||||
node.classname = parseIdent(true);
|
||||
@@ -2122,42 +2125,147 @@ var preIfLevel = 0;
|
||||
node.endOfIvars = tokStart;
|
||||
}
|
||||
node.body = [];
|
||||
while(!eat(_end)) {
|
||||
if (tokType === _eof) raise(tokPos, "Expected '@end' after '@interface'");
|
||||
node.body.push(parseClassElement());
|
||||
}
|
||||
return finishNode(node, "InterfaceDeclarationStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _implementation:
|
||||
if (options.objj) {
|
||||
next();
|
||||
node.classname = parseIdent(true);
|
||||
if (eat(_colon))
|
||||
node.superclassname = parseIdent(true);
|
||||
else if (eat(_parenL)) {
|
||||
node.categoryname = parseIdent(true);
|
||||
expect(_parenR, "Expected closing ')' after category name");
|
||||
}
|
||||
if (tokVal === '<') {
|
||||
next();
|
||||
var protocols = [],
|
||||
first = true;
|
||||
node.protocols = protocols;
|
||||
while (tokVal !== '>') {
|
||||
if (!first)
|
||||
expect(_comma, "Expected ',' between protocol names");
|
||||
else first = false;
|
||||
protocols.push(parseIdent(true));
|
||||
}
|
||||
next();
|
||||
}
|
||||
if (tokVal === '<') {
|
||||
next();
|
||||
var protocols = [],
|
||||
first = true;
|
||||
node.protocols = protocols;
|
||||
while (tokVal !== '>') {
|
||||
if (!first)
|
||||
expect(_comma, "Expected ',' between protocol names");
|
||||
else first = false;
|
||||
protocols.push(parseIdent(true));
|
||||
}
|
||||
next();
|
||||
}
|
||||
if (eat(_braceL)) {
|
||||
node.ivardeclarations = [];
|
||||
for (;;) {
|
||||
if (eat(_braceR)) break;
|
||||
parseIvarDeclaration(node);
|
||||
}
|
||||
node.endOfIvars = tokStart;
|
||||
}
|
||||
node.body = [];
|
||||
while(!eat(_end)) {
|
||||
if (tokType === _eof) raise(tokPos, "Expected '@end' after '@implementation'");
|
||||
node.body.push(parseClassElement());
|
||||
}
|
||||
return finishNode(node, "ClassDeclarationStatement");
|
||||
}
|
||||
return finishNode(node, "ClassDeclarationStatement");
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _protocol:
|
||||
// If next token is a left parenthesis it is a ProtocolLiternal expression so bail out
|
||||
if (options.objj && input.charCodeAt(tokPos) !== 40) { // '('
|
||||
next();
|
||||
node.protocolname = parseIdent(true);
|
||||
if (tokVal === '<') {
|
||||
next();
|
||||
var protocols = [],
|
||||
first = true;
|
||||
node.protocols = protocols;
|
||||
while (tokVal !== '>') {
|
||||
if (!first)
|
||||
expect(_comma, "Expected ',' between protocol names");
|
||||
else first = false;
|
||||
protocols.push(parseIdent(true));
|
||||
}
|
||||
next();
|
||||
}
|
||||
while(!eat(_end)) {
|
||||
if (tokType === _eof) raise(tokPos, "Expected '@end' after '@protocol'");
|
||||
if (eat(_optional)) {
|
||||
while(!eat(_required && tokType !== _end)) {
|
||||
(node.optional || (node.optional = [])).push(parseProtocolClassElement());
|
||||
}
|
||||
} else {
|
||||
(node.required || (node.required = [])).push(parseProtocolClassElement());
|
||||
}
|
||||
}
|
||||
return finishNode(node, "ProtocolDeclarationStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _import:
|
||||
next();
|
||||
if (tokType === _string)
|
||||
node.localfilepath = true;
|
||||
else if (tokType ===_filename)
|
||||
node.localfilepath = false;
|
||||
else
|
||||
unexpected();
|
||||
if (options.objj) {
|
||||
next();
|
||||
if (tokType === _string)
|
||||
node.localfilepath = true;
|
||||
else if (tokType ===_filename)
|
||||
node.localfilepath = false;
|
||||
else
|
||||
unexpected();
|
||||
|
||||
node.filename = parseStringNumRegExpLiteral();
|
||||
return finishNode(node, "ImportStatement");
|
||||
node.filename = parseStringNumRegExpLiteral();
|
||||
return finishNode(node, "ImportStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _preprocess:
|
||||
next();
|
||||
return finishNode(node, "PreprocessStatement");
|
||||
if (options.objj) {
|
||||
next();
|
||||
return finishNode(node, "PreprocessStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _class:
|
||||
next();
|
||||
node.id = parseIdent(false);
|
||||
return finishNode(node, "ClassStatement");
|
||||
if (options.objj) {
|
||||
next();
|
||||
node.id = parseIdent(false);
|
||||
return finishNode(node, "ClassStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
// This is a Objective-J statement
|
||||
case _global:
|
||||
next();
|
||||
node.id = parseIdent(false);
|
||||
return finishNode(node, "GlobalStatement");
|
||||
if (options.objj) {
|
||||
next();
|
||||
node.id = parseIdent(false);
|
||||
return finishNode(node, "GlobalStatement");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// The indentation is one step to the right here to make sure it
|
||||
// is the same as in the original acorn parser. Easier merge
|
||||
|
||||
// If the statement does not start with a statement keyword or a
|
||||
// brace, it's an ExpressionStatement or LabeledStatement. We
|
||||
@@ -2165,7 +2273,6 @@ var preIfLevel = 0;
|
||||
// next token is a colon and the expression was a simple
|
||||
// Identifier node, we switch to interpreting it as a label.
|
||||
|
||||
default:
|
||||
var maybeName = tokVal, expr = parseExpression();
|
||||
if (starttype === _name && expr.type === "Identifier" && eat(_colon)) {
|
||||
for (var i = 0; i < labels.length; ++i)
|
||||
@@ -2181,18 +2288,8 @@ var preIfLevel = 0;
|
||||
semicolon();
|
||||
return finishNode(node, "ExpressionStatement");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CompoundIvarDeclaration =
|
||||
// IvarType _ IvarDeclaration (_ "," _ IvarDeclaration)* EOS
|
||||
|
||||
// IvarDeclaration =
|
||||
// Identifier _ Accessors?
|
||||
|
||||
// Accessors =
|
||||
// "@accessors" ("(" (AccessorsConfiguration (_ "," _ AccessorsConfiguration)*)? ")")?
|
||||
|
||||
function parseIvarDeclaration(node) {
|
||||
var outlet;
|
||||
if (eat(_outlet))
|
||||
@@ -2252,49 +2349,53 @@ var preIfLevel = 0;
|
||||
semicolon();
|
||||
}
|
||||
|
||||
function parseClassElement() {
|
||||
var methodType = tokVal,
|
||||
element = startNode();
|
||||
if (eat(_plusmin)) {
|
||||
element.methodtype = methodType;
|
||||
// If we find a '(' we have a return type to parse
|
||||
function parseMethodDeclaration(node) {
|
||||
node.methodtype = tokVal;
|
||||
expect(_plusmin, "Method declaration must start with '+' or '-'");
|
||||
// If we find a '(' we have a return type to parse
|
||||
if (eat(_parenL)) {
|
||||
if (eat(_action))
|
||||
node.action = true;
|
||||
if (!eat(_parenR)) {
|
||||
node.returntype = parseObjectiveJType();
|
||||
expect(_parenR, "Expected closing ')' after method return type");
|
||||
}
|
||||
}
|
||||
// Now we parse the selector
|
||||
var first = true,
|
||||
selectors = [],
|
||||
args = [];
|
||||
node.selectors = selectors;
|
||||
node.arguments = args;
|
||||
for (;;) {
|
||||
if (tokType !== _colon) {
|
||||
selectors.push(parseIdent(true));
|
||||
if (first && tokType !== _colon) break;
|
||||
} else
|
||||
selectors.push(null);
|
||||
expect(_colon, "Expected ':' in selector");
|
||||
var argument = {};
|
||||
args.push(argument);
|
||||
if (eat(_parenL)) {
|
||||
if (eat(_action))
|
||||
element.action = true;
|
||||
if (!eat(_parenR)) {
|
||||
element.returntype = parseObjectiveJType();
|
||||
expect(_parenR, "Expected closing ')' after method return type");
|
||||
}
|
||||
argument.type = parseObjectiveJType();
|
||||
expect(_parenR, "Expected closing ')' after method argument type");
|
||||
}
|
||||
// Now we parse the selector
|
||||
var first = true,
|
||||
selectors = [],
|
||||
args = [];
|
||||
element.selectors = selectors;
|
||||
element.arguments = args;
|
||||
for (;;) {
|
||||
if (tokType !== _colon) {
|
||||
selectors.push(parseIdent(true));
|
||||
if (first && tokType !== _colon) break;
|
||||
} else
|
||||
selectors.push(null);
|
||||
expect(_colon, "Expected ':' in selector");
|
||||
var argument = {};
|
||||
args.push(argument);
|
||||
if (eat(_parenL)) {
|
||||
argument.type = parseObjectiveJType();
|
||||
expect(_parenR, "Expected closing ')' after method argument type");
|
||||
}
|
||||
argument.identifier = parseIdent(false);
|
||||
if (tokType === _braceL || eat(_semi)) break;
|
||||
if (eat(_comma)) {
|
||||
expect(_dotdotdot, "Expected '...' after ',' in method declaration");
|
||||
element.parameters = true;
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
argument.identifier = parseIdent(false);
|
||||
if (tokType === _braceL || tokType === _semi) break;
|
||||
if (eat(_comma)) {
|
||||
expect(_dotdotdot, "Expected '...' after ',' in method declaration");
|
||||
node.parameters = true;
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
function parseClassElement() {
|
||||
var element = startNode();
|
||||
if (tokVal === '+' || tokVal === '-') {
|
||||
parseMethodDeclaration(element);
|
||||
eat(_semi);
|
||||
element.startOfBody = lastEnd;
|
||||
// Start a new scope with regard to labels and the `inFunction`
|
||||
// flag (restore them to their old value afterwards).
|
||||
@@ -2307,6 +2408,14 @@ var preIfLevel = 0;
|
||||
return parseStatement();
|
||||
}
|
||||
|
||||
function parseProtocolClassElement() {
|
||||
var element = startNode();
|
||||
parseMethodDeclaration(element);
|
||||
|
||||
semicolon();
|
||||
return finishNode(element, "MethodDeclarationStatement");
|
||||
}
|
||||
|
||||
// Used for constructs like `switch` and `if` that insist on
|
||||
// parentheses around their expression.
|
||||
|
||||
@@ -2622,6 +2731,14 @@ var preIfLevel = 0;
|
||||
expect(_parenR, "Expected closing ')' after selector");
|
||||
return finishNode(node, "SelectorLiteralExpression");
|
||||
|
||||
case _protocol:
|
||||
var node = startNode();
|
||||
next();
|
||||
expect(_parenL, "Expected '(' after '@protocol'");
|
||||
node.id = parseIdent(true);
|
||||
expect(_parenR, "Expected closing ')' after protocol name");
|
||||
return finishNode(node, "ProtocolLiteralExpression");
|
||||
|
||||
case _ref:
|
||||
var node = startNode();
|
||||
next();
|
||||
@@ -2878,12 +2995,21 @@ var preIfLevel = 0;
|
||||
function parseObjectiveJType() {
|
||||
var node = startNode();
|
||||
if (tokType === _name) {
|
||||
node.name = tokVal;
|
||||
var type = tokVal;
|
||||
node.name = type;
|
||||
next();
|
||||
if (tokVal === '<') {
|
||||
next();
|
||||
node.protocol = parseIdent(true);
|
||||
if (tokVal !== '>') unexpected();
|
||||
if (type === "id" && tokVal === '<') {
|
||||
var first = true,
|
||||
protocols = [];
|
||||
node.protocols = protocols;
|
||||
do {
|
||||
next();
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
eat(_comma);
|
||||
protocols.push(parseIdent(true));
|
||||
} while (tokVal !== '>');
|
||||
next();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -208,14 +208,23 @@ if (!exports.acorn) {
|
||||
|
||||
exports.IvarDeclaration = ignore;
|
||||
|
||||
exports.MethodDeclarationStatement = ignore;
|
||||
|
||||
exports.PreprocessStatement = ignore;
|
||||
exports.ClassStatement = ignore;
|
||||
exports.GlobalStatement = ignore;
|
||||
|
||||
exports.ProtocolDeclarationStatement = function(node, st, c) {
|
||||
if (node.required) for (var i = 0; i < node.required.length; ++i) {
|
||||
c(node.required[i], st, "Statement");
|
||||
}
|
||||
if (node.optional) for (var i = 0; i < node.optional.length; ++i) {
|
||||
c(node.optional[i], st, "Statement");
|
||||
}
|
||||
}
|
||||
|
||||
exports.MethodDeclarationStatement = function(node, st, c) {
|
||||
c(node.body, st, "Statement");
|
||||
var body = node.body;
|
||||
if (body)
|
||||
c(body, st, "Statement");
|
||||
}
|
||||
|
||||
exports.MessageSendExpression = function(node, st, c) {
|
||||
@@ -227,6 +236,7 @@ if (!exports.acorn) {
|
||||
}
|
||||
|
||||
exports.SelectorLiteralExpression = ignore;
|
||||
exports.ProtocolLiteralExpression = ignore;
|
||||
|
||||
exports.Reference = function(node, st, c) {
|
||||
c(node.element, st, "Identifier");
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@protocol MyProtocol
|
||||
|
||||
- (int)myFunction:(int)aValue;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MyProtocol2
|
||||
|
||||
- (int)myFunction2:(int)aValue;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MyProtocol3 <MyProtocol, MyProtocol2>
|
||||
|
||||
- (int)myFunction3:(int)aValue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MyClass : CPObject <MyProtocol>
|
||||
|
||||
- (int)myOtherFunction:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
- (int)myFunction:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MyClass2 : CPObject <MyProtocol3>
|
||||
|
||||
- (int)myOtherFunction:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
- (int)myFunction:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
- (int)myFunction2:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
- (int)myFunction3:(int)aValue
|
||||
{
|
||||
return aValue * 2;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation ProtocolTest : OJTestCase
|
||||
|
||||
- (void)testConformsToProtocol
|
||||
{
|
||||
[self assert:true equals:[[[MyClass alloc] init] conformsToProtocol:@protocol(MyProtocol)]];
|
||||
[self assert:false equals:[[[MyClass alloc] init] conformsToProtocol:@protocol(MyProtocol2)]];
|
||||
[self assert:false equals:[[[MyClass alloc] init] conformsToProtocol:@protocol(xxxxxx)]];
|
||||
[self assert:true equals:[[[MyClass2 alloc] init] conformsToProtocol:@protocol(MyProtocol)]];
|
||||
[self assert:true equals:[[[MyClass2 alloc] init] conformsToProtocol:@protocol(MyProtocol2)]];
|
||||
[self assert:true equals:[[[MyClass2 alloc] init] conformsToProtocol:@protocol(MyProtocol3)]];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user