mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
780 lines
27 KiB
JavaScript
780 lines
27 KiB
JavaScript
/*
|
|
* 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;
|
|
}
|
|
|
|
Scope.prototype.copyAddedSelfToIvarsToParent = function()
|
|
{
|
|
if (this.prev && this.addedSelfToIvars) for (var key in this.addedSelfToIvars)
|
|
{
|
|
var addedSelfToIvar = this.addedSelfToIvars[key],
|
|
scopeAddedSelfToIvar = (this.prev.addedSelfToIvars || (this.prev.addedSelfToIvars = Object.create(null)))[key] || (this.prev.addedSelfToIvars[key] = []);
|
|
|
|
scopeAddedSelfToIvar.push.apply(scopeAddedSelfToIvar, addedSelfToIvar); // Append at end in parent scope
|
|
}
|
|
}
|
|
|
|
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 = [];
|
|
|
|
//console.log("Start Parse: " + aURL);
|
|
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 = [];
|
|
//console.log("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();
|
|
}
|
|
|
|
var currentCompilerFlags = "";
|
|
|
|
exports.setCurrentCompilerFlags = function(/*String*/ compilerFlags)
|
|
{
|
|
currentCompilerFlags = compilerFlags;
|
|
}
|
|
|
|
exports.currentCompilerFlags = function(/*String*/ compilerFlags)
|
|
{
|
|
return currentCompilerFlags;
|
|
}
|
|
|
|
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");
|
|
inner.copyAddedSelfToIvarsToParent();
|
|
},
|
|
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");
|
|
inner.copyAddedSelfToIvarsToParent();
|
|
}
|
|
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],
|
|
identifier = decl.id.name;
|
|
scope.vars[identifier] = {type: "var", node: decl.id};
|
|
if (decl.init) c(decl.init, scope, "Expression");
|
|
if (scope.addedSelfToIvars) {
|
|
var addedSelfToIvar = scope.addedSelfToIvars[identifier];
|
|
if (addedSelfToIvar) {
|
|
var buffer = scope.compiler.jsBuffer.atoms;
|
|
for (var i = 0; i < addedSelfToIvar.length; i++) {
|
|
var dict = addedSelfToIvar[i];
|
|
buffer[dict.index] = "";
|
|
scope.compiler.addWarning(createMessage("Local declaration of '" + identifier + "' hides instance variable", dict.node, scope.compiler.source));
|
|
}
|
|
scope.addedSelfToIvars[identifier] = [];
|
|
}
|
|
}
|
|
}
|
|
},
|
|
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.currentMethodType() === "-" && !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) === "(")
|
|
// Save the index in where the "self." string is stored and the node.
|
|
// These will be used if we find a variable declaration that is hoisting this identifier.
|
|
((st.addedSelfToIvars || (st.addedSelfToIvars = Object.create(null)))[identifier] || (st.addedSelfToIvars[identifier] = [])).push({node: node, index: compiler.jsBuffer.atoms.length});
|
|
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");
|
|
}
|
|
},
|
|
PreprocessStatement: function(node, st, c) {
|
|
CONCAT(st.compiler.jsBuffer, st.compiler.source.substring(st.compiler.lastPos, node.start));
|
|
st.compiler.lastPos = node.start;
|
|
CONCAT(st.compiler.jsBuffer, "//");
|
|
}
|
|
});
|