From 5fa3d01400ffb4903fe4184c3886f5d8b1c223b0 Mon Sep 17 00:00:00 2001 From: "matthew.woolf@mailonline.co.uk" Date: Wed, 2 May 2018 11:38:46 +0100 Subject: [PATCH] Fix for https://github.com/MatAtBread/nodent/issues/108 This change assumes loop blocks have scope (they do in ES6), which means it "fixes" (i.e. changes) the pre-ES6 behaviour of `const` declarations within loops. Whilst this is "correct", it differs from the previous behaviour and so early JS engines that previously returned "incorrect" results will now return "correct" results: specifically `const` in a loop is local and can be deferenced in a callback with it's loop value rather than it's terminal value. This change affects NodeJS versions before 6.x, Chrome until approx v48, Safari 9 and possibly 10 (I've not verified which browser versions are actually affected). These engine will run generate "correct" ES6 results for loops like: ``` async function nop(x) { return x } var resolve,p = new Promise(function(r){resolve = r}) ; var i = 0, x = 0, s = 1 ; while (i<5) { const j = i+1 ; await nop() ; setTimeout(function(){ x += 1 ; s *= j ; // REFERS to "j" if (x===5) { resolve(s) ; } }, 0); i++ ; } return await p ; ``` --- arboriculture.js | 43 ++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/arboriculture.js b/arboriculture.js index b52b646..caa66d2 100644 --- a/arboriculture.js +++ b/arboriculture.js @@ -105,7 +105,7 @@ var examinations = { case 'ClassExpression': return true; case 'VariableDeclaration': - return this.node.kind && this.node.kind !== 'var'; + return this.node.kind && this.node.kind === 'let'; case 'FunctionDeclaration': case 'FunctionExpression': return !(!this.node.generator); @@ -183,6 +183,7 @@ function asynchronize(pr, opts, logger, parsePart, printNode) { var continuations = {}; var generatedSymbol = 1; var genIdent = {}; + Object.keys(opts).filter(function (k) { return k[0] === '$'; }).forEach(function (k) { @@ -429,7 +430,12 @@ function asynchronize(pr, opts, logger, parsePart, printNode) { } return null; } - + + + var looksLikeES6 = opts.es6target || pr.ast.type === 'Program' && pr.ast.sourceType === 'module' || contains(pr.ast, function (n) { + return examine(n).isES6; + }, true); + // actually do the transforms if (opts.engine) { // Only Transform extensions: @@ -454,18 +460,8 @@ function asynchronize(pr, opts, logger, parsePart, printNode) { pr.ast = fixSuperReferences(pr.ast); asyncTransforms(pr.ast); } - /* - if (opts.babelTree) { - treeWalk(pr.ast, function (node, descend, path) { - if (node.type === 'Literal') { - coerce(node, literal(node.value)); - } else { - descend(); - } - }); - } - */ return pr; + function asyncTransforms(ast, awaitFlag) { var useLazyLoops = !(opts.promises || opts.generators || opts.engine) && opts.lazyThenables; // Because we create functions (and scopes), we need all declarations before use @@ -1956,6 +1952,7 @@ function asynchronize(pr, opts, logger, parsePart, printNode) { /* Find all nodes within this scope matching the specified function */ function scopedNodes(ast, matching, flat) { + if (!flat) flat = ['isScope','isLoop'] ; var matches = []; treeWalk(ast, function (node, descend, path) { if (node === ast) @@ -1964,9 +1961,11 @@ function asynchronize(pr, opts, logger, parsePart, printNode) { matches.push([].concat(path)); return; } - if (flat || examine(node).isScope) { - return; - } + if (flat === true) return ; + var x = examine(node) ; + for (var i=0; i