Fix tdz checks in transform-block-scoping plugin (#9498)
* Better tdz tests - Use jest's expect.toThrow/expect.not.toThrow - Add input/output tests * Fix basic tdz (a = 2; let a) Fixes #6848 * Make _guessExecutionStatusRelativeTo more robust * Add tests * Return less "unkown" execution status * "function" execution status does not exist * Fix recursive functions * Update helper version * "finally" blocks are always executed * Typo
This commit is contained in:
@@ -16,7 +16,7 @@ export default declare((api, opts) => {
|
||||
throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
|
||||
}
|
||||
if (typeof tdzEnabled !== "boolean") {
|
||||
throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
|
||||
throw new Error(`.tdz must be a boolean, or undefined`);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -33,11 +33,13 @@ export default declare((api, opts) => {
|
||||
|
||||
for (let i = 0; i < node.declarations.length; i++) {
|
||||
const decl = node.declarations[i];
|
||||
if (decl.init) {
|
||||
const assign = t.assignmentExpression("=", decl.id, decl.init);
|
||||
assign._ignoreBlockScopingTDZ = true;
|
||||
nodes.push(t.expressionStatement(assign));
|
||||
}
|
||||
const assign = t.assignmentExpression(
|
||||
"=",
|
||||
decl.id,
|
||||
decl.init || scope.buildUndefinedNode(),
|
||||
);
|
||||
assign._ignoreBlockScopingTDZ = true;
|
||||
nodes.push(t.expressionStatement(assign));
|
||||
decl.init = this.addHelper("temporalUndefined");
|
||||
}
|
||||
|
||||
@@ -181,6 +183,8 @@ const letReferenceBlockVisitor = traverse.visitors.merge([
|
||||
// simply rename the variables.
|
||||
if (state.loopDepth > 0) {
|
||||
path.traverse(letReferenceFunctionVisitor, state);
|
||||
} else {
|
||||
path.traverse(tdzVisitor, state);
|
||||
}
|
||||
return path.skip();
|
||||
},
|
||||
@@ -756,7 +760,7 @@ class BlockScoping {
|
||||
closurify: false,
|
||||
loopDepth: 0,
|
||||
tdzEnabled: this.tdzEnabled,
|
||||
addHelper: name => this.addHelper(name),
|
||||
addHelper: name => this.state.addHelper(name),
|
||||
};
|
||||
|
||||
if (isInLoop(this.blockPath)) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { types as t } from "@babel/core";
|
||||
import { types as t, template } from "@babel/core";
|
||||
|
||||
function getTDZStatus(refPath, bindingPath) {
|
||||
const executionStatus = bindingPath._guessExecutionStatusRelativeTo(refPath);
|
||||
|
||||
if (executionStatus === "before") {
|
||||
return "inside";
|
||||
} else if (executionStatus === "after") {
|
||||
return "outside";
|
||||
} else if (executionStatus === "after") {
|
||||
return "inside";
|
||||
} else {
|
||||
return "maybe";
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export const visitor = {
|
||||
if (bindingPath.isFunctionDeclaration()) return;
|
||||
|
||||
const status = getTDZStatus(path, bindingPath);
|
||||
if (status === "inside") return;
|
||||
if (status === "outside") return;
|
||||
|
||||
if (status === "maybe") {
|
||||
const assert = buildTDZAssert(node, state);
|
||||
@@ -57,19 +57,8 @@ export const visitor = {
|
||||
} else {
|
||||
path.replaceWith(assert);
|
||||
}
|
||||
} else if (status === "outside") {
|
||||
path.replaceWith(
|
||||
t.throwStatement(
|
||||
t.inherits(
|
||||
t.newExpression(t.identifier("ReferenceError"), [
|
||||
t.stringLiteral(
|
||||
`${node.name} is not defined - temporal dead zone`,
|
||||
),
|
||||
]),
|
||||
node,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (status === "inside") {
|
||||
path.replaceWith(template.ast`${state.addHelper("tdz")}("${node.name}")`);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -87,14 +76,14 @@ export const visitor = {
|
||||
const id = ids[name];
|
||||
|
||||
if (isReference(id, path.scope, state)) {
|
||||
nodes.push(buildTDZAssert(id, state));
|
||||
nodes.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.length) {
|
||||
node._ignoreBlockScopingTDZ = true;
|
||||
nodes.push(node);
|
||||
path.replaceWithMultiple(nodes.map(t.expressionStatement));
|
||||
path.replaceWithMultiple(nodes.map(n => t.expressionStatement(n)));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ if (x) {
|
||||
|
||||
var innerScope = true;
|
||||
var res = transform(code, {
|
||||
configFile: false,
|
||||
plugins: opts.plugins.concat([
|
||||
function (b) {
|
||||
var t = b.types;
|
||||
@@ -34,3 +35,4 @@ if (x) {
|
||||
}`;
|
||||
|
||||
expect(res.code).toBe(expected);
|
||||
expect(innerScope).toBe(false);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
f();
|
||||
expect(() => {
|
||||
f();
|
||||
|
||||
const f = function f() {}
|
||||
const f = function f() {}
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
f();
|
||||
|
||||
const f = function f() {}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "f is not defined - temporal dead zone"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
babelHelpers.tdz("f")();
|
||||
|
||||
var f = function f() {};
|
||||
@@ -1 +1,3 @@
|
||||
let { b: d } = { d }
|
||||
expect(() => {
|
||||
let { b: d } = { d }
|
||||
}).toThrow(ReferenceError);
|
||||
@@ -0,0 +1 @@
|
||||
let { b: d } = { d }
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "d is not defined - temporal dead zone"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
var {
|
||||
b: d
|
||||
} = {
|
||||
d: babelHelpers.tdz("d")
|
||||
};
|
||||
7
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-after/exec.js
vendored
Normal file
7
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-after/exec.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
expect(() => {
|
||||
function f() {
|
||||
x;
|
||||
}
|
||||
let x;
|
||||
f();
|
||||
}).not.toThrow();
|
||||
@@ -0,0 +1,6 @@
|
||||
function f() {
|
||||
x;
|
||||
}
|
||||
|
||||
var x;
|
||||
f();
|
||||
7
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-before/exec.js
vendored
Normal file
7
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-before/exec.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
expect(() => {
|
||||
function f() {
|
||||
x;
|
||||
}
|
||||
f();
|
||||
let x;
|
||||
}).toThrow(ReferenceError);
|
||||
@@ -0,0 +1,5 @@
|
||||
function f() {
|
||||
x;
|
||||
}
|
||||
f();
|
||||
let x;
|
||||
@@ -0,0 +1,6 @@
|
||||
function f() {
|
||||
babelHelpers.tdz("x");
|
||||
}
|
||||
|
||||
f();
|
||||
var x;
|
||||
@@ -0,0 +1,6 @@
|
||||
expect(() => {
|
||||
function f() { x }
|
||||
Math.random() === 2 && f();
|
||||
let x;
|
||||
f();
|
||||
}).not.toThrow();
|
||||
@@ -0,0 +1,4 @@
|
||||
function f() { x }
|
||||
Math.random() === 2 && f();
|
||||
let x;
|
||||
f();
|
||||
@@ -0,0 +1,9 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f() {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
}
|
||||
|
||||
Math.random() === 2 && f();
|
||||
x = void 0;
|
||||
f();
|
||||
@@ -0,0 +1,5 @@
|
||||
function f() { x }
|
||||
Math.random() === 2 && f();
|
||||
let x = 3;
|
||||
|
||||
expect(x).toBe(3);
|
||||
@@ -0,0 +1,5 @@
|
||||
function f() { x }
|
||||
Math.random() === 2 && f();
|
||||
let x = 3;
|
||||
|
||||
expect(x).toBe(3);
|
||||
@@ -0,0 +1,9 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f() {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
}
|
||||
|
||||
Math.random() === 2 && f();
|
||||
x = 3;
|
||||
expect(x).toBe(3);
|
||||
29
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-maybe/exec.js
vendored
Normal file
29
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-maybe/exec.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// "random" :)
|
||||
let random = (i => {
|
||||
const vals = [0, 0, 1, 1];
|
||||
return () => vals[i++];
|
||||
})(0);
|
||||
|
||||
expect(() => {
|
||||
function f() { x }
|
||||
random() && f();
|
||||
let x;
|
||||
}).not.toThrow();
|
||||
|
||||
expect(() => {
|
||||
function f() { x }
|
||||
random() || f();
|
||||
let x;
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
expect(() => {
|
||||
function f() { x }
|
||||
random() && f();
|
||||
let x;
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
expect(() => {
|
||||
function f() { x }
|
||||
random() || f();
|
||||
let x;
|
||||
}).not.toThrow();
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-maybe/input.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-maybe/input.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
function f() { x }
|
||||
Math.random() && f();
|
||||
let x;
|
||||
@@ -0,0 +1,9 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f() {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
}
|
||||
|
||||
Math.random() && f();
|
||||
x = void 0;
|
||||
void 0;
|
||||
@@ -0,0 +1,17 @@
|
||||
expect(() => {
|
||||
function f() {
|
||||
return function() { x };
|
||||
}
|
||||
let g = f();
|
||||
let x;
|
||||
g();
|
||||
}).not.toThrow();
|
||||
|
||||
expect(() => {
|
||||
function f() {
|
||||
return function() { x };
|
||||
}
|
||||
let g = f();
|
||||
g();
|
||||
let x;
|
||||
}).toThrow(ReferenceError);
|
||||
@@ -0,0 +1,5 @@
|
||||
function f() {
|
||||
return function() { x };
|
||||
}
|
||||
f();
|
||||
let x;
|
||||
@@ -0,0 +1,11 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f() {
|
||||
return function () {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
};
|
||||
}
|
||||
|
||||
f();
|
||||
x = void 0;
|
||||
void 0;
|
||||
@@ -0,0 +1,9 @@
|
||||
expect(() => {
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
x;
|
||||
}
|
||||
|
||||
let x;
|
||||
f(3);
|
||||
}).not.toThrow();
|
||||
@@ -0,0 +1,7 @@
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
x;
|
||||
}
|
||||
|
||||
let x;
|
||||
f(3);
|
||||
@@ -0,0 +1,7 @@
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
x;
|
||||
}
|
||||
|
||||
var x;
|
||||
f(3);
|
||||
@@ -0,0 +1,9 @@
|
||||
expect(() => {
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
x;
|
||||
}
|
||||
|
||||
f(3);
|
||||
let x;
|
||||
}).toThrow(ReferenceError);
|
||||
@@ -0,0 +1,7 @@
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
x;
|
||||
}
|
||||
|
||||
f(3);
|
||||
let x;
|
||||
@@ -0,0 +1,7 @@
|
||||
function f(i) {
|
||||
if (i) f(i - 1);
|
||||
babelHelpers.tdz("x");
|
||||
}
|
||||
|
||||
f(3);
|
||||
var x;
|
||||
@@ -0,0 +1,12 @@
|
||||
expect(() => {
|
||||
function f(i) {
|
||||
return () => {
|
||||
x;
|
||||
f(i - 1);
|
||||
};
|
||||
}
|
||||
|
||||
const g = f(1);
|
||||
let x;
|
||||
g();
|
||||
}).not.toThrow();
|
||||
@@ -0,0 +1,10 @@
|
||||
function f(i) {
|
||||
return () => {
|
||||
x;
|
||||
f(i - 1);
|
||||
};
|
||||
}
|
||||
|
||||
const g = f(1);
|
||||
let x;
|
||||
g();
|
||||
@@ -0,0 +1,12 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f(i) {
|
||||
return () => {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
f(i - 1);
|
||||
};
|
||||
}
|
||||
|
||||
var g = f(1);
|
||||
x = void 0;
|
||||
g();
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-ref/input.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-ref/input.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
function f() { x }
|
||||
maybeCall(f);
|
||||
let x;
|
||||
9
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-ref/output.js
vendored
Normal file
9
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-ref/output.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
var x = babelHelpers.temporalUndefined;
|
||||
|
||||
function f() {
|
||||
babelHelpers.temporalRef(x, "x");
|
||||
}
|
||||
|
||||
maybeCall(f);
|
||||
x = void 0;
|
||||
void 0;
|
||||
@@ -1,3 +1,5 @@
|
||||
f();
|
||||
expect(() => {
|
||||
f();
|
||||
|
||||
function f() {}
|
||||
function f() {}
|
||||
}).not.toThrow();
|
||||
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-function/input.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-function/input.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
f();
|
||||
|
||||
function f() {}
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-function/output.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-function/output.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
f();
|
||||
|
||||
function f() {}
|
||||
@@ -1,3 +1,5 @@
|
||||
x = 3;
|
||||
expect(() => {
|
||||
x = 3;
|
||||
|
||||
var x;
|
||||
var x;
|
||||
}).not.toThrow();
|
||||
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-var/input.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-var/input.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
x = 3;
|
||||
|
||||
var x;
|
||||
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-var/output.js
vendored
Normal file
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/hoisted-var/output.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
x = 3;
|
||||
var x;
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"plugins": [["transform-block-scoping", { "tdz": true }]]
|
||||
"plugins": [
|
||||
["transform-block-scoping", { "tdz": true }],
|
||||
["external-helpers", { "helperVersion": "7.1000.0" }]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
let x = x;
|
||||
expect(() => {
|
||||
let x = x;
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
1
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/self-reference/input.js
vendored
Normal file
1
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/self-reference/input.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
let x = x;
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "x is not defined - temporal dead zone"
|
||||
}
|
||||
1
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/self-reference/output.js
vendored
Normal file
1
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/self-reference/output.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
var x = babelHelpers.tdz("x");
|
||||
@@ -1,3 +1,8 @@
|
||||
var a = 5;
|
||||
if (a){ console.log(a); let a = 2; }
|
||||
console.log(a);
|
||||
expect(() => {
|
||||
var a = 5;
|
||||
if (a) {
|
||||
a;
|
||||
let a = 2;
|
||||
}
|
||||
a;
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
6
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/shadow-outer-var/input.js
vendored
Normal file
6
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/shadow-outer-var/input.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
var a = 5;
|
||||
if (a) {
|
||||
a;
|
||||
let a = 2;
|
||||
}
|
||||
a;
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "a is not defined - temporal dead zone"
|
||||
}
|
||||
8
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/shadow-outer-var/output.js
vendored
Normal file
8
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/shadow-outer-var/output.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
var a = 5;
|
||||
|
||||
if (a) {
|
||||
babelHelpers.tdz("a");
|
||||
var _a = 2;
|
||||
}
|
||||
|
||||
a;
|
||||
4
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/exec.js
vendored
Normal file
4
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/exec.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
expect(() => {
|
||||
i = 2;
|
||||
let i
|
||||
}).toThrow(ReferenceError);
|
||||
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/input.js
vendored
Normal file
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/input.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
i = 2;
|
||||
let i
|
||||
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/output.js
vendored
Normal file
3
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-assign/output.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
babelHelpers.tdz("i");
|
||||
i = 2;
|
||||
var i;
|
||||
@@ -1,2 +1,4 @@
|
||||
i
|
||||
let i
|
||||
expect(() => {
|
||||
i
|
||||
let i
|
||||
}).toThrow(ReferenceError);
|
||||
|
||||
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-reference/input.js
vendored
Normal file
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-reference/input.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
i
|
||||
let i
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"throws": "i is not defined - temporal dead zone"
|
||||
}
|
||||
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-reference/output.js
vendored
Normal file
2
packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/simple-reference/output.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
babelHelpers.tdz("i");
|
||||
var i;
|
||||
Reference in New Issue
Block a user