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:
Mauro Bringolf 2017-11-26 16:56:04 +01:00 committed by Mateusz Burzyński
parent cdf420d4d8
commit 6330a152ce
13 changed files with 97 additions and 0 deletions

View File

@ -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(`
export default {};
`);

View File

@ -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) => {
return template.statement`
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)
// and in the right order in combination with static properties
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(
computedNode.key,
);

View File

@ -0,0 +1,3 @@
class A {
static [{ x: A || 0 }.x];
}

View File

@ -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
});

View File

@ -0,0 +1,3 @@
class C {
static [C + 3] = 3;
}

View File

@ -0,0 +1,3 @@
class C {
static [C + 3] = 3;
}

View File

@ -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
});

View File

@ -0,0 +1,3 @@
{
"throws": "Class \"C\" cannot be referenced in computed property keys."
}

View File

@ -0,0 +1,3 @@
class C {
static [C + 3] = 3;
}

View File

@ -0,0 +1,3 @@
class C {
static [C + 3] = 3;
}

View File

@ -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;

View File

@ -0,0 +1,4 @@
{
"throws": "Class \"C\" cannot be referenced in computed property keys.",
"plugins": [["proposal-class-properties", {"loose": true}]]
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["proposal-class-properties", "transform-classes"],
"minNodeVersion": "6.0.0"
}