You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pu...@apache.org on 2012/09/11 01:57:45 UTC
[51/52] [abbrv] [partial] re-org
http://git-wip-us.apache.org/repos/asf/incubator-cordova-windows/blob/03bf0cde/windows8/framework/Cordova-Metro/lib/Jscex/src/jscex-jit.js
----------------------------------------------------------------------
diff --git a/windows8/framework/Cordova-Metro/lib/Jscex/src/jscex-jit.js b/windows8/framework/Cordova-Metro/lib/Jscex/src/jscex-jit.js
new file mode 100644
index 0000000..43e09fd
--- /dev/null
+++ b/windows8/framework/Cordova-Metro/lib/Jscex/src/jscex-jit.js
@@ -0,0 +1,1692 @@
+(function () {
+ "use strict";
+
+ var Jscex;
+
+ var codeGenerator = (typeof eval("(function () {})") == "function") ?
+ function (code) { return code; } :
+ function (code) { return "false || " + code; };
+
+ // support string type only.
+ var stringify = (typeof JSON !== "undefined" && JSON.stringify) ?
+ function (s) { return JSON.stringify(s); } :
+ (function () {
+ // Implementation comes from JSON2 (http://www.json.org/js.html)
+
+ var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+
+ var meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ }
+
+ return function (s) {
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can safely slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe escape
+ // sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(s) ? '"' + s.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 's' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + s + '"';
+ };
+ })();
+
+ function sprintf(format) {
+ var args = arguments;
+ return format.toString().replace(new RegExp("{\\d+}", "g"), function (p) {
+ var n = parseInt(p.substring(1, p.length - 1), 10);
+ return args[n + 1];
+ });
+ }
+
+ function trim(s) {
+ return s.replace(/ +/g, "");
+ }
+
+ function getPrecedence(ast) {
+ var type = ast[0];
+ switch (type) {
+ case "dot": // .
+ case "sub": // []
+ case "call": // ()
+ return 1;
+ case "unary-postfix": // ++ -- - ~ ! delete new typeof void
+ case "unary-prefix":
+ return 2;
+ case "var":
+ case "binary":
+ switch (ast[1]) {
+ case "*":
+ case "/":
+ case "%":
+ return 3;
+ case "+":
+ case "-":
+ return 4;
+ case "<<":
+ case ">>":
+ case ">>>":
+ return 5;
+ case "<":
+ case "<=":
+ case ">":
+ case ">=":
+ case "instanceof":
+ return 6;
+ case "==":
+ case "!=":
+ case "===":
+ case "!==":
+ return 7;
+ case "&":
+ return 8;
+ case "^":
+ return 9;
+ case "|":
+ return 10;
+ case "&&":
+ return 11;
+ case "||":
+ return 12;
+ }
+ case "conditional":
+ return 13;
+ case "assign":
+ return 14;
+ case "new":
+ return 15;
+ case "seq":
+ case "stat":
+ case "name":
+ case "object":
+ case "array":
+ case "num":
+ case "regexp":
+ case "string":
+ case "function":
+ case "defun":
+ case "for":
+ case "for-in":
+ case "block":
+ case "while":
+ case "do":
+ case "if":
+ case "break":
+ case "continue":
+ case "return":
+ case "throw":
+ case "try":
+ case "switch":
+ return 0;
+ default:
+ return 100; // the lowest
+ }
+ }
+
+ var CodeWriter = function (indent) {
+ this._indent = indent || " ";
+ this._indentLevel = 0;
+
+ this.lines = [];
+ }
+ CodeWriter.prototype = {
+ write: function (str) {
+ if (str === undefined) return;
+
+ if (this.lines.length == 0) {
+ this.lines.push("");
+ }
+
+ this.lines[this.lines.length - 1] += str;
+ return this;
+ },
+
+ writeLine: function () {
+ this.write.apply(this, arguments);
+ this.lines.push("");
+ return this;
+ },
+
+ writeIndents: function () {
+ var indents = new Array(this._indentLevel);
+ for (var i = 0; i < this._indentLevel; i++) {
+ indents[i] = this._indent;
+ }
+
+ this.write(indents.join(""));
+ return this;
+ },
+
+ addIndentLevel: function (diff) {
+ this._indentLevel += diff;
+ return this;
+ }
+ };
+
+ var SeedProvider = function () {
+ this._seeds = {};
+ }
+ SeedProvider.prototype.next = function (key) {
+ var value = this._seeds[key];
+ if (value == undefined) {
+ this._seeds[key] = 0;
+ return 0;
+ } else {
+ this._seeds[key] = ++value;
+ return value;
+ }
+ }
+
+ function isJscexPattern(ast) {
+ if (ast[0] != "call") return false;
+
+ var evalName = ast[1];
+ if (evalName[0] != "name" || evalName[1] != "eval") return false;
+
+ var compileCall = ast[2][0];
+ if (!compileCall || compileCall[0] != "call") return false;
+
+ var compileMethod = compileCall[1];
+ if (!compileMethod || compileMethod[0] != "dot" || compileMethod[2] != "compile") return false;
+
+ var jscexName = compileMethod[1];
+ if (!jscexName || jscexName[0] != "name" || jscexName[1] != compile.rootName) return false;
+
+ var builder = compileCall[2][0];
+ if (!builder || builder[0] != "string") return false;
+
+ var func = compileCall[2][1];
+ if (!func || func[0] != "function") return false;
+
+ return true;
+ }
+
+ function compileJscexPattern(ast, seedProvider, codeWriter, commentWriter) {
+
+ var builderName = ast[2][0][2][0][1];
+ var funcAst = ast[2][0][2][1];
+
+ var jscexTreeGenerator = new JscexTreeGenerator(builderName, seedProvider);
+ var jscexAst = jscexTreeGenerator.generate(funcAst);
+
+ commentWriter.write(builderName + " << ");
+ var codeGenerator = new CodeGenerator(builderName, seedProvider, codeWriter, commentWriter);
+ codeGenerator.generate(funcAst[2], jscexAst);
+ }
+
+ var JscexTreeGenerator = function (builderName, seedProvider) {
+ this._binder = Jscex.binders[builderName];
+ this._seedProvider = seedProvider;
+ }
+ JscexTreeGenerator.prototype = {
+
+ generate: function (ast) {
+
+ var params = ast[2], statements = ast[3];
+
+ var rootAst = { type: "delay", stmts: [] };
+
+ this._visitStatements(statements, rootAst.stmts);
+
+ return rootAst;
+ },
+
+ _getBindInfo: function (stmt) {
+
+ var type = stmt[0];
+ if (type == "stat") {
+ var expr = stmt[1];
+ if (expr[0] == "call") {
+ var callee = expr[1];
+ if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
+ return {
+ expression: expr[2][0],
+ argName: "",
+ assignee: null
+ };
+ }
+ } else if (expr[0] == "assign") {
+ var assignee = expr[2];
+ expr = expr[3];
+ if (expr[0] == "call") {
+ var callee = expr[1];
+ if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
+ return {
+ expression: expr[2][0],
+ argName: "_result_$",
+ assignee: assignee
+ };
+ }
+ }
+ }
+ } else if (type == "var") {
+ var defs = stmt[1];
+ if (defs.length == 1) {
+ var item = defs[0];
+ var name = item[0];
+ var expr = item[1];
+ if (expr && expr[0] == "call") {
+ var callee = expr[1];
+ if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
+ return {
+ expression: expr[2][0],
+ argName: name,
+ assignee: null
+ };
+ }
+ }
+ }
+ } else if (type == "return") {
+ var expr = stmt[1];
+ if (expr && expr[0] == "call") {
+ var callee = expr[1];
+ if (callee[0] == "name" && callee[1] == this._binder && expr[2].length == 1) {
+ return {
+ expression: expr[2][0],
+ argName: "_result_$",
+ assignee: "return"
+ };
+ }
+ }
+ }
+
+ return null;
+ },
+
+ _visitStatements: function (statements, stmts, index) {
+ if (arguments.length <= 2) index = 0;
+
+ if (index >= statements.length) {
+ stmts.push({ type: "normal" });
+ return this;
+ }
+
+ var currStmt = statements[index];
+ var bindInfo = this._getBindInfo(currStmt);
+
+ if (bindInfo) {
+ var bindStmt = { type: "bind", info: bindInfo };
+ stmts.push(bindStmt);
+
+ if (bindInfo.assignee != "return") {
+ bindStmt.stmts = [];
+ this._visitStatements(statements, bindStmt.stmts, index + 1);
+ }
+
+ } else {
+ var type = currStmt[0];
+ if (type == "return" || type == "break" || type == "continue" || type == "throw") {
+
+ stmts.push({ type: type, stmt: currStmt });
+
+ } else if (type == "if" || type == "try" || type == "for" || type == "do"
+ || type == "while" || type == "switch" || type == "for-in") {
+
+ var newStmt = this._visit(currStmt);
+
+ if (newStmt.type == "raw") {
+ stmts.push(newStmt);
+ this._visitStatements(statements, stmts, index + 1);
+ } else {
+ var isLast = (index == statements.length - 1);
+ if (isLast) {
+ stmts.push(newStmt);
+ } else {
+
+ var combineStmt = {
+ type: "combine",
+ first: { type: "delay", stmts: [newStmt] },
+ second: { type: "delay", stmts: [] }
+ };
+ stmts.push(combineStmt);
+
+ this._visitStatements(statements, combineStmt.second.stmts, index + 1);
+ }
+ }
+
+ } else {
+
+ stmts.push({ type: "raw", stmt: currStmt });
+
+ this._visitStatements(statements, stmts, index + 1);
+ }
+ }
+
+ return this;
+ },
+
+ _visit: function (ast) {
+
+ var type = ast[0];
+
+ function throwUnsupportedError() {
+ throw new Error('"' + type + '" is not currently supported.');
+ }
+
+ var visitor = this._visitors[type];
+
+ if (visitor) {
+ return visitor.call(this, ast);
+ } else {
+ throwUnsupportedError();
+ }
+ },
+
+ _visitBody: function (ast, stmts) {
+ if (ast[0] == "block") {
+ this._visitStatements(ast[1], stmts);
+ } else {
+ this._visitStatements([ast], stmts);
+ }
+ },
+
+ _noBinding: function (stmts) {
+ switch (stmts[stmts.length - 1].type) {
+ case "normal":
+ case "return":
+ case "break":
+ case "throw":
+ case "continue":
+ return true;
+ }
+
+ return false;
+ },
+
+ _collectCaseStatements: function (cases, index) {
+ var res = [];
+
+ for (var i = index; i < cases.length; i++) {
+ var rawStmts = cases[i][1];
+ for (var j = 0; j < rawStmts.length; j++) {
+ if (rawStmts[j][0] == "break") {
+ return res
+ }
+
+ res.push(rawStmts[j]);
+ }
+ }
+
+ return res;
+ },
+
+ _visitors: {
+
+ "for": function (ast) {
+ var bodyStmts = [];
+ var body = ast[4];
+ this._visitBody(body, bodyStmts);
+
+ if (this._noBinding(bodyStmts)) {
+ return { type: "raw", stmt: ast };
+ }
+
+ var delayStmt = { type: "delay", stmts: [] };
+
+ var setup = ast[1];
+ if (setup) {
+ delayStmt.stmts.push({ type: "raw", stmt: setup });
+ }
+
+ var forStmt = { type: "for", bodyStmt: { type: "delay", stmts: bodyStmts } };
+ delayStmt.stmts.push(forStmt);
+
+ var condition = ast[2];
+ if (condition) {
+ forStmt.condition = condition;
+ }
+
+ var update = ast[3];
+ if (update) {
+ forStmt.update = update;
+ }
+
+ return delayStmt;
+ },
+
+ "for-in": function (ast) {
+
+ var body = ast[4];
+
+ var bodyStmts = [];
+ this._visitBody(body, bodyStmts);
+
+ if (this._noBinding(bodyStmts)) {
+ return { type: "raw", stmt: ast };
+ }
+
+ var forInStmt = { type: "for-in", bodyStmts: bodyStmts, obj: ast[3] };
+
+ var argName = ast[2][1]; // ast[2] == ["name", m]
+ if (ast[1][0] == "var") {
+ forInStmt.argName = argName;
+ } else {
+ var keyVar = "_forInKey_$" + this._seedProvider.next("forInKey");
+ forInStmt.argName = keyVar;
+ forInStmt.bodyStmts.unshift({
+ type: "raw",
+ stmt: Jscex.parse(argName + " = " + keyVar + ";")[1][0]
+ });
+ }
+
+ return forInStmt;
+ },
+
+ "while": function (ast) {
+
+ var bodyStmts = [];
+ var body = ast[2];
+ this._visitBody(body, bodyStmts);
+
+ if (this._noBinding(bodyStmts)) {
+ return { type: "raw", stmt: ast }
+ }
+
+ var loopStmt = { type: "while", bodyStmt: { type: "delay", stmts: bodyStmts } };
+
+ var condition = ast[1];
+ loopStmt.condition = condition;
+
+ return loopStmt;
+ },
+
+ "do": function (ast) {
+
+ var bodyStmts = [];
+ var body = ast[2];
+ this._visitBody(body, bodyStmts);
+
+ if (this._noBinding(bodyStmts)) {
+ return { type: "raw", stmt: ast };
+ }
+
+ var doStmt = {
+ type: "do",
+ bodyStmt: { type: "delay", stmts: bodyStmts },
+ condition: ast[1]
+ };
+
+ return doStmt;
+ },
+
+ "switch": function (ast) {
+ var noBinding = true;
+
+ var switchStmt = { type: "switch", item: ast[1], caseStmts: [] };
+
+ var cases = ast[2];
+ for (var i = 0; i < cases.length; i++) {
+ var caseStmt = { item: cases[i][0], stmts: [] };
+ switchStmt.caseStmts.push(caseStmt);
+
+ var statements = this._collectCaseStatements(cases, i);
+ this._visitStatements(statements, caseStmt.stmts);
+ noBinding = noBinding && this._noBinding(caseStmt.stmts);
+ }
+
+ if (noBinding) {
+ return { type: "raw", stmt: ast };
+ } else {
+ return switchStmt;
+ }
+ },
+
+ "if": function (ast) {
+
+ var noBinding = true;
+
+ var ifStmt = { type: "if", conditionStmts: [] };
+
+ var currAst = ast;
+ while (true) {
+ var condition = currAst[1];
+ var condStmt = { cond: condition, stmts: [] };
+ ifStmt.conditionStmts.push(condStmt);
+
+ var thenPart = currAst[2];
+ this._visitBody(thenPart, condStmt.stmts);
+
+ noBinding = noBinding && this._noBinding(condStmt.stmts);
+
+ var elsePart = currAst[3];
+ if (elsePart && elsePart[0] == "if") {
+ currAst = elsePart;
+ } else {
+ break;
+ }
+ }
+
+ var elsePart = currAst[3];
+ if (elsePart) {
+ ifStmt.elseStmts = [];
+
+ this._visitBody(elsePart, ifStmt.elseStmts);
+
+ noBinding = noBinding && this._noBinding(ifStmt.elseStmts);
+ }
+
+ if (noBinding) {
+ return { type: "raw", stmt: ast };
+ } else {
+ return ifStmt;
+ }
+ },
+
+ "try": function (ast, stmts) {
+
+ var bodyStmts = [];
+ var bodyStatements = ast[1];
+ this._visitStatements(bodyStatements, bodyStmts);
+
+ var noBinding = this._noBinding(bodyStmts)
+
+ var tryStmt = { type: "try", bodyStmt: { type: "delay", stmts: bodyStmts } };
+
+ var catchClause = ast[2];
+ if (catchClause) {
+ var exVar = catchClause[0];
+ tryStmt.exVar = exVar;
+ tryStmt.catchStmts = [];
+
+ this._visitStatements(catchClause[1], tryStmt.catchStmts);
+
+ noBinding = noBinding && this._noBinding(tryStmt.catchStmts);
+ }
+
+ var finallyStatements = ast[3];
+ if (finallyStatements) {
+ tryStmt.finallyStmt = { type: "delay", stmts: [] };
+
+ this._visitStatements(finallyStatements, tryStmt.finallyStmt.stmts);
+
+ noBinding = noBinding && this._noBinding(tryStmt.finallyStmt.stmts);
+ }
+
+ if (noBinding) {
+ return { type: "raw", stmt: ast };
+ } else {
+ return tryStmt;
+ }
+ }
+ }
+ }
+
+ var CodeGenerator = function (builderName, seedProvider, codeWriter, commentWriter) {
+ this._builderName = builderName;
+ this._binder = Jscex.binders[builderName];
+ this._seedProvider = seedProvider;
+
+ this._codeWriter = codeWriter;
+ this._commentWriter = commentWriter;
+ }
+ CodeGenerator.prototype = {
+
+ _code: function () {
+ this._codeWriter.write.apply(this._codeWriter, arguments);
+ return this;
+ },
+
+ _codeLine: function () {
+ this._codeWriter.writeLine.apply(this._codeWriter, arguments);
+ return this;
+ },
+
+ _codeIndents: function () {
+ this._codeWriter.writeIndents();
+ return this;
+ },
+
+ _codeIndentLevel: function (diff) {
+ this._codeWriter.addIndentLevel(diff);
+ return this;
+ },
+
+ _comment: function () {
+ this._commentWriter.write.apply(this._commentWriter, arguments);
+ return this;
+ },
+
+ _commentLine: function () {
+ this._commentWriter.writeLine.apply(this._commentWriter, arguments);
+ return this;
+ },
+
+ _commentIndents: function () {
+ this._commentWriter.writeIndents();
+ return this;
+ },
+
+ _commentIndentLevel: function (diff) {
+ this._commentWriter.addIndentLevel(diff);
+ return this;
+ },
+
+ _both: function () {
+ this._codeWriter.write.apply(this._codeWriter, arguments);
+ this._commentWriter.write.apply(this._commentWriter, arguments);
+
+ return this;
+ },
+
+ _bothLine: function () {
+ this._codeWriter.writeLine.apply(this._codeWriter, arguments);
+ this._commentWriter.writeLine.apply(this._commentWriter, arguments);
+
+ return this;
+ },
+
+ _bothIndents: function () {
+ this._codeWriter.writeIndents();
+ this._commentWriter.writeIndents();
+
+ return this;
+ },
+
+ _bothIndentLevel: function (diff) {
+ this._codeWriter.addIndentLevel(diff);
+ this._commentWriter.addIndentLevel(diff);
+
+ return this;
+ },
+
+ _newLine: function () {
+ this._codeWriter.writeLine.apply(this._codeWriter, arguments);
+ this._commentWriter.writeLine(); // To Remove
+ return this;
+ },
+
+ generate: function (params, jscexAst) {
+ this._normalMode = false;
+ this._builderVar = "_builder_$" + this._seedProvider.next("builderId");
+
+ this._codeLine("(function (" + params.join(", ") + ") {")._commentLine("function (" + params.join(", ") + ") {");
+ this._bothIndentLevel(1);
+
+ this._codeIndents()._newLine("var " + this._builderVar + " = " + compile.rootName + ".builders[" + stringify(this._builderName) + "];");
+
+ this._codeIndents()._newLine("return " + this._builderVar + ".Start(this,");
+ this._codeIndentLevel(1);
+
+ this._pos = { };
+
+ this._bothIndents()._visitJscex(jscexAst)._newLine();
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._newLine(");");
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()._code("})")._comment("}");
+ },
+
+ _visitJscex: function (ast) {
+ this._jscexVisitors[ast.type].call(this, ast);
+ return this;
+ },
+
+ _visitRaw: function (ast) {
+ var type = ast[0];
+
+ var visitor = this._rawVisitors[type];
+ if (visitor) {
+ visitor.call(this, ast);
+ } else {
+ throw new Error('"' + type + '" is not currently supported.');
+ }
+
+ return this;
+ },
+
+ _visitJscexStatements: function (statements) {
+ for (var i = 0; i < statements.length; i++) {
+ var stmt = statements[i];
+
+ if (stmt.type == "raw" || stmt.type == "if" || stmt.type == "switch") {
+ this._bothIndents()._visitJscex(stmt)._newLine();
+ } else if (stmt.type == "delay") {
+ this._visitJscexStatements(stmt.stmts);
+ } else {
+ this._bothIndents()._code("return ")._visitJscex(stmt)._newLine(";");
+ }
+ }
+ },
+
+ _visitRawStatements: function (statements) {
+ for (var i = 0; i < statements.length; i++) {
+ var s = statements[i];
+
+ this._bothIndents()._visitRaw(s)._bothLine();
+
+ switch (s[0]) {
+ case "break":
+ case "return":
+ case "continue":
+ case "throw":
+ return;
+ }
+ }
+ },
+
+ _visitRawBody: function (body) {
+ if (body[0] == "block") {
+ this._visitRaw(body);
+ } else {
+ this._bothLine();
+ this._bothIndentLevel(1);
+
+ this._bothIndents()._visitRaw(body);
+ this._bothIndentLevel(-1);
+ }
+
+ return this;
+ },
+
+ _visitRawFunction: function (ast) {
+ var funcName = ast[1] || "";
+ var args = ast[2];
+ var statements = ast[3];
+
+ this._bothLine("function " + funcName + "(" + args.join(", ") + ") {")
+ this._bothIndentLevel(1);
+
+ var currInFunction = this._pos.inFunction;
+ this._pos.inFunction = true;
+
+ this._visitRawStatements(statements);
+ this._bothIndentLevel(-1);
+
+ this._pos.inFunction = currInFunction;
+
+ this._bothIndents()._both("}");
+ },
+
+ _jscexVisitors: {
+ "delay": function (ast) {
+ if (ast.stmts.length == 1) {
+ var subStmt = ast.stmts[0];
+ switch (subStmt.type) {
+ case "delay":
+ case "combine":
+ case "normal":
+ case "break":
+ case "continue":
+ case "for":
+ case "for-in":
+ case "while":
+ case "do":
+ case "try":
+ this._visitJscex(subStmt);
+ return;
+ case "return":
+ if (!subStmt.stmt[1]) {
+ this._visitJscex(subStmt);
+ return;
+ }
+ }
+ }
+
+ this._newLine(this._builderVar + ".Delay(function () {");
+ this._codeIndentLevel(1);
+
+ this._visitJscexStatements(ast.stmts);
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._code("})");
+ },
+
+ "combine": function (ast) {
+ this._newLine(this._builderVar + ".Combine(");
+ this._codeIndentLevel(1);
+
+ this._bothIndents()._visitJscex(ast.first)._newLine(",");
+ this._bothIndents()._visitJscex(ast.second)._newLine();
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._code(")");
+ },
+
+ "for": function (ast) {
+ if (ast.condition) {
+ this._codeLine(this._builderVar + ".For(function () {")
+ ._commentLine("for (");
+ this._codeIndentLevel(1);
+
+ this._bothIndents()
+ ._code("return ")
+ ._comment("; ")
+ ._visitRaw(ast.condition)
+ ._newLine(";");
+ this._codeIndentLevel(-1);
+
+ this._bothIndents()._code("}, ");
+ } else {
+ this._code(this._builderVar + ".For(null, ")
+ ._comment("for (; ");
+ }
+
+ if (ast.update) {
+ this._newLine("function () {");
+ this._codeIndentLevel(1);
+
+ this._bothIndents()
+ ._comment("; ")
+ ._visitRaw(ast.update)
+ ._codeLine(";")
+ ._commentLine(") {");
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._newLine("},");
+ } else {
+ this._codeLine("null,")._commentLine("; ) {");
+ }
+ this._bothIndentLevel(1);
+
+ this._bothIndents()._visitJscex(ast.bodyStmt)._newLine();
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()._code(")")._comment("}");
+ },
+
+ "for-in": function (ast) {
+ this._code(this._builderVar + ".ForIn(")
+ ._comment("for (var " + ast.argName + " in ")
+ ._visitRaw(ast.obj)
+ ._codeLine(", function (" + ast.argName + ") {")
+ ._commentLine(") {");
+ this._bothIndentLevel(1);
+
+ this._visitJscexStatements(ast.bodyStmts);
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()._code("})")._comment("}");
+ },
+
+ "while": function (ast) {
+ this._newLine(this._builderVar + ".While(function () {");
+ this._codeIndentLevel(1);
+
+ this._bothIndents()
+ ._code("return ")
+ ._comment("while (")
+ ._visitRaw(ast.condition)
+ ._codeLine(";")
+ ._commentLine(") {");
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._newLine("},");
+ this._bothIndentLevel(1);
+
+ this._bothIndents()._visitJscex(ast.bodyStmt)._newLine();
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()._code(")")._comment("}");
+ },
+
+ "do": function (ast) {
+ this._codeLine(this._builderVar + ".Do(")._commentLine("do {");
+ this._bothIndentLevel(1);
+
+ this._bothIndents()._visitJscex(ast.bodyStmt)._newLine(",");
+ this._commentIndentLevel(-1);
+
+ this._codeIndents()._newLine("function () {");
+ this._codeIndentLevel(1);
+
+ this._bothIndents()
+ ._code("return ")
+ ._comment("} while (")
+ ._visitRaw(ast.condition)
+ ._codeLine(";")
+ ._commentLine(");");
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._newLine("}");
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._code(")");
+ },
+
+ "raw": function (ast) {
+ this._visitRaw(ast.stmt, true);
+ },
+
+ "bind": function (ast) {
+ var info = ast.info;
+
+ var commentPrefix = "";
+ if (info.assignee == "return") {
+ commentPrefix = "return ";
+ } else if (info.argName != "") {
+ commentPrefix = "var " + info.argName + " = ";
+ }
+
+ this._code(this._builderVar + ".Bind(")._comment(commentPrefix + this._binder + "(")._visitRaw(info.expression)._comment(");")._newLine(", function (" + info.argName + ") {");
+ this._codeIndentLevel(1);
+
+ if (info.assignee == "return") {
+ this._codeIndents()
+ ._newLine("return " + this._builderVar + ".Return(" + info.argName + ");");
+ } else {
+ if (info.assignee) {
+ this._bothIndents()
+ ._visitRaw(info.assignee)._bothLine(" = " + info.argName + ";");
+ }
+
+ this._visitJscexStatements(ast.stmts);
+ }
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()
+ ._code("})");
+ },
+
+ "if": function (ast) {
+
+ for (var i = 0; i < ast.conditionStmts.length; i++) {
+ var stmt = ast.conditionStmts[i];
+
+ this._both("if (")._visitRaw(stmt.cond)._bothLine(") {");
+ this._bothIndentLevel(1);
+
+ this._visitJscexStatements(stmt.stmts);
+ this._bothIndentLevel(-1);
+
+ if (i < ast.conditionStmts.length - 1 || ast.elseStmts) {
+ this._bothIndents()._both("} else ");
+ } else {
+ this._bothIndents()._code("} else ")._comment("}");
+ }
+ }
+
+ if (ast.elseStmts) {
+ this._bothLine("{");
+ this._bothIndentLevel(1);
+ } else {
+ this._newLine("{");
+ this._codeIndentLevel(1);
+ }
+
+ if (ast.elseStmts) {
+ this._visitJscexStatements(ast.elseStmts);
+ } else {
+ this._codeIndents()
+ ._newLine("return " + this._builderVar + ".Normal();");
+ }
+
+ if (ast.elseStmts) {
+ this._bothIndentLevel(-1);
+ } else {
+ this._codeIndentLevel(-1);
+ }
+
+ if (ast.elseStmts) {
+ this._bothIndents()
+ ._both("}");
+ } else {
+ this._codeIndents()
+ ._code("}");
+ }
+ },
+
+ "switch": function (ast) {
+ this._both("switch (")._visitRaw(ast.item)._bothLine(") {");
+ this._bothIndentLevel(1);
+
+ for (var i = 0; i < ast.caseStmts.length; i++) {
+ var caseStmt = ast.caseStmts[i];
+
+ if (caseStmt.item) {
+ this._bothIndents()
+ ._both("case ")._visitRaw(caseStmt.item)._bothLine(":");
+ } else {
+ this._bothIndents()._bothLine("default:");
+ }
+ this._bothIndentLevel(1);
+
+ this._visitJscexStatements(caseStmt.stmts);
+ this._bothIndentLevel(-1);
+ }
+
+ this._bothIndents()._code("}");
+ },
+
+ "try": function (ast) {
+ this._codeLine(this._builderVar + ".Try(")._commentLine("try {");
+ this._bothIndentLevel(1);
+
+ this._bothIndents()._visitJscex(ast.bodyStmt)._newLine(",");
+ this._commentIndentLevel(-1);
+
+ if (ast.catchStmts) {
+ this._bothIndents()
+ ._codeLine("function (" + ast.exVar + ") {")
+ ._commentLine("} catch (" + ast.exVar + ") {");
+ this._bothIndentLevel(1);
+
+ this._visitJscexStatements(ast.catchStmts);
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()._codeLine("},");
+ if (ast.finallyStmt) {
+ this._commentLine("} finally {");
+ } else {
+ this._commentLine("}");
+ }
+ } else {
+ this._bothIndents()._codeLine("null,")._commentLine("} finally {");
+ }
+
+ if (ast.finallyStmt) {
+ this._commentIndentLevel(1);
+ this._bothIndents()._visitJscex(ast.finallyStmt)._newLine();
+ this._commentIndentLevel(-1);
+ } else {
+ this._codeIndents()._newLine("null");
+ }
+ this._codeIndentLevel(-1);
+
+ this._codeIndents()._code(")");
+ if (ast.finallyStmt) {
+ this._commentIndents()._comment("}");
+ }
+ },
+
+ "normal": function (ast) {
+ this._code(this._builderVar + ".Normal()");
+ },
+
+ "throw": function (ast) {
+ this
+ ._code(this._builderVar + ".Throw(")
+ ._comment("throw ")
+ ._visitRaw(ast.stmt[1])
+ ._code(")")._comment(";");
+ },
+
+ "break": function (ast) {
+ this._code(this._builderVar + ".Break()")._comment("break;");
+ },
+
+ "continue": function (ast) {
+ this._code(this._builderVar + ".Continue()")._comment("continue;");
+ },
+
+ "return": function (ast) {
+ this._code(this._builderVar + ".Return(")._comment("return");
+ if (ast.stmt[1]) {
+ this._comment(" ")._visitRaw(ast.stmt[1]);
+ }
+
+ this._code(")")._comment(";");
+ }
+ },
+
+ _rawVisitors: {
+ "var": function (ast) {
+ this._both("var ");
+
+ var items = ast[1];
+ for (var i = 0; i < items.length; i++) {
+ this._both(items[i][0]);
+ if (items[i].length > 1) {
+ this._both(" = ")._visitRaw(items[i][1]);
+ }
+ if (i < items.length - 1) this._both(", ");
+ }
+
+ this._both(";");
+ },
+
+ "seq": function (ast, noBracket) {
+ var left = ast[1];
+ var right = ast[2];
+
+ if (!noBracket) this._both("(");
+
+ this._visitRaw(left);
+ this._both(", ");
+
+ if (right[0] == "seq") {
+ arguments.callee.call(this, right, true);
+ } else {
+ this._visitRaw(right);
+ }
+
+ if (!noBracket) this._both(")");
+ },
+
+ "binary": function (ast) {
+ var op = ast[1], left = ast[2], right = ast[3];
+
+ if (getPrecedence(ast) < getPrecedence(left)) {
+ this._both("(")._visitRaw(left)._both(") ");
+ } else {
+ this._visitRaw(left)._both(" ");
+ }
+
+ this._both(op);
+
+ if (getPrecedence(ast) <= getPrecedence(right)) {
+ this._both(" (")._visitRaw(right)._both(")");
+ } else {
+ this._both(" ")._visitRaw(right);
+ }
+ },
+
+ "sub": function (ast) {
+ var prop = ast[1], index = ast[2];
+
+ if (getPrecedence(ast) < getPrecedence(prop)) {
+ this._both("(")._visitRaw(prop)._both(")[")._visitRaw(index)._both("]");
+ } else {
+ this._visitRaw(prop)._both("[")._visitRaw(index)._both("]");
+ }
+ },
+
+ "unary-postfix": function (ast) {
+ var op = ast[1];
+ var item = ast[2];
+
+ if (getPrecedence(ast) <= getPrecedence(item)) {
+ this._both("(")._visitRaw(item)._both(")");
+ } else {
+ this._visitRaw(item);
+ }
+
+ this._both(" " + op);
+ },
+
+ "unary-prefix": function (ast) {
+ var op = ast[1];
+ var item = ast[2];
+
+ this._both(op + " ");
+
+ if (getPrecedence(ast) < getPrecedence(item)) {
+ this._both("(")._visitRaw(item)._both(")");
+ } else {
+ this._visitRaw(item);
+ }
+ },
+
+ "assign": function (ast) {
+ var op = ast[1];
+ var name = ast[2];
+ var value = ast[3];
+
+ if (name[0] == "assign") {
+ this._both("(")._visitRaw(name)._both(")");
+ } else {
+ this._visitRaw(name);
+ }
+
+ if ((typeof op) == "string") {
+ this._both(" " + op + "= ");
+ } else {
+ this._both(" = ");
+ }
+
+ this._visitRaw(value);
+ },
+
+ "stat": function (ast) {
+ this._visitRaw(ast[1])._both(";");
+ },
+
+ "dot": function (ast) {
+ var left = ast[1];
+ var right = ast[2];
+
+ if (getPrecedence(ast) < getPrecedence(left)) {
+ this._both("(")._visitRaw(left)._both(").")._both(right);
+ } else {
+ this._visitRaw(left)._both(".")._both(right);
+ }
+ },
+
+ "new": function (ast) {
+ var ctor = ast[1];
+
+ this._both("new ")._visitRaw(ctor)._both("(");
+
+ var args = ast[2];
+ for (var i = 0, len = args.length; i < len; i++) {
+ this._visitRaw(args[i]);
+ if (i < len - 1) this._both(", ");
+ }
+
+ this._both(")");
+ },
+
+ "call": function (ast) {
+
+ if (isJscexPattern(ast)) {
+ compileJscexPattern(ast, this._seedProvider, this._codeWriter, this._commentWriter);
+ } else {
+ var caller = ast[1];
+
+ var invalidBind = (caller[0] == "name") && (caller[1] == this._binder);
+ // throw?
+
+ if (getPrecedence(ast) < getPrecedence(caller)) {
+ this._both("(")._visitRaw(caller)._both(")");
+ } else {
+ this._visitRaw(caller);
+ }
+
+ this._both("(");
+
+ var args = ast[2];
+ for (var i = 0; i < args.length; i++) {
+ this._visitRaw(args[i]);
+ if (i < args.length - 1) this._both(", ");
+ }
+
+ this._both(")");
+ }
+ },
+
+ "name": function (ast) {
+ this._both(ast[1]);
+ },
+
+ "object": function (ast) {
+ var items = ast[1];
+ if (items.length <= 0) {
+ this._both("{ }");
+ } else {
+ this._bothLine("{");
+ this._bothIndentLevel(1);
+
+ for (var i = 0; i < items.length; i++) {
+ this._bothIndents()
+ ._both(stringify(items[i][0]) + ": ")
+ ._visitRaw(items[i][1]);
+
+ if (i < items.length - 1) {
+ this._bothLine(",");
+ } else {
+ this._bothLine("");
+ }
+ }
+
+ this._bothIndentLevel(-1);
+ this._bothIndents()._both("}");
+ }
+ },
+
+ "array": function (ast) {
+ this._both("[");
+
+ var items = ast[1];
+ for (var i = 0; i < items.length; i++) {
+ this._visitRaw(items[i]);
+ if (i < items.length - 1) this._both(", ");
+ }
+
+ this._both("]");
+ },
+
+ "num": function (ast) {
+ this._both(ast[1]);
+ },
+
+ "regexp": function (ast) {
+ this._both("/" + ast[1] + "/" + ast[2]);
+ },
+
+ "string": function (ast) {
+ this._both(stringify(ast[1]));
+ },
+
+ "function": function (ast) {
+ this._visitRawFunction(ast);
+ },
+
+ "defun": function (ast) {
+ this._visitRawFunction(ast);
+ },
+
+ "for": function (ast) {
+ this._both("for (");
+
+ var setup = ast[1];
+ if (setup) {
+ this._visitRaw(setup);
+ if (setup[0] != "var") {
+ this._both("; ");
+ } else {
+ this._both(" ");
+ }
+ } else {
+ this._both("; ");
+ }
+
+ var condition = ast[2];
+ if (condition) this._visitRaw(condition);
+ this._both("; ");
+
+ var update = ast[3];
+ if (update) this._visitRaw(update);
+ this._both(") ");
+
+ var currInLoop = this._pos.inLoop;
+ this._pos.inLoop = true;
+
+ var body = ast[4];
+ this._visitRawBody(body);
+
+ this._pos.inLoop = currInLoop;
+ },
+
+ "for-in": function (ast) {
+ this._both("for (");
+
+ var declare = ast[1];
+ if (declare[0] == "var") { // declare == ["var", [["m"]]]
+ this._both("var " + declare[1][0][0]);
+ } else {
+ this._visitRaw(declare);
+ }
+
+ this._both(" in ")._visitRaw(ast[3])._both(") ");
+
+ var body = ast[4];
+ this._visitRawBody(body);
+ },
+
+ "block": function (ast) {
+ this._bothLine("{")
+ this._bothIndentLevel(1);
+
+ this._visitRawStatements(ast[1]);
+ this._bothIndentLevel(-1);
+
+ this._bothIndents()
+ ._both("}");
+ },
+
+ "while": function (ast) {
+ var condition = ast[1];
+ var body = ast[2];
+
+ var currInLoop = this._pos.inLoop;
+ this._pos.inLoop = true;
+
+ this._both("while (")._visitRaw(condition)._both(") ")._visitRawBody(body);
+
+ this._pos.inLoop = currInLoop;
+ },
+
+ "do": function (ast) {
+ var condition = ast[1];
+ var body = ast[2];
+
+ var currInLoop = this._pos.inLoop;
+ this._pos.inLoop = true;
+
+ this._both("do ")._visitRawBody(body);
+
+ this._pos.inLoop = currInLoop;
+
+ if (body[0] == "block") {
+ this._both(" ");
+ } else {
+ this._bothLine()
+ ._bothIndents();
+ }
+
+ this._both("while (")._visitRaw(condition)._both(");");
+ },
+
+ "if": function (ast) {
+ var condition = ast[1];
+ var thenPart = ast[2];
+
+ this._both("if (")._visitRaw(condition)._both(") ")._visitRawBody(thenPart);
+
+ var elsePart = ast[3];
+ if (elsePart) {
+ if (thenPart[0] == "block") {
+ this._both(" ");
+ } else {
+ this._bothLine("")
+ ._bothIndents();
+ }
+
+ if (elsePart[0] == "if") {
+ this._both("else ")._visitRaw(elsePart);
+ } else {
+ this._both("else ")._visitRawBody(elsePart);
+ }
+ }
+ },
+
+ "break": function (ast) {
+ if (this._pos.inLoop || this._pos.inSwitch) {
+ this._both("break;");
+ } else {
+ this._code("return ")._visitJscex({ type: "break", stmt: ast })._code(";");
+ }
+ },
+
+ "continue": function (ast) {
+ if (this._pos.inLoop) {
+ this._both("continue;");
+ } else {
+ this._code("return ")._visitJscex({ type: "continue", stmt: ast })._code(";");
+ }
+ },
+
+ "return": function (ast) {
+ if (this._pos.inFunction) {
+ this._both("return");
+ var value = ast[1];
+ if (value) this._both(" ")._visitRaw(value);
+ this._both(";");
+ } else {
+ this._code("return ")._visitJscex({ type: "return", stmt: ast })._code(";");
+ }
+ },
+
+ "throw": function (ast) {
+ var pos = this._pos;
+ if (pos.inTry || pos.inFunction) {
+ this._both("throw ")._visitRaw(ast[1])._both(";");
+ } else {
+ this._code("return ")._visitJscex({ type: "throw", stmt: ast })._code(";");
+ }
+ },
+
+ "conditional": function (ast) {
+ this._both("(")._visitRaw(ast[1])._both(") ? (")._visitRaw(ast[2])._both(") : (")._visitRaw(ast[3])._both(")");
+ },
+
+ "try": function (ast) {
+
+ this._bothLine("try {");
+ this._bothIndentLevel(1);
+
+ var currInTry = this._pos.inTry;
+ this._pos.inTry = true;
+
+ this._visitRawStatements(ast[1]);
+ this._bothIndentLevel(-1);
+
+ this._pos.inTry = currInTry;
+
+ var catchClause = ast[2];
+ var finallyStatements = ast[3];
+
+ if (catchClause) {
+ this._bothIndents()
+ ._bothLine("} catch (" + catchClause[0] + ") {")
+ this._bothIndentLevel(1);
+
+ this._visitRawStatements(catchClause[1]);
+ this._bothIndentLevel(-1);
+ }
+
+ if (finallyStatements) {
+ this._bothIndents()
+ ._bothLine("} finally {");
+ this._bothIndentLevel(1);
+
+ this._visitRawStatements(finallyStatements);
+ this._bothIndentLevel(-1);
+ }
+
+ this._bothIndents()
+ ._both("}");
+ },
+
+ "switch": function (ast) {
+ this._both("switch (")._visitRaw(ast[1])._bothLine(") {");
+ this._bothIndentLevel(1);
+
+ var currInSwitch = this._pos.inSwitch;
+ this._pos.inSwitch = true;
+
+ var cases = ast[2];
+ for (var i = 0; i < cases.length; i++) {
+ var c = cases[i];
+ this._bothIndents();
+
+ if (c[0]) {
+ this._both("case ")._visitRaw(c[0])._bothLine(":");
+ } else {
+ this._bothLine("default:");
+ }
+ this._bothIndentLevel(1);
+
+ this._visitRawStatements(c[1]);
+ this._bothIndentLevel(-1);
+ }
+ this._bothIndentLevel(-1);
+
+ this._pos.inSwitch = currInSwitch;
+
+ this._bothIndents()
+ ._both("}");
+ }
+ }
+ };
+
+ var merge = function (commentLines, codeLines) {
+ var length = commentLines.length;
+
+ var maxShift = 0;
+
+ for (var i = 0; i < length; i++) {
+ var matches = codeLines[i].match(" +");
+ var spaceLength = matches ? matches[0].length : 0;
+
+ var shift = commentLines[i].length - spaceLength + 10;
+ if (shift > maxShift) {
+ maxShift = shift;
+ }
+ }
+
+ var shiftBuffer = new Array(maxShift);
+ for (var i = 0; i < maxShift; i++) {
+ shiftBuffer[i] = " ";
+ }
+
+ var shiftSpaces = shiftBuffer.join("");
+
+ var buffer = [];
+ for (var i = 0; i < length; i++) {
+ var comment = commentLines[i];
+ if (comment.replace(/ +/g, "").length > 0) {
+ comment = "/* " + comment + " */ ";
+ }
+
+ var code = shiftSpaces + codeLines[i];
+
+ buffer.push(comment);
+ buffer.push(code.substring(comment.length));
+ buffer.push("\n");
+ }
+
+ return buffer.join("");
+ }
+
+ var compile = function (builderName, func, separateCodeAndComment) {
+ var funcCode = func.toString();
+ var evalCode = "eval(" + compile.rootName + ".compile(" + stringify(builderName) + ", " + funcCode + "))"
+ var evalCodeAst = Jscex.parse(evalCode);
+
+ var codeWriter = new CodeWriter();
+ var commentWriter = new CodeWriter();
+
+ // [ "toplevel", [ [ "stat", [ "call", ... ] ] ] ]
+ var evalAst = evalCodeAst[1][0][1];
+ compileJscexPattern(evalAst, new SeedProvider(), codeWriter, commentWriter);
+
+ if (separateCodeAndComment) {
+ return {
+ code: codeWriter.lines.join("\n"),
+ codeLines: codeWriter.lines,
+ comment: commentWriter.lines.join("\n"),
+ commentLines: commentWriter.lines
+ };
+ } else {
+ var newCode = merge(commentWriter.lines, codeWriter.lines);
+ Jscex.logger.debug("// Original: \r\n" + funcCode + "\r\n\r\n// Jscexified: \r\n" + newCode + "\r\n");
+
+ return codeGenerator(newCode);
+ }
+ }
+
+ compile.rootName = "Jscex";
+
+ // CommonJS
+ var isCommonJS = !!(typeof require === "function" && typeof module !== "undefined" && module.exports);
+ // CommonJS AMD
+ var isAmd = !!(typeof require === "function" && typeof define === "function" && define.amd);
+
+ var defineModule = function () {
+ Jscex.define({
+ name: "jit",
+ version: "0.6.6",
+ exports: isCommonJS && module.exports,
+ require: isCommonJS && require,
+ autoloads: [ "parser" ],
+ dependencies: { parser: "~0.6.5" },
+ init: function () {
+ Jscex.compile = compile;
+ }
+ });
+ }
+
+ if (isCommonJS) {
+ try {
+ Jscex = require("./jscex");
+ } catch (ex) {
+ Jscex = require("jscex");
+ }
+
+ defineModule();
+ } else if (isAmd) {
+ require(["jscex"], function (jscex) {
+ Jscex = jscex;
+ defineModule();
+ });
+ } else {
+ var Fn = Function, global = Fn('return this')();
+ if (!global.Jscex) {
+ throw new Error('Missing the root object, please load "jscex" component first.');
+ }
+
+ Jscex = global.Jscex;
+ defineModule();
+ }
+})();