Skip to content

Commit

Permalink
Decorators normative updates (#55276)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Aug 5, 2023
1 parent b1c4dc4 commit 8fc8c95
Show file tree
Hide file tree
Showing 35 changed files with 735 additions and 89 deletions.
4 changes: 3 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32875,7 +32875,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
*/
function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression | undefined {
const expression = node.kind === SyntaxKind.CallExpression ? node.expression :
node.kind === SyntaxKind.TaggedTemplateExpression ? node.tag : undefined;
node.kind === SyntaxKind.TaggedTemplateExpression ? node.tag :
node.kind === SyntaxKind.Decorator && !legacyDecorators ? node.expression :
undefined;
if (expression) {
const callee = skipOuterExpressions(expression);
if (isAccessExpression(callee)) {
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ export function createAccessorPropertyBackingField(factory: NodeFactory, node: P
*
* @internal
*/
export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): GetAccessorDeclaration {
export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: readonly Modifier[] | undefined, name: PropertyName, receiver: Expression = factory.createThis()): GetAccessorDeclaration {
return factory.createGetAccessorDeclaration(
modifiers,
name,
Expand All @@ -1654,7 +1654,7 @@ export function createAccessorPropertyGetRedirector(factory: NodeFactory, node:
factory.createBlock([
factory.createReturnStatement(
factory.createPropertyAccessExpression(
factory.createThis(),
receiver,
factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage")
)
)
Expand All @@ -1667,7 +1667,7 @@ export function createAccessorPropertyGetRedirector(factory: NodeFactory, node:
*
* @internal
*/
export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName) {
export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: readonly Modifier[] | undefined, name: PropertyName, receiver: Expression = factory.createThis()) {
return factory.createSetAccessorDeclaration(
modifiers,
name,
Expand All @@ -1680,7 +1680,7 @@ export function createAccessorPropertySetRedirector(factory: NodeFactory, node:
factory.createExpressionStatement(
factory.createAssignment(
factory.createPropertyAccessExpression(
factory.createThis(),
receiver,
factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage")
),
factory.createIdentifier("value")
Expand Down
26 changes: 19 additions & 7 deletions src/compiler/transformers/classFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ import {
Modifier,
ModifierFlags,
ModifierLike,
modifiersToFlags,
moveRangePastModifiers,
moveRangePos,
newPrivateEnvironment,
Expand Down Expand Up @@ -896,6 +897,12 @@ export function transformClassFields(context: TransformationContext): (x: Source
}
}

function getClassThis() {
const lex = getClassLexicalEnvironment();
const classThis = lex.classThis ?? lex.classConstructor ?? currentClassContainer?.name;
return Debug.checkDefined(classThis);
}

function transformAutoAccessor(node: AutoAccessorPropertyDeclaration): VisitResult<Node> {
// transforms:
// accessor x = 1;
Expand Down Expand Up @@ -935,12 +942,15 @@ export function transformClassFields(context: TransformationContext): (x: Source
setEmitFlags(backingField, EmitFlags.NoComments);
setSourceMapRange(backingField, sourceMapRange);

const getter = createAccessorPropertyGetRedirector(factory, node, modifiers, getterName);
const receiver = isStatic(node) ? getClassThis() : factory.createThis();
const getter = createAccessorPropertyGetRedirector(factory, node, modifiers, getterName, receiver);
setOriginalNode(getter, node);
setCommentRange(getter, commentRange);
setSourceMapRange(getter, sourceMapRange);

const setter = createAccessorPropertySetRedirector(factory, node, modifiers, setterName);
// create a fresh copy of the modifiers so that we don't duplicate comments
const setterModifiers = factory.createModifiersFromModifierFlags(modifiersToFlags(modifiers));
const setter = createAccessorPropertySetRedirector(factory, node, setterModifiers, setterName, receiver);
setOriginalNode(setter, node);
setEmitFlags(setter, EmitFlags.NoComments);
setSourceMapRange(setter, sourceMapRange);
Expand Down Expand Up @@ -1692,11 +1702,13 @@ export function transformClassFields(context: TransformationContext): (x: Source
let containsInstanceAutoAccessors = false;
for (const member of node.members) {
if (isStatic(member)) {
if (member.name &&
(isPrivateIdentifier(member.name) || isAutoAccessorPropertyDeclaration(member)) &&
if (member.name && (isPrivateIdentifier(member.name) || isAutoAccessorPropertyDeclaration(member)) &&
shouldTransformPrivateElementsOrClassStaticBlocks) {
facts |= ClassFacts.NeedsClassConstructorReference;
}
else if (isAutoAccessorPropertyDeclaration(member) && shouldTransformAutoAccessors === Ternary.True && !node.name && !node.emitNode?.classThis) {
facts |= ClassFacts.NeedsClassConstructorReference;
}
if (isPropertyDeclaration(member) || isClassStaticBlockDeclaration(member)) {
if (shouldTransformThisInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalThis) {
facts |= ClassFacts.NeedsSubstitutionForThisInClassStaticField;
Expand Down Expand Up @@ -1846,10 +1858,10 @@ export function transformClassFields(context: TransformationContext): (x: Source
getClassLexicalEnvironment().classConstructor = factory.cloneNode(temp);
pendingClassReferenceAssignment = factory.createAssignment(temp, factory.getInternalName(node));
}
}

if (node.emitNode?.classThis) {
getClassLexicalEnvironment().classThis = node.emitNode.classThis;
}
if (node.emitNode?.classThis) {
getClassLexicalEnvironment().classThis = node.emitNode.classThis;
}

const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ContainsConstructorReference;
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/transformers/esDecorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
getAllDecoratorsOfClassElement,
getCommentRange,
getEffectiveBaseTypeNode,
getEmitScriptTarget,
getFirstConstructorWithBody,
getHeritageClause,
getNonAssignmentOperatorForCompoundAssignment,
Expand All @@ -62,6 +63,7 @@ import {
injectClassNamedEvaluationHelperBlockIfMissing,
injectClassThisAssignmentIfMissing,
InternalEmitFlags,
isAccessExpression,
isAmbientPropertyDeclaration,
isArrayBindingOrAssignmentElement,
isArrayLiteralExpression,
Expand Down Expand Up @@ -289,6 +291,7 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
hoistVariableDeclaration,
} = context;

const languageVersion = getEmitScriptTarget(context.getCompilerOptions());
let top: LexicalEnvironmentStackEntry | undefined;
let classInfo: ClassInfo | undefined;
let classThis: Identifier | undefined;
Expand Down Expand Up @@ -2147,6 +2150,13 @@ export function transformESDecorators(context: TransformationContext): (x: Sourc
function transformDecorator(decorator: Decorator) {
const expression = visitNode(decorator.expression, visitor, isExpression);
setEmitFlags(expression, EmitFlags.NoComments);

// preserve the 'this' binding for an access expression
const innerExpression = skipOuterExpressions(expression);
if (isAccessExpression(innerExpression)) {
const { target, thisArg } = factory.createCallBinding(expression, hoistVariableDeclaration, languageVersion, /*cacheIdentifiers*/ true);
return factory.restoreOuterExpressions(expression, factory.createFunctionBindCall(target, thisArg, []));
}
return expression;
}

Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor1(target=es2015).js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class C1 {
set a(value) { __classPrivateFieldSet(this, _C1_a_accessor_storage, value, "f"); }
get b() { return __classPrivateFieldGet(this, _C1_b_accessor_storage, "f"); }
set b(value) { __classPrivateFieldSet(this, _C1_b_accessor_storage, value, "f"); }
static get c() { return __classPrivateFieldGet(this, _a, "f", _C1_c_accessor_storage); }
static set c(value) { __classPrivateFieldSet(this, _a, value, "f", _C1_c_accessor_storage); }
static get d() { return __classPrivateFieldGet(this, _a, "f", _C1_d_accessor_storage); }
static set d(value) { __classPrivateFieldSet(this, _a, value, "f", _C1_d_accessor_storage); }
static get c() { return __classPrivateFieldGet(_a, _a, "f", _C1_c_accessor_storage); }
static set c(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1_c_accessor_storage); }
static get d() { return __classPrivateFieldGet(_a, _a, "f", _C1_d_accessor_storage); }
static set d(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1_d_accessor_storage); }
}
_a = C1, _C1_a_accessor_storage = new WeakMap(), _C1_b_accessor_storage = new WeakMap();
_C1_c_accessor_storage = { value: void 0 };
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor1(target=es2022).js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class C1 {
get b() { return this.#b_accessor_storage; }
set b(value) { this.#b_accessor_storage = value; }
static #c_accessor_storage;
static get c() { return this.#c_accessor_storage; }
static set c(value) { this.#c_accessor_storage = value; }
static get c() { return C1.#c_accessor_storage; }
static set c(value) { C1.#c_accessor_storage = value; }
static #d_accessor_storage = 2;
static get d() { return this.#d_accessor_storage; }
static set d(value) { this.#d_accessor_storage = value; }
static get d() { return C1.#d_accessor_storage; }
static set d(value) { C1.#d_accessor_storage = value; }
}
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor10.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ class C3 {
}
class C4_1 {
static #a3_accessor_storage = 1;
static get a3() { return this.#a3_accessor_storage; }
static set a3(value) { this.#a3_accessor_storage = value; }
static get a3() { return C4_1.#a3_accessor_storage; }
static set a3(value) { C4_1.#a3_accessor_storage = value; }
}
class C4_2 {
static #a3_accessor_storage = 1;
static get a3() { return this.#a3_accessor_storage; }
static set a3(value) { this.#a3_accessor_storage = value; }
static get a3() { return C4_2.#a3_accessor_storage; }
static set a3(value) { C4_2.#a3_accessor_storage = value; }
}
2 changes: 1 addition & 1 deletion tests/baselines/reference/autoAccessor2(target=es2015).js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class C1 {
__classPrivateFieldSet(this, _C1_instances, 4, "a", _C1_b_set);
}
}
_a = C1, _C1_instances = new WeakSet(), _C1_a_accessor_storage = new WeakMap(), _C1_b_accessor_storage = new WeakMap(), _C1_a_get = function _C1_a_get() { return __classPrivateFieldGet(this, _C1_a_accessor_storage, "f"); }, _C1_a_set = function _C1_a_set(value) { __classPrivateFieldSet(this, _C1_a_accessor_storage, value, "f"); }, _C1_b_get = function _C1_b_get() { return __classPrivateFieldGet(this, _C1_b_accessor_storage, "f"); }, _C1_b_set = function _C1_b_set(value) { __classPrivateFieldSet(this, _C1_b_accessor_storage, value, "f"); }, _C1_c_get = function _C1_c_get() { return __classPrivateFieldGet(this, _a, "f", _C1_c_accessor_storage); }, _C1_c_set = function _C1_c_set(value) { __classPrivateFieldSet(this, _a, value, "f", _C1_c_accessor_storage); }, _C1_d_get = function _C1_d_get() { return __classPrivateFieldGet(this, _a, "f", _C1_d_accessor_storage); }, _C1_d_set = function _C1_d_set(value) { __classPrivateFieldSet(this, _a, value, "f", _C1_d_accessor_storage); };
_a = C1, _C1_instances = new WeakSet(), _C1_a_accessor_storage = new WeakMap(), _C1_b_accessor_storage = new WeakMap(), _C1_a_get = function _C1_a_get() { return __classPrivateFieldGet(this, _C1_a_accessor_storage, "f"); }, _C1_a_set = function _C1_a_set(value) { __classPrivateFieldSet(this, _C1_a_accessor_storage, value, "f"); }, _C1_b_get = function _C1_b_get() { return __classPrivateFieldGet(this, _C1_b_accessor_storage, "f"); }, _C1_b_set = function _C1_b_set(value) { __classPrivateFieldSet(this, _C1_b_accessor_storage, value, "f"); }, _C1_c_get = function _C1_c_get() { return __classPrivateFieldGet(_a, _a, "f", _C1_c_accessor_storage); }, _C1_c_set = function _C1_c_set(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1_c_accessor_storage); }, _C1_d_get = function _C1_d_get() { return __classPrivateFieldGet(_a, _a, "f", _C1_d_accessor_storage); }, _C1_d_set = function _C1_d_set(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1_d_accessor_storage); };
_C1_c_accessor_storage = { value: void 0 };
_C1_d_accessor_storage = { value: 2 };
(() => {
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor2(target=es2022).js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class C1 {
get #b() { return this.#b_accessor_storage; }
set #b(value) { this.#b_accessor_storage = value; }
static #c_accessor_storage;
static get #c() { return this.#c_accessor_storage; }
static set #c(value) { this.#c_accessor_storage = value; }
static get #c() { return C1.#c_accessor_storage; }
static set #c(value) { C1.#c_accessor_storage = value; }
static #d_accessor_storage = 2;
static get #d() { return this.#d_accessor_storage; }
static set #d(value) { this.#d_accessor_storage = value; }
static get #d() { return C1.#d_accessor_storage; }
static set #d(value) { C1.#d_accessor_storage = value; }
constructor() {
this.#a = 3;
this.#b = 4;
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor3(target=es2015).js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class C1 {
set "w"(value) { __classPrivateFieldSet(this, _C1__a_accessor_storage, value, "f"); }
get "x"() { return __classPrivateFieldGet(this, _C1__b_accessor_storage, "f"); }
set "x"(value) { __classPrivateFieldSet(this, _C1__b_accessor_storage, value, "f"); }
static get "y"() { return __classPrivateFieldGet(this, _a, "f", _C1__c_accessor_storage); }
static set "y"(value) { __classPrivateFieldSet(this, _a, value, "f", _C1__c_accessor_storage); }
static get "z"() { return __classPrivateFieldGet(this, _a, "f", _C1__d_accessor_storage); }
static set "z"(value) { __classPrivateFieldSet(this, _a, value, "f", _C1__d_accessor_storage); }
static get "y"() { return __classPrivateFieldGet(_a, _a, "f", _C1__c_accessor_storage); }
static set "y"(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__c_accessor_storage); }
static get "z"() { return __classPrivateFieldGet(_a, _a, "f", _C1__d_accessor_storage); }
static set "z"(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__d_accessor_storage); }
}
_a = C1, _C1__a_accessor_storage = new WeakMap(), _C1__b_accessor_storage = new WeakMap();
_C1__c_accessor_storage = { value: void 0 };
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor3(target=es2022).js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class C1 {
get "x"() { return this.#_b_accessor_storage; }
set "x"(value) { this.#_b_accessor_storage = value; }
static #_c_accessor_storage;
static get "y"() { return this.#_c_accessor_storage; }
static set "y"(value) { this.#_c_accessor_storage = value; }
static get "y"() { return C1.#_c_accessor_storage; }
static set "y"(value) { C1.#_c_accessor_storage = value; }
static #_d_accessor_storage = 2;
static get "z"() { return this.#_d_accessor_storage; }
static set "z"(value) { this.#_d_accessor_storage = value; }
static get "z"() { return C1.#_d_accessor_storage; }
static set "z"(value) { C1.#_d_accessor_storage = value; }
}
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor4(target=es2015).js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ class C1 {
set 0(value) { __classPrivateFieldSet(this, _C1__a_accessor_storage, value, "f"); }
get 1() { return __classPrivateFieldGet(this, _C1__b_accessor_storage, "f"); }
set 1(value) { __classPrivateFieldSet(this, _C1__b_accessor_storage, value, "f"); }
static get 2() { return __classPrivateFieldGet(this, _a, "f", _C1__c_accessor_storage); }
static set 2(value) { __classPrivateFieldSet(this, _a, value, "f", _C1__c_accessor_storage); }
static get 3() { return __classPrivateFieldGet(this, _a, "f", _C1__d_accessor_storage); }
static set 3(value) { __classPrivateFieldSet(this, _a, value, "f", _C1__d_accessor_storage); }
static get 2() { return __classPrivateFieldGet(_a, _a, "f", _C1__c_accessor_storage); }
static set 2(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__c_accessor_storage); }
static get 3() { return __classPrivateFieldGet(_a, _a, "f", _C1__d_accessor_storage); }
static set 3(value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__d_accessor_storage); }
}
_a = C1, _C1__a_accessor_storage = new WeakMap(), _C1__b_accessor_storage = new WeakMap();
_C1__c_accessor_storage = { value: void 0 };
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor4(target=es2022).js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class C1 {
get 1() { return this.#_b_accessor_storage; }
set 1(value) { this.#_b_accessor_storage = value; }
static #_c_accessor_storage;
static get 2() { return this.#_c_accessor_storage; }
static set 2(value) { this.#_c_accessor_storage = value; }
static get 2() { return C1.#_c_accessor_storage; }
static set 2(value) { C1.#_c_accessor_storage = value; }
static #_d_accessor_storage = 2;
static get 3() { return this.#_d_accessor_storage; }
static set 3(value) { this.#_d_accessor_storage = value; }
static get 3() { return C1.#_d_accessor_storage; }
static set 3(value) { C1.#_d_accessor_storage = value; }
}
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor5(target=es2015).js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ class C1 {
set ["w"](value) { __classPrivateFieldSet(this, _C1__a_accessor_storage, value, "f"); }
get ["x"]() { return __classPrivateFieldGet(this, _C1__b_accessor_storage, "f"); }
set ["x"](value) { __classPrivateFieldSet(this, _C1__b_accessor_storage, value, "f"); }
static get ["y"]() { return __classPrivateFieldGet(this, _a, "f", _C1__c_accessor_storage); }
static set ["y"](value) { __classPrivateFieldSet(this, _a, value, "f", _C1__c_accessor_storage); }
static get ["z"]() { return __classPrivateFieldGet(this, _a, "f", _C1__d_accessor_storage); }
static set ["z"](value) { __classPrivateFieldSet(this, _a, value, "f", _C1__d_accessor_storage); }
static get ["y"]() { return __classPrivateFieldGet(_a, _a, "f", _C1__c_accessor_storage); }
static set ["y"](value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__c_accessor_storage); }
static get ["z"]() { return __classPrivateFieldGet(_a, _a, "f", _C1__d_accessor_storage); }
static set ["z"](value) { __classPrivateFieldSet(_a, _a, value, "f", _C1__d_accessor_storage); }
}
_a = C1;
_C1__c_accessor_storage = { value: void 0 };
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/autoAccessor5(target=es2022).js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ class C1 {
get ["x"]() { return this.#_b_accessor_storage; }
set ["x"](value) { this.#_b_accessor_storage = value; }
static #_c_accessor_storage;
static get ["y"]() { return this.#_c_accessor_storage; }
static set ["y"](value) { this.#_c_accessor_storage = value; }
static get ["y"]() { return C1.#_c_accessor_storage; }
static set ["y"](value) { C1.#_c_accessor_storage = value; }
static #_d_accessor_storage = 2;
static get ["z"]() { return this.#_d_accessor_storage; }
static set ["z"](value) { this.#_d_accessor_storage = value; }
static get ["z"]() { return C1.#_d_accessor_storage; }
static set ["z"](value) { C1.#_d_accessor_storage = value; }
}
class C2 {
#_e_accessor_storage = 1;
Expand Down

0 comments on commit 8fc8c95

Please sign in to comment.