Add a few additional syntax checks.

Throw an error if a method uses a parameter name that is also an ivar name.
Stop using with{} block for class methods.
This commit is contained in:
Ross Boucher committed 2010-03-23 11:21:59 -07:00
1 parent bbbae36e6a
commit 7013b34897
1 file changed
+70 -21
+70 -21
View File
@@ -171,9 +171,37 @@ var Preprocessor = function(/*String*/ aString, /*CFURL|String*/ aURL, /*unsigne
this._classMethod = false;
this._executable = NULL;
this._classLookupTable = {};
this.preprocess(this._tokens, this._buffer);
}
Preprocessor.prototype.setClassInfo = function(className, superClassName, ivars)
{
this._classLookupTable[className] = {superClassName:superClassName, ivars:ivars};
}
Preprocessor.prototype.getClassInfo = function(className)
{
return this._classLookupTable[className];
}
Preprocessor.prototype.allIvarNamesForClassName = function(className)
{
var names = {},
classInfo = this.getClassInfo(className);
while (classInfo)
{
for (var i in classInfo.ivars)
names[i] = 1;
classInfo = this.getClassInfo(classInfo.superClassName);
}
return names;
}
exports.Preprocessor = Preprocessor;
Preprocessor.Flags = { };
@@ -384,9 +412,9 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
// If we are at an opening curly brace ('{'), then we have an ivar declaration.
if (token == TOKEN_OPEN_BRACE)
{
var ivar_count = 0,
var ivar_names = {},
ivar_count = 0,
declaration = [],
attributes,
accessors = {};
@@ -402,15 +430,16 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
}
else if (token == TOKEN_SEMICOLON)
{
if (ivar_count++ == 0)
if (ivar_count++ === 0)
CONCAT(buffer, "class_addIvars(the_class, [");
else
CONCAT(buffer, ", ");
var name = declaration[declaration.length - 1];
CONCAT(buffer, "new objj_ivar(\"" + name + "\")");
ivar_names[name] = 1;
declaration = [];
if (attributes)
@@ -422,7 +451,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
else
declaration.push(token);
}
// If we have objects in our declaration, the user forgot a ';'.
if (declaration.length)
throw new SyntaxError(this.error_message("*** Expected ';' in ivar declaration, found '}'."));
@@ -433,6 +462,11 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (!token)
throw new SyntaxError(this.error_message("*** Expected '}'"));
this.setClassInfo(class_name, superclass_name === "Nil" ? null : superclass_name, ivar_names);
// build up the list of illegal method param names
var ivar_names = this.allIvarNamesForClassName(class_name);
for (ivar_name in accessors)
{
var accessor = accessors[ivar_name],
@@ -445,7 +479,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ",\n");
CONCAT(instance_methods, this.method(new Lexer(getterCode)));
CONCAT(instance_methods, this.method(new Lexer(getterCode), ivar_names));
// setter
if (accessor["readonly"])
@@ -469,7 +503,7 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ",\n");
CONCAT(instance_methods, this.method(new Lexer(setterCode)));
CONCAT(instance_methods, this.method(new Lexer(setterCode), ivar_names));
}
}
else
@@ -478,7 +512,10 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
// We must make a new class object for our class definition.
CONCAT(buffer, "objj_registerClassPair(the_class);\n");
}
if (!ivar_names)
var ivar_names = this.allIvarNamesForClassName(class_name);
while ((token = tokens.skip_whitespace()))
{
if (token == TOKEN_PLUS)
@@ -490,7 +527,6 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
CONCAT(class_methods, this.method(tokens));
}
else if (token == TOKEN_MINUS)
{
this._classMethod = false;
@@ -498,19 +534,19 @@ Preprocessor.prototype.implementation = function(tokens, /*StringBuffer*/ aStrin
if (IS_NOT_EMPTY(instance_methods))
CONCAT(instance_methods, ", ");
CONCAT(instance_methods, this.method(tokens));
CONCAT(instance_methods, this.method(tokens, ivar_names));
}
// Check if we've reached @end...
else if (token == TOKEN_PREPROCESSOR)
{
// The only preprocessor directive we should ever encounter at this point is @end.
if ((token = tokens.next()) == TOKEN_END)
break;
else
throw new SyntaxError(this.error_message("*** Expected \"@end\", found \"@" + token + "\"."));
}
else
throw new SyntaxError(this.error_message("*** Expected a method declaration, or \"@end\", found \"" + token + "\"."));
}
if (IS_NOT_EMPTY(instance_methods))
@@ -560,14 +596,16 @@ Preprocessor.prototype._import = function(tokens)
this._dependencies.push(new FileDependency(new CFURL(URLString), isQuoted));
}
Preprocessor.prototype.method = function(/*Lexer*/ tokens)
Preprocessor.prototype.method = function(/*Lexer*/ tokens, ivar_names)
{
var buffer = new StringBuffer(),
token,
selector = "",
parameters = [],
types = [null];
ivar_names = ivar_names || {};
while((token = tokens.skip_whitespace()) && token != TOKEN_OPEN_BRACE)
{
if (token == TOKEN_COLON)
@@ -593,8 +631,10 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens)
// Since this follows a colon, this must be the parameter name.
parameters[parameters.length] = token;
if (token in ivar_names)
throw new SyntaxError(this.error_message("*** Method ( "+selector+" ) uses a parameter name that is already in use ( "+token+" )"));
}
else if (token == TOKEN_OPEN_PARENTHESIS)
{
var type = "";
@@ -606,7 +646,6 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens)
// types[0] is the return argument
types[0] = type || null;
}
// Argument list ", ..."
else if (token == TOKEN_COMMA)
{
@@ -616,7 +655,6 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens)
// FIXME: Shouldn't allow any more after this.
}
// Build selector name.
else
selector += token;
@@ -642,12 +680,23 @@ Preprocessor.prototype.method = function(/*Lexer*/ tokens)
CONCAT(buffer, parameters[index]);
}
CONCAT(buffer, ")\n{ with(self)\n{");
CONCAT(buffer, this.preprocess(tokens, NULL, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE));
CONCAT(buffer, "}\n}");
if (this._classMethod)
{
CONCAT(buffer, ")\n{");
CONCAT(buffer, this.preprocess(tokens, NULL, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE));
CONCAT(buffer, "}");
}
else
{
CONCAT(buffer, ")\n{ with(self)\n{");
CONCAT(buffer, this.preprocess(tokens, NULL, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE));
CONCAT(buffer, "}\n}");
}
// TODO: actually use Flags.IncludeTypeSignatures flag instead of tying to Flags.IncludeDebugSymbols
if (this._flags & Preprocessor.Flags.IncludeDebugSymbols) //flags.IncludeTypeSignatures)
CONCAT(buffer, ","+JSON.stringify(types));
CONCAT(buffer, ")");
this._currentSelector = "";