Added new faster compiler based on acorn parser

This commit is contained in:
Martin Carlberg
2013-01-07 09:59:10 +01:00
parent 140cf0debe
commit 9cb14d6db3
9 changed files with 3111 additions and 30 deletions
@@ -804,7 +804,8 @@ BundleTask.prototype.defineStaticTask = function()
BundleTask.prototype.defineSourceTasks = function()
{
// Use new compiler
require("objective-j").ObjJCompiler.setCurrentUsedVersion("objj_compiler2");
//require("objective-j").ObjJCompiler.setCurrentUsedVersion("acorn");
//require("objective-j").ObjJCompiler.setCurrentUsedVersion("objj_compiler2");
var sources = this.sources();
if (!sources)
+1 -1
View File
@@ -497,7 +497,7 @@ Executable.fileExecutableSearcherForURL = function(/*CFURL*/ referenceURL)
{
if (!aStaticResource)
{
var compilingFileUrl = ObjJCompiler && ObjJCompiler.currentCompileFile ? ObjJCompiler.currentCompileFile : null;
var compilingFileUrl = ObjJCompiler && ObjJCompiler.currentCompileFile ? ObjJCompiler.currentCompileFile : ObjJAcornCompiler ? ObjJAcornCompiler.currentCompileFile : null;
throw new Error("Could not load file at " + aURL + (compilingFileUrl ? " when compiling " + compilingFileUrl : ""));
}
+10 -4
View File
@@ -43,12 +43,18 @@ function FileExecutable(/*CFURL|String*/ aURL, /*Dictionary*/ aFilenameTranslate
else if ((extension === "j" || !extension) && !fileContents.match(/^{/))
{
if (!exports.ObjJCompiler.usedVersion || exports.ObjJCompiler.usedVersion === "preprocessor")
executable = exports.preprocess(fileContents, aURL, Preprocessor.Flags.IncludeDebugSymbols);
var start = new Date().getTime();
if (!exports.ObjJCompiler.usedVersion || exports.ObjJCompiler.usedVersion === "acorn")
executable = exports.ObjJAcornCompiler.compileFileDependencies(fileContents, aURL, ObjJCompiler.Flags.IncludeDebugSymbols);
else if (exports.ObjJCompiler.usedVersion === "objj_compiler2")
executable = exports.compileFileDependencies(fileContents, aURL, ObjJCompiler.Flags.IncludeDebugSymbols);
executable = exports.ObjJCompiler.compileFileDependencies(fileContents, aURL, ObjJCompiler.Flags.IncludeDebugSymbols);
else if (exports.ObjJCompiler.usedVersion === "preprocessor")
executable = exports.preprocess(fileContents, aURL, Preprocessor.Flags.IncludeDebugSymbols);
else
throw new Error("Compiler to use is set to " + exports.ObjJCompiler.usedVersion + " but we only support 'preprocessor' (old compiler) and 'objj_compiler2' (new compiler)");
throw new Error("Compiler to use is set to " + exports.ObjJCompiler.usedVersion + " but we only support 'preprocessor' (old compiler), 'objj_compiler2' and 'acorn'");
var time = (new Date().getTime() - start) / 1000;
//print("Compile '" + (exports.ObjJCompiler.usedVersion || "preprocessor") + "' " + aURL + " in " + time + " seconds");
}
else
executable = new Executable(fileContents, [], aURL);
+3
View File
@@ -45,6 +45,9 @@
#include "Preprocessor.js"
#include "Parser.js"
#include "ObjJCompiler.js"
#include "acorn.js"
#include "acornwalk.js"
#include "ObjJAcornCompiler.js"
#include "FileDependency.js"
#include "Executable.js"
#include "FileExecutable.js"
+732
View File
@@ -0,0 +1,732 @@
/*
* ObjJAcornCompiler.js
* Objective-J
*
* Created by Martin Carlberg.
* Copyright 2013, Martin Carlberg.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
var Scope = function(prev, base)
{
this.vars = Object.create(null);
if (base) for (var key in base) this[key] = base[key];
this.prev = prev;
if (prev) this.compiler = prev.compiler;
}
Scope.prototype.compiler = function()
{
return this.compiler;
}
Scope.prototype.currentClassName = function()
{
return this.classDef ? this.classDef.className : this.prev ? this.prev.currentClassName() : null;
}
Scope.prototype.getIvarForCurrentClass = function(/* String */ ivarName)
{
if (this.ivars)
{
var ivar = this.ivars[ivarName];
if (ivar)
return ivar;
}
var prev = this.prev;
// Stop at the class declaration
if (prev && !this.classDef)
return prev.getIvarForCurrentClass(ivarName);
return null;
}
Scope.prototype.getLvarForCurrentMethod = function(/* String */ lvarName)
{
if (this.vars)
{
var lvar = this.vars[lvarName];
if (lvar)
return lvar;
}
var prev = this.prev;
// Stop at the method declaration
if (prev && !this.methodtype)
return prev.getLvarForCurrentMethod(lvarName);
return null;
}
Scope.prototype.currentMethodType = function()
{
return this.methodType ? this.methodType : this.prev ? this.prev.currentMethodType() : null;
}
var currentCompilerFlags = "";
var ObjJAcornCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, /*unsigned*/ pass)
{
this.source = aString;
this.URL = new CFURL(aURL);
this.pass = pass;
this.jsBuffer = new StringBuffer();
this.imBuffer = null;
this.cmBuffer = null;
this.warnings = [];
var start = new Date().getTime();
#ifdef BROWSER
console.time("Parse with Acorn - " + aURL);
#endif
try {
this.tokens = exports.acorn.parse(aString);
}
catch (e) {
if (e.lineStart)
{
var message = this.prettifyMessage(e, "ERROR");
#ifdef BROWSER
console.log(message);
#else
print(message);
#endif
}
throw e;
}
var end = new Date().getTime();
var time = (end - start) / 1000;
//print("Parse with Acorn: " + aURL + " in " + time + " seconds");
#ifdef BROWSER
console.timeEnd("Parse with Acorn - " + aURL);
#endif
this.dependencies = [];
this.flags = flags | ObjJAcornCompiler.Flags.IncludeDebugSymbols;
this.classDefs = Object.create(null);
this.lastPos = 0;
//var start = new Date().getTime();
#ifdef BROWSER
console.time("Compile pass " + pass + " - " + aURL);
#endif
try {
compile(this.tokens, new Scope(null ,{ compiler: this }), pass === 2 ? pass2 : pass1);
}
catch (e) {
#ifdef BROWSER
//console.log("Error: " + e + ", file content: " + aString);
#else
//print("Error: " + e + ", file content: " + aString);
#endif
throw e;
}
//var end = new Date().getTime();
//var time = (end - start) / 1000;
//print("Compile pass 1: " + aURL + " in " + time + " seconds");
#ifdef BROWSER
console.timeEnd("Compile pass " + pass + " - " + aURL);
#endif
// console.log("JS: " + this.jsBuffer);
}
exports.ObjJAcornCompiler = ObjJAcornCompiler;
exports.ObjJAcornCompiler.compileToExecutable = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJAcornCompiler.currentCompileFile = aURL;
return new ObjJAcornCompiler(aString, aURL, flags, 2).executable();
}
exports.ObjJAcornCompiler.compileToIMBuffer = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
return new ObjJAcornCompiler(aString, aURL, flags, 2).IMBuffer();
}
exports.ObjJAcornCompiler.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJAcornCompiler.currentCompileFile = aURL;
return new ObjJAcornCompiler(aString, aURL, flags, 1).executable();
}
ObjJAcornCompiler.prototype.compilePass2 = function()
{
ObjJAcornCompiler.currentCompileFile = this.URL;
this.pass = 2;
this.jsBuffer = new StringBuffer();
this.warnings = [];
//print("Start Compile2: " + this.URL);
//var start = new Date().getTime();
#ifdef BROWSER
console.time("Compile pass 2" + this.pass + " - " + this.URL);
#endif
compile(this.tokens, new Scope(null ,{ compiler: this }), pass2);
//var end = new Date().getTime();
//var time = (end - start) / 1000;
//print("Compile pass 2: " + this.URL + " in " + time + " seconds");
#ifdef BROWSER
console.timeEnd("Compile pass 2" + this.pass + " - " + this.URL);
#endif
//print("Compiled: \n" + this.jsBuffer.toString());
for (var i = 0; i < this.warnings.length; i++)
{
var message = this.prettifyMessage(this.warnings[i], "WARNING");
#ifdef BROWSER
console.log(message);
#else
print(message);
#endif
}
return this.jsBuffer.toString();
}
ObjJAcornCompiler.Flags = { };
ObjJAcornCompiler.Flags.IncludeDebugSymbols = 1 << 0;
ObjJAcornCompiler.Flags.IncludeTypeSignatures = 1 << 1;
ObjJAcornCompiler.prototype.addWarning = function(/* Warning */ aWarning)
{
this.warnings.push(aWarning);
}
ObjJAcornCompiler.prototype.getIvarForClass = function(/* String */ ivarName, /* Scope */ scope)
{
var ivar = scope.getIvarForCurrentClass(ivarName);
if (ivar)
return ivar;
var c = this.getClassDef(scope.currentClassName());
while (c)
{
var ivars = c.ivars;
if (ivars)
{
var ivarDef = ivars[ivarName];
if (ivarDef)
return ivarDef;
}
c = this.getClassDef(c.superClassName);
}
}
ObjJAcornCompiler.prototype.getClassDef = function(/* String */ aClassName)
{
if (!aClassName) return null;
var c = this.classDefs[aClassName];
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;
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};
if (superClass)
c.superClassName = superClass.name;
this.classDefs[aClassName] = c;
return c;
}
}
return null;
// classDef = {"className": className, "superClassName": superClassName, "ivars": Object.create(null), "methods": Object.create(null)};
}
ObjJAcornCompiler.prototype.executable = function()
{
if (!this._executable)
this._executable = new Executable(this.jsBuffer ? this.jsBuffer.toString() : null, this.dependencies, this.URL, null, this);
return this._executable;
}
ObjJAcornCompiler.prototype.IMBuffer = function()
{
return this.imBuffer;
}
ObjJAcornCompiler.prototype.JSBuffer = function()
{
return this.jsBuffer;
}
ObjJAcornCompiler.prototype.prettifyMessage = function(/* Message */ aMessage, /* String */ messageType)
{
var line = this.source.substring(aMessage.lineStart, aMessage.lineEnd);
var message = "\n" + line;
//print("e: " + e + ", e.lineStart: " + e.lineStart + ", e.lineEnd: " + e.lineEnd + ", e.column: " + e.column);
message += (new Array(aMessage.column + 1)).join(" ");
message += (new Array(Math.min(1, line.length) + 1)).join("^") + "\n";
message += messageType + " line " + aMessage.line + " in " + this.URL + ": " + aMessage.message;
return message;
}
ObjJAcornCompiler.prototype.error_message = function(errorMessage, astNode)
{
return errorMessage + " <Context File: "+ this.URL +
(this.currentClass ? " Class: "+this.currentClass : "") +
(this.currentSelector ? " Method: "+this.currentSelector : "") +">";
}
function createMessage(/* String */ aMessage, /* SpiderMonkey AST node */ node, /* String */ code)
{
var message = exports.acorn.getLineInfo(code, node.start);
message.message = aMessage;
return message;
}
function compile(node, state, visitor) {
function c(node, st, override) {
visitor[override || node.type](node, st, c);
}
c(node, state);
};
var pass1 = exports.acorn.walk.make({
ImportStatement: function(node, st, c) {
var urlString = node.filename.value;
st.compiler.dependencies.push(new FileDependency(new CFURL(urlString), node.localfilepath));
}
});
var pass2 = exports.acorn.walk.make({
Program: function(node, st, c) {
for (var i = 0; i < node.body.length; ++i) {
c(node.body[i], st, "Statement");
}
CONCAT(st.compiler.jsBuffer,st.compiler.source.substring(st.compiler.lastPos, node.end));
},
Function: function(node, scope, c) {
var inner = new Scope(scope);
for (var i = 0; i < node.params.length; ++i)
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
if (node.id) {
var decl = node.type == "FunctionDeclaration";
(decl ? scope : inner).vars[node.id.name] =
{type: decl ? "function" : "function name", node: node.id};
CONCAT(scope.compiler.jsBuffer,scope.compiler.source.substring(scope.compiler.lastPos, node.start));
CONCAT(scope.compiler.jsBuffer, node.id.name);
CONCAT(scope.compiler.jsBuffer, " = function");
scope.compiler.lastPos = node.id.end;
}
c(node.body, inner, "ScopeBody");
},
TryStatement: function(node, scope, c) {
c(node.block, scope, "Statement");
for (var i = 0; i < node.handlers.length; ++i) {
var handler = node.handlers[i], inner = new Scope(scope);
inner.vars[handler.param.name] = {type: "catch clause", node: handler.param};
c(handler.body, inner, "ScopeBody");
}
if (node.finalizer) c(node.finalizer, scope, "Statement");
},
VariableDeclaration: function(node, scope, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
scope.vars[decl.id.name] = {type: "var", node: decl.id};
if (decl.init) c(decl.init, scope, "Expression");
}
},
MemberExpression: function(node, st, c) {
c(node.object, st, "Expression");
st.secondMemberExpression = !node.computed;
c(node.property, st, "Expression");
st.secondMemberExpression = false;
},
ImportStatement: function(node, st, c) {
var buffer = st.compiler.jsBuffer;
if (!buffer) return;
CONCAT(buffer,st.compiler.source.substring(st.compiler.lastPos, node.start));
CONCAT(buffer, "objj_executeFile(\"");
CONCAT(buffer, node.filename.value);
CONCAT(buffer, node.localfilepath ? "\", YES);" : "\", NO);");
st.compiler.lastPos = node.end;
},
ClassDeclarationStatement: function(node, st, c) {
var classDef,
saveJSBuffer = st.compiler.jsBuffer,
className = node.classname.name,
classScope = new Scope(st);
st.compiler.imBuffer = new StringBuffer();
st.compiler.cmBuffer = new StringBuffer();
st.compiler.classBodyBuffer = new StringBuffer(); // TODO: Check if this is needed
CONCAT(saveJSBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
// First we declare the class
if (node.superclassname)
{
if (st.compiler.getClassDef(className))
throw new SyntaxError(st.compiler.error_message("Duplicate class " + className, node.classname));
if (!st.compiler.getClassDef(node.superclassname.name))
throw new SyntaxError(st.compiler.error_message("Can't find superclass " + node.superclassname.name, node.superclassname));
classDef = {"className": className, "superClassName": node.superclassname.name, "ivars": Object.create(null), "methods": Object.create(null)};
st.compiler.classDefs[className] = classDef;
CONCAT(saveJSBuffer, "{var the_class = objj_allocateClassPair(" + node.superclassname.name + ", \"" + className + "\"),\nmeta_class = the_class.isa;");
}
else if (node.categoryname)
{
classDef = st.compiler.getClassDef(className);
if (!classDef)
throw new SyntaxError(st.compiler.error_message("Class " + className + " not found ", node.classname));
CONCAT(saveJSBuffer, "{\nvar the_class = objj_getClass(\"" + className + "\")\n");
CONCAT(saveJSBuffer, "if(!the_class) throw new SyntaxError(\"*** Could not find definition for class \\\"" + className + "\\\"\");\n");
CONCAT(saveJSBuffer, "var meta_class = the_class.isa;");
}
else
{
classDef = {"className": className, "superClassName": null, "ivars": Object.create(null), "methods": Object.create(null)};
st.compiler.classDefs[className] = classDef;
CONCAT(saveJSBuffer, "{var the_class = objj_allocateClassPair(Nil, \"" + className + "\"),\nmeta_class = the_class.isa;");
}
classScope.classDef = classDef;
st.compiler.currentSuperClass = "objj_getClass(\"" + className + "\").super_class";
st.compiler.currentSuperMetaClass = "objj_getMetaClass(\"" + className + "\").super_class";
var firstIvarDeclaration = true,
hasAccessors = false;
// Then we add all ivars
if (node.ivardeclarations) for (var i = 0; i < node.ivardeclarations.length; ++i)
{
var ivarDecl = node.ivardeclarations[i],
ivarType = ivarDecl.ivartype ? ivarDecl.ivartype.name : null,
ivarName = ivarDecl.id.name,
ivar = {"type": ivarType, "name": ivarName};
if (firstIvarDeclaration)
{
firstIvarDeclaration = false;
CONCAT(saveJSBuffer, "class_addIvars(the_class, [");
}
else
CONCAT(saveJSBuffer, ", ");
if (st.compiler.flags & ObjJAcornCompiler.Flags.IncludeTypeSignatures)
CONCAT(saveJSBuffer, "new objj_ivar(\"" + ivarName + "\", \"" + ivarType + "\")");
else
CONCAT(saveJSBuffer, "new objj_ivar(\"" + ivarName + "\")");
if (ivarDecl.outlet)
ivar.outlet = true;
classDef.ivars[ivarName] = ivar;
if (!classScope.ivars)
classScope.ivars = Object.create(null);
classScope.ivars[ivarName] = {type: "ivar", name: ivarName, node: ivarDecl.id, ivar: ivar};
if (!hasAccessors && ivarDecl.accessors)
hasAccessors = true;
}
if (!firstIvarDeclaration)
CONCAT(saveJSBuffer, "]);");
// If we have accessors add get and set methods for them
if (hasAccessors)
{
var getterSetterBuffer = new StringBuffer();
// Add the class declaration to compile accessors correctly
CONCAT(getterSetterBuffer, st.compiler.source.substring(node.start, node.endOfIvars));
CONCAT(getterSetterBuffer, "\n");
for (var i = 0; i < node.ivardeclarations.length; ++i)
{
var ivarDecl = node.ivardeclarations[i],
ivarType = ivarDecl.ivartype ? ivarDecl.ivartype.name : null,
ivarName = ivarDecl.id.name,
accessors = ivarDecl.accessors;
if (!accessors)
continue;
var property = (accessors.property && accessors.property.name) || ivarName,
getterName = (accessors.getter && accessors.getter.name) || property,
getterCode = "- (" + (ivarType ? ivarType : "id") + ")" + getterName + "\n{\nreturn " + ivarName + ";\n}\n";
CONCAT(getterSetterBuffer, getterCode);
if (accessors.readonly)
continue;
var setterName = accessors.setter ? accessors.setter.name : null;
if (!setterName)
{
var start = property.charAt(0) == '_' ? 1 : 0;
setterName = (start ? "_" : "") + "set" + property.substr(start, 1).toUpperCase() + property.substring(start + 1) + ":";
}
var setterCode = "- (void)" + setterName + "(" + (ivarType ? ivarType : "id") + ")newValue\n{\n";
if (accessors.copy)
setterCode += "if (" + ivarName + " !== newValue)\n" + ivarName + " = [newValue copy];\n}\n";
else
setterCode += ivarName + " = newValue;\n}\n";
CONCAT(getterSetterBuffer, setterCode);
}
CONCAT(getterSetterBuffer, "\n@end");
// 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", st.compiler.flags);
// Add the accessors methods first to instance method buffer.
// This will allow manually added set and get methods to override the compiler generated
CONCAT(st.compiler.imBuffer, imBuffer);
}
if (node.body.length > 0)
{
st.compiler.lastPos = node.body[0].start;
// And last add methods and other statements
for (var i = 0; i < node.body.length; ++i) {
var body = node.body[i];
c(body, classScope, "Statement");
}
CONCAT(saveJSBuffer, st.compiler.source.substring(st.compiler.lastPos, body.end));
}
// We must make a new class object for our class definition if it's not a category
if (!node.categoryname) {
CONCAT(saveJSBuffer, "objj_registerClassPair(the_class);\n");
}
// Add instance methods
if (IS_NOT_EMPTY(st.compiler.imBuffer))
{
CONCAT(saveJSBuffer, "class_addMethods(the_class, [");
saveJSBuffer.atoms.push.apply(saveJSBuffer.atoms, st.compiler.imBuffer.atoms); // FIXME: Move this append to StringBuffer
CONCAT(saveJSBuffer, "]);\n");
}
// Add class methods
if (IS_NOT_EMPTY(st.compiler.cmBuffer))
{
CONCAT(saveJSBuffer, "class_addMethods(meta_class, [");
saveJSBuffer.atoms.push.apply(saveJSBuffer.atoms, st.compiler.cmBuffer.atoms); // FIXME: Move this append to StringBuffer
CONCAT(saveJSBuffer, "]);\n");
}
CONCAT(saveJSBuffer, "}");
st.compiler.jsBuffer = saveJSBuffer;
// Skip the "@end"
st.compiler.lastPos = node.end;
},
MethodDeclarationStatement: function(node, st, c) {
var saveJSBuffer = st.compiler.jsBuffer,
methodScope = new Scope(st),
selectors = node.selectors,
arguments = node.arguments,
types = [node.returntype ? node.returntype.name : "id"],
selector = selectors[0].name; // There is always at least one selector
CONCAT(saveJSBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
st.compiler.jsBuffer = node.methodtype === '-' ? st.compiler.imBuffer : st.compiler.cmBuffer;
// Put together the selector. Maybe this should be done in the parser...
for (var i = 0; i < arguments.length; i++) {
if (i === 0)
selector += ":";
else
selector += (selectors[i] ? selectors[i].name : "") + ":";
}
if (IS_NOT_EMPTY(st.compiler.jsBuffer)) // Add comma separator if this is not first method in this buffer
CONCAT(st.compiler.jsBuffer, ", ");
CONCAT(st.compiler.jsBuffer, "new objj_method(sel_getUid(\"");
CONCAT(st.compiler.jsBuffer, selector);
CONCAT(st.compiler.jsBuffer, "\"), function");
// this.currentSelector = selector;
if (st.compiler.flags & ObjJAcornCompiler.Flags.IncludeDebugSymbols)
{
CONCAT(st.compiler.jsBuffer, " $" + st.currentClassName() + "__" + selector.replace(/:/g, "_"));
}
CONCAT(st.compiler.jsBuffer, "(self, _cmd");
methodScope.methodType = node.methodtype;
if (arguments) for (var i = 0; i < arguments.length; i++)
{
var argument = arguments[i],
argumentName = argument.identifier.name;
CONCAT(st.compiler.jsBuffer, ", ");
CONCAT(st.compiler.jsBuffer, argumentName);
types.push(argument.type ? argument.type.name : null);
methodScope.vars[argumentName] = {type: "method argument", node: argument};
}
CONCAT(st.compiler.jsBuffer, ")");
st.compiler.lastPos = node.startOfBody;
c(node.body, methodScope, "Statement");
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.body.end));
CONCAT(st.compiler.jsBuffer, "\n");
if (st.compiler.flags & ObjJAcornCompiler.Flags.IncludeDebugSymbols) //flags.IncludeTypeSignatures)
CONCAT(st.compiler.jsBuffer, ","+JSON.stringify(types));
CONCAT(st.compiler.jsBuffer, ")");
st.compiler.jsBuffer = saveJSBuffer;
st.compiler.lastPos = node.end;
},
MessageSendExpression: function(node, st, c) {
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
st.compiler.lastPos = node.object ? node.object.start : node.arguments.length ? node.arguments[0].start : node.end;
if (node.superObject)
{
CONCAT(st.compiler.jsBuffer, "objj_msgSendSuper(");
CONCAT(st.compiler.jsBuffer, "{ receiver:self, super_class:" + (st.currentMethodType() === "+" ? st.compiler.currentSuperMetaClass : st.compiler.currentSuperClass ) + " }");
}
else
{
CONCAT(st.compiler.jsBuffer, "objj_msgSend(");
c(node.object, st, "Expression");
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.object.end));
}
var selectors = node.selectors,
arguments = node.arguments,
selector = selectors[0].name; // There is always at least one selector
// Put together the selector. Maybe this should be done in the parser...
for (var i = 0; i < arguments.length; i++)
if (i === 0)
selector += ":";
else
selector += (selectors[i] ? selectors[i].name : "") + ":";
CONCAT(st.compiler.jsBuffer, ", \"");
CONCAT(st.compiler.jsBuffer, selector); // FIXME: sel_getUid(selector + "") ? This FIXME is from the old preprocessor compiler
CONCAT(st.compiler.jsBuffer, "\"");
if (node.arguments) for (var i = 0; i < node.arguments.length; i++)
{
var argument = node.arguments[i];
CONCAT(st.compiler.jsBuffer, ", ");
st.compiler.lastPos = argument.start;
c(argument, st, "Expression");
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, argument.end));
st.compiler.lastPos = argument.end;
}
// TODO: Move this 'if' wtih body up inside the node.argument 'if'
if (node.parameters) for (var i = 0; i < node.parameters.length; ++i)
{
var parameter = node.parameters[i];
CONCAT(st.compiler.jsBuffer, ", ");
st.compiler.lastPos = parameter.start;
c(parameter, st, "Expression");
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, parameter.end));
st.compiler.lastPos = parameter.end;
}
CONCAT(st.compiler.jsBuffer, ")");
st.compiler.lastPos = node.end;
},
Identifier: function(node, st, c) {
if (!st.secondMemberExpression)
{
var identifier = node.name,
lvar = st.getLvarForCurrentMethod(identifier),
ivar = st.compiler.getIvarForClass(identifier, st);
if (ivar)
{
if (lvar)
st.compiler.addWarning(createMessage("Local declaration of '" + identifier + "' hides instance variable", node, st.compiler.source));
else
{
var nodeStart = node.start,
compiler = st.compiler;
do { // The Spider Monkey AST tree includes any parentheses in start and end properties so we have to make sure we skip those
CONCAT(compiler.jsBuffer, compiler.source.substring(compiler.lastPos, nodeStart));
compiler.lastPos = nodeStart;
} while (compiler.source.substr(nodeStart++, 1) === "(")
CONCAT(compiler.jsBuffer, "self.");
}
}
}
},
SelectorLiteralExpression: function(node, st, c) {
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
CONCAT(st.compiler.jsBuffer, "sel_getUid(\"");
CONCAT(st.compiler.jsBuffer, node.selector);
CONCAT(st.compiler.jsBuffer, "\")");
st.compiler.lastPos = node.end;
},
Literal: function(node, st, c) {
if (node.raw && node.raw.charAt(0) === "@")
{
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
st.compiler.lastPos = node.start + 1;
}
},
ObjectExpression: function(node, st, c) {
for (var i = 0; i < node.properties.length; ++i)
{
var prop = node.properties[i];
if (prop.key.raw && prop.key.raw.charAt(0) === "@")
{
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, prop.key.start));
st.compiler.lastPos = prop.key.start + 1;
}
c(prop.value, st, "Expression");
}
}
});
+23 -24
View File
@@ -20,25 +20,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
var ObjJCompiler = { },
currentCompilerFlags = "";
exports.compileToExecutable = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJCompiler.currentCompileFile = aURL;
return new ObjJCompiler(aString, aURL, flags, 2).executable();
}
exports.compileToIMBuffer = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
return new ObjJCompiler(aString, aURL, flags, 2).IMBuffer();
}
exports.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJCompiler.currentCompileFile = aURL;
return new ObjJCompiler(aString, aURL, flags, 1).executable();
}
var currentCompilerFlags = "";
var ObjJCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags, /*unsigned*/ pass)
{
@@ -91,6 +73,25 @@ var ObjJCompiler = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ fla
// console.log("JS: " + this._jsBuffer);
}
exports.ObjJCompiler = ObjJCompiler;
exports.ObjJCompiler.compileToExecutable = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJCompiler.currentCompileFile = aURL;
return new ObjJCompiler(aString, aURL, flags, 2).executable();
}
exports.ObjJCompiler.compileToIMBuffer = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
return new ObjJCompiler(aString, aURL, flags, 2).IMBuffer();
}
exports.ObjJCompiler.compileFileDependencies = function(/*String*/ aString, /*CFURL*/ aURL, /*unsigned*/ flags)
{
ObjJCompiler.currentCompileFile = aURL;
return new ObjJCompiler(aString, aURL, flags, 1).executable();
}
ObjJCompiler.prototype.compilePass2 = function()
{
ObjJCompiler.currentCompileFile = this._URL;
@@ -111,12 +112,11 @@ ObjJCompiler.prototype.compilePass2 = function()
return this._jsBuffer.toString();
}
exports.ObjJCompiler = ObjJCompiler;
// This will set the compiler version to use.
// These version works:
// "preprocessor" -> Old Cappuccino compiler
// "objj_compiler2" -> New Cappuccino compiler
// "acorn" -> New Cappuccino compiler with acorn parser
GLOBAL(ObjJCompilerSetUsedVersion) = function(version)
{
@@ -1400,7 +1400,7 @@ ObjJCompiler.prototype.nodeClassDeclationStatement = function(/*SyntaxNode*/ ast
if (this.getClassDef(className))
throw new SyntaxError(this.error_message("Duplicate class " + className, children[2]));
if (!this.getClassDef(superClassName))
throw new SyntaxError(this.error_message("Can't find superclass " + superClassName, child));
throw new SyntaxError(this.error_message("Can't find superclass " + superClassName, child));
classDef = {"className": className, "superClassName": superClassName, "ivars": {}, "methods": {}};
@@ -1540,7 +1540,7 @@ ObjJCompiler.prototype.nodeClassDeclationStatement = function(/*SyntaxNode*/ ast
CONCAT(getterSetterBuffer, "\n@end");
// Remove all @accessors or we will get a recursive loop in infinity
var b = getterSetterBuffer.toString().replace(/@accessors(\(.*\))?/g, "");
var imBuffer = exports.compileToIMBuffer(b, "getter", this._flags);
var imBuffer = ObjJCompiler.compileToIMBuffer(b, "getter", this._flags);
CONCAT(this._imBuffer, imBuffer);
}
@@ -4704,4 +4704,3 @@ ObjJCompiler.prototype.error_message = function(errorMessage, astNode)
(this._currentClass ? " Class: "+this._currentClass : "") +
(this._currentSelector ? " Method: "+this._currentSelector : "") +">";
}
//})(window, ObjJCompiler, { exports: ObjJCompiler });
+2063
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
Copyright (C) 2012 by Marijn Haverbeke <marijnh@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Please note that some subdirectories of the CodeMirror distribution
include their own LICENSE files, and are released under different
licences.
+254
View File
@@ -0,0 +1,254 @@
// AST walker module for Mozilla Parser API compatible trees
if (!exports.acorn) {
exports.acorn = {};
exports.acorn.walk = {};
}
(function(exports) {
"use strict";
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
exports.simple = function(node, visitors, base, state) {
if (!base) base = exports;
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
if (found) found(node, st);
base[type](node, st, c);
}
c(node, state);
};
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
exports.recursive = function(node, state, funcs, base) {
var visitor = exports.make(funcs, base);
function c(node, st, override) {
visitor[override || node.type](node, st, c);
}
c(node, state);
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
exports.make = function(funcs, base) {
if (!base) base = exports;
var visitor = {};
for (var type in base) visitor[type] = base[type];
for (var type in funcs) visitor[type] = funcs[type];
return visitor;
};
function skipThrough(node, st, c) { c(node, st); }
function ignore(node, st, c) {}
// Node walkers.
exports.Program = exports.BlockStatement = function(node, st, c) {
for (var i = 0; i < node.body.length; ++i) {
c(node.body[i], st, "Statement");
}
};
exports.Statement = skipThrough;
exports.EmptyStatement = ignore;
exports.ExpressionStatement = function(node, st, c) {
c(node.expression, st, "Expression");
};
exports.IfStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) c(node.alternate, st, "Statement");
};
exports.LabeledStatement = function(node, st, c) {
c(node.body, st, "Statement");
};
exports.BreakStatement = exports.ContinueStatement = ignore;
exports.WithStatement = function(node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
exports.SwitchStatement = function(node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0; i < node.cases.length; ++i) {
var cs = node.cases[i];
if (cs.test) c(cs.test, st, "Expression");
for (var j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement");
}
};
exports.ReturnStatement = function(node, st, c) {
if (node.argument) c(node.argument, st, "Expression");
};
exports.ThrowStatement = function(node, st, c) {
c(node.argument, st, "Expression");
};
exports.TryStatement = function(node, st, c) {
c(node.block, st, "Statement");
for (var i = 0; i < node.handlers.length; ++i)
c(node.handlers[i].body, st, "ScopeBody");
if (node.finalizer) c(node.finalizer, st, "Statement");
};
exports.WhileStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
exports.DoWhileStatement = function(node, st, c) {
c(node.body, st, "Statement");
c(node.test, st, "Expression");
};
exports.ForStatement = function(node, st, c) {
if (node.init) c(node.init, st, "ForInit");
if (node.test) c(node.test, st, "Expression");
if (node.update) c(node.update, st, "Expression");
c(node.body, st, "Statement");
};
exports.ForInStatement = function(node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
exports.ForInit = function(node, st, c) {
if (node.type == "VariableDeclaration") c(node, st);
else c(node, st, "Expression");
};
exports.DebuggerStatement = ignore;
exports.FunctionDeclaration = function(node, st, c) {
c(node, st, "Function");
};
exports.VariableDeclaration = function(node, st, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
if (decl.init) c(decl.init, st, "Expression");
}
};
exports.Function = function(node, st, c) {
c(node.body, st, "ScopeBody");
};
exports.ScopeBody = function(node, st, c) {
c(node, st, "Statement");
};
exports.Expression = skipThrough;
exports.ThisExpression = ignore;
exports.ArrayExpression = function(node, st, c) {
for (var i = 0; i < node.elements.length; ++i) {
var elt = node.elements[i];
if (elt) c(elt, st, "Expression");
}
};
exports.ObjectExpression = function(node, st, c) {
for (var i = 0; i < node.properties.length; ++i)
c(node.properties[i].value, st, "Expression");
};
exports.FunctionExpression = exports.FunctionDeclaration;
exports.SequenceExpression = function(node, st, c) {
for (var i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression");
};
exports.UnaryExpression = exports.UpdateExpression = function(node, st, c) {
c(node.argument, st, "Expression");
};
exports.BinaryExpression = exports.AssignmentExpression = exports.LogicalExpression = function(node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
exports.ConditionalExpression = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
exports.NewExpression = exports.CallExpression = function(node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression");
};
exports.MemberExpression = function(node, st, c) {
c(node.object, st, "Expression");
if (node.computed) c(node.property, st, "Expression");
};
exports.Identifier = exports.Literal = ignore;
exports.ClassDeclarationStatement = function(node, st, c) {
if (node.ivardeclarations) for (var i = 0; i < node.ivardeclarations.length; ++i) {
c(node.ivardeclarations[i], st, "IvarDeclaration");
}
for (var i = 0; i < node.body.length; ++i) {
c(node.body[i], st, "Statement");
}
}
exports.ImportStatement = ignore;
exports.IvarDeclaration = ignore;
exports.MethodDeclarationStatement = ignore;
exports.MethodDeclarationStatement = function(node, st, c) {
c(node.body, st, "Statement");
}
exports.MessageSendExpression = function(node, st, c) {
if (!node.superObject) c(node.object, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression");
if (node.parameters) for (var i = 0; i < node.parameters.length; ++i)
c(node.parameters[i], st, "Expression");
}
exports.SelectorLiteralExpression = ignore;
// A custom walker that keeps track of the scope chain and the
// variables defined in it.
function makeScope(prev) {
return {vars: Object.create(null), prev: prev};
}
exports.scopeVisitor = exports.make({
Function: function(node, scope, c) {
var inner = makeScope(scope);
for (var i = 0; i < node.params.length; ++i)
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
if (node.id) {
var decl = node.type == "FunctionDeclaration";
(decl ? scope : inner).vars[node.id.name] =
{type: decl ? "function" : "function name", node: node.id};
}
c(node.body, inner, "ScopeBody");
},
TryStatement: function(node, scope, c) {
c(node.block, scope, "Statement");
for (var i = 0; i < node.handlers.length; ++i) {
var handler = node.handlers[i], inner = makeScope(scope);
inner.vars[handler.param.name] = {type: "catch clause", node: handler.param};
c(handler.body, inner, "ScopeBody");
}
if (node.finalizer) c(node.finalizer, scope, "Statement");
},
VariableDeclaration: function(node, scope, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
scope.vars[decl.id.name] = {type: "var", node: decl.id};
if (decl.init) c(decl.init, scope, "Expression");
}
}
});
})(typeof exports == "undefined" ? acorn.walk = {} : exports.acorn.walk);