Implement TDZ for ClassFieldDefinitionEvaluation (#6855)
* First implementation sketch and testcase * Use helper instead of inline IIFE's * minNodeVersion 6.0.0 * Hoisted visitor for subtraversal and fixed edgest case * Resolve merge conflicts in helpers * Remove duplicated helper from messing up Git
This commit is contained in:
parent
cdf420d4d8
commit
6330a152ce
@ -633,6 +633,12 @@ helpers.readOnlyError = defineHelper(`
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
helpers.classNameTDZError = defineHelper(`
|
||||||
|
export default function _classNameTDZError(name) {
|
||||||
|
throw new Error("Class \\"" + name + "\\" cannot be referenced in computed property keys.");
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
helpers.temporalUndefined = defineHelper(`
|
helpers.temporalUndefined = defineHelper(`
|
||||||
export default {};
|
export default {};
|
||||||
`);
|
`);
|
||||||
|
|||||||
@ -25,6 +25,26 @@ export default function(api, options) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ClassFieldDefinitionEvaluationTDZVisitor = {
|
||||||
|
Expression(path) {
|
||||||
|
if (path === this.shouldSkip) {
|
||||||
|
path.skip();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ReferencedIdentifier(path) {
|
||||||
|
if (this.classRef === path.scope.getBinding(path.node.name)) {
|
||||||
|
const classNameTDZError = this.file.addHelper("classNameTDZError");
|
||||||
|
const throwNode = t.callExpression(classNameTDZError, [
|
||||||
|
t.stringLiteral(path.node.name),
|
||||||
|
]);
|
||||||
|
|
||||||
|
path.replaceWith(t.sequenceExpression([throwNode, path.node]));
|
||||||
|
path.skip();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const buildClassPropertySpec = (ref, { key, value, computed }, scope) => {
|
const buildClassPropertySpec = (ref, { key, value, computed }, scope) => {
|
||||||
return template.statement`
|
return template.statement`
|
||||||
Object.defineProperty(REF, KEY, {
|
Object.defineProperty(REF, KEY, {
|
||||||
@ -95,6 +115,11 @@ export default function(api, options) {
|
|||||||
// Make sure computed property names are only evaluated once (upon class definition)
|
// Make sure computed property names are only evaluated once (upon class definition)
|
||||||
// and in the right order in combination with static properties
|
// and in the right order in combination with static properties
|
||||||
if (!computedPath.get("key").isConstantExpression()) {
|
if (!computedPath.get("key").isConstantExpression()) {
|
||||||
|
computedPath.traverse(ClassFieldDefinitionEvaluationTDZVisitor, {
|
||||||
|
classRef: path.scope.getBinding(ref.name),
|
||||||
|
file: this.file,
|
||||||
|
shouldSkip: computedPath.get("value"),
|
||||||
|
});
|
||||||
const ident = path.scope.generateUidIdentifierBasedOnNode(
|
const ident = path.scope.generateUidIdentifierBasedOnNode(
|
||||||
computedNode.key,
|
computedNode.key,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
class A {
|
||||||
|
static [{ x: A || 0 }.x];
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||||
|
|
||||||
|
function _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); }
|
||||||
|
|
||||||
|
let A = function A() {
|
||||||
|
_classCallCheck(this, A);
|
||||||
|
};
|
||||||
|
|
||||||
|
var _x$x = {
|
||||||
|
x: (_classNameTDZError("A"), A) || 0
|
||||||
|
}.x;
|
||||||
|
Object.defineProperty(A, _x$x, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
value: void 0
|
||||||
|
});
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class C {
|
||||||
|
static [C + 3] = 3;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class C {
|
||||||
|
static [C + 3] = 3;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||||
|
|
||||||
|
function _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); }
|
||||||
|
|
||||||
|
let C = function C() {
|
||||||
|
_classCallCheck(this, C);
|
||||||
|
};
|
||||||
|
|
||||||
|
var _ref = (_classNameTDZError("C"), C) + 3;
|
||||||
|
|
||||||
|
Object.defineProperty(C, _ref, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
value: 3
|
||||||
|
});
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"throws": "Class \"C\" cannot be referenced in computed property keys."
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class C {
|
||||||
|
static [C + 3] = 3;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class C {
|
||||||
|
static [C + 3] = 3;
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
function _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); }
|
||||||
|
|
||||||
|
class C {}
|
||||||
|
|
||||||
|
var _ref = (_classNameTDZError("C"), C) + 3;
|
||||||
|
|
||||||
|
C[_ref] = 3;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"throws": "Class \"C\" cannot be referenced in computed property keys.",
|
||||||
|
"plugins": [["proposal-class-properties", {"loose": true}]]
|
||||||
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["proposal-class-properties", "transform-classes"],
|
||||||
|
"minNodeVersion": "6.0.0"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user