mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Fix incorrect 'this' substitution in decorated static 'accessor' fields (#54474)
This commit is contained in:
parent
d495200a4a
commit
4c01b2f9ee
@ -73,6 +73,7 @@ import {
|
||||
Identifier,
|
||||
InKeyword,
|
||||
InternalEmitFlags,
|
||||
isAccessor,
|
||||
isAccessorModifier,
|
||||
isArrayBindingOrAssignmentElement,
|
||||
isArrayLiteralExpression,
|
||||
@ -414,8 +415,11 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
let lexicalEnvironment: LexicalEnv | undefined;
|
||||
const lexicalEnvironmentMap = new Map<Node, LexicalEnv>();
|
||||
|
||||
// Nodes that should not be replaced during emit substitution.
|
||||
const noSubstitution = new Set<Node>();
|
||||
|
||||
let currentClassContainer: ClassLikeDeclaration | undefined;
|
||||
let currentStaticPropertyDeclarationOrStaticBlock: PropertyDeclaration | ClassStaticBlockDeclaration | undefined;
|
||||
let currentClassElement: ClassElement | undefined;
|
||||
let shouldSubstituteThisWithClassThis = false;
|
||||
let previousShouldSubstituteThisWithClassThis = false;
|
||||
|
||||
@ -497,13 +501,19 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
return visitForStatement(node as ForStatement);
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
// If we are descending into a new scope, clear the current class element
|
||||
return setCurrentClassElementAnd(
|
||||
/*classElement*/ undefined,
|
||||
fallbackVisitor,
|
||||
node
|
||||
);
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor: {
|
||||
// If we are descending into a new scope, clear the current static property or block
|
||||
return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
|
||||
/*current*/ undefined,
|
||||
// If we are descending into a class element, set the class element
|
||||
return setCurrentClassElementAnd(
|
||||
node as ConstructorDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration,
|
||||
fallbackVisitor,
|
||||
node
|
||||
);
|
||||
@ -582,21 +592,27 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
function classElementVisitor(node: Node): VisitResult<Node | undefined> {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
return visitConstructorDeclaration(node as ConstructorDeclaration);
|
||||
return setCurrentClassElementAnd(
|
||||
node as ConstructorDeclaration,
|
||||
visitConstructorDeclaration,
|
||||
node as ConstructorDeclaration);
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
|
||||
/*current*/ undefined,
|
||||
return setCurrentClassElementAnd(
|
||||
node as MethodDeclaration | AccessorDeclaration,
|
||||
visitMethodOrAccessorDeclaration,
|
||||
node as MethodDeclaration | AccessorDeclaration);
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
|
||||
/*current*/ undefined,
|
||||
return setCurrentClassElementAnd(
|
||||
node as PropertyDeclaration,
|
||||
visitPropertyDeclaration,
|
||||
node as PropertyDeclaration);
|
||||
case SyntaxKind.ClassStaticBlockDeclaration:
|
||||
return visitClassStaticBlockDeclaration(node as ClassStaticBlockDeclaration);
|
||||
return setCurrentClassElementAnd(
|
||||
node as ClassStaticBlockDeclaration,
|
||||
visitClassStaticBlockDeclaration,
|
||||
node as ClassStaticBlockDeclaration);
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
return visitComputedPropertyName(node as ComputedPropertyName);
|
||||
case SyntaxKind.SemicolonClassElement:
|
||||
@ -881,16 +897,19 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function setCurrentStaticPropertyDeclarationOrStaticBlockAnd<T, U>(
|
||||
current: ClassStaticBlockDeclaration | PropertyDeclaration | undefined,
|
||||
function setCurrentClassElementAnd<T, U>(
|
||||
classElement: ClassElement | undefined,
|
||||
visitor: (arg: T) => U,
|
||||
arg: T,
|
||||
) {
|
||||
const savedCurrentStaticPropertyDeclarationOrStaticBlock = currentStaticPropertyDeclarationOrStaticBlock;
|
||||
currentStaticPropertyDeclarationOrStaticBlock = current;
|
||||
const result = visitor(arg);
|
||||
currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock;
|
||||
return result;
|
||||
if (classElement !== currentClassElement) {
|
||||
const savedCurrentClassElement = currentClassElement;
|
||||
currentClassElement = classElement;
|
||||
const result = visitor(arg);
|
||||
currentClassElement = savedCurrentClassElement;
|
||||
return result;
|
||||
}
|
||||
return visitor(arg);
|
||||
}
|
||||
|
||||
function getHoistedFunctionName(node: MethodDeclaration | AccessorDeclaration) {
|
||||
@ -1095,8 +1114,31 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
return transformFieldInitializer(node);
|
||||
}
|
||||
|
||||
function shouldForceDynamicThis() {
|
||||
return !!currentClassElement &&
|
||||
hasStaticModifier(currentClassElement) &&
|
||||
isAccessor(currentClassElement) &&
|
||||
isAutoAccessorPropertyDeclaration(getOriginalNode(currentClassElement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent substitution of `this` to `_classThis` in static getters and setters that wrap `accessor` fields.
|
||||
*/
|
||||
function ensureDynamicThisIfNeeded(node: Expression) {
|
||||
if (shouldForceDynamicThis()) {
|
||||
// do not substitute `this` with `_classThis` when `this`
|
||||
// should be bound dynamically.
|
||||
const innerExpression = skipOuterExpressions(node);
|
||||
if (innerExpression.kind === SyntaxKind.ThisKeyword) {
|
||||
noSubstitution.add(innerExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createPrivateIdentifierAccess(info: PrivateIdentifierInfo, receiver: Expression): Expression {
|
||||
return createPrivateIdentifierAccessHelper(info, visitNode(receiver, visitor, isExpression));
|
||||
receiver = visitNode(receiver, visitor, isExpression);
|
||||
ensureDynamicThisIfNeeded(receiver);
|
||||
return createPrivateIdentifierAccessHelper(info, receiver);
|
||||
}
|
||||
|
||||
function createPrivateIdentifierAccessHelper(info: PrivateIdentifierInfo, receiver: Expression): Expression {
|
||||
@ -1146,9 +1188,10 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
}
|
||||
if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node) &&
|
||||
isIdentifier(node.name) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data) {
|
||||
const { classConstructor, superClassReference, facts } = lexicalEnvironment.data;
|
||||
if (facts & ClassFacts.ClassWasDecorated) {
|
||||
@ -1171,8 +1214,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
|
||||
function visitElementAccessExpression(node: ElementAccessExpression) {
|
||||
if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data) {
|
||||
const { classConstructor, superClassReference, facts } = lexicalEnvironment.data;
|
||||
if (facts & ClassFacts.ClassWasDecorated) {
|
||||
@ -1203,6 +1247,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
let info: PrivateIdentifierInfo | undefined;
|
||||
if (info = accessPrivateIdentifier(operand.name)) {
|
||||
const receiver = visitNode(operand.expression, visitor, isExpression);
|
||||
ensureDynamicThisIfNeeded(receiver);
|
||||
const { readExpression, initializeExpression } = createCopiableReceiverExpr(receiver);
|
||||
|
||||
let expression: Expression = createPrivateIdentifierAccess(info, readExpression);
|
||||
@ -1224,8 +1269,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
}
|
||||
else if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(operand) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data) {
|
||||
// converts `++super.a` into `(Reflect.set(_baseTemp, "a", (_a = Reflect.get(_baseTemp, "a", _classTemp), _b = ++_a), _classTemp), _b)`
|
||||
// converts `++super[f()]` into `(Reflect.set(_baseTemp, _a = f(), (_b = Reflect.get(_baseTemp, _a, _classTemp), _c = ++_b), _classTemp), _c)`
|
||||
@ -1299,6 +1345,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
|
||||
function createCopiableReceiverExpr(receiver: Expression): { readExpression: Expression; initializeExpression: Expression | undefined } {
|
||||
const clone = nodeIsSynthesized(receiver) ? receiver : factory.cloneNode(receiver);
|
||||
if (receiver.kind === SyntaxKind.ThisKeyword && noSubstitution.has(receiver)) {
|
||||
noSubstitution.add(clone);
|
||||
}
|
||||
if (isSimpleInlineableExpression(receiver)) {
|
||||
return { readExpression: clone, initializeExpression: undefined };
|
||||
}
|
||||
@ -1332,8 +1381,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
|
||||
if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node.expression) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data?.classConstructor) {
|
||||
// super.x()
|
||||
// super[x]()
|
||||
@ -1369,8 +1419,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
);
|
||||
}
|
||||
if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node.tag) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data?.classConstructor) {
|
||||
|
||||
// converts `` super.f`x` `` into `` Reflect.get(_baseTemp, "f", _classTemp).bind(_classTemp)`x` ``
|
||||
@ -1398,7 +1449,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
|
||||
if (shouldTransformPrivateElementsOrClassStaticBlocks) {
|
||||
startLexicalEnvironment();
|
||||
let statements = setCurrentStaticPropertyDeclarationOrStaticBlockAnd(
|
||||
let statements = setCurrentClassElementAnd(
|
||||
node,
|
||||
statements => visitNodes(statements, visitor, isStatement),
|
||||
node.body.statements
|
||||
@ -1506,8 +1557,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
}
|
||||
else if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node.left) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data) {
|
||||
// super.x = ...
|
||||
// super[x] = ...
|
||||
@ -1641,6 +1693,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
function createPrivateIdentifierAssignment(info: PrivateIdentifierInfo, receiver: Expression, right: Expression, operator: AssignmentOperator): Expression {
|
||||
receiver = visitNode(receiver, visitor, isExpression);
|
||||
right = visitNode(right, visitor, isExpression);
|
||||
ensureDynamicThisIfNeeded(receiver);
|
||||
|
||||
if (isCompoundAssignment(operator)) {
|
||||
const { readExpression, initializeExpression } = createCopiableReceiverExpr(receiver);
|
||||
@ -2420,7 +2473,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
* @param receiver The object receiving the property assignment.
|
||||
*/
|
||||
function transformProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) {
|
||||
const savedCurrentStaticPropertyDeclarationOrStaticBlock = currentStaticPropertyDeclarationOrStaticBlock;
|
||||
const savedCurrentClassElement = currentClassElement;
|
||||
const transformed = transformPropertyWorker(property, receiver);
|
||||
if (transformed && hasStaticModifier(property) && lexicalEnvironment?.data?.facts) {
|
||||
// capture the lexical environment for the member
|
||||
@ -2429,7 +2482,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
setSourceMapRange(transformed, getSourceMapRange(property.name));
|
||||
lexicalEnvironmentMap.set(getOriginalNode(property), lexicalEnvironment);
|
||||
}
|
||||
currentStaticPropertyDeclarationOrStaticBlock = savedCurrentStaticPropertyDeclarationOrStaticBlock;
|
||||
currentClassElement = savedCurrentClassElement;
|
||||
return transformed;
|
||||
}
|
||||
|
||||
@ -2458,7 +2511,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
property.name;
|
||||
|
||||
if (hasStaticModifier(property)) {
|
||||
currentStaticPropertyDeclarationOrStaticBlock = property;
|
||||
currentClassElement = property;
|
||||
}
|
||||
|
||||
const initializerVisitor: Visitor =
|
||||
@ -2931,8 +2984,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
return wrapPrivateIdentifierForDestructuringTarget(node);
|
||||
}
|
||||
else if (shouldTransformSuperInStaticInitializers &&
|
||||
currentClassElement &&
|
||||
isSuperProperty(node) &&
|
||||
currentStaticPropertyDeclarationOrStaticBlock &&
|
||||
isStaticPropertyDeclarationOrClassStaticBlock(currentClassElement) &&
|
||||
lexicalEnvironment?.data) {
|
||||
const { classConstructor, superClassReference, facts } = lexicalEnvironment.data;
|
||||
if (facts & ClassFacts.ClassWasDecorated) {
|
||||
@ -3164,6 +3218,7 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
*/
|
||||
function onSubstituteNode(hint: EmitHint, node: Node) {
|
||||
node = previousOnSubstituteNode(hint, node);
|
||||
|
||||
if (hint === EmitHint.Expression) {
|
||||
return substituteExpression(node as Expression);
|
||||
}
|
||||
@ -3181,7 +3236,9 @@ export function transformClassFields(context: TransformationContext): (x: Source
|
||||
}
|
||||
|
||||
function substituteThisExpression(node: ThisExpression) {
|
||||
if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference && lexicalEnvironment?.data) {
|
||||
if (enabledSubstitutions & ClassPropertySubstitutionFlags.ClassStaticThisOrSuperReference &&
|
||||
lexicalEnvironment?.data &&
|
||||
!noSubstitution.has(node)) {
|
||||
const { facts, classConstructor, classThis } = lexicalEnvironment.data;
|
||||
if (facts & ClassFacts.ClassWasDecorated && legacyDecorators) {
|
||||
return factory.createParenthesizedExpression(factory.createVoidZero());
|
||||
@ -3264,3 +3321,11 @@ function isPrivateIdentifierInExpression(node: BinaryExpression): node is Privat
|
||||
return isPrivateIdentifier(node.left)
|
||||
&& node.operatorToken.kind === SyntaxKind.InKeyword;
|
||||
}
|
||||
|
||||
function isStaticPropertyDeclaration(node: Node): node is PropertyDeclaration {
|
||||
return isPropertyDeclaration(node) && hasStaticModifier(node);
|
||||
}
|
||||
|
||||
function isStaticPropertyDeclarationOrClassStaticBlock(node: Node): node is ClassStaticBlockDeclaration | PropertyDeclaration {
|
||||
return isClassStaticBlockDeclaration(node) || isStaticPropertyDeclaration(node);
|
||||
}
|
||||
@ -19,8 +19,8 @@ let C = (() => {
|
||||
let _classExtraInitializers = [];
|
||||
let _classThis;
|
||||
var C = _classThis = class {
|
||||
static get a() { return __classPrivateFieldGet(_classThis, _classThis, "f", _a_accessor_storage); }
|
||||
static set a(value) { __classPrivateFieldSet(_classThis, _classThis, value, "f", _a_accessor_storage); }
|
||||
static get a() { return __classPrivateFieldGet(this, _classThis, "f", _a_accessor_storage); }
|
||||
static set a(value) { __classPrivateFieldSet(this, _classThis, value, "f", _a_accessor_storage); }
|
||||
static m() { this; }
|
||||
static get g() { return this; }
|
||||
};
|
||||
|
||||
@ -31,12 +31,12 @@ let C = (() => {
|
||||
let _static_member_decorators_1;
|
||||
let _static_member_initializers_1 = [];
|
||||
return _a = class C {
|
||||
static get field1() { return __classPrivateFieldGet(_a, _a, "f", _C_field1_accessor_storage); }
|
||||
static set field1(value) { __classPrivateFieldSet(_a, _a, value, "f", _C_field1_accessor_storage); }
|
||||
static get ["field2"]() { return __classPrivateFieldGet(_a, _a, "f", _C__a_accessor_storage); }
|
||||
static set ["field2"](value) { __classPrivateFieldSet(_a, _a, value, "f", _C__a_accessor_storage); }
|
||||
static get [(_static_field1_decorators = [dec(1)], _static_member_decorators = [dec(2)], _static_member_decorators_1 = [dec(3)], _b = __propKey(field3))]() { return __classPrivateFieldGet(_a, _a, "f", _C__b_accessor_storage); }
|
||||
static set [_b](value) { __classPrivateFieldSet(_a, _a, value, "f", _C__b_accessor_storage); }
|
||||
static get field1() { return __classPrivateFieldGet(this, _a, "f", _C_field1_accessor_storage); }
|
||||
static set field1(value) { __classPrivateFieldSet(this, _a, value, "f", _C_field1_accessor_storage); }
|
||||
static get ["field2"]() { return __classPrivateFieldGet(this, _a, "f", _C__a_accessor_storage); }
|
||||
static set ["field2"](value) { __classPrivateFieldSet(this, _a, value, "f", _C__a_accessor_storage); }
|
||||
static get [(_static_field1_decorators = [dec(1)], _static_member_decorators = [dec(2)], _static_member_decorators_1 = [dec(3)], _b = __propKey(field3))]() { return __classPrivateFieldGet(this, _a, "f", _C__b_accessor_storage); }
|
||||
static set [_b](value) { __classPrivateFieldSet(this, _a, value, "f", _C__b_accessor_storage); }
|
||||
},
|
||||
(() => {
|
||||
__esDecorate(_a, null, _static_field1_decorators, { kind: "accessor", name: "field1", static: true, private: false, access: { has: obj => "field1" in obj, get: obj => obj.field1, set: (obj, value) => { obj.field1 = value; } } }, _static_field1_initializers, _staticExtraInitializers);
|
||||
@ -56,8 +56,8 @@ let D = (() => {
|
||||
let _classExtraInitializers = [];
|
||||
let _classThis;
|
||||
var D = _classThis = class {
|
||||
static get field1() { return __classPrivateFieldGet(_classThis, _classThis, "f", _field1_accessor_storage); }
|
||||
static set field1(value) { __classPrivateFieldSet(_classThis, _classThis, value, "f", _field1_accessor_storage); }
|
||||
static get field1() { return __classPrivateFieldGet(this, _classThis, "f", _field1_accessor_storage); }
|
||||
static set field1(value) { __classPrivateFieldSet(this, _classThis, value, "f", _field1_accessor_storage); }
|
||||
};
|
||||
__setFunctionName(_classThis, "D");
|
||||
(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user