diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9194f03407e..07474bbb2ab 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1365,8 +1365,10 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri let privateNameTempFlags: TempFlags; // TempFlags for the current name generation scope. let tempFlagsStack: TempFlags[]; // Stack of enclosing name generation scopes. let tempFlags: TempFlags; // TempFlags for the current name generation scope. - let reservedNamesStack: Set[]; // Stack of TempFlags reserved in enclosing name generation scopes. - let reservedNames: Set; // TempFlags to reserve in nested name generation scopes. + let reservedNamesStack: (Set | undefined)[]; // Stack of reserved names in enclosing name generation scopes. + let reservedNames: Set | undefined; // Names reserved in nested name generation scopes. + let reservedPrivateNamesStack: (Set | undefined)[]; // Stack of reserved member names in enclosing name generation scopes. + let reservedPrivateNames: Set | undefined; // Member names reserved in nested name generation scopes. let preserveSourceNewlines = printerOptions.preserveSourceNewlines; // Can be overridden inside nodes with the `IgnoreSourceNewlines` emit flag. let nextListElementPos: number | undefined; // See comment in `getLeadingLineTerminatorCount`. @@ -1658,6 +1660,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri tempFlagsStack = []; tempFlags = TempFlags.Auto; reservedNamesStack = []; + reservedNames = undefined; + reservedPrivateNamesStack = []; + reservedPrivateNames = undefined; currentSourceFile = undefined; currentLineMap = undefined; detachedCommentsInfo = undefined; @@ -2501,9 +2506,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function emitComputedPropertyName(node: ComputedPropertyName) { + const savedPrivateNameTempFlags = privateNameTempFlags; + const savedReservedMemberNames = reservedPrivateNames; + popPrivateNameGenerationScope(); writePunctuation("["); emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfComputedPropertyName); writePunctuation("]"); + pushPrivateNameGenerationScope(savedPrivateNameTempFlags, savedReservedMemberNames); } // @@ -2723,10 +2732,16 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function emitTypeLiteral(node: TypeLiteralNode) { + // Type literals don't have private names, but we need to push a new scope so that + // we can step out of it when emitting a computed property. + pushPrivateNameGenerationScope(TempFlags.Auto, /*newReservedMemberNames*/ undefined); + writePunctuation("{"); const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers; emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty); writePunctuation("}"); + + popPrivateNameGenerationScope(); } function emitArrayType(node: ArrayTypeNode) { @@ -2943,6 +2958,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function emitObjectLiteralExpression(node: ObjectLiteralExpression) { + // Object literals don't have private names, but we need to push a new scope so that + // we can step out of it when emitting a computed property. + pushPrivateNameGenerationScope(TempFlags.Auto, /*newReservedMemberNames*/ undefined); forEach(node.properties, generateMemberNames); const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; @@ -2957,6 +2975,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (indentedFlag) { decreaseIndent(); } + + popPrivateNameGenerationScope(); } function emitPropertyAccessExpression(node: PropertyAccessExpression) { @@ -3763,6 +3783,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } function emitClassDeclarationOrExpression(node: ClassDeclaration | ClassExpression) { + pushPrivateNameGenerationScope(TempFlags.Auto, /*newReservedMemberNames*/ undefined); + forEach(node.members, generateMemberNames); emitDecoratorsAndModifiers(node, node.modifiers); @@ -3788,9 +3810,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (indentedFlag) { decreaseIndent(); } + + popPrivateNameGenerationScope(); } function emitInterfaceDeclaration(node: InterfaceDeclaration) { + // Interfaces don't have private names, but we need to push a new scope so that + // we can step out of it when emitting a computed property. + pushPrivateNameGenerationScope(TempFlags.Auto, /*newReservedMemberNames*/ undefined); + emitModifiers(node, node.modifiers); writeKeyword("interface"); writeSpace(); @@ -3801,6 +3829,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri writePunctuation("{"); emitList(node, node.members, ListFormat.InterfaceMembers); writePunctuation("}"); + + popPrivateNameGenerationScope(); } function emitTypeAliasDeclaration(node: TypeAliasDeclaration) { @@ -5486,8 +5516,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } tempFlagsStack.push(tempFlags); tempFlags = TempFlags.Auto; - privateNameTempFlagsStack.push(privateNameTempFlags); - privateNameTempFlags = TempFlags.Auto; formattedNameTempFlagsStack.push(formattedNameTempFlags); formattedNameTempFlags = undefined; reservedNamesStack.push(reservedNames); @@ -5501,9 +5529,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri return; } tempFlags = tempFlagsStack.pop()!; - privateNameTempFlags = privateNameTempFlagsStack.pop()!; formattedNameTempFlags = formattedNameTempFlagsStack.pop(); - reservedNames = reservedNamesStack.pop()!; + reservedNames = reservedNamesStack.pop(); } function reserveNameInNestedScopes(name: string) { @@ -5513,6 +5540,31 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri reservedNames.add(name); } + /** + * Push a new member name generation scope. + */ + function pushPrivateNameGenerationScope(newPrivateNameTempFlags: TempFlags, newReservedMemberNames: Set | undefined) { + privateNameTempFlagsStack.push(privateNameTempFlags); + privateNameTempFlags = newPrivateNameTempFlags; + reservedPrivateNamesStack.push(reservedNames); + reservedPrivateNames = newReservedMemberNames; + } + + /** + * Pop the current member name generation scope. + */ + function popPrivateNameGenerationScope() { + privateNameTempFlags = privateNameTempFlagsStack.pop()!; + reservedPrivateNames = reservedPrivateNamesStack.pop(); + } + + function reservePrivateNameInNestedScopes(name: string) { + if (!reservedPrivateNames || reservedPrivateNames === lastOrUndefined(reservedPrivateNamesStack)) { + reservedPrivateNames = new Set(); + } + reservedPrivateNames.add(name); + } + function generateNames(node: Node | undefined) { if (!node) return; switch (node.kind) { @@ -5650,16 +5702,23 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * Returns a value indicating whether a name is unique globally, within the current file, * or within the NameGenerator. */ - function isUniqueName(name: string): boolean { - return isFileLevelUniqueName(name) - && !generatedNames.has(name) - && !(reservedNames && reservedNames.has(name)); + function isUniqueName(name: string, privateName: boolean): boolean { + return isFileLevelUniqueName(name, privateName) + && !isReservedName(name, privateName) + && !generatedNames.has(name); + } + + function isReservedName(name: string, privateName: boolean): boolean { + return privateName ? !!reservedPrivateNames?.has(name) : !!reservedNames?.has(name); } /** * Returns a value indicating whether a name is unique globally or within the current file. + * + * @param _isPrivate (unused) this parameter exists to avoid an unnecessary adaptor frame in v8 + * when `isfileLevelUniqueName` is passed as a callback to `makeUniqueName`. */ - function isFileLevelUniqueName(name: string) { + function isFileLevelUniqueName(name: string, _isPrivate: boolean) { return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true; } @@ -5722,9 +5781,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (flags && !(tempFlags & flags)) { const name = flags === TempFlags._i ? "_i" : "_n"; const fullName = formatGeneratedName(privateName, prefix, name, suffix); - if (isUniqueName(fullName)) { + if (isUniqueName(fullName, privateName)) { tempFlags |= flags; - if (reservedInNestedScopes) { + if (privateName) { + reservePrivateNameInNestedScopes(fullName); + } + else if (reservedInNestedScopes) { reserveNameInNestedScopes(fullName); } setTempFlags(key, tempFlags); @@ -5741,8 +5803,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri ? "_" + String.fromCharCode(CharacterCodes.a + count) : "_" + (count - 26); const fullName = formatGeneratedName(privateName, prefix, name, suffix); - if (isUniqueName(fullName)) { - if (reservedInNestedScopes) { + if (isUniqueName(fullName, privateName)) { + if (privateName) { + reservePrivateNameInNestedScopes(fullName); + } + else if (reservedInNestedScopes) { reserveNameInNestedScopes(fullName); } setTempFlags(key, tempFlags); @@ -5759,7 +5824,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * makeUniqueName are guaranteed to never conflict. * If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1' */ - function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic: boolean, scoped: boolean, privateName: boolean, prefix: string, suffix: string): string { + function makeUniqueName(baseName: string, checkFn: (name: string, privateName: boolean) => boolean = isUniqueName, optimistic: boolean, scoped: boolean, privateName: boolean, prefix: string, suffix: string): string { if (baseName.length > 0 && baseName.charCodeAt(0) === CharacterCodes.hash) { baseName = baseName.slice(1); } @@ -5768,8 +5833,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } if (optimistic) { const fullName = formatGeneratedName(privateName, prefix, baseName, suffix); - if (checkFn(fullName)) { - if (scoped) { + if (checkFn(fullName, privateName)) { + if (privateName) { + reservePrivateNameInNestedScopes(fullName); + } + else if (scoped) { reserveNameInNestedScopes(fullName); } else { @@ -5785,8 +5853,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri let i = 1; while (true) { const fullName = formatGeneratedName(privateName, prefix, baseName + i, suffix); - if (checkFn(fullName)) { - if (scoped) { + if (checkFn(fullName, privateName)) { + if (privateName) { + reservePrivateNameInNestedScopes(fullName); + } + else if (scoped) { reserveNameInNestedScopes(fullName); } else { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d2f481e50dd..25319bfba5b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1454,7 +1454,6 @@ namespace Parser { let currentToken: SyntaxKind; let nodeCount: number; let identifiers: Map; - let privateIdentifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; @@ -1681,7 +1680,6 @@ namespace Parser { parseDiagnostics = []; parsingContext = 0; identifiers = new Map(); - privateIdentifiers = new Map(); identifierCount = 0; nodeCount = 0; sourceFlags = 0; @@ -2661,17 +2659,9 @@ namespace Parser { return finishNode(factory.createComputedPropertyName(expression), pos); } - function internPrivateIdentifier(text: string): string { - let privateIdentifier = privateIdentifiers.get(text); - if (privateIdentifier === undefined) { - privateIdentifiers.set(text, privateIdentifier = text); - } - return privateIdentifier; - } - function parsePrivateIdentifier(): PrivateIdentifier { const pos = getNodePos(); - const node = factory.createPrivateIdentifier(internPrivateIdentifier(scanner.getTokenValue())); + const node = factory.createPrivateIdentifier(internIdentifier(scanner.getTokenValue())); nextToken(); return finishNode(node, pos); } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 50fb29df0a7..9ef101538df 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -155,6 +155,7 @@ import { SuperProperty, SyntaxKind, TaggedTemplateExpression, + Ternary, ThisExpression, TransformationContext, TransformFlags, @@ -293,6 +294,7 @@ const enum ClassFacts { NeedsClassConstructorReference = 1 << 1, NeedsClassSuperReference = 1 << 2, NeedsSubstitutionForThisInClassStaticField = 1 << 3, + WillHoistInitializersToConstructor = 1 << 4, } /** @@ -328,8 +330,12 @@ export function transformClassFields(context: TransformationContext): (x: Source // We need to transform private members and class static blocks when target < ES2022. const shouldTransformPrivateElementsOrClassStaticBlocks = languageVersion < ScriptTarget.ES2022; - // We need to transform `accessor` fields when target < ESNext - const shouldTransformAutoAccessors = languageVersion < ScriptTarget.ESNext; + // We need to transform `accessor` fields when target < ESNext. + // We may need to transform `accessor` fields when `useDefineForClassFields: false` + const shouldTransformAutoAccessors = + languageVersion < ScriptTarget.ESNext ? Ternary.True : + !useDefineForClassFields ? Ternary.Maybe : + Ternary.False; // We need to transform `this` in a static initializer into a reference to the class // when target < ES2022 since the assignment will be moved outside of the class body. @@ -342,7 +348,7 @@ export function transformClassFields(context: TransformationContext): (x: Source const shouldTransformAnything = shouldTransformInitializers || shouldTransformPrivateElementsOrClassStaticBlocks || - shouldTransformAutoAccessors; + shouldTransformAutoAccessors === Ternary.True; const previousOnSubstituteNode = context.onSubstituteNode; context.onSubstituteNode = onSubstituteNode; @@ -394,7 +400,7 @@ export function transformClassFields(context: TransformationContext): (x: Source switch (node.kind) { case SyntaxKind.AccessorKeyword: - return shouldTransformAutoAccessors ? undefined : node; + return shouldTransformAutoAccessorsInCurrentClass() ? undefined : node; case SyntaxKind.ClassDeclaration: return visitClassDeclaration(node as ClassDeclaration); case SyntaxKind.ClassExpression: @@ -730,7 +736,7 @@ export function transformClassFields(context: TransformationContext): (x: Source return info.isValid ? undefined : node; } - if (shouldTransformInitializersUsingSet && !isStatic(node)) { + if (shouldTransformInitializersUsingSet && !isStatic(node) && currentClassLexicalEnvironment && currentClassLexicalEnvironment.facts & ClassFacts.WillHoistInitializersToConstructor) { // If we are transforming initializers using Set semantics we will elide the initializer as it will // be moved to the constructor to preserve evaluation order next to public instance fields. We don't // need to do this transformation for private static fields since public static fields can be @@ -749,7 +755,7 @@ export function transformClassFields(context: TransformationContext): (x: Source } function transformPublicFieldInitializer(node: PropertyDeclaration) { - if (shouldTransformInitializers) { + if (shouldTransformInitializers && !isAutoAccessorPropertyDeclaration(node)) { // Create a temporary variable to store a computed property name (if necessary). // If it's not inlineable, then we emit an expression after the class which assigns // the property name to the temporary variable. @@ -791,10 +797,16 @@ export function transformClassFields(context: TransformationContext): (x: Source transformPublicFieldInitializer(node); } + function shouldTransformAutoAccessorsInCurrentClass() { + return shouldTransformAutoAccessors === Ternary.True || + shouldTransformAutoAccessors === Ternary.Maybe && + !!currentClassLexicalEnvironment && !!(currentClassLexicalEnvironment.facts & ClassFacts.WillHoistInitializersToConstructor); + } + function visitPropertyDeclaration(node: PropertyDeclaration) { // If this is an auto-accessor, we defer to `transformAutoAccessor`. That function // will in turn call `transformFieldInitializer` as needed. - if (shouldTransformAutoAccessors && isAutoAccessorPropertyDeclaration(node)) { + if (shouldTransformAutoAccessorsInCurrentClass() && isAutoAccessorPropertyDeclaration(node)) { return transformAutoAccessor(node); } @@ -1289,25 +1301,54 @@ export function transformClassFields(context: TransformationContext): (x: Source if (isClassDeclaration(original) && classOrConstructorParameterIsDecorated(original)) { facts |= ClassFacts.ClassWasDecorated; } + let containsPublicInstanceFields = false; + let containsInitializedPublicInstanceFields = false; + let containsInstancePrivateElements = false; + let containsInstanceAutoAccessors = false; for (const member of node.members) { - if (!isStatic(member)) continue; - if (member.name && (isPrivateIdentifier(member.name) || isAutoAccessorPropertyDeclaration(member)) && shouldTransformPrivateElementsOrClassStaticBlocks) { - facts |= ClassFacts.NeedsClassConstructorReference; - } - if (isPropertyDeclaration(member) || isClassStaticBlockDeclaration(member)) { - if (shouldTransformThisInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalThis) { - facts |= ClassFacts.NeedsSubstitutionForThisInClassStaticField; - if (!(facts & ClassFacts.ClassWasDecorated)) { - facts |= ClassFacts.NeedsClassConstructorReference; + if (isStatic(member)) { + if (member.name && (isPrivateIdentifier(member.name) || isAutoAccessorPropertyDeclaration(member)) && shouldTransformPrivateElementsOrClassStaticBlocks) { + facts |= ClassFacts.NeedsClassConstructorReference; + } + if (isPropertyDeclaration(member) || isClassStaticBlockDeclaration(member)) { + if (shouldTransformThisInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalThis) { + facts |= ClassFacts.NeedsSubstitutionForThisInClassStaticField; + if (!(facts & ClassFacts.ClassWasDecorated)) { + facts |= ClassFacts.NeedsClassConstructorReference; + } + } + if (shouldTransformSuperInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalSuper) { + if (!(facts & ClassFacts.ClassWasDecorated)) { + facts |= ClassFacts.NeedsClassConstructorReference | ClassFacts.NeedsClassSuperReference; + } } } - if (shouldTransformSuperInStaticInitializers && member.transformFlags & TransformFlags.ContainsLexicalSuper) { - if (!(facts & ClassFacts.ClassWasDecorated)) { - facts |= ClassFacts.NeedsClassConstructorReference | ClassFacts.NeedsClassSuperReference; - } + } + else if (!hasAbstractModifier(getOriginalNode(member))) { + if (isAutoAccessorPropertyDeclaration(member)) { + containsInstanceAutoAccessors = true; + containsInstancePrivateElements ||= isPrivateIdentifierClassElementDeclaration(member); + } + else if (isPrivateIdentifierClassElementDeclaration(member)) { + containsInstancePrivateElements = true; + } + else if (isPropertyDeclaration(member)) { + containsPublicInstanceFields = true; + containsInitializedPublicInstanceFields ||= !!member.initializer; } } } + + const willHoistInitializersToConstructor = + shouldTransformInitializersUsingDefine && containsPublicInstanceFields || + shouldTransformInitializersUsingSet && containsInitializedPublicInstanceFields || + shouldTransformPrivateElementsOrClassStaticBlocks && containsInstancePrivateElements || + shouldTransformPrivateElementsOrClassStaticBlocks && containsInstanceAutoAccessors && shouldTransformAutoAccessors === Ternary.True; + + if (willHoistInitializersToConstructor) { + facts |= ClassFacts.WillHoistInitializersToConstructor; + } + return facts; } @@ -1632,20 +1673,9 @@ export function transformClassFields(context: TransformationContext): (x: Source ); } - function isClassElementThatRequiresConstructorStatement(member: ClassElement) { - if (isStatic(member) || hasAbstractModifier(getOriginalNode(member))) { - return false; - } - - return shouldTransformInitializersUsingDefine && isPropertyDeclaration(member) || - shouldTransformInitializersUsingSet && isInitializedProperty(member) || - shouldTransformPrivateElementsOrClassStaticBlocks && isPrivateIdentifierClassElementDeclaration(member) || - shouldTransformPrivateElementsOrClassStaticBlocks && shouldTransformAutoAccessors && isAutoAccessorPropertyDeclaration(member); - } - function transformConstructor(constructor: ConstructorDeclaration | undefined, container: ClassDeclaration | ClassExpression) { constructor = visitNode(constructor, visitor, isConstructorDeclaration); - if (!some(container.members, isClassElementThatRequiresConstructorStatement)) { + if (!currentClassLexicalEnvironment || !(currentClassLexicalEnvironment.facts & ClassFacts.WillHoistInitializersToConstructor)) { return constructor; } diff --git a/tests/baselines/reference/autoAccessor10.js b/tests/baselines/reference/autoAccessor10.js new file mode 100644 index 00000000000..4443bef9849 --- /dev/null +++ b/tests/baselines/reference/autoAccessor10.js @@ -0,0 +1,65 @@ +//// [autoAccessor10.ts] +class C1 { + accessor a0 = 1; +} + +class C2 { + #a1_accessor_storage = 1; + accessor a1 = 2; +} + +class C3 { + static #a2_accessor_storage = 1; + static { + class C3_Inner { + accessor a2 = 2; + static { + #a2_accessor_storage in C3; + } + } + } +} + +class C4_1 { + static accessor a3 = 1; +} + +class C4_2 { + static accessor a3 = 1; +} + +//// [autoAccessor10.js] +class C1 { + #a0_accessor_storage = 1; + get a0() { return this.#a0_accessor_storage; } + set a0(value) { this.#a0_accessor_storage = value; } +} +class C2 { + #a1_accessor_storage = 1; + #a1_1_accessor_storage = 2; + get a1() { return this.#a1_1_accessor_storage; } + set a1(value) { this.#a1_1_accessor_storage = value; } +} +class C3 { + static #a2_accessor_storage = 1; + static { + class C3_Inner { + #a2_1_accessor_storage = 2; + get a2() { return this.#a2_1_accessor_storage; } + set a2(value) { this.#a2_1_accessor_storage = value; } + static { + #a2_accessor_storage in 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; } +} +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; } +} diff --git a/tests/baselines/reference/autoAccessor10.symbols b/tests/baselines/reference/autoAccessor10.symbols new file mode 100644 index 00000000000..a21d4accf95 --- /dev/null +++ b/tests/baselines/reference/autoAccessor10.symbols @@ -0,0 +1,53 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor10.ts === +class C1 { +>C1 : Symbol(C1, Decl(autoAccessor10.ts, 0, 0)) + + accessor a0 = 1; +>a0 : Symbol(C1.a0, Decl(autoAccessor10.ts, 0, 10)) +} + +class C2 { +>C2 : Symbol(C2, Decl(autoAccessor10.ts, 2, 1)) + + #a1_accessor_storage = 1; +>#a1_accessor_storage : Symbol(C2.#a1_accessor_storage, Decl(autoAccessor10.ts, 4, 10)) + + accessor a1 = 2; +>a1 : Symbol(C2.a1, Decl(autoAccessor10.ts, 5, 29)) +} + +class C3 { +>C3 : Symbol(C3, Decl(autoAccessor10.ts, 7, 1)) + + static #a2_accessor_storage = 1; +>#a2_accessor_storage : Symbol(C3.#a2_accessor_storage, Decl(autoAccessor10.ts, 9, 10)) + + static { + class C3_Inner { +>C3_Inner : Symbol(C3_Inner, Decl(autoAccessor10.ts, 11, 12)) + + accessor a2 = 2; +>a2 : Symbol(C3_Inner.a2, Decl(autoAccessor10.ts, 12, 24)) + + static { + #a2_accessor_storage in C3; +>#a2_accessor_storage : Symbol(C3.#a2_accessor_storage, Decl(autoAccessor10.ts, 9, 10)) +>C3 : Symbol(C3, Decl(autoAccessor10.ts, 7, 1)) + } + } + } +} + +class C4_1 { +>C4_1 : Symbol(C4_1, Decl(autoAccessor10.ts, 19, 1)) + + static accessor a3 = 1; +>a3 : Symbol(C4_1.a3, Decl(autoAccessor10.ts, 21, 12)) +} + +class C4_2 { +>C4_2 : Symbol(C4_2, Decl(autoAccessor10.ts, 23, 1)) + + static accessor a3 = 1; +>a3 : Symbol(C4_2.a3, Decl(autoAccessor10.ts, 25, 12)) +} diff --git a/tests/baselines/reference/autoAccessor10.types b/tests/baselines/reference/autoAccessor10.types new file mode 100644 index 00000000000..bd26ccd90b2 --- /dev/null +++ b/tests/baselines/reference/autoAccessor10.types @@ -0,0 +1,61 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor10.ts === +class C1 { +>C1 : C1 + + accessor a0 = 1; +>a0 : number +>1 : 1 +} + +class C2 { +>C2 : C2 + + #a1_accessor_storage = 1; +>#a1_accessor_storage : number +>1 : 1 + + accessor a1 = 2; +>a1 : number +>2 : 2 +} + +class C3 { +>C3 : C3 + + static #a2_accessor_storage = 1; +>#a2_accessor_storage : number +>1 : 1 + + static { + class C3_Inner { +>C3_Inner : C3_Inner + + accessor a2 = 2; +>a2 : number +>2 : 2 + + static { + #a2_accessor_storage in C3; +>#a2_accessor_storage in C3 : boolean +>#a2_accessor_storage : any +>C3 : typeof C3 + } + } + } +} + +class C4_1 { +>C4_1 : C4_1 + + static accessor a3 = 1; +>a3 : number +>1 : 1 +} + +class C4_2 { +>C4_2 : C4_2 + + static accessor a3 = 1; +>a3 : number +>1 : 1 +} diff --git a/tests/baselines/reference/autoAccessor6(target=esnext,usedefineforclassfields=false).js b/tests/baselines/reference/autoAccessor6(target=esnext,usedefineforclassfields=false).js index 9dfddc101ab..318657aa1d4 100644 --- a/tests/baselines/reference/autoAccessor6(target=esnext,usedefineforclassfields=false).js +++ b/tests/baselines/reference/autoAccessor6(target=esnext,usedefineforclassfields=false).js @@ -14,6 +14,7 @@ class C3 extends C1 { //// [autoAccessor6.js] class C1 { + accessor a; } class C2 extends C1 { constructor() { diff --git a/tests/baselines/reference/autoAccessor7(target=es2022,usedefineforclassfields=false).js b/tests/baselines/reference/autoAccessor7(target=es2022,usedefineforclassfields=false).js index 403aaa048a1..29f5e54f408 100644 --- a/tests/baselines/reference/autoAccessor7(target=es2022,usedefineforclassfields=false).js +++ b/tests/baselines/reference/autoAccessor7(target=es2022,usedefineforclassfields=false).js @@ -16,11 +16,7 @@ class C3 extends C1 { class C1 { } class C2 extends C1 { - constructor() { - super(...arguments); - this.#a_accessor_storage = 1; - } - #a_accessor_storage; + #a_accessor_storage = 1; get a() { return this.#a_accessor_storage; } set a(value) { this.#a_accessor_storage = value; } } diff --git a/tests/baselines/reference/autoAccessor7(target=esnext,usedefineforclassfields=false).js b/tests/baselines/reference/autoAccessor7(target=esnext,usedefineforclassfields=false).js index 2b8e93b75f8..7d3989d8cbd 100644 --- a/tests/baselines/reference/autoAccessor7(target=esnext,usedefineforclassfields=false).js +++ b/tests/baselines/reference/autoAccessor7(target=esnext,usedefineforclassfields=false).js @@ -16,10 +16,7 @@ class C3 extends C1 { class C1 { } class C2 extends C1 { - constructor() { - super(...arguments); - this.#a_1 = 1; - } + accessor a = 1; } class C3 extends C1 { get a() { return 1; } diff --git a/tests/baselines/reference/autoAccessor9.js b/tests/baselines/reference/autoAccessor9.js new file mode 100644 index 00000000000..1f0f202c336 --- /dev/null +++ b/tests/baselines/reference/autoAccessor9.js @@ -0,0 +1,102 @@ +//// [autoAccessor9.ts] +// Auto-accessors do not use Set semantics themselves, so do not need to be transformed if there are no other +// initializers that need to be transformed: +class C1 { + accessor x = 1; +} + +// If there are other field initializers to transform, we must transform auto-accessors so that we can preserve +// initialization order: +class C2 { + x = 1; + accessor y = 2; + z = 3; +} + +// Private field initializers also do not use Set semantics, so they do not force an auto-accessor transformation: +class C3 { + #x = 1; + accessor y = 2; +} + +// However, we still need to hoist private field initializers to the constructor if we need to preserve initialization +// order: +class C4 { + x = 1; + #y = 2; + z = 3; +} + +class C5 { + #x = 1; + accessor y = 2; + z = 3; +} + +// Static accessors aren't affected: +class C6 { + static accessor x = 1; +} + +// Static accessors aren't affected: +class C7 { + static x = 1; + static accessor y = 2; + static z = 3; +} + + +//// [autoAccessor9.js] +// Auto-accessors do not use Set semantics themselves, so do not need to be transformed if there are no other +// initializers that need to be transformed: +class C1 { + accessor x = 1; +} +// If there are other field initializers to transform, we must transform auto-accessors so that we can preserve +// initialization order: +class C2 { + constructor() { + this.x = 1; + this.#y_accessor_storage = 2; + this.z = 3; + } + #y_accessor_storage; + get y() { return this.#y_accessor_storage; } + set y(value) { this.#y_accessor_storage = value; } +} +// Private field initializers also do not use Set semantics, so they do not force an auto-accessor transformation: +class C3 { + #x = 1; + accessor y = 2; +} +// However, we still need to hoist private field initializers to the constructor if we need to preserve initialization +// order: +class C4 { + constructor() { + this.x = 1; + this.#y = 2; + this.z = 3; + } + #y; +} +class C5 { + constructor() { + this.#x = 1; + this.#y_accessor_storage = 2; + this.z = 3; + } + #x; + #y_accessor_storage; + get y() { return this.#y_accessor_storage; } + set y(value) { this.#y_accessor_storage = value; } +} +// Static accessors aren't affected: +class C6 { + static accessor x = 1; +} +// Static accessors aren't affected: +class C7 { + static { this.x = 1; } + static accessor y = 2; + static { this.z = 3; } +} diff --git a/tests/baselines/reference/autoAccessor9.symbols b/tests/baselines/reference/autoAccessor9.symbols new file mode 100644 index 00000000000..b06e6f344f5 --- /dev/null +++ b/tests/baselines/reference/autoAccessor9.symbols @@ -0,0 +1,86 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor9.ts === +// Auto-accessors do not use Set semantics themselves, so do not need to be transformed if there are no other +// initializers that need to be transformed: +class C1 { +>C1 : Symbol(C1, Decl(autoAccessor9.ts, 0, 0)) + + accessor x = 1; +>x : Symbol(C1.x, Decl(autoAccessor9.ts, 2, 10)) +} + +// If there are other field initializers to transform, we must transform auto-accessors so that we can preserve +// initialization order: +class C2 { +>C2 : Symbol(C2, Decl(autoAccessor9.ts, 4, 1)) + + x = 1; +>x : Symbol(C2.x, Decl(autoAccessor9.ts, 8, 10)) + + accessor y = 2; +>y : Symbol(C2.y, Decl(autoAccessor9.ts, 9, 10)) + + z = 3; +>z : Symbol(C2.z, Decl(autoAccessor9.ts, 10, 19)) +} + +// Private field initializers also do not use Set semantics, so they do not force an auto-accessor transformation: +class C3 { +>C3 : Symbol(C3, Decl(autoAccessor9.ts, 12, 1)) + + #x = 1; +>#x : Symbol(C3.#x, Decl(autoAccessor9.ts, 15, 10)) + + accessor y = 2; +>y : Symbol(C3.y, Decl(autoAccessor9.ts, 16, 11)) +} + +// However, we still need to hoist private field initializers to the constructor if we need to preserve initialization +// order: +class C4 { +>C4 : Symbol(C4, Decl(autoAccessor9.ts, 18, 1)) + + x = 1; +>x : Symbol(C4.x, Decl(autoAccessor9.ts, 22, 10)) + + #y = 2; +>#y : Symbol(C4.#y, Decl(autoAccessor9.ts, 23, 10)) + + z = 3; +>z : Symbol(C4.z, Decl(autoAccessor9.ts, 24, 11)) +} + +class C5 { +>C5 : Symbol(C5, Decl(autoAccessor9.ts, 26, 1)) + + #x = 1; +>#x : Symbol(C5.#x, Decl(autoAccessor9.ts, 28, 10)) + + accessor y = 2; +>y : Symbol(C5.y, Decl(autoAccessor9.ts, 29, 11)) + + z = 3; +>z : Symbol(C5.z, Decl(autoAccessor9.ts, 30, 19)) +} + +// Static accessors aren't affected: +class C6 { +>C6 : Symbol(C6, Decl(autoAccessor9.ts, 32, 1)) + + static accessor x = 1; +>x : Symbol(C6.x, Decl(autoAccessor9.ts, 35, 10)) +} + +// Static accessors aren't affected: +class C7 { +>C7 : Symbol(C7, Decl(autoAccessor9.ts, 37, 1)) + + static x = 1; +>x : Symbol(C7.x, Decl(autoAccessor9.ts, 40, 10)) + + static accessor y = 2; +>y : Symbol(C7.y, Decl(autoAccessor9.ts, 41, 17)) + + static z = 3; +>z : Symbol(C7.z, Decl(autoAccessor9.ts, 42, 26)) +} + diff --git a/tests/baselines/reference/autoAccessor9.types b/tests/baselines/reference/autoAccessor9.types new file mode 100644 index 00000000000..68a2fa33f6e --- /dev/null +++ b/tests/baselines/reference/autoAccessor9.types @@ -0,0 +1,102 @@ +=== tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor9.ts === +// Auto-accessors do not use Set semantics themselves, so do not need to be transformed if there are no other +// initializers that need to be transformed: +class C1 { +>C1 : C1 + + accessor x = 1; +>x : number +>1 : 1 +} + +// If there are other field initializers to transform, we must transform auto-accessors so that we can preserve +// initialization order: +class C2 { +>C2 : C2 + + x = 1; +>x : number +>1 : 1 + + accessor y = 2; +>y : number +>2 : 2 + + z = 3; +>z : number +>3 : 3 +} + +// Private field initializers also do not use Set semantics, so they do not force an auto-accessor transformation: +class C3 { +>C3 : C3 + + #x = 1; +>#x : number +>1 : 1 + + accessor y = 2; +>y : number +>2 : 2 +} + +// However, we still need to hoist private field initializers to the constructor if we need to preserve initialization +// order: +class C4 { +>C4 : C4 + + x = 1; +>x : number +>1 : 1 + + #y = 2; +>#y : number +>2 : 2 + + z = 3; +>z : number +>3 : 3 +} + +class C5 { +>C5 : C5 + + #x = 1; +>#x : number +>1 : 1 + + accessor y = 2; +>y : number +>2 : 2 + + z = 3; +>z : number +>3 : 3 +} + +// Static accessors aren't affected: +class C6 { +>C6 : C6 + + static accessor x = 1; +>x : number +>1 : 1 +} + +// Static accessors aren't affected: +class C7 { +>C7 : C7 + + static x = 1; +>x : number +>1 : 1 + + static accessor y = 2; +>y : number +>2 : 2 + + static z = 3; +>z : number +>3 : 3 +} + diff --git a/tests/baselines/reference/autoAccessorExperimentalDecorators(target=es2022).js b/tests/baselines/reference/autoAccessorExperimentalDecorators(target=es2022).js index 2b1543d3047..854ee9ffc82 100644 --- a/tests/baselines/reference/autoAccessorExperimentalDecorators(target=es2022).js +++ b/tests/baselines/reference/autoAccessorExperimentalDecorators(target=es2022).js @@ -40,10 +40,10 @@ __decorate([ dec ], C1, "b", null); class C2 { - #a_1_accessor_storage; - get #a() { return this.#a_1_accessor_storage; } - set #a(value) { this.#a_1_accessor_storage = value; } - static #b_1_accessor_storage; - static get #b() { return this.#b_1_accessor_storage; } - static set #b(value) { this.#b_1_accessor_storage = value; } + #a_accessor_storage; + get #a() { return this.#a_accessor_storage; } + set #a(value) { this.#a_accessor_storage = value; } + static #b_accessor_storage; + static get #b() { return this.#b_accessor_storage; } + static set #b(value) { this.#b_accessor_storage = value; } } diff --git a/tests/baselines/reference/classIndexer5.js b/tests/baselines/reference/classIndexer5.js index 43c65fc4e80..693be6d2e73 100644 --- a/tests/baselines/reference/classIndexer5.js +++ b/tests/baselines/reference/classIndexer5.js @@ -9,9 +9,6 @@ class Foo { //// [classIndexer5.js] class Foo { - constructor() { - this.#b = false; - } #a; - #b; + #b = false; } diff --git a/tests/baselines/reference/privateNameWhenNotUseDefineForClassFieldsInEsNext(target=esnext).js b/tests/baselines/reference/privateNameWhenNotUseDefineForClassFieldsInEsNext(target=esnext).js index e0ce069f349..7d962abb222 100644 --- a/tests/baselines/reference/privateNameWhenNotUseDefineForClassFieldsInEsNext(target=esnext).js +++ b/tests/baselines/reference/privateNameWhenNotUseDefineForClassFieldsInEsNext(target=esnext).js @@ -54,16 +54,10 @@ class TestNonStatics { //// [privateNameWhenNotUseDefineForClassFieldsInEsNext.js] "use strict"; class TestWithStatics { - constructor() { - this.#prop = 0; - } - #prop; + #prop = 0; static { this.dd = new TestWithStatics().#prop; } // OK static { this["X_ z_ zz"] = class Inner { - constructor() { - this.#foo = 10; - } - #foo; + #foo = 10; m() { new TestWithStatics().#prop; // OK } diff --git a/tests/baselines/reference/privateNamesAssertion(target=es2022).js b/tests/baselines/reference/privateNamesAssertion(target=es2022).js index b6be4d5d4ad..9369f27a160 100644 --- a/tests/baselines/reference/privateNamesAssertion(target=es2022).js +++ b/tests/baselines/reference/privateNamesAssertion(target=es2022).js @@ -27,14 +27,11 @@ class Foo2 { //// [privateNamesAssertion.js] "use strict"; class Foo { - constructor() { - this.#p1 = (v) => { - if (typeof v !== "string") { - throw new Error(); - } - }; - } - #p1; + #p1 = (v) => { + if (typeof v !== "string") { + throw new Error(); + } + }; m1(v) { this.#p1(v); v; diff --git a/tests/baselines/reference/privateNamesAssertion(target=esnext).js b/tests/baselines/reference/privateNamesAssertion(target=esnext).js index b6be4d5d4ad..9369f27a160 100644 --- a/tests/baselines/reference/privateNamesAssertion(target=esnext).js +++ b/tests/baselines/reference/privateNamesAssertion(target=esnext).js @@ -27,14 +27,11 @@ class Foo2 { //// [privateNamesAssertion.js] "use strict"; class Foo { - constructor() { - this.#p1 = (v) => { - if (typeof v !== "string") { - throw new Error(); - } - }; - } - #p1; + #p1 = (v) => { + if (typeof v !== "string") { + throw new Error(); + } + }; m1(v) { this.#p1(v); v; diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor10.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor10.ts new file mode 100644 index 00000000000..eda1bf41331 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor10.ts @@ -0,0 +1,30 @@ +// @target: es2022 + +class C1 { + accessor a0 = 1; +} + +class C2 { + #a1_accessor_storage = 1; + accessor a1 = 2; +} + +class C3 { + static #a2_accessor_storage = 1; + static { + class C3_Inner { + accessor a2 = 2; + static { + #a2_accessor_storage in C3; + } + } + } +} + +class C4_1 { + static accessor a3 = 1; +} + +class C4_2 { + static accessor a3 = 1; +} \ No newline at end of file diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor9.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor9.ts new file mode 100644 index 00000000000..17fb453f2b7 --- /dev/null +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor9.ts @@ -0,0 +1,48 @@ +// @target: esnext +// @useDefineForClassFields: false + +// Auto-accessors do not use Set semantics themselves, so do not need to be transformed if there are no other +// initializers that need to be transformed: +class C1 { + accessor x = 1; +} + +// If there are other field initializers to transform, we must transform auto-accessors so that we can preserve +// initialization order: +class C2 { + x = 1; + accessor y = 2; + z = 3; +} + +// Private field initializers also do not use Set semantics, so they do not force an auto-accessor transformation: +class C3 { + #x = 1; + accessor y = 2; +} + +// However, we still need to hoist private field initializers to the constructor if we need to preserve initialization +// order: +class C4 { + x = 1; + #y = 2; + z = 3; +} + +class C5 { + #x = 1; + accessor y = 2; + z = 3; +} + +// Static accessors aren't affected: +class C6 { + static accessor x = 1; +} + +// Static accessors aren't affected: +class C7 { + static x = 1; + static accessor y = 2; + static z = 3; +}