mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
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:
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +221,8 @@ if (!exports.acorn) {
|
||||
}
|
||||
}
|
||||
|
||||
exports.TypeDefStatement = ignore;
|
||||
|
||||
exports.MethodDeclarationStatement = function(node, st, c) {
|
||||
var body = node.body;
|
||||
if (body)
|
||||
|
||||
Reference in New Issue
Block a user