From 66ef19e1b2a86c50d69a8d92129274570c5967a5 Mon Sep 17 00:00:00 2001 From: Bruno Jouhier Date: Mon, 2 Mar 2015 00:41:42 +0100 Subject: [PATCH] issue #254 - fixed bugs in esprima transform --- lib/callbacks/transform-esprima.js | 84 ++++++++++++++++++------------ 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/lib/callbacks/transform-esprima.js b/lib/callbacks/transform-esprima.js index 5d51e8e6..3587ba78 100644 --- a/lib/callbacks/transform-esprima.js +++ b/lib/callbacks/transform-esprima.js @@ -53,7 +53,6 @@ if (typeof exports !== 'undefined') { // EmptyStatement: ; // ObjectExpression: object initializer // Property: prop: inside ObjectExpression - // SwitchCase: case inside SwitchStatement // WithStatement @@ -357,7 +356,7 @@ if (typeof exports !== 'undefined') { return node.right; } // !_ -> false - if (node.type === Syntax.UnaryExpression && node.operator === '!' && _isMarker(argument)) { + if (node.type === Syntax.UnaryExpression && node.operator === '!' && _isMarker(node.argument)) { options.needsTransform = true; node.type = Syntax.Literal; node.value = false; @@ -483,7 +482,7 @@ if (typeof exports !== 'undefined') { if (parent.type === Syntax.AssignmentExpression) { n = parent.left; var s = ""; - while ((isDot(n) && (nn = n.property).type === Syntax.Identifier) || (isIndex(n) && (nn = n.property).type === Literal)) { + while ((isDot(n) && (nn = n.property).type === Syntax.Identifier) || (isIndex(n) && (nn = n.property).type === Syntax.Literal)) { s = s ? (nn.name || nn.value) + "_" + s : (nn.name || nn.value); n = n.object; } @@ -573,14 +572,15 @@ if (typeof exports !== 'undefined') { $lhs: _identifier(child.id.name), $rhs: child.init }); - // ??? - //if (parent.type === Syntax.ForStatement) child = child.expression; + if (parent.type === Syntax.ForStatement) child = child.expression; return child; }).filter(function(child) { return child != null; }); if (declarations.length == 0) { - return; + // leave variable if `for (var x in y)` + return parent.type === Syntax.ForInStatement // + ? node.declarations[node.declarations.length - 1].id : undefined; } var result; if (parent.type == Syntax.BlockStatement || parent.type === Syntax.Program) { @@ -636,6 +636,12 @@ if (typeof exports !== 'undefined') { _convertApply(node, options); // fall through default: + if (node.type === Syntax.SwitchCase) { + // wrap consequent into a block, to reuse block logic in subsequent steps + node.consequent = [_node(node, Syntax.BlockStatement, { + body: node.consequent, + })]; + } // todo: set breaks flag node = _propagate(node, _doIt); _setBreaks(node); @@ -764,8 +770,8 @@ if (typeof exports !== 'undefined') { var ident = child.test.left; if (ident.type !== Syntax.Identifier) return false; if (child.test.right.type !== Syntax.Literal || child.test.right.value !== null) return false; - if (!child.consequent.body || child.consequent.body.length !== 1) return false; - var assign = child.consequent.body[0]; + if (!child.consequent || child.consequent.length !== 1) return false; + var assign = child.consequent[0]; if (assign.type !== Syntax.ExpressionStatement) return false; assign = assign.expression; if (assign.type !== Syntax.AssignmentExpression) if (assign.left.type !== Syntax.Identifier) return false; @@ -845,19 +851,19 @@ if (typeof exports !== 'undefined') { break; case Syntax.SwitchStatement: for (var i = 0; i < node.cases.length; i++) { - var stmts = node.cases[i].consequent; - if (node._async && stmts.body.length > 0 && !stmts._breaks) { + var stmts = node.cases[i]; + if (node._async && stmts.consequent.length > 0 && !stmts._breaks) { // narcissus has the strange idea of inserting an empty default after last case. // If we detect this and if the last case is not terminated by a break, we do not consider it an error // and we just fix it by adding a break. if (i == node.cases.length - 2 && node.cases[i + 1].test == null // - && node.cases[i + 1].consequent.body.length === 1 // - && node.cases[i + 1].consequent.body[0].type === Syntax.ExpressionStatement // - && node.cases[i + 1].consequent.body[0].expression == null) { - stmts.body.push(_node(node, Syntax.BreakStatement)); + && node.cases[i + 1].consequent.length === 1 // + && node.cases[i + 1].consequent[0].type === Syntax.ExpressionStatement // + && node.cases[i + 1].consequent[0].expression == null) { + stmts.consequent.push(_node(node, Syntax.BreakStatement)); stmts._breaks = true; } else if (i === node.cases.length - 1) { - stmts.body.push(_node(node, Syntax.BreakStatement)); + stmts.consequent.push(_node(node, Syntax.BreakStatement)); stmts._breaks = true; } else { // we rewrite: @@ -873,14 +879,16 @@ if (typeof exports !== 'undefined') { // if (__B) no_break_B // breaking_C var v = _identifier(_genId(node)); - node.cases[i].consequent = _switchVarTemplate.generate(node.cases[i], { - $v: v, - }); + node.cases[i].consequent = [_node(node, Syntax.BlockStatement, { + body: [_switchVarTemplate.generate(node.cases[i], { + $v: v, + })], + })]; var ifStmt = _switchIfTemplate.generate(node.cases[i], { $v: v, - $block: stmts, + $block: stmts.consequent[0], }); - node.cases[i + 1].consequent.body.splice(0, 0, ifStmt); + node.cases[i + 1].consequent[0].body.splice(0, 0, ifStmt); } } } @@ -894,6 +902,10 @@ if (typeof exports !== 'undefined') { node._breaks |= child._breaks; }); break; + case Syntax.SwitchCase: + if (node.consequent.length !== 1) throw new Error("internal error: SwitchCase not wrapped: " + node.consequent.length); + node._breaks |= node.consequent[0]._breaks; + break; case Syntax.ReturnStatement: case Syntax.ThrowStatement: case Syntax.BreakStatement: @@ -1050,14 +1062,14 @@ if (typeof exports !== 'undefined') { })[0]; if (!def) { def = _node(node, Syntax.SwitchCase, { - consequent: _node(node, Syntax.BlockStatement, { - body: [] - }), + consequent: [_node(node, Syntax.BlockStatement, { + body: [], + })], }); node.cases.push(def); } if (!def._breaks) { - def.consequent.body.push(_node(node, Syntax.BreakStatement)) + def.consequent.push(_node(node, Syntax.BreakStatement)); } } break; @@ -1102,8 +1114,8 @@ if (typeof exports !== 'undefined') { case Syntax.ForInStatement: node.body = _blockify(node.body); if (node._async) { - if (node.iterator.type != Syntax.Identifier) { - throw new Error("unsupported 'for ... in' syntax: type=" + node.iterator.type); + if (node.left.type != Syntax.Identifier) { + throw new Error("unsupported 'for ... in' syntax: type=" + node.left.type); } node = _flowsTemplates.FOR_IN.generate(node, { $array: _identifier(_genId(node)), @@ -1486,7 +1498,7 @@ if (typeof exports !== 'undefined') { _extractTail(parent, i); if (node.label) { node = _cbTemplates.LABELLED_BREAK.generate(node, { - $break: _safeName(options.precious, '__break__' + node.label) + $break: _safeName(options.precious, '__break__' + node.label.name) }); } else { node = _cbTemplates.BREAK.generate(node, {}); @@ -1499,8 +1511,8 @@ if (typeof exports !== 'undefined') { _extractTail(parent, i); if (node.label) { node = _cbTemplates.LABELLED_CONTINUE.generate(node, { - $loop: _safeName(options.precious, '__loop__' + node.label), - $more: _safeName(options.precious, '__more__' + node.label), + $loop: _safeName(options.precious, '__loop__' + node.label.name), + $more: _safeName(options.precious, '__more__' + node.label.name), }); } else { node = _cbTemplates.CONTINUE.generate(node, {}); @@ -1550,7 +1562,7 @@ if (typeof exports !== 'undefined') { break; case Syntax.LabeledStatement: var l = label; - label = node.label; + label = node.label.name; node = _cbTemplates.LABEL.generate(node, { $name: "__$" + node._scope.name, $statement: node.body, @@ -1600,7 +1612,7 @@ if (typeof exports !== 'undefined') { _assert(call.type === Syntax.CallExpression) return _restructureCall(call, tail); default: - throw new Error("internal error: bad node type: " + _tag(node) + ": " + escodegen.generate(node)); + throw new Error("internal error: bad node type: " + node.type + ": " + escodegen.generate(node)); } } } @@ -1790,9 +1802,12 @@ if (typeof exports !== 'undefined') { options.precious = {}; // identifiers found inside source //console.log("TRANSFORMING " + options.sourceName) //console.log("source=" + source); - var node = esprima.parse(source + "\n", { + // esprima does not like return at top level so we wrap into a function + // also \n is needed before } in case last line ends on a // comment + var node = esprima.parse("function dummy(){" + source + "\n}", { loc: true, - }); // final newline avoids infinite loop if unterminated string literal at the end + }); + node = node.body[0].body; //console.log(JSON.stringify(node, null, ' ')); var strict = node.body[0] && node.body[0].expression && node.body[0].expression.value == "use strict"; strict && node.body.splice(0, 1); @@ -1814,6 +1829,9 @@ if (typeof exports !== 'undefined') { node = _simplify(node, options, used); var result = escodegen.generate(node); //, options.lines); + // remove curly braces around generated source + result = result.substring(1, result.length - 1); + // add helpers at beginning so that __g is initialized before any other code if (!options.noHelpers) {