NEW: @typedef and ivar type warning

Previously, ObjJ was ignoring unknow ivar type. This patch adds some check to ensure the type is either a known class, the current class
itself, a global, a basic JS type or a declared custom type.

In order to declare custom types, this patch introduces the @typedef keyword.

For instance, this will throw a warning:

```objj
@import <Foundation/Foundation.j>

@implementation MyClass: CPObject
{
    NUSuppaType mode;
}
@end
```

This will not:

```objj
@import <Foundation/Foundation.j>

@typedef NUSuppaType

@implementation NUMyClass: CPObject
{
     NUSuppaType mode;
}
@end
```

Declared types are shared accross all application, one type can only be declared once.
This commit is contained in:
Antoine Mercadal
2014-11-06 10:10:05 -08:00
parent 6d956c6138
commit 8ee443a43c
4 changed files with 161 additions and 34 deletions
+77 -5
View File
@@ -353,6 +353,11 @@ ProtocolDef.prototype.getClassMethod = function(name) {
return null;
}
var TypeDef = function(name)
{
this.name = name;
}
// methodDef = {"types": types, "name": selector}
var MethodDef = function(name, types)
{
@@ -369,7 +374,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, /* Dictionary */ protocolDefs)
var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, /*unsigned*/ pass, /* Dictionary */ classDefs, /* Dictionary */ protocolDefs, /* Dictionary */ typeDefs)
{
this.source = aString;
this.URL = new CFURL(aURL);
@@ -399,6 +404,7 @@ var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*
this.flags = flags | ObjJAcornCompiler.Flags.IncludeDebugSymbols;
this.classDefs = classDefs ? classDefs : Object.create(null);
this.protocolDefs = protocolDefs ? protocolDefs : Object.create(null);
this.typeDefs = typeDefs ? typeDefs : Object.create(null);
this.lastPos = 0;
if (currentCompilerFlags & ObjJAcornCompiler.Flags.Generate)
this.generate = true;
@@ -415,9 +421,9 @@ exports.ObjJAcornCompiler.compileToExecutable = function(/*String*/ aString, /*C
return new ObjJAcornCompiler(aString, aURL, flags, 2).executable();
}
exports.ObjJAcornCompiler.compileToIMBuffer = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, classDefs, protocolDefs)
exports.ObjJAcornCompiler.compileToIMBuffer = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, classDefs, protocolDefs, typeDefs)
{
return new ObjJAcornCompiler(aString, aURL, flags, 2, classDefs, protocolDefs).IMBuffer();
return new ObjJAcornCompiler(aString, aURL, flags, 2, classDefs, protocolDefs, typeDefs).IMBuffer();
}
exports.ObjJAcornCompiler.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
@@ -581,6 +587,31 @@ ObjJAcornCompiler.prototype.getProtocolDef = function(/* String */ aProtocolName
// protocolDef = {"name": protocolName, "protocols": Object.create(null), "required": Object.create(null), "optional": Object.create(null)};
}
ObjJAcornCompiler.prototype.getTypeDef = function(/* String */ aTypeDefName)
{
if (!aTypeDefName)
return null;
var t = this.typeDefs[aTypeDefName];
if (t)
return t;
if (typeof objj_getTypeDef === 'function')
{
var aTypeDef = objj_getTypeDef(aTypeDefName);
if (aTypeDef)
{
var typeDefName = typeDef_getName(aTypeDef)
t = new TypeDef(typeDefName);
this.typeDefs[typeDefName] = t;
return t;
}
}
return null;
}
ObjJAcornCompiler.methodDefsFromMethodList = function(/* Array */ methodList)
{
var methodSize = methodList.length,
@@ -1715,12 +1746,19 @@ ClassDeclarationStatement: function(node, st, c) {
{
var ivarDecl = node.ivardeclarations[i],
ivarType = ivarDecl.ivartype ? ivarDecl.ivartype.name : null,
ivarTypeIsClass = ivarDecl.ivartype ? ivarDecl.ivartype.typeisclass : false,
ivarName = ivarDecl.id.name,
ivar = {"type": ivarType, "name": ivarName},
accessors = ivarDecl.accessors;
if (ivars[ivarName])
throw compiler.error_message("Instance variable '" + ivarName + "'is already declared for class " + className, ivarDecl.id);
throw compiler.error_message("Instance variable '" + ivarName + "' is already declared for class " + className, ivarDecl.id);
var isTypeDefined = !ivarTypeIsClass || typeof global[ivarType] !== "undefined" || typeof window[ivarType] !== "undefined"
|| compiler.getClassDef(ivarType) || compiler.getTypeDef(ivarType) || ivarType == classDef.name;
if (!isTypeDefined)
compiler.addWarning(createMessage("Unknown type '" + ivarType + "' for ivar '" + ivarName + "'", ivarDecl.id, compiler.source));
if (firstIvarDeclaration)
{
@@ -1824,7 +1862,7 @@ ClassDeclarationStatement: function(node, st, c) {
// Remove all @accessors or we will get a recursive loop in infinity
var b = getterSetterBuffer.toString().replace(/@accessors(\(.*\))?/g, "");
var imBuffer = ObjJAcornCompiler.compileToIMBuffer(b, "Accessors", compiler.flags, compiler.classDefs, compiler.protocolDefs);
var imBuffer = ObjJAcornCompiler.compileToIMBuffer(b, "Accessors", compiler.flags, compiler.classDefs, compiler.protocolDefs, compiler.typeDefs);
// Add the accessors methods first to instance method buffer.
// This will allow manually added set and get methods to override the compiler generated
@@ -2393,5 +2431,39 @@ PreprocessStatement: function(node, st, c) {
compiler.lastPos = node.start;
compiler.jsBuffer.concat("//");
}
},
TypeDefStatement: function(node, st, c) {
var compiler = st.compiler,
generate = compiler.generate,
buffer = compiler.jsBuffer,
typeDefName = node.typedefname.name,
typeDef = compiler.getTypeDef(typeDefName),
typeDefScope = new Scope(st);
if (typeDef)
throw compiler.error_message("Duplicate type definition " + typeDefName, node.typedefname);
compiler.imBuffer = new StringBuffer();
compiler.cmBuffer = new StringBuffer();
if (!generate)
buffer.concat(compiler.source.substring(compiler.lastPos, node.start));
buffer.concat("{var the_typedef = objj_allocateTypeDef(\"" + typeDefName + "\");");
typeDef = new TypeDef(typeDefName);
compiler.typeDefs[typeDefName] = typeDef;
typeDefScope.typeDef = typeDef;
buffer.concat("\nobjj_registerTypeDef(the_typedef);\n");
buffer.concat("}");
compiler.jsBuffer = buffer;
// Skip the "@end"
if (!generate)
compiler.lastPos = node.end;
}
});
+33
View File
@@ -100,6 +100,11 @@ GLOBAL(objj_object) = function()
this._UID = -1;
}
GLOBAL(objj_typeDef) = function(/*String*/ aName)
{
this.name = aName;
}
// Working with Classes
GLOBAL(class_getName) = function(/*Class*/ aClass)
@@ -440,6 +445,26 @@ GLOBAL(protocol_addProtocol) = function(/*Protocol*/ proto, /*Protocol*/ additio
(proto.protocol_list || (proto.protocol_list = [])).push(addition);
}
var REGISTERED_TYPEDEFS = Object.create(null);
GLOBAL(objj_allocateTypeDef) = function(/*String*/ aName)
{
var typeDef = new objj_typeDef(aName);
return typeDef;
}
GLOBAL(objj_registerTypeDef) = function(/*TypeDef*/ typeDef)
{
REGISTERED_TYPEDEFS[typeDef.name] = typeDef;
}
GLOBAL(typeDef_getName) = function(/*TypeDef*/ typeDef)
{
return typeDef.name;
}
var _class_initialize = function(/*Class*/ aClass)
{
var meta = GETMETA(aClass);
@@ -619,6 +644,7 @@ GLOBAL(objj_resetRegisterClasses) = function()
REGISTERED_CLASSES = Object.create(null);
REGISTERED_PROTOCOLS = Object.create(null);
REGISTERED_TYPEDEFS = Object.create(null);
resetBundle();
}
@@ -751,6 +777,13 @@ GLOBAL(objj_getProtocol) = function(/*String*/ aName)
return REGISTERED_PROTOCOLS[aName];
}
// Working with typeDef
GLOBAL(objj_getTypeDef) = function(/*String*/ aName)
{
return REGISTERED_TYPEDEFS[aName];
}
// Working with Instance Variables
GLOBAL(ivar_getName) = function(anIvar)
+49 -29
View File
@@ -375,12 +375,15 @@ if (typeof exports != "undefined" && !exports.acorn) {
var _ref = {keyword: "ref"}, _deref = {keyword: "deref"};
var _protocol = {keyword: "protocol"}, _optional = {keyword: "optional"}, _required = {keyword: "required"};
var _interface = {keyword: "interface"};
var _typedef = {keyword: "typedef"};
// Objective-J keywords
var _filename = {keyword: "filename"}, _unsigned = {keyword: "unsigned", okAsIdent: true}, _signed = {keyword: "signed", okAsIdent: true};
var _byte = {keyword: "byte", okAsIdent: true}, _char = {keyword: "char", okAsIdent: true}, _short = {keyword: "short", okAsIdent: true};
var _int = {keyword: "int", okAsIdent: true}, _long = {keyword: "long", okAsIdent: true}, _id = {keyword: "id", okAsIdent: true};
var _boolean = {keyword: "BOOL", okAsIdent: true}, _SEL = {keyword: "SEL", okAsIdent: true}, _float = {keyword: "float", okAsIdent: true};
var _double = {keyword: "double", okAsIdent: true};
var _preprocess = {keyword: "#"};
// Preprocessor keywords
@@ -415,14 +418,15 @@ if (typeof exports != "undefined" && !exports.acorn) {
// Map Objective-J keyword names to token types.
var keywordTypesObjJ = {"IBAction": _action, "IBOutlet": _outlet, "unsigned": _unsigned, "signed": _signed, "byte": _byte, "char": _char,
"short": _short, "int": _int, "long": _long, "id": _id };
"short": _short, "int": _int, "long": _long, "id": _id, "float": _float, "BOOL": _boolean, "SEL": _SEL,
"double": _double};
// Map Objective-J "@" keyword names to token types.
var objJAtKeywordTypes = {"implementation": _implementation, "outlet": _outlet, "accessors": _accessors, "end": _end,
"import": _import, "action": _action, "selector": _selector, "class": _class, "global": _global,
"ref": _ref, "deref": _deref, "protocol": _protocol, "optional": _optional, "required": _required,
"interface": _interface};
"interface": _interface, "typedef": _typedef};
// Map Preprocessor keyword names to token types.
@@ -547,7 +551,7 @@ if (typeof exports != "undefined" && !exports.acorn) {
// The Objective-J keywords.
var isKeywordObjJ = makePredicate("IBAction IBOutlet byte char short int long unsigned signed id");
var isKeywordObjJ = makePredicate("IBAction IBOutlet byte char short int long float unsigned signed id BOOL SEL double");
// The preprocessor keywords.
@@ -662,6 +666,7 @@ var preprocessTokens = [_preIf, _preIfdef, _preIfndef, _preElse, _preElseIf, _pr
}
}
}
tokVal = val;
lastTokCommentsAfter = tokCommentsAfter;
lastTokSpacesAfter = tokSpacesAfter;
@@ -2277,6 +2282,15 @@ var preIfLevel = 0;
}
break;
// This is a Objective-J statement
case _typedef:
if (options.objj) {
next();
node.typedefname = parseIdent(true);
return finishNode(node, "TypeDefStatement");
}
break;
}
// The indentation is one step to the right here to make sure it
@@ -2317,6 +2331,7 @@ var preIfLevel = 0;
if (outlet)
decl.outlet = outlet;
decl.ivartype = type;
// print("keyword: " + type.name + " is class " + type.typeisclass)
decl.id = parseIdent();
if (strict && isStrictBadIdWord(decl.id.name))
raise(decl.id.start, "Binding " + decl.id.name + " in strict mode");
@@ -3021,6 +3036,7 @@ var preIfLevel = 0;
node.typeisclass = true;
next();
} else {
node.typeisclass = false;
node.name = tokType.keyword;
// Do nothing more if it is 'void'
if (!eat(_void)) {
@@ -3043,32 +3059,36 @@ var preIfLevel = 0;
} else {
// Now check if it is some basic type or an approved combination of basic types
var nextKeyWord;
if (eat(_signed) || eat(_unsigned))
nextKeyWord = tokType.keyword || true;
if (eat(_char) || eat(_byte) || eat(_short)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
} else {
if (eat(_int)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
}
if (eat(_long)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
if (eat(_long)) {
node.name += " " + nextKeyWord;
}
}
}
if (!nextKeyWord) {
// It must be a class name if it was not a basic type. // FIXME: This is not true
node.name = (!options.forbidReserved && tokType.keyword) || unexpected();
node.typeisclass = true;
next();
if (eat(_float) || eat(_boolean) || eat(_SEL) || eat(_double))
nextKeyWord = tokType.keyword;
else {
if (eat(_signed) || eat(_unsigned))
nextKeyWord = tokType.keyword || true;
if (eat(_char) || eat(_byte) || eat(_short)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
} else {
if (eat(_int)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
}
if (eat(_long)) {
if (nextKeyWord)
node.name += " " + nextKeyWord;
nextKeyWord = tokType.keyword || true;
if (eat(_long)) {
node.name += " " + nextKeyWord;
}
}
}
if (!nextKeyWord) {
// It must be a class name if it was not a basic type. // FIXME: This is not true
node.name = (!options.forbidReserved && tokType.keyword) || unexpected();
node.typeisclass = true;
next();
}
}
}
}
+2
View File
@@ -221,6 +221,8 @@ if (!exports.acorn) {
}
}
exports.TypeDefStatement = ignore;
exports.MethodDeclarationStatement = function(node, st, c) {
var body = node.body;
if (body)