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:
Martin Carlberg
2013-08-09 19:02:45 +02:00
parent 3beb753c63
commit d38125db13
6 changed files with 1041 additions and 154 deletions
+20
View File
@@ -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
/*!
+585 -75
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+13 -3
View File
@@ -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