Fix over-aggressive transformation of super properties

This commit is contained in:
Ron Buckton 2018-02-01 15:22:34 -08:00
parent 421c927150
commit 94b2d40cac
3 changed files with 64 additions and 59 deletions

View File

@ -3439,64 +3439,71 @@ namespace ts {
function visitCallExpressionWithPotentialCapturedThisAssignment(node: CallExpression, assignToCapturedThis: boolean): CallExpression | BinaryExpression {
// We are here either because SuperKeyword was used somewhere in the expression, or
// because we contain a SpreadElementExpression.
if (node.transformFlags & TransformFlags.ContainsSpread ||
node.expression.kind === SyntaxKind.SuperKeyword ||
isSuperProperty(skipOuterExpressions(node.expression))) {
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
setEmitFlags(thisArg, EmitFlags.NoSubstitution);
}
let resultingCall: CallExpression | BinaryExpression;
if (node.transformFlags & TransformFlags.ContainsSpread) {
// [source]
// f(...a, b)
// x.m(...a, b)
// super(...a, b)
// super.m(...a, b) // in static
// super.m(...a, b) // in instance
//
// [output]
// f.apply(void 0, a.concat([b]))
// (_a = x).m.apply(_a, a.concat([b]))
// _super.apply(this, a.concat([b]))
// _super.m.apply(this, a.concat([b]))
// _super.prototype.m.apply(this, a.concat([b]))
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
setEmitFlags(thisArg, EmitFlags.NoSubstitution);
}
resultingCall = createFunctionApply(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*hasTrailingComma*/ false)
);
}
else {
// [source]
// super(a)
// super.m(a) // in static
// super.m(a) // in instance
//
// [output]
// _super.call(this, a)
// _super.m.call(this, a)
// _super.prototype.m.call(this, a)
resultingCall = createFunctionCall(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
visitNodes(node.arguments, visitor, isExpression),
/*location*/ node
);
}
let resultingCall: CallExpression | BinaryExpression;
if (node.transformFlags & TransformFlags.ContainsSpread) {
// [source]
// f(...a, b)
// x.m(...a, b)
// super(...a, b)
// super.m(...a, b) // in static
// super.m(...a, b) // in instance
//
// [output]
// f.apply(void 0, a.concat([b]))
// (_a = x).m.apply(_a, a.concat([b]))
// _super.apply(this, a.concat([b]))
// _super.m.apply(this, a.concat([b]))
// _super.prototype.m.apply(this, a.concat([b]))
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const actualThis = createThis();
setEmitFlags(actualThis, EmitFlags.NoSubstitution);
const initializer =
createLogicalOr(
resultingCall,
actualThis
resultingCall = createFunctionApply(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
transformAndSpreadElements(node.arguments, /*needsUniqueCopy*/ false, /*multiLine*/ false, /*hasTrailingComma*/ false)
);
resultingCall = assignToCapturedThis
? createAssignment(createIdentifier("_this"), initializer)
: initializer;
}
else {
// [source]
// super(a)
// super.m(a) // in static
// super.m(a) // in instance
//
// [output]
// _super.call(this, a)
// _super.m.call(this, a)
// _super.prototype.m.call(this, a)
resultingCall = createFunctionCall(
visitNode(target, callExpressionVisitor, isExpression),
visitNode(thisArg, visitor, isExpression),
visitNodes(node.arguments, visitor, isExpression),
/*location*/ node
);
}
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const actualThis = createThis();
setEmitFlags(actualThis, EmitFlags.NoSubstitution);
const initializer =
createLogicalOr(
resultingCall,
actualThis
);
resultingCall = assignToCapturedThis
? createAssignment(createIdentifier("_this"), initializer)
: initializer;
}
return setOriginalNode(resultingCall, node);
}
return setOriginalNode(resultingCall, node);
return visitEachChild(node, visitor, context);
}
/**

View File

@ -69,16 +69,15 @@ var MyDerived = /** @class */ (function (_super) {
}
MyDerived.prototype.foo = function () {
_super.prototype["m1"].call(this, "hi"); // Should be allowed, method on base prototype
var l2 = (_a = _super.prototype["m1"]).bind.call(_a, this); // Should be allowed, can access properties as well as invoke
var l2 = _super.prototype["m1"].bind(this); // Should be allowed, can access properties as well as invoke
var x = _super.prototype["m1"]; // Should be allowed, can assign to var with compatible signature
(_b = _super.prototype["m2"]).bind.call(_b, this); // Should error, instance property, not a public instance member function
_super.prototype["m2"].bind(this); // Should error, instance property, not a public instance member function
_super.prototype["p1"].call(this); // Should error, private not public instance member function
var l1 = _super.prototype["d1"]; // Should error, instance data property not a public instance member function
var l1 = _super.prototype["d2"]; // Should error, instance data property not a public instance member function
_super.prototype["m1"] = function (a) { return ""; }; // Should be allowed, we will not restrict assignment
_super.prototype["value"] = 0; // Should error, instance data property not a public instance member function
var z = _super.prototype["value"]; // Should error, instance data property not a public instance member function
var _a, _b;
};
return MyDerived;
}(MyBase));

View File

@ -69,16 +69,15 @@ var MyDerived = /** @class */ (function (_super) {
}
MyDerived.prototype.foo = function () {
_super.prototype.m1.call(this, "hi"); // Should be allowed, method on base prototype
var l2 = (_a = _super.prototype.m1).bind.call(_a, this); // Should be allowed, can access properties as well as invoke
var l2 = _super.prototype.m1.bind(this); // Should be allowed, can access properties as well as invoke
var x = _super.prototype.m1; // Should be allowed, can assign to var with compatible signature
(_b = _super.prototype.m2).bind.call(_b, this); // Should error, instance property, not a public instance member function
_super.prototype.m2.bind(this); // Should error, instance property, not a public instance member function
_super.prototype.p1.call(this); // Should error, private not public instance member function
var l1 = _super.prototype.d1; // Should error, instance data property not a public instance member function
var l1 = _super.prototype.d2; // Should error, instance data property not a public instance member function
_super.prototype.m1 = function (a) { return ""; }; // Should be allowed, we will not restrict assignment
_super.prototype.value = 0; // Should error, instance data property not a public instance member function
var z = _super.prototype.value; // Should error, instance data property not a public instance member function
var _a, _b;
};
return MyDerived;
}(MyBase));