Fixes for review comments.

* rename _super to _superIndex and _superProps to _super.
* reinstate early exit for transformers by marking super accesses as
  esnext/es2017 in `binder.ts`.
* adjust comment in `checker.ts` to new emit.
This commit is contained in:
Martin Probst
2018-08-31 11:01:59 +02:00
parent f0826cfeaa
commit e618d752b6
7 changed files with 70 additions and 57 deletions

View File

@@ -3445,7 +3445,9 @@ namespace ts {
// ES6 syntax, and requires a lexical `this` binding.
if (transformFlags & TransformFlags.Super) {
transformFlags ^= TransformFlags.Super;
transformFlags |= TransformFlags.ContainsSuper;
// super inside of an async function requires hoisting the super access (ES2017).
// same for super inside of an async generator, which is ESNext.
transformFlags |= TransformFlags.ContainsSuper | TransformFlags.ContainsES2017 | TransformFlags.ContainsESNext;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
@@ -3461,7 +3463,9 @@ namespace ts {
// ES6 syntax, and requires a lexical `this` binding.
if (expressionFlags & TransformFlags.Super) {
transformFlags &= ~TransformFlags.Super;
transformFlags |= TransformFlags.ContainsSuper;
// super inside of an async function requires hoisting the super access (ES2017).
// same for super inside of an async generator, which is ESNext.
transformFlags |= TransformFlags.ContainsSuper | TransformFlags.ContainsES2017 | TransformFlags.ContainsESNext;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;

View File

@@ -16180,16 +16180,18 @@ namespace ts {
// // js
// ...
// asyncMethod() {
// const _super_asyncMethod = name => super.asyncMethod;
// const _super = Object.create(null, {
// asyncMethod: { get: () => super.asyncMethod },
// });
// return __awaiter(this, arguments, Promise, function *() {
// let x = yield _super_asyncMethod.call(this);
// let x = yield _super.asyncMethod.call(this);
// return x;
// });
// }
// ...
//
// The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases
// are legal in ES6, but also likely less frequent, we emit the same more complex helper for both scenarios:
// are legal in ES6, but also likely less frequent, we only emit setters if there is an assignment:
//
// // ts
// ...
@@ -16201,17 +16203,18 @@ namespace ts {
// // js
// ...
// asyncMethod(ar) {
// const _super_a = {get value() { return super.a; }, set value(v) { super.a = v; }};
// const _super_b = {get value() { return super.b; }, set value(v) { super.b = v; }};
// const _super = Object.create(null, {
// a: { get: () => super.a, set: (v) => super.a = v },
// b: { get: () => super.b, set: (v) => super.b = v }
// };
// return __awaiter(this, arguments, Promise, function *() {
// [_super_a.value, _super_b.value] = yield ar;
// [_super.a, _super.b] = yield ar;
// });
// }
// ...
//
// This helper creates an object with a "value" property that wraps the `super` property for both get and set. This is required for
// destructuring assignments, as a call expression cannot be used as the target of a destructuring assignment while a property
// access can.
// Creating an object that has getter and setters instead of just an accessor funtion is required for destructuring assignments
// as a call expression cannot be used as the target of a destructuring assignment while a property access can.
//
// For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations.
if (container.kind === SyntaxKind.MethodDeclaration && hasModifier(container, ModifierFlags.Async)) {

View File

@@ -62,6 +62,9 @@ namespace ts {
}
function visitor(node: Node): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.AsyncKeyword:
// ES2017 async modifier should be elided for targets < ES2017
@@ -553,7 +556,7 @@ namespace ts {
// Disable substitution in the generated super accessor itself.
else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) {
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
enclosingSuperContainerFlags = 0 as NodeCheckFlags;
enclosingSuperContainerFlags = 0;
previousOnEmitNode(hint, node, emitCallback);
enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
return;
@@ -592,7 +595,7 @@ namespace ts {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return setTextRange(
createPropertyAccess(
createFileLevelUniqueName("_superProps"),
createFileLevelUniqueName("_super"),
node.name),
node
);
@@ -642,7 +645,7 @@ namespace ts {
return setTextRange(
createPropertyAccess(
createCall(
createFileLevelUniqueName("_super"),
createFileLevelUniqueName("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -654,7 +657,7 @@ namespace ts {
else {
return setTextRange(
createCall(
createFileLevelUniqueName("_super"),
createFileLevelUniqueName("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -729,7 +732,7 @@ namespace ts {
createVariableDeclarationList(
[
createVariableDeclaration(
createFileLevelUniqueName("_superProps"),
createFileLevelUniqueName("_super"),
/* type */ undefined,
createCall(
createPropertyAccess(
@@ -794,14 +797,14 @@ namespace ts {
name: "typescript:async-super",
scoped: true,
text: helperString`
const ${"_super"} = name => super[name];`
const ${"_superIndex"} = name => super[name];`
};
export const advancedAsyncSuperHelper: EmitHelper = {
name: "typescript:advanced-async-super",
scoped: true,
text: helperString`
const ${"_super"} = (function (geti, seti) {
const ${"_superIndex"} = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`

View File

@@ -61,6 +61,9 @@ namespace ts {
}
function visitorWorker(node: Node, noDestructuringValue: boolean): VisitResult<Node> {
if ((node.transformFlags & TransformFlags.ContainsESNext) === 0) {
return node;
}
switch (node.kind) {
case SyntaxKind.AwaitExpression:
return visitAwaitExpression(node as AwaitExpression);
@@ -854,7 +857,7 @@ namespace ts {
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return setTextRange(
createPropertyAccess(
createFileLevelUniqueName("_superProps"),
createFileLevelUniqueName("_super"),
node.name),
node
);
@@ -904,7 +907,7 @@ namespace ts {
return setTextRange(
createPropertyAccess(
createCall(
createIdentifier("_super"),
createIdentifier("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),
@@ -916,7 +919,7 @@ namespace ts {
else {
return setTextRange(
createCall(
createIdentifier("_super"),
createIdentifier("_superIndex"),
/*typeArguments*/ undefined,
[argumentExpression]
),

View File

@@ -77,8 +77,8 @@ class A {
class B extends A {
// async method with only call/get on 'super' does not require a binding
simple() {
const _super_1 = name => super[name];
const _superProps_1 = Object.create(null, {
const _superIndex = name => super[name];
const _super_1 = Object.create(null, {
x: { get: () => super.x },
y: { get: () => super.y }
});
@@ -86,24 +86,24 @@ class B extends A {
const _super = null;
const _superProps = null;
// call with property access
_superProps_1.x.call(this);
_super_1.x.call(this);
// call additional property.
_superProps_1.y.call(this);
_super_1.y.call(this);
// call with element access
_super_1("x").call(this);
_superIndex("x").call(this);
// property access (read)
const a = _superProps_1.x;
const a = _super_1.x;
// element access (read)
const b = _super_1("x");
const b = _superIndex("x");
});
}
// async method with assignment/destructuring on 'super' requires a binding
advanced() {
const _super_1 = (function (geti, seti) {
const _superIndex = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);
const _superProps_1 = Object.create(null, {
const _super_1 = Object.create(null, {
x: { get: () => super.x, set: v => super.x = v }
});
return __awaiter(this, void 0, void 0, function* () {
@@ -111,21 +111,21 @@ class B extends A {
const _superProps = null;
const f = () => { };
// call with property access
_superProps_1.x.call(this);
_super_1.x.call(this);
// call with element access
_super_1("x").value.call(this);
_superIndex("x").value.call(this);
// property access (read)
const a = _superProps_1.x;
const a = _super_1.x;
// element access (read)
const b = _super_1("x").value;
const b = _superIndex("x").value;
// property access (assign)
_superProps_1.x = f;
_super_1.x = f;
// element access (assign)
_super_1("x").value = f;
_superIndex("x").value = f;
// destructuring assign with property access
({ f: _superProps_1.x } = { f });
({ f: _super_1.x } = { f });
// destructuring assign with element access
({ f: _super_1("x").value } = { f });
({ f: _superIndex("x").value } = { f });
});
}
}

View File

@@ -65,51 +65,51 @@ class A {
class B extends A {
// async method with only call/get on 'super' does not require a binding
simple() {
const _super = name => super[name];
const _superProps = Object.create(null, {
const _superIndex = name => super[name];
const _super = Object.create(null, {
x: { get: () => super.x },
y: { get: () => super.y }
});
return __awaiter(this, void 0, void 0, function* () {
// call with property access
_superProps.x.call(this);
_super.x.call(this);
// call additional property.
_superProps.y.call(this);
_super.y.call(this);
// call with element access
_super("x").call(this);
_superIndex("x").call(this);
// property access (read)
const a = _superProps.x;
const a = _super.x;
// element access (read)
const b = _super("x");
const b = _superIndex("x");
});
}
// async method with assignment/destructuring on 'super' requires a binding
advanced() {
const _super = (function (geti, seti) {
const _superIndex = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);
const _superProps = Object.create(null, {
const _super = Object.create(null, {
x: { get: () => super.x, set: v => super.x = v }
});
return __awaiter(this, void 0, void 0, function* () {
const f = () => { };
// call with property access
_superProps.x.call(this);
_super.x.call(this);
// call with element access
_super("x").value.call(this);
_superIndex("x").value.call(this);
// property access (read)
const a = _superProps.x;
const a = _super.x;
// element access (read)
const b = _super("x").value;
const b = _superIndex("x").value;
// property access (assign)
_superProps.x = f;
_super.x = f;
// element access (assign)
_super("x").value = f;
_superIndex("x").value = f;
// destructuring assign with property access
({ f: _superProps.x } = { f });
({ f: _super.x } = { f });
// destructuring assign with element access
({ f: _super("x").value } = { f });
({ f: _superIndex("x").value } = { f });
});
}
}

View File

@@ -263,11 +263,11 @@ class B9 {
}
class C9 extends B9 {
f() {
const _superProps = Object.create(null, {
const _super = Object.create(null, {
g: { get: () => super.g }
});
return __asyncGenerator(this, arguments, function* f_1() {
_superProps.g.call(this);
_super.g.call(this);
});
}
}