(function () { 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 + '"'; }; })(); // seed defined in global if (typeof __jscex__tempVarSeed === "undefined") { __jscex__tempVarSeed = 0; } var init = function (root) { if (root.modules["jit"]) { return; } function JscexTreeGenerator(binder) { this._binder = binder; this._root = null; } JscexTreeGenerator.prototype = { generate: function (ast) { var params = ast[2], statements = ast[3]; this._root = { type: "delay", stmts: [] }; this._visitStatements(statements, this._root.stmts); return this._root; }, _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 loopStmt = { type: "loop", bodyFirst: false, bodyStmt: { type: "delay", stmts: bodyStmts } }; delayStmt.stmts.push(loopStmt); var condition = ast[2]; if (condition) { loopStmt.condition = condition; } var update = ast[3]; if (update) { loopStmt.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 id = (__jscex__tempVarSeed++); var keysVar = "$$_keys_$$_" + id; var indexVar = "$$_index_$$_" + id; // var memVar = "$$_mem_$$_" + id; var delayStmt = { type: "delay", stmts: [] }; // var members = Jscex._forInKeys(obj); var keysAst = root.parse("var " + keysVar + " = Jscex._forInKeys(obj);")[1][0]; keysAst[1][0][1][2][0] = ast[3]; // replace obj with real AST; delayStmt.stmts.push({ type: "raw", stmt: keysAst }); /* // var members = []; delayStmt.stmts.push({ type: "raw", stmt: uglifyJS.parse("var " + membersVar + " = [];")[1][0] }); // for (var mem in obj) members.push(mem); var keysAst = uglifyJS.parse("for (var " + memVar +" in obj) " + membersVar + ".push(" + memVar + ");")[1][0]; keysAst[3] = ast[3]; // replace the "obj" with real AST. delayStmt.stmts.push({ type : "raw", stmt: keysAst}); */ // var index = 0; delayStmt.stmts.push({ type: "raw", stmt: root.parse("var " + indexVar + " = 0;")[1][0] }); // index < members.length var condition = root.parse(indexVar + " < " + keysVar + ".length")[1][0][1]; // index++ var update = root.parse(indexVar + "++")[1][0][1]; var loopStmt = { type: "loop", bodyFirst: false, update: update, condition: condition, bodyStmt: { type: "delay", stmts: [] } }; delayStmt.stmts.push(loopStmt); var varName = ast[2][1]; // ast[2] == ["name", m] if (ast[1][0] == "var") { loopStmt.bodyStmt.stmts.push({ type: "raw", stmt: root.parse("var " + varName + " = " + keysVar + "[" + indexVar + "];")[1][0] }); } else { loopStmt.bodyStmt.stmts.push({ type: "raw", stmt: root.parse(varName + " = " + keysVar + "[" + indexVar + "];")[1][0] }); } this._visitBody(body, loopStmt.bodyStmt.stmts); return delayStmt; }, "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: "loop", bodyFirst: false, 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 loopStmt = { type: "loop", bodyFirst: true, bodyStmt: { type: "delay", stmts: bodyStmts } }; var condition = ast[1]; loopStmt.condition = condition; return loopStmt; }, "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; } } } } function CodeGenerator(builderName, binder, indent) { this._builderName = builderName; this._binder = binder; this._normalMode = false; this._indent = indent; this._indentLevel = 0; this._builderVar = "$$_builder_$$_" + (__jscex__tempVarSeed++); } CodeGenerator.prototype = { _write: function (s) { this._buffer.push(s); return this; }, _writeLine: function (s) { this._write(s)._write("\n"); return this; }, _writeIndents: function () { for (var i = 0; i < this._indent; i++) { this._write(" "); } for (var i = 0; i < this._indentLevel; i++) { this._write(" "); } return this; }, generate: function (params, jscexAst) { this._buffer = []; this._writeLine("(function (" + params.join(", ") + ") {"); this._indentLevel++; this._writeIndents() ._writeLine("var " + this._builderVar + " = Jscex.builders[" + stringify(this._builderName) + "];"); this._writeIndents() ._writeLine("return " + this._builderVar + ".Start(this,"); this._indentLevel++; this._pos = { }; this._writeIndents() ._visitJscex(jscexAst) ._writeLine(); this._indentLevel--; this._writeIndents() ._writeLine(");"); this._indentLevel--; this._writeIndents() ._write("})"); return this._buffer.join(""); }, _visitJscex: function (ast) { this._jscexVisitors[ast.type].call(this, ast); return this; }, _visitRaw: function (ast) { var type = ast[0]; function throwUnsupportedError() { throw new Error('"' + type + '" is not currently supported.'); } var visitor = this._rawVisitors[type]; if (visitor) { visitor.call(this, ast); } else { throwUnsupportedError(); } 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._writeIndents() ._visitJscex(stmt)._writeLine(); } else if (stmt.type == "delay") { this._visitJscexStatements(stmt.stmts); } else { this._writeIndents() ._write("return ")._visitJscex(stmt)._writeLine(";"); } } }, _visitRawStatements: function (statements) { for (var i = 0; i < statements.length; i++) { var s = statements[i]; this._writeIndents() ._visitRaw(s)._writeLine(); switch (s[0]) { case "break": case "return": case "continue": case "throw": return; } } }, _visitRawBody: function (body) { if (body[0] == "block") { this._visitRaw(body); } else { this._writeLine(); this._indentLevel++; this._writeIndents() ._visitRaw(body); this._indentLevel--; } return this; }, _visitRawFunction: function (ast) { var funcName = ast[1] || ""; var args = ast[2]; var statements = ast[3]; this._writeLine("function " + funcName + "(" + args.join(", ") + ") {") this._indentLevel++; var currInFunction = this._pos.inFunction; this._pos.inFunction = true; this._visitRawStatements(statements); this._indentLevel--; this._pos.inFunction = currInFunction; this._writeIndents() ._write("}"); }, _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 "loop": case "try": this._visitJscex(subStmt); return; case "return": if (!subStmt.stmt[1]) { this._visitJscex(subStmt); return; } } } this._writeLine(this._builderVar + ".Delay(function () {"); this._indentLevel++; this._visitJscexStatements(ast.stmts); this._indentLevel--; this._writeIndents() ._write("})"); }, "combine": function (ast) { this._writeLine(this._builderVar + ".Combine("); this._indentLevel++; this._writeIndents() ._visitJscex(ast.first)._writeLine(","); this._writeIndents() ._visitJscex(ast.second)._writeLine(); this._indentLevel--; this._writeIndents() ._write(")"); }, "loop": function (ast) { this._writeLine(this._builderVar + ".Loop("); this._indentLevel++; if (ast.condition) { this._writeIndents() ._writeLine("function () {"); this._indentLevel++; this._writeIndents() ._write("return ")._visitRaw(ast.condition)._writeLine(";"); this._indentLevel--; this._writeIndents() ._writeLine("},"); } else { this._writeIndents()._writeLine("null,"); } if (ast.update) { this._writeIndents() ._writeLine("function () {"); this._indentLevel++; this._writeIndents() ._visitRaw(ast.update)._writeLine(";"); this._indentLevel--; this._writeIndents() ._writeLine("},"); } else { this._writeIndents()._writeLine("null,"); } this._writeIndents() ._visitJscex(ast.bodyStmt)._writeLine(","); this._writeIndents() ._writeLine(ast.bodyFirst); this._indentLevel--; this._writeIndents() ._write(")"); }, "raw": function (ast) { this._visitRaw(ast.stmt); }, "bind": function (ast) { var info = ast.info; this._write(this._builderVar + ".Bind(")._visitRaw(info.expression)._writeLine(", function (" + info.argName + ") {"); this._indentLevel++; if (info.assignee == "return") { this._writeIndents() ._writeLine("return " + this._builderVar + ".Return(" + info.argName + ");"); } else { if (info.assignee) { this._writeIndents() ._visitRaw(info.assignee)._writeLine(" = " + info.argName + ";"); } this._visitJscexStatements(ast.stmts); } this._indentLevel--; this._writeIndents() ._write("})"); }, "if": function (ast) { for (var i = 0; i < ast.conditionStmts.length; i++) { var stmt = ast.conditionStmts[i]; this._write("if (")._visitRaw(stmt.cond)._writeLine(") {"); this._indentLevel++; this._visitJscexStatements(stmt.stmts); this._indentLevel--; this._writeIndents() ._write("} else "); } this._writeLine("{"); this._indentLevel++; if (ast.elseStmts) { this._visitJscexStatements(ast.elseStmts); } else { this._writeIndents() ._writeLine("return " + this._builderVar + ".Normal();"); } this._indentLevel--; this._writeIndents() ._write("}"); }, "switch": function (ast) { this._write("switch (")._visitRaw(ast.item)._writeLine(") {"); this._indentLevel++; for (var i = 0; i < ast.caseStmts.length; i++) { var caseStmt = ast.caseStmts[i]; if (caseStmt.item) { this._writeIndents() ._write("case ")._visitRaw(caseStmt.item)._writeLine(":"); } else { this._writeIndents()._writeLine("default:"); } this._indentLevel++; this._visitJscexStatements(caseStmt.stmts); this._indentLevel--; } this._writeIndents() ._write("}"); }, "try": function (ast) { this._writeLine(this._builderVar + ".Try("); this._indentLevel++; this._writeIndents() ._visitJscex(ast.bodyStmt)._writeLine(","); if (ast.catchStmts) { this._writeIndents() ._writeLine("function (" + ast.exVar + ") {"); this._indentLevel++; this._visitJscexStatements(ast.catchStmts); this._indentLevel--; this._writeIndents() ._writeLine("},"); } else { this._writeIndents() ._writeLine("null,"); } if (ast.finallyStmt) { this._writeIndents() ._visitJscex(ast.finallyStmt)._writeLine(); } else { this._writeIndents() ._writeLine("null"); } this._indentLevel--; this._writeIndents() ._write(")"); }, "normal": function (ast) { this._write(this._builderVar + ".Normal()"); }, "throw": function (ast) { this._write(this._builderVar + ".Throw(")._visitRaw(ast.stmt[1])._write(")"); }, "break": function (ast) { this._write(this._builderVar + ".Break()"); }, "continue": function (ast) { this._write(this._builderVar + ".Continue()"); }, "return": function (ast) { this._write(this._builderVar + ".Return("); if (ast.stmt[1]) this._visitRaw(ast.stmt[1]); this._write(")"); } }, _rawVisitors: { "var": function (ast) { this._write("var "); var items = ast[1]; for (var i = 0; i < items.length; i++) { this._write(items[i][0]); if (items[i].length > 1) { this._write(" = ")._visitRaw(items[i][1]); } if (i < items.length - 1) this._write(", "); } this._write(";"); }, "seq": function (ast) { for (var i = 1; i < ast.length; i++) { this._visitRaw(ast[i]); if (i < ast.length - 1) this._write(", "); } }, "binary": function (ast) { var op = ast[1], left = ast[2], right = ast[3]; function needBracket(item) { var type = item[0]; return !(type == "num" || type == "name" || type == "dot"); } if (needBracket(left)) { this._write("(")._visitRaw(left)._write(") "); } else { this._visitRaw(left)._write(" "); } this._write(op); if (needBracket(right)) { this._write(" (")._visitRaw(right)._write(")"); } else { this._write(" ")._visitRaw(right); } }, "sub": function (ast) { var prop = ast[1], index = ast[2]; function needBracket() { return !(prop[0] == "name") } if (needBracket()) { this._write("(")._visitRaw(prop)._write(")[")._visitRaw(index)._write("]"); } else { this._visitRaw(prop)._write("[")._visitRaw(index)._write("]"); } }, "unary-postfix": function (ast) { var op = ast[1]; var item = ast[2]; this._visitRaw(item)._write(op); }, "unary-prefix": function (ast) { var op = ast[1]; var item = ast[2]; this._write(op); if (op == "typeof") { this._write("(")._visitRaw(item)._write(")"); } else { this._visitRaw(item); } }, "assign": function (ast) { var op = ast[1]; var name = ast[2]; var value = ast[3]; this._visitRaw(name); if ((typeof op) == "string") { this._write(" " + op + "= "); } else { this._write(" = "); } this._visitRaw(value); }, "stat": function (ast) { this._visitRaw(ast[1])._write(";"); }, "dot": function (ast) { function needBracket() { var leftOp = ast[1][0]; return !(leftOp == "dot" || leftOp == "name"); } if (needBracket()) { this._write("(")._visitRaw(ast[1])._write(").")._write(ast[2]); } else { this._visitRaw(ast[1])._write(".")._write(ast[2]); } }, "new": function (ast) { var ctor = ast[1]; this._write("new ")._visitRaw(ctor)._write("("); var args = ast[2]; for (var i = 0, len = args.length; i < len; i++) { this._visitRaw(args[i]); if (i < len - 1) this._write(", "); } this._write(")"); }, "call": function (ast) { if (_isJscexPattern(ast)) { var indent = this._indent + this._indentLevel * 4; var newCode = _compileJscexPattern(ast, indent); this._write(newCode); } else { var invalidBind = (ast[1][0] == "name") && (ast[1][1] == this._binder); if (invalidBind) { this._pos = { inFunction: true }; this._buffer = []; } this._visitRaw(ast[1])._write("("); var args = ast[2]; for (var i = 0; i < args.length; i++) { this._visitRaw(args[i]); if (i < args.length - 1) this._write(", "); } this._write(")"); if (invalidBind) { throw ("Invalid bind operation: " + this._buffer.join("")); } } }, "name": function (ast) { this._write(ast[1]); }, "object": function (ast) { var items = ast[1]; if (items.length <= 0) { this._write("{ }"); } else { this._writeLine("{"); this._indentLevel++; for (var i = 0; i < items.length; i++) { this._writeIndents() ._write(stringify(items[i][0]) + ": ") ._visitRaw(items[i][1]); if (i < items.length - 1) { this._writeLine(","); } else { this._writeLine(""); } } this._indentLevel--; this._writeIndents()._write("}"); } }, "array": function (ast) { this._write("["); var items = ast[1]; for (var i = 0; i < items.length; i++) { this._visitRaw(items[i]); if (i < items.length - 1) this._write(", "); } this._write("]"); }, "num": function (ast) { this._write(ast[1]); }, "regexp": function (ast) { this._write("/" + ast[1] + "/" + ast[2]); }, "string": function (ast) { this._write(stringify(ast[1])); }, "function": function (ast) { this._visitRawFunction(ast); }, "defun": function (ast) { this._visitRawFunction(ast); }, "return": function (ast) { if (this._pos.inFunction) { this._write("return"); var value = ast[1]; if (value) this._write(" ")._visitRaw(value); this._write(";"); } else { this._write("return ")._visitJscex({ type: "return", stmt: ast })._write(";"); } }, "for": function (ast) { this._write("for ("); var setup = ast[1]; if (setup) { this._visitRaw(setup); if (setup[0] != "var") { this._write("; "); } else { this._write(" "); } } else { this._write("; "); } var condition = ast[2]; if (condition) this._visitRaw(condition); this._write("; "); var update = ast[3]; if (update) this._visitRaw(update); this._write(") "); 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._write("for ("); var declare = ast[1]; if (declare[0] == "var") { // declare == ["var", [["m"]]] this._write("var " + declare[1][0][0]); } else { this._visitRaw(declare); } this._write(" in ")._visitRaw(ast[3])._write(") "); var body = ast[4]; this._visitRawBody(body); }, "block": function (ast) { this._writeLine("{") this._indentLevel++; this._visitRawStatements(ast[1]); this._indentLevel--; this._writeIndents() ._write("}"); }, "while": function (ast) { var condition = ast[1]; var body = ast[2]; var currInLoop = this._pos.inLoop this._pos.inLoop = true; this._write("while (")._visitRaw(condition)._write(") ")._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._write("do ")._visitRawBody(body); this._pos.inLoop = currInLoop; if (body[0] == "block") { this._write(" "); } else { this._writeLine()._writeIndents(); } this._write("while (")._visitRaw(condition)._write(");"); }, "if": function (ast) { var condition = ast[1]; var thenPart = ast[2]; this._write("if (")._visitRaw(condition)._write(") ")._visitRawBody(thenPart); var elsePart = ast[3]; if (elsePart) { if (thenPart[0] == "block") { this._write(" "); } else { this._writeLine("") ._writeIndents(); } if (elsePart[0] == "if") { this._write("else ")._visitRaw(elsePart); } else { this._write("else ")._visitRawBody(elsePart); } } }, "break": function (ast) { if (this._pos.inLoop || this._pos.inSwitch) { this._write("break;"); } else { this._write("return ")._visitJscex({ type: "break", stmt: ast })._write(";"); } }, "continue": function (ast) { if (this._pos.inLoop) { this._write("continue;"); } else { this._write("return ")._visitJscex({ type: "continue", stmt: ast })._write(";"); } }, "throw": function (ast) { var pos = this._pos; if (pos.inTry || pos.inFunction) { this._write("throw ")._visitRaw(ast[1])._write(";"); } else { this._write("return ")._visitJscex({ type: "throw", stmt: ast })._write(";"); } }, "conditional": function (ast) { this._write("(")._visitRaw(ast[1])._write(") ? (")._visitRaw(ast[2])._write(") : (")._visitRaw(ast[3])._write(")"); }, "try": function (ast) { this._writeLine("try {"); this._indentLevel++; var currInTry = this._pos.inTry; this._pos.inTry = true; this._visitRawStatements(ast[1]); this._indentLevel--; this._pos.inTry = currInTry; var catchClause = ast[2]; var finallyStatements = ast[3]; if (catchClause) { this._writeIndents() ._writeLine("} catch (" + catchClause[0] + ") {") this._indentLevel++; this._visitRawStatements(catchClause[1]); this._indentLevel--; } if (finallyStatements) { this._writeIndents() ._writeLine("} finally {"); this._indentLevel++; this._visitRawStatements(finallyStatements); this._indentLevel--; } this._writeIndents() ._write("}"); }, "switch": function (ast) { this._write("switch (")._visitRaw(ast[1])._writeLine(") {"); this._indentLevel++; 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._writeIndents(); if (c[0]) { this._write("case ")._visitRaw(c[0])._writeLine(":"); } else { this._writeLine("default:"); } this._indentLevel++; this._visitRawStatements(c[1]); this._indentLevel--; } this._indentLevel--; this._pos.inSwitch = currInSwitch; this._writeIndents() ._write("}"); } } } 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] != "Jscex") 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, indent) { var builderName = ast[2][0][2][0][1]; var funcAst = ast[2][0][2][1]; var binder = root.binders[builderName]; var jscexTreeGenerator = new JscexTreeGenerator(binder); var jscexAst = jscexTreeGenerator.generate(funcAst); var codeGenerator = new CodeGenerator(builderName, binder, indent); var newCode = codeGenerator.generate(funcAst[2], jscexAst); return newCode; } function compile(builderName, func) { var funcCode = func.toString(); var evalCode = "eval(Jscex.compile(" + stringify(builderName) + ", " + funcCode + "))" var evalCodeAst = root.parse(evalCode); // [ "toplevel", [ [ "stat", [ "call", ... ] ] ] ] var evalAst = evalCodeAst[1][0][1]; var newCode = _compileJscexPattern(evalAst, 0); root.logger.debug(funcCode + "\n\n>>>\n\n" + newCode); return codeGenerator(newCode); }; root.compile = compile; root.modules["jit"] = true; } var isCommonJS = (typeof require !== "undefined" && typeof module !== "undefined" && module.exports); var isAmd = (typeof define !== "undefined" && define.amd); if (isCommonJS) { module.exports.init = function (root) { if (!root.modules["parser"]) { require("./jscex-parser").init(root); }; init(root); } } else if (isAmd) { define("jscex-jit", ["jscex-parser"], function (parser) { return { init: function (root) { if (!root.modules["parser"]) { parser.init(root); } init(root); } }; }); } else { if (typeof Jscex === "undefined") { throw new Error('Missing root object, please load "jscex" module first.'); } if (!Jscex.modules["parser"]) { throw new Error('Missing essential components, please initialize "parser" module first.'); } init(Jscex); } })();