From 9419c52adf191785348a7d3096fcb5ea1ec432dd Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Fri, 3 May 2024 10:20:19 -0400 Subject: [PATCH] initial implementation of the decorators proposal --- .github/workflows/ci.yml | 4 + CHANGELOG.md | 30 + Makefile | 12 +- internal/js_parser/js_parser.go | 4 - internal/js_parser/js_parser_lower.go | 3 - internal/js_parser/js_parser_lower_class.go | 525 +- internal/js_parser/js_parser_test.go | 83 +- internal/js_parser/ts_parser_test.go | 87 +- internal/runtime/runtime.go | 48 + scripts/decorator-tests.js | 4922 +++++++++++++++++++ scripts/decorator-tests.ts | 3714 ++++++++++++++ 11 files changed, 9397 insertions(+), 35 deletions(-) create mode 100644 scripts/decorator-tests.js create mode 100644 scripts/decorator-tests.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d627d44772b..4884bffc581 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,6 +160,10 @@ jobs: if: matrix.os == 'ubuntu-latest' run: make lib-typecheck + - name: Decorator Tests + if: matrix.os == 'ubuntu-latest' + run: make decorator-tests + - name: WebAssembly API Tests (browser) if: matrix.os == 'ubuntu-latest' run: make test-wasm-browser diff --git a/CHANGELOG.md b/CHANGELOG.md index 12a6c02278e..b40d407af88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ ## Unreleased +* Implement the JavaScript decorators proposal ([#104](https://github.com/evanw/esbuild/issues/104)) + + With this release, esbuild now contains an implementation of the upcoming [JavaScript decorators proposal](https://github.com/tc39/proposal-decorators). This is the same feature that shipped in [TypeScript 5.0](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#decorators). You can read more about them in that blog post and in this other (now slightly outdated) extensive blog post here: https://2ality.com/2022/10/javascript-decorators.html. Here's a quick example: + + ```js + const log = (fn, context) => function() { + console.log(`before ${context.name}`) + const it = fn.apply(this, arguments) + console.log(`after ${context.name}`) + return it + } + + class Foo { + @log static foo() { + console.log('in foo') + } + } + + // Logs "before foo", "in foo", "after foo" + Foo.foo() + ``` + + Note that this feature is different than the existing "TypeScript experimental decorators" feature that esbuild already implements. It uses similar syntax but behaves very differently, and the two are not compatible (although it's sometimes possible to write decorators that work with both). TypeScript experimental decorators will still be supported by esbuild going forward as they have been around for a long time, are very widely used, and let you do certain things that are not possible with JavaScript decorators (such as decorating function parameters). By default esbuild will parse and transform JavaScript decorators, but you can tell esbuild to parse and transform TypeScript experimental decorators instead by setting `"experimentalDecorators": true` in your `tsconfig.json` file. + + Probably at least half of the work for this feature went into creating a test suite that exercises many of the proposal's edge cases: https://github.com/evanw/decorator-tests. It has given me a reasonable level of confidence that esbuild's initial implementation is acceptable. However, I don't have access to a significant sample of real code that uses JavaScript decorators. If you're currently using JavaScript decorators in a real code base, please try out esbuild's implementation and let me know if anything seems off. + + **⚠️ WARNING ⚠️** + + This proposal has been in the works for a very long time (work began around 10 years ago in 2014) and it is finally getting close to becoming part of the JavaScript language. However, it's still a work in progress and isn't a part of JavaScript yet, so keep in mind that any code that uses JavaScript decorators may need to be updated as the feature continues to evolve. The decorators proposal is pretty close to its final form but it can and likely will undergo some small behavioral adjustments before it ends up becoming a part of the standard. If/when that happens, I will update esbuild's implementation to match the specification. I will not be supporting old versions of the specification. + * Optimize the generated code for private methods Previously when lowering private methods for old browsers, esbuild would generate one `WeakSet` for each private method. This mirrors similar logic for generating one `WeakSet` for each private field. Using a separate `WeakMap` for private fields is necessary as their assignment can be observable: diff --git a/Makefile b/Makefile index 51ca95e1899..48894d38951 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ test: @$(MAKE) --no-print-directory -j6 test-common # These tests are for development -test-common: test-go vet-go no-filepath verify-source-map end-to-end-tests js-api-tests plugin-tests register-test node-unref-tests +test-common: test-go vet-go no-filepath verify-source-map end-to-end-tests js-api-tests plugin-tests register-test node-unref-tests decorator-tests # These tests are for release (the extra tests are not included in "test" because they are pretty slow) test-all: @@ -85,6 +85,16 @@ end-to-end-tests: version-go node scripts/esbuild.js npm/esbuild/package.json --version node scripts/end-to-end-tests.js +# Note: The TypeScript source code for these tests was copied from the repo +# https://github.com/evanw/decorator-tests, which is the official location of +# the source code for these tests. Any changes to these tests should be made +# there first and then copied here afterward. +decorator-tests: esbuild + ./esbuild scripts/decorator-tests.ts --target=es2022 --outfile=scripts/decorator-tests.js + node scripts/decorator-tests.js + node scripts/decorator-tests.js | grep -q 'All checks passed' + git diff --exit-code scripts/decorator-tests.js + js-api-tests: version-go node scripts/esbuild.js npm/esbuild/package.json --version node scripts/js-api-tests.js diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 10b1f159e44..de85ed6b909 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -6641,15 +6641,11 @@ func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logg p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), "Parameter decorators only work when experimental decorators are enabled", []logger.MsgData{{ Text: "You can enable experimental decorators by adding \"experimentalDecorators\": true to your \"tsconfig.json\" file.", }}) - } else { - p.markSyntaxFeature(compat.Decorators, p.lexer.Range()) } } } else { if (context & decoratorInFnArgs) != 0 { p.log.AddError(&p.tracker, p.lexer.Range(), "Parameter decorators are not allowed in JavaScript") - } else { - p.markSyntaxFeature(compat.Decorators, p.lexer.Range()) } } } diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index 56a1810831b..f9f9958a781 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -78,9 +78,6 @@ func (p *parser) markSyntaxFeature(feature compat.JSFeature, r logger.Range) (di case compat.NestedRestBinding: name = "non-identifier array rest patterns" - case compat.Decorators: - name = "JavaScript decorators" - case compat.ImportAttributes: p.log.AddError(&p.tracker, r, fmt.Sprintf( "Using an arbitrary value as the second argument to \"import()\" is not possible in %s", where)) diff --git a/internal/js_parser/js_parser_lower_class.go b/internal/js_parser/js_parser_lower_class.go index 0e900130862..85946a1abf4 100644 --- a/internal/js_parser/js_parser_lower_class.go +++ b/internal/js_parser/js_parser_lower_class.go @@ -411,6 +411,27 @@ func (p *parser) computeClassLoweringInfo(class *js_ast.Class) (result classLowe result.lowerAllStaticFields = true } + // If something has decorators, just lower everything for now. It's possible + // that we could avoid lowering in certain cases, but doing so is very tricky + // due to the complexity of the decorator specification. The specification is + // also still evolving so trying to optimize it now is also potentially + // premature. + if p.options.unsupportedJSFeatures.Has(compat.Decorators) && + (!p.options.ts.Parse || p.options.ts.Config.ExperimentalDecorators != config.True) { + for _, prop := range class.Properties { + if len(prop.Decorators) > 0 { + for _, prop := range class.Properties { + if private, ok := prop.Key.Data.(*js_ast.EPrivateIdentifier); ok { + p.symbols[private.Ref.InnerIndex].Flags |= ast.PrivateSymbolMustBeLowered + } + } + result.lowerAllStaticFields = true + result.lowerAllInstanceFields = true + break + } + } + } + // Conservatively lower fields of a given type (instance or static) when any // member of that type needs to be lowered. This must be done to preserve // evaluation order. For example: @@ -625,6 +646,17 @@ type lowerClassContext struct { instanceExperimentalDecorators []js_ast.Expr staticExperimentalDecorators []js_ast.Expr + // These are used for implementing JavaScript decorators + decoratorContextRef ast.Ref + decoratorClassDecorators js_ast.Expr + decoratorPropertyToInitializerMap map[int]int + decoratorCallInstanceMethodExtraInitializers bool + decoratorCallStaticMethodExtraInitializers bool + decoratorStaticNonFieldElements []js_ast.Expr + decoratorInstanceNonFieldElements []js_ast.Expr + decoratorStaticFieldElements []js_ast.Expr + decoratorInstanceFieldElements []js_ast.Expr + // These are used by "lowerMethod" privateInstanceMethodRef ast.Ref privateStaticMethodRef ast.Ref @@ -649,6 +681,7 @@ type lowerClassContext struct { // way for performance so that we don't need to do another AST pass. func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr, result visitClassResult) ([]js_ast.Stmt, js_ast.Expr) { ctx := lowerClassContext{ + decoratorContextRef: ast.InvalidRef, privateInstanceMethodRef: ast.InvalidRef, privateStaticMethodRef: ast.InvalidRef, } @@ -675,6 +708,9 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr, result visitClas ctx.class.Name = nil } } + if p.nameToKeepIsFor == e { + ctx.optionalNameHint = p.nameToKeep + } } else if s, ok := stmt.Data.(*js_ast.SClass); ok { ctx.class = &s.Class if ctx.class.Name != nil { @@ -786,6 +822,7 @@ func (ctx *lowerClassContext) lowerField( private *js_ast.EPrivateIdentifier, shouldOmitFieldInitializer bool, staticFieldToBlockAssign bool, + initializerIndex int, ) (js_ast.Property, bool) { mustLowerPrivate := private != nil && p.privateSymbolNeedsToBeLowered(private) @@ -811,6 +848,19 @@ func (ctx *lowerClassContext) lowerField( init = js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared} } + // Optionally call registered decorator initializers + if initializerIndex != -1 { + args := []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: loc, Data: &js_ast.ENumber{Value: float64((3 + 2*initializerIndex) << 1)}}, + } + if _, ok := init.Data.(*js_ast.EUndefined); !ok { + args = append(args, init) + } + init = p.callRuntime(init.Loc, "__runInitializers", args) + p.recordUsage(ctx.decoratorContextRef) + } + // Generate the assignment target var memberExpr js_ast.Expr if mustLowerPrivate { @@ -863,6 +913,22 @@ func (ctx *lowerClassContext) lowerField( memberExpr = js_ast.Assign(target, init) } + // Run extra initializers + if initializerIndex != -1 { + var value js_ast.Expr + if prop.Flags.Has(js_ast.PropertyIsStatic) { + value = ctx.nameFunc() + } else { + value = js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} + } + memberExpr = js_ast.JoinWithComma(memberExpr, p.callRuntime(loc, "__runInitializers", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: loc, Data: &js_ast.ENumber{Value: float64(((4 + 2*initializerIndex) << 1) | 1)}}, + value, + })) + p.recordUsage(ctx.decoratorContextRef) + } + if prop.Flags.Has(js_ast.PropertyIsStatic) { // Move this property to an assignment after the class ends if staticFieldToBlockAssign { @@ -1016,6 +1082,7 @@ func (ctx *lowerClassContext) lowerMethod(p *parser, prop js_ast.Property, priva type propertyAnalysis struct { private *js_ast.EPrivateIdentifier propExperimentalDecorators []js_ast.Decorator + propDecorators []js_ast.Decorator mustLowerField bool needsValueOfKey bool rewriteAutoAccessorToGetSet bool @@ -1061,10 +1128,18 @@ func (ctx *lowerClassContext) analyzeProperty(p *parser, prop js_ast.Property, c analysis.shouldOmitFieldInitializer = true } + // For convenience, split decorators off into separate fields based on how + // they will end up being lowered (if they are even being lowered at all) if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True { analysis.propExperimentalDecorators = prop.Decorators + } else if p.options.unsupportedJSFeatures.Has(compat.Decorators) { + analysis.propDecorators = prop.Decorators } - analysis.rewriteAutoAccessorToGetSet = prop.Kind == js_ast.PropertyAutoAccessor && (p.options.unsupportedJSFeatures.Has(compat.Decorators) || analysis.mustLowerField) + + // Note: Auto-accessors use a different transform when they are decorated. + // This transform trades off worse run-time performance for better code size. + analysis.rewriteAutoAccessorToGetSet = len(analysis.propDecorators) == 0 && prop.Kind == js_ast.PropertyAutoAccessor && + (p.options.unsupportedJSFeatures.Has(compat.Decorators) || analysis.mustLowerField) // Transform non-lowered static fields that use assign semantics into an // assignment in an inline static block instead of lowering them. This lets @@ -1080,6 +1155,7 @@ func (ctx *lowerClassContext) analyzeProperty(p *parser, prop js_ast.Property, c analysis.needsValueOfKey = true if prop.Flags.Has(js_ast.PropertyIsComputed) && (len(analysis.propExperimentalDecorators) > 0 || + len(analysis.propDecorators) > 0 || analysis.mustLowerField || analysis.staticFieldToBlockAssign || analysis.rewriteAutoAccessorToGetSet) { @@ -1088,6 +1164,7 @@ func (ctx *lowerClassContext) analyzeProperty(p *parser, prop js_ast.Property, c // Determine if we don't actually need the value of the key (only the side // effects). In that case we don't need a temporary variable. if len(analysis.propExperimentalDecorators) == 0 && + len(analysis.propDecorators) == 0 && !analysis.rewriteAutoAccessorToGetSet && analysis.shouldOmitFieldInitializer { analysis.needsValueOfKey = false @@ -1096,7 +1173,23 @@ func (ctx *lowerClassContext) analyzeProperty(p *parser, prop js_ast.Property, c return } -func (ctx *lowerClassContext) hoistComputedProperties(p *parser, classLoweringInfo classLoweringInfo) (propertyKeyTempRefs map[int]ast.Ref) { +func (p *parser) propertyNameHint(key js_ast.Expr, suffix string) string { + var text string + switch k := key.Data.(type) { + case *js_ast.EString: + text = helpers.UTF16ToString(k.Value) + case *js_ast.EIdentifier: + text = p.symbols[k.Ref.InnerIndex].OriginalName + case *js_ast.EPrivateIdentifier: + text = p.symbols[k.Ref.InnerIndex].OriginalName[1:] + default: + return suffix + } + return fmt.Sprintf("_%s%s", text, suffix) +} + +func (ctx *lowerClassContext) hoistComputedProperties(p *parser, classLoweringInfo classLoweringInfo) ( + propertyKeyTempRefs map[int]ast.Ref, decoratorTempRefs map[int]ast.Ref) { var nextComputedPropertyKey *js_ast.Expr // Computed property keys must be evaluated in a specific order for their @@ -1129,14 +1222,46 @@ func (ctx *lowerClassContext) hoistComputedProperties(p *parser, classLoweringIn // may contain "await" and static blocks do not allow "await". for propIndex := len(ctx.class.Properties) - 1; propIndex >= 0; propIndex-- { prop := &ctx.class.Properties[propIndex] + analysis := ctx.analyzeProperty(p, *prop, classLoweringInfo) + + // Evaluate the decorator expressions inline before computed property keys + var decorators js_ast.Expr + if len(analysis.propDecorators) > 0 { + ref := p.generateTempRef(tempRefNeedsDeclare, p.propertyNameHint(prop.Key, "_dec")) + values := make([]js_ast.Expr, len(analysis.propDecorators)) + for i, decorator := range analysis.propDecorators { + values[i] = decorator.Value + } + decorators = js_ast.Assign( + js_ast.Expr{Loc: prop.Loc, Data: &js_ast.EIdentifier{Ref: ref}}, + js_ast.Expr{Loc: prop.Loc, Data: &js_ast.EArray{Items: values, IsSingleLine: true}}) + p.recordUsage(ref) + if decoratorTempRefs == nil { + decoratorTempRefs = make(map[int]ast.Ref) + } + decoratorTempRefs[propIndex] = ref + } // Skip property keys that we know are side-effect free switch prop.Key.Data.(type) { - case *js_ast.EString, *js_ast.ENameOfSymbol, *js_ast.ENumber: + case *js_ast.EString, *js_ast.ENameOfSymbol, *js_ast.ENumber, *js_ast.EPrivateIdentifier: + // Figure out where to stick the decorator side effects to preserve their order + if nextComputedPropertyKey != nil { + // Insert it before everything that comes after it + *nextComputedPropertyKey = js_ast.JoinWithComma(decorators, *nextComputedPropertyKey) + } else { + // Insert it after the first thing that comes before it + ctx.computedPropertyChain = js_ast.JoinWithComma(decorators, ctx.computedPropertyChain) + } continue - } - analysis := ctx.analyzeProperty(p, *prop, classLoweringInfo) + default: + // Otherwise, evaluate the decorators right before the property key + if decorators.Data != nil { + prop.Key = js_ast.JoinWithComma(decorators, prop.Key) + prop.Flags |= js_ast.PropertyIsComputed + } + } // If this key is referenced elsewhere, make sure to still preserve // its side effects in the property's original location @@ -1279,9 +1404,94 @@ func (ctx *lowerClassContext) hoistComputedProperties(p *parser, classLoweringIn return } +// This corresponds to the initialization order in the specification: +// +// 27. For each element e of staticElements, do +// a. If e is a ClassElementDefinition Record and e.[[Kind]] is not field, then +// +// 28. For each element e of instanceElements, do +// a. If e.[[Kind]] is not field, then +// +// 29. For each element e of staticElements, do +// a. If e.[[Kind]] is field, then +// +// 30. For each element e of instanceElements, do +// a. If e.[[Kind]] is field, then +func fieldOrAccessorOrder(kind js_ast.PropertyKind, flags js_ast.PropertyFlags) (int, bool) { + if kind == js_ast.PropertyAutoAccessor { + if flags.Has(js_ast.PropertyIsStatic) { + return 0, true + } else { + return 1, true + } + } else if kind == js_ast.PropertyField { + if flags.Has(js_ast.PropertyIsStatic) { + return 2, true + } else { + return 3, true + } + } + return 0, false +} + func (ctx *lowerClassContext) processProperties(p *parser, classLoweringInfo classLoweringInfo, result visitClassResult) { properties := make([]js_ast.Property, 0, len(ctx.class.Properties)) - propertyKeyTempRefs := ctx.hoistComputedProperties(p, classLoweringInfo) + propertyKeyTempRefs, decoratorTempRefs := ctx.hoistComputedProperties(p, classLoweringInfo) + + // Save the initializer index for each field and accessor element + if p.options.unsupportedJSFeatures.Has(compat.Decorators) && (!p.options.ts.Parse || p.options.ts.Config.ExperimentalDecorators != config.True) { + var counts [4]int + + // Count how many initializers there are in each section + for _, prop := range ctx.class.Properties { + if len(prop.Decorators) > 0 { + if i, ok := fieldOrAccessorOrder(prop.Kind, prop.Flags); ok { + counts[i]++ + } else if prop.Flags.Has(js_ast.PropertyIsStatic) { + ctx.decoratorCallStaticMethodExtraInitializers = true + } else { + ctx.decoratorCallInstanceMethodExtraInitializers = true + } + } + } + + // Give each on an index for the order it will be initialized in + if counts[0] > 0 || counts[1] > 0 || counts[2] > 0 || counts[3] > 0 { + indices := [4]int{0, counts[0], counts[0] + counts[1], counts[0] + counts[1] + counts[2]} + ctx.decoratorPropertyToInitializerMap = make(map[int]int) + + for propIndex, prop := range ctx.class.Properties { + if len(prop.Decorators) > 0 { + if i, ok := fieldOrAccessorOrder(prop.Kind, prop.Flags); ok { + ctx.decoratorPropertyToInitializerMap[propIndex] = indices[i] + indices[i]++ + } + } + } + } + } + + // Evaluate the decorator expressions inline + if p.options.unsupportedJSFeatures.Has(compat.Decorators) && len(ctx.class.Decorators) > 0 && + (!p.options.ts.Parse || p.options.ts.Config.ExperimentalDecorators != config.True) { + name := ctx.optionalNameHint + if name == "" { + name = "class" + } + decoratorsRef := p.generateTempRef(tempRefNeedsDeclare, fmt.Sprintf("_%s_decorators", name)) + values := make([]js_ast.Expr, len(ctx.class.Decorators)) + for i, decorator := range ctx.class.Decorators { + values[i] = decorator.Value + } + ctx.computedPropertyChain = js_ast.JoinWithComma(js_ast.Assign( + js_ast.Expr{Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: decoratorsRef}}, + js_ast.Expr{Loc: ctx.classLoc, Data: &js_ast.EArray{Items: values, IsSingleLine: true}}, + ), ctx.computedPropertyChain) + p.recordUsage(decoratorsRef) + ctx.decoratorClassDecorators = js_ast.Expr{Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: decoratorsRef}} + p.recordUsage(decoratorsRef) + ctx.class.Decorators = nil + } for propIndex, prop := range ctx.class.Properties { if prop.Kind == js_ast.PropertyClassStaticBlock { @@ -1380,6 +1590,218 @@ func (ctx *lowerClassContext) processProperties(p *parser, classLoweringInfo cla } } + // Handle JavaScript decorators + initializerIndex := -1 + if len(analysis.propDecorators) > 0 { + prop.Decorators = nil + loc := prop.Key.Loc + + // Encode information about this property using bit flags + var flags int + switch prop.Kind { + case js_ast.PropertyMethod: + flags = 1 + case js_ast.PropertyGetter: + flags = 2 + case js_ast.PropertySetter: + flags = 3 + case js_ast.PropertyAutoAccessor: + flags = 4 + case js_ast.PropertyField: + flags = 5 + } + if flags >= 4 { + initializerIndex = ctx.decoratorPropertyToInitializerMap[propIndex] + } + if prop.Flags.Has(js_ast.PropertyIsStatic) { + flags |= 8 + } + if analysis.private != nil { + flags |= 16 + } + + // Start the arguments for the call to "__decorateElement" + var key js_ast.Expr + decoratorsRef := decoratorTempRefs[propIndex] + if ctx.decoratorContextRef == ast.InvalidRef { + ctx.decoratorContextRef = p.generateTempRef(tempRefNeedsDeclare, "_init") + } + if analysis.private != nil { + key = js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(p.symbols[analysis.private.Ref.InnerIndex].OriginalName)}} + } else { + key = cloneKeyForLowerClass(keyExprNoSideEffects) + } + args := []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: loc, Data: &js_ast.ENumber{Value: float64(flags)}}, + key, + {Loc: loc, Data: &js_ast.EIdentifier{Ref: decoratorsRef}}, + } + p.recordUsage(ctx.decoratorContextRef) + p.recordUsage(decoratorsRef) + + // Append any optional additional arguments + privateFnRef := ast.InvalidRef + if analysis.private != nil { + // Add the "target" argument (the weak set) + args = append(args, js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: analysis.private.Ref}}) + p.recordUsage(analysis.private.Ref) + + // Add the "extra" argument (the function) + switch prop.Kind { + case js_ast.PropertyMethod: + privateFnRef = p.privateGetters[analysis.private.Ref] + case js_ast.PropertyGetter: + privateFnRef = p.privateGetters[analysis.private.Ref] + case js_ast.PropertySetter: + privateFnRef = p.privateSetters[analysis.private.Ref] + } + if privateFnRef != ast.InvalidRef { + args = append(args, js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: privateFnRef}}) + p.recordUsage(privateFnRef) + } + } else { + // Add the "target" argument (the class object) + args = append(args, ctx.nameFunc()) + } + + autoAccessorWeakMapRef := ast.InvalidRef + if prop.Kind == js_ast.PropertyAutoAccessor { + // Initialize the private field to a new WeakMap + if p.weakMapRef == ast.InvalidRef { + p.weakMapRef = p.newSymbol(ast.SymbolUnbound, "WeakMap") + p.moduleScope.Generated = append(p.moduleScope.Generated, p.weakMapRef) + } + + // Pass the WeakMap instance into the decorator helper + autoAccessorWeakMapRef = p.generateTempRef(tempRefNeedsDeclare, p.propertyNameHint(prop.Key, "")) + args = append(args, js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: autoAccessorWeakMapRef}}) + p.recordUsage(autoAccessorWeakMapRef) + } + + // Assign the result + element := p.callRuntime(loc, "__decorateElement", args) + if privateFnRef != ast.InvalidRef { + element = js_ast.Assign(js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: privateFnRef}}, element) + p.recordUsage(privateFnRef) + } else if prop.Kind == js_ast.PropertyAutoAccessor && analysis.private != nil { + ref := p.generateTempRef(tempRefNeedsDeclare, "") + privateGetFnRef := p.generateTempRef(tempRefNeedsDeclare, "_") + privateSetFnRef := p.generateTempRef(tempRefNeedsDeclare, "_") + p.symbols[privateGetFnRef.InnerIndex].Link = p.privateGetters[analysis.private.Ref] + p.symbols[privateSetFnRef.InnerIndex].Link = p.privateSetters[analysis.private.Ref] + + // Unpack the "get" and "set" properties from the returned property descriptor + element = js_ast.JoinWithComma(js_ast.JoinWithComma( + js_ast.Assign( + js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}}, + element), + js_ast.Assign( + js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: privateGetFnRef}}, + js_ast.Expr{Loc: loc, Data: &js_ast.EDot{Target: js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}}, Name: "get", NameLoc: loc}})), + js_ast.Assign( + js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: privateSetFnRef}}, + js_ast.Expr{Loc: loc, Data: &js_ast.EDot{Target: js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}}, Name: "set", NameLoc: loc}})) + p.recordUsage(ref) + p.recordUsage(privateGetFnRef) + p.recordUsage(ref) + p.recordUsage(privateSetFnRef) + p.recordUsage(ref) + } + + // Put the call to the decorators in the right place + if prop.Kind == js_ast.PropertyField { + // Field + if prop.Flags.Has(js_ast.PropertyIsStatic) { + ctx.decoratorStaticFieldElements = append(ctx.decoratorStaticFieldElements, element) + } else { + ctx.decoratorInstanceFieldElements = append(ctx.decoratorInstanceFieldElements, element) + } + } else { + // Non-field + if prop.Flags.Has(js_ast.PropertyIsStatic) { + ctx.decoratorStaticNonFieldElements = append(ctx.decoratorStaticNonFieldElements, element) + } else { + ctx.decoratorInstanceNonFieldElements = append(ctx.decoratorInstanceNonFieldElements, element) + } + } + + // Omit decorated auto-accessors as they will be now generated at run-time instead + if prop.Kind == js_ast.PropertyAutoAccessor { + // Determine where to store the field + var target js_ast.Expr + if prop.Flags.Has(js_ast.PropertyIsStatic) && !analysis.staticFieldToBlockAssign { + target = ctx.nameFunc() + } else { + target = js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} + } + + // Generate the assignment initializer + var init js_ast.Expr + if prop.InitializerOrNil.Data != nil { + init = prop.InitializerOrNil + } else { + init = js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared} + } + + // Optionally call registered decorator initializers + if initializerIndex != -1 { + args := []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: loc, Data: &js_ast.ENumber{Value: float64((3 + 2*initializerIndex) << 1)}}, + } + if _, ok := init.Data.(*js_ast.EUndefined); !ok { + args = append(args, init) + } + init = p.callRuntime(init.Loc, "__runInitializers", args) + p.recordUsage(ctx.decoratorContextRef) + } + + // Initialize the private field to a new WeakMap + ctx.privateMembers = append(ctx.privateMembers, js_ast.Assign( + js_ast.Expr{Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: autoAccessorWeakMapRef}}, + js_ast.Expr{Loc: prop.Key.Loc, Data: &js_ast.ENew{Target: js_ast.Expr{Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: p.weakMapRef}}}}, + )) + p.recordUsage(autoAccessorWeakMapRef) + + // Add every newly-constructed instance into this map + key := js_ast.Expr{Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: autoAccessorWeakMapRef}} + args := []js_ast.Expr{target, key} + if _, ok := init.Data.(*js_ast.EUndefined); !ok { + args = append(args, init) + } + memberExpr := p.callRuntime(loc, "__privateAdd", args) + p.recordUsage(autoAccessorWeakMapRef) + + // Run extra initializers + if initializerIndex != -1 { + var value js_ast.Expr + if prop.Flags.Has(js_ast.PropertyIsStatic) { + value = ctx.nameFunc() + } else { + value = js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} + } + memberExpr = js_ast.JoinWithComma(memberExpr, p.callRuntime(loc, "__runInitializers", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: loc, Data: &js_ast.ENumber{Value: float64(((4 + 2*initializerIndex) << 1) | 1)}}, + value, + })) + p.recordUsage(ctx.decoratorContextRef) + } + + if prop.Flags.Has(js_ast.PropertyIsStatic) { + ctx.staticMembers = append(ctx.staticMembers, memberExpr) + } else { + ctx.instanceMembers = append(ctx.instanceMembers, js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: memberExpr}}) + } + + if analysis.private != nil { + ctx.lowerPrivateMethod(p, prop, analysis.private) + } + continue + } + } + // Generate get/set methods for auto-accessors if analysis.rewriteAutoAccessorToGetSet { properties = ctx.rewriteAutoAccessorToGetSet(p, prop, properties, keyExprNoSideEffects, analysis.mustLowerField, analysis.private, result) @@ -1389,7 +1811,7 @@ func (ctx *lowerClassContext) processProperties(p *parser, classLoweringInfo cla // Lower fields if (!prop.Kind.IsMethodDefinition() && analysis.mustLowerField) || analysis.staticFieldToBlockAssign { var keep bool - prop, keep = ctx.lowerField(p, prop, analysis.private, analysis.shouldOmitFieldInitializer, analysis.staticFieldToBlockAssign) + prop, keep = ctx.lowerField(p, prop, analysis.private, analysis.shouldOmitFieldInitializer, analysis.staticFieldToBlockAssign, initializerIndex) if !keep { continue } @@ -1496,7 +1918,7 @@ func (ctx *lowerClassContext) rewriteAutoAccessorToGetSet( } if !mustLowerField { properties = append(properties, storageProp) - } else if prop, ok := ctx.lowerField(p, storageProp, storagePrivate, false, false); ok { + } else if prop, ok := ctx.lowerField(p, storageProp, storagePrivate, false, false, -1); ok { properties = append(properties, prop) } @@ -1578,6 +2000,7 @@ func (ctx *lowerClassContext) rewriteAutoAccessorToGetSet( func (ctx *lowerClassContext) insertInitializersIntoConstructor(p *parser, classLoweringInfo classLoweringInfo, result visitClassResult) { if len(ctx.parameterFields) == 0 && + !ctx.decoratorCallInstanceMethodExtraInitializers && len(ctx.instancePrivateMethods) == 0 && len(ctx.instanceMembers) == 0 && (ctx.ctor == nil || result.superCtorRef == ast.InvalidRef) { @@ -1613,6 +2036,17 @@ func (ctx *lowerClassContext) insertInitializersIntoConstructor(p *parser, class } } + // Run instanceMethodExtraInitializers if needed + var decoratorInstanceMethodExtraInitializers js_ast.Expr + if ctx.decoratorCallInstanceMethodExtraInitializers { + decoratorInstanceMethodExtraInitializers = p.callRuntime(ctx.classLoc, "__runInitializers", []js_ast.Expr{ + {Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: ctx.classLoc, Data: &js_ast.ENumber{Value: 5}}, + {Loc: ctx.classLoc, Data: js_ast.EThisShared}, + }) + p.recordUsage(ctx.decoratorContextRef) + } + // Make sure the instance field initializers come after "super()" since // they need "this" to ba available generatedStmts := make([]js_ast.Stmt, 0, @@ -1620,6 +2054,9 @@ func (ctx *lowerClassContext) insertInitializersIntoConstructor(p *parser, class len(ctx.instancePrivateMethods)+ len(ctx.instanceMembers)) generatedStmts = append(generatedStmts, ctx.parameterFields...) + if decoratorInstanceMethodExtraInitializers.Data != nil { + generatedStmts = append(generatedStmts, js_ast.Stmt{Loc: decoratorInstanceMethodExtraInitializers.Loc, Data: &js_ast.SExpr{Value: decoratorInstanceMethodExtraInitializers}}) + } generatedStmts = append(generatedStmts, ctx.instancePrivateMethods...) generatedStmts = append(generatedStmts, ctx.instanceMembers...) p.insertStmtsAfterSuperCall(&ctx.ctor.Fn.Body, generatedStmts, result.superCtorRef) @@ -1684,10 +2121,31 @@ func (ctx *lowerClassContext) finishAndGenerateCode(p *parser, result visitClass // do this just in case it's needed. mustConvertStmtToExpr := ctx.kind != classKindExpr && p.currentScope.Parent == nil && (p.options.mode == config.ModeBundle || p.willWrapModuleInTryCatchForUsing) + // Check to see if we have lowered decorators on the class itself + var classDecorators js_ast.Expr var classExperimentalDecorators []js_ast.Decorator if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True { classExperimentalDecorators = ctx.class.Decorators ctx.class.Decorators = nil + } else if p.options.unsupportedJSFeatures.Has(compat.Decorators) { + classDecorators = ctx.decoratorClassDecorators + } + + // Handle JavaScript decorators on the class itself + var decorateClassExpr js_ast.Expr + if classDecorators.Data != nil { + if ctx.decoratorContextRef == ast.InvalidRef { + ctx.decoratorContextRef = p.generateTempRef(tempRefNeedsDeclare, "_init") + } + decorateClassExpr = p.callRuntime(ctx.classLoc, "__decorateElement", []js_ast.Expr{ + {Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: ctx.classLoc, Data: &js_ast.ENumber{Value: 0}}, + {Loc: ctx.classLoc, Data: &js_ast.EString{Value: helpers.StringToUTF16(ctx.optionalNameHint)}}, + classDecorators, + ctx.nameFunc(), + }) + p.recordUsage(ctx.decoratorContextRef) + decorateClassExpr = js_ast.Assign(ctx.nameFunc(), decorateClassExpr) } // If this is true, we have removed some code from the class body that could @@ -1728,6 +2186,19 @@ func (ctx *lowerClassContext) finishAndGenerateCode(p *parser, result visitClass var prefixExprs []js_ast.Expr var suffixExprs []js_ast.Expr + // If there are JavaScript decorators, start by allocating a context object + if ctx.decoratorContextRef != ast.InvalidRef { + prefixExprs = append(prefixExprs, js_ast.Assign( + js_ast.Expr{Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + js_ast.Expr{Loc: ctx.classLoc, Data: &js_ast.EArray{IsSingleLine: true, Items: []js_ast.Expr{ + {Loc: ctx.classLoc, Data: js_ast.EMissingShared}, // classExtraInitializers + {Loc: ctx.classLoc, Data: js_ast.EMissingShared}, // staticMethodExtraInitializers + {Loc: ctx.classLoc, Data: js_ast.EMissingShared}, // instanceMethodExtraInitializers + }}}, + )) + p.recordUsage(ctx.decoratorContextRef) + } + // Any of the computed property chain that we hoisted out of the class // body needs to come before the class expression. if ctx.computedPropertyChain.Data != nil { @@ -1737,9 +2208,30 @@ func (ctx *lowerClassContext) finishAndGenerateCode(p *parser, result visitClass // WeakSets and WeakMaps suffixExprs = append(suffixExprs, ctx.privateMembers...) + // Evaluate JavaScript decorators here + suffixExprs = append(suffixExprs, ctx.decoratorStaticNonFieldElements...) + suffixExprs = append(suffixExprs, ctx.decoratorInstanceNonFieldElements...) + suffixExprs = append(suffixExprs, ctx.decoratorStaticFieldElements...) + suffixExprs = append(suffixExprs, ctx.decoratorInstanceFieldElements...) + // Lowered initializers for static methods (including getters and setters) suffixExprs = append(suffixExprs, ctx.staticPrivateMethods...) + // Run JavaScript class decorators at the end of class initialization + if decorateClassExpr.Data != nil { + suffixExprs = append(suffixExprs, decorateClassExpr) + } + + // For each element initializer of staticMethodExtraInitializers + if ctx.decoratorCallStaticMethodExtraInitializers { + suffixExprs = append(suffixExprs, p.callRuntime(ctx.classLoc, "__runInitializers", []js_ast.Expr{ + {Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: ctx.classLoc, Data: &js_ast.ENumber{Value: 3}}, + ctx.nameFunc(), + })) + p.recordUsage(ctx.decoratorContextRef) + } + // Lowered initializers for static fields, static accessors, and static blocks suffixExprs = append(suffixExprs, ctx.staticMembers...) @@ -1748,6 +2240,16 @@ func (ctx *lowerClassContext) finishAndGenerateCode(p *parser, result visitClass suffixExprs = append(suffixExprs, ctx.instanceExperimentalDecorators...) suffixExprs = append(suffixExprs, ctx.staticExperimentalDecorators...) + // For each element initializer of classExtraInitializers + if decorateClassExpr.Data != nil { + suffixExprs = append(suffixExprs, p.callRuntime(ctx.classLoc, "__runInitializers", []js_ast.Expr{ + {Loc: ctx.classLoc, Data: &js_ast.EIdentifier{Ref: ctx.decoratorContextRef}}, + {Loc: ctx.classLoc, Data: &js_ast.ENumber{Value: 1}}, + ctx.nameFunc(), + })) + p.recordUsage(ctx.decoratorContextRef) + } + // Run TypeScript experimental class decorators at the end of class initialization if len(classExperimentalDecorators) > 0 { values := make([]js_ast.Expr, len(classExperimentalDecorators)) @@ -1842,8 +2344,13 @@ func (ctx *lowerClassContext) finishAndGenerateCode(p *parser, result visitClass p.currentScope.Generated = append(p.currentScope.Generated, captureRef) p.recordDeclaredSymbol(captureRef) p.mergeSymbols(result.innerClassNameRef, captureRef) + kind := js_ast.LocalConst + if classDecorators.Data != nil { + // Class decorators need to be able to potentially mutate this binding + kind = js_ast.LocalLet + } stmts = append(stmts, js_ast.Stmt{Loc: ctx.classLoc, Data: &js_ast.SLocal{ - Kind: p.selectLocalKind(js_ast.LocalConst), + Kind: p.selectLocalKind(kind), Decls: []js_ast.Decl{{ Binding: js_ast.Binding{Loc: nameForClassDecorators.Loc, Data: &js_ast.BIdentifier{Ref: captureRef}}, ValueOrNil: init, diff --git a/internal/js_parser/js_parser_test.go b/internal/js_parser/js_parser_test.go index 86c4790fec4..274ea8f1228 100644 --- a/internal/js_parser/js_parser_test.go +++ b/internal/js_parser/js_parser_test.go @@ -2072,14 +2072,81 @@ func TestDecorators(t *testing.T) { ": ERROR: JavaScript decorator syntax does not allow \".\" after a call expression\n"+ ": NOTE: Wrap this decorator in parentheses to allow arbitrary expressions:\n") - errorText := ": ERROR: Transforming JavaScript decorators to the configured target environment is not supported yet\n" - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "@dec class Foo {}", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec x }", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec x() {} }", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec accessor x }", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static x }", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static x() {} }", errorText) - expectParseErrorWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static accessor x }", errorText) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "@dec class Foo {}", + `var _Foo_decorators, _init; +_init = [, , ,]; +_Foo_decorators = [dec]; +class Foo { +} +Foo = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo); +__runInitializers(_init, 1, Foo); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec x }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __publicField(this, "x", __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } +} +__decorateElement(_init, 5, "x", _x_dec, Foo); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec x() {} }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __runInitializers(_init, 5, this); + } + x() { + } +} +__decorateElement(_init, 1, "x", _x_dec, Foo); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec accessor x }", + `var _x_dec, _init, _x; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __privateAdd(this, _x, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } +} +_x = new WeakMap(); +__decorateElement(_init, 4, "x", _x_dec, Foo, _x); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static x }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { +} +__decorateElement(_init, 13, "x", _x_dec, Foo); +__publicField(Foo, "x", __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static x() {} }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + static x() { + } +} +__decorateElement(_init, 9, "x", _x_dec, Foo); +__runInitializers(_init, 3, Foo); +`) + expectPrintedWithUnsupportedFeatures(t, compat.Decorators, "class Foo { @dec static accessor x }", + `var _x_dec, _init, _x; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { +} +_x = new WeakMap(); +__decorateElement(_init, 12, "x", _x_dec, Foo, _x); +__privateAdd(Foo, _x, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo); +`) // Check ASI for "abstract" expectParseError(t, "@x abstract class Foo {}", ": ERROR: Expected \";\" but found \"class\"\n") diff --git a/internal/js_parser/ts_parser_test.go b/internal/js_parser/ts_parser_test.go index 511eb82c35b..41a5008f220 100644 --- a/internal/js_parser/ts_parser_test.go +++ b/internal/js_parser/ts_parser_test.go @@ -28,9 +28,9 @@ func expectParseErrorExperimentalDecoratorTS(t *testing.T, contents string, expe }) } -func expectParseErrorWithUnsupportedFeaturesTS(t *testing.T, unsupportedJSFeatures compat.JSFeature, contents string, expected string) { +func expectPrintedWithUnsupportedFeaturesTS(t *testing.T, unsupportedJSFeatures compat.JSFeature, contents string, expected string) { t.Helper() - expectParseErrorCommon(t, contents, expected, config.Options{ + expectPrintedCommon(t, contents, expected, config.Options{ TS: config.TSOptions{ Parse: true, }, @@ -2117,14 +2117,81 @@ func TestTSDecorators(t *testing.T) { ": ERROR: JavaScript decorator syntax does not allow \".\" after a call expression\n"+ ": NOTE: Wrap this decorator in parentheses to allow arbitrary expressions:\n") - errorText := ": ERROR: Transforming JavaScript decorators to the configured target environment is not supported yet\n" - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "@dec class Foo {}", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec x }", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec x() {} }", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec accessor x }", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static x }", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static x() {} }", errorText) - expectParseErrorWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static accessor x }", errorText) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "@dec class Foo {}", + `var _Foo_decorators, _init; +_init = [, , ,]; +_Foo_decorators = [dec]; +class Foo { +} +Foo = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo); +__runInitializers(_init, 1, Foo); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec x }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __publicField(this, "x", __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } +} +__decorateElement(_init, 5, "x", _x_dec, Foo); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec x() {} }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __runInitializers(_init, 5, this); + } + x() { + } +} +__decorateElement(_init, 1, "x", _x_dec, Foo); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec accessor x }", + `var _x_dec, _init, _x; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + constructor() { + __privateAdd(this, _x, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } +} +_x = new WeakMap(); +__decorateElement(_init, 4, "x", _x_dec, Foo, _x); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static x }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { +} +__decorateElement(_init, 13, "x", _x_dec, Foo); +__publicField(Foo, "x", __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static x() {} }", + `var _x_dec, _init; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { + static x() { + } +} +__decorateElement(_init, 9, "x", _x_dec, Foo); +__runInitializers(_init, 3, Foo); +`) + expectPrintedWithUnsupportedFeaturesTS(t, compat.Decorators, "class Foo { @dec static accessor x }", + `var _x_dec, _init, _x; +_init = [, , ,]; +_x_dec = [dec]; +class Foo { +} +_x = new WeakMap(); +__decorateElement(_init, 12, "x", _x_dec, Foo, _x); +__privateAdd(Foo, _x, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo); +`) // Check ASI for "abstract" expectPrintedTS(t, "@x abstract class Foo {}", "@x class Foo {\n}\n") diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index fdeca4aa2d4..9144855d667 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -260,6 +260,54 @@ func Source(unsupportedJSFeatures compat.JSFeature) logger.Source { } export var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index) + // For JavaScript decorators + var __decoratorStrings = ['class', 'method', 'getter', 'setter', 'accessor', 'field', 'value', 'get', 'set'] + var __expectFn = fn => fn !== void 0 && typeof fn !== 'function' ? __typeError('Function expected') : fn + var __decoratorContext = (kind, name, done, fns) => ({ kind: __decoratorStrings[kind], name, addInitializer: fn => + done._ ? __typeError('Already initialized') : fns.push(__expectFn(fn || null)), }) + export var __runInitializers = (array, flags, value) => { + for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(value) : value = (0, fns[i])(value) + return value + } + export var __decorateElement = (array, flags, name, decorators, target, extra) => { + var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16) + var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5] + var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []) + var desc = k && ( + !p && !s && (target = target.prototype), + k < 5 && (k > 3 || !p) && + ` + + // Avoid object extensions when not using ES6 + if !unsupportedJSFeatures.Has(compat.ObjectExtensions) { + text += `__getOwnPropDesc(k < 4 ? target : { get [name]() { return __privateGet(this, extra) }, set [name](x) { return __privateSet(this, extra, x) } }, name)` + } else { + text += `(k < 4 ? __getOwnPropDesc(target, name) : { get: () => __privateGet(this, extra), set: x => __privateSet(this, extra, x) })` + } + + text += ` + ) + k ? p && k < 4 && __name(extra, (k > 2 ? 'set ' : k > 1 ? 'get ' : '') + name) : __name(target, name) + + for (var i = decorators.length - 1; i >= 0; i--) { + ctx = __decoratorContext(k, name, done = {}, extraInitializers) + + if (k) { + ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? x => __privateIn(target, x) : x => name in x } + if (k ^ 3) access.get = p ? x => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : x => x[name] + if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y + } + + it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1 + + if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it) + else if (typeof it !== 'object' || it === null) __typeError('Object expected') + else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn) + } + + return desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target + } + // For class members export var __publicField = (obj, key, value) => ( __defNormalProp(obj, typeof key !== 'symbol' ? key + '' : key, value) diff --git a/scripts/decorator-tests.js b/scripts/decorator-tests.js new file mode 100644 index 00000000000..22f891d0811 --- /dev/null +++ b/scripts/decorator-tests.js @@ -0,0 +1,4922 @@ +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __typeError = (msg) => { + throw TypeError(msg); +}; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); +var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; +var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn; +var __decoratorContext = (kind, name, done, fns) => ({ kind: __decoratorStrings[kind], name, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) }); +var __runInitializers = (array, flags, value) => { + for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(value) : value = (0, fns[i])(value); + return value; +}; +var __decorateElement = (array, flags, name, decorators, target, extra) => { + var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16); + var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5]; + var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []); + var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() { + return __privateGet(this, extra); + }, set [name](x) { + return __privateSet(this, extra, x); + } }, name)); + k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name); + for (var i = decorators.length - 1; i >= 0; i--) { + ctx = __decoratorContext(k, name, done = {}, extraInitializers); + if (k) { + ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x }; + if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name]; + if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y; + } + it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1; + if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it); + else if (typeof it !== "object" || it === null) __typeError("Object expected"); + else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn); + } + return desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target; +}; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); +var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj); +var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); +var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); +var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); +const tests = { + // Class decorators + "Class decorators: Basic statement": () => { + var _Foo_decorators, _init; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + _init = [, , ,]; + _Foo_decorators = [dec]; + class Foo2 { + } + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 1, Foo2); + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Anonymous": () => { + var _class_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, ""); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, ""); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + const Foo2 = /* @__PURE__ */ ((x) => x)((_init = [, , ,], _class_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "", _class_decorators, _a), __runInitializers(_init, 1, _a), _a)); + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Property value": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + const obj = { + Foo: (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a) + }; + assertEq(() => obj.Foo, old); + }, + "Class decorators: Basic expression: Variable initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + const Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Array binding": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + const [Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a)] = []; + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Object binding": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + const { Foo: Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a) } = {}; + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Assignment initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + let Foo2; + Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Assignment array binding": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + let Foo2; + [Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a)] = []; + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Assignment object binding": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + let Foo2; + ({ Foo: Foo2 = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a) } = {}); + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Instance field initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + class Class { + Foo = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + } + const Foo2 = new Class().Foo; + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Static field initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + class Class { + static Foo = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + } + assertEq(() => Class.Foo, old); + }, + "Class decorators: Basic expression: Instance auto-accessor initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + class Class { + #Foo = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + get Foo() { + return this.#Foo; + } + set Foo(_) { + this.#Foo = _; + } + } + const Foo2 = new Class().Foo; + assertEq(() => Foo2, old); + }, + "Class decorators: Basic expression: Static auto-accessor initializer": () => { + var _Foo_decorators, _init, _a; + let old; + const dec = (cls, ctx) => { + assertEq(() => typeof cls, "function"); + assertEq(() => cls.name, "Foo"); + assertEq(() => ctx.kind, "class"); + assertEq(() => ctx.name, "Foo"); + assertEq(() => "static" in ctx, false); + assertEq(() => "private" in ctx, false); + assertEq(() => "access" in ctx, false); + old = cls; + }; + class Class { + static #Foo = (_init = [, , ,], _Foo_decorators = [dec], _a = class { + }, _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 1, _a), _a); + static get Foo() { + return this.#Foo; + } + static set Foo(_) { + this.#Foo = _; + } + } + assertEq(() => Class.Foo, old); + }, + "Class decorators: Order": () => { + var _Foo_decorators, _init; + const log = []; + let Bar; + let Baz; + const dec1 = (cls, ctx) => { + log.push(2); + Bar = function() { + log.push(4); + return new cls(); + }; + return Bar; + }; + const dec2 = (cls, ctx) => { + log.push(1); + Baz = function() { + log.push(5); + return new cls(); + }; + return Baz; + }; + log.push(0); + _init = [, , ,]; + _Foo_decorators = [dec1, dec2]; + class Foo2 { + constructor() { + log.push(6); + } + } + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 1, Foo2); + log.push(3); + new Foo2(); + log.push(7); + assertEq(() => Foo2, Bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Class decorators: Return null": () => { + assertThrows(() => { + var _Foo_decorators, _init; + const dec = (cls, ctx) => { + return null; + }; + _init = [, , ,]; + _Foo_decorators = [dec]; + class Foo2 { + } + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 1, Foo2); + }, TypeError); + }, + "Class decorators: Return object": () => { + assertThrows(() => { + var _Foo_decorators, _init; + const dec = (cls, ctx) => { + return {}; + }; + _init = [, , ,]; + _Foo_decorators = [dec]; + class Foo2 { + } + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 1, Foo2); + }, TypeError); + }, + "Class decorators: Extra initializer": () => { + var _Foo_decorators, _init; + let oldAddInitializer; + let got; + const dec = (cls, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _Foo_decorators = [dec, dec]; + class Foo2 { + } + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 1, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Method decorators + "Method decorators: Basic (instance method)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const old = {}; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "method"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => "set" in ctx.access, false); + old[key] = fn; + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + foo() { + } + [(_foo_dec = [dec("foo", "foo")], _b = (_bar_dec = [dec(bar, "[bar]")], bar))]() { + } + [_a = (_baz_dec = [dec(baz, "")], baz)]() { + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + __decorateElement(_init, 1, _b, _bar_dec, Foo2); + __decorateElement(_init, 1, _a, _baz_dec, Foo2); + assertEq(() => Foo2.prototype.foo, old["foo"]); + assertEq(() => Foo2.prototype[bar], old[bar]); + assertEq(() => Foo2.prototype[baz], old[baz]); + }, + "Method decorators: Basic (static method)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const old = {}; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "method"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => "set" in ctx.access, false); + old[key] = fn; + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + static foo() { + } + static [(_foo_dec = [dec("foo", "foo")], _b = (_bar_dec = [dec(bar, "[bar]")], bar))]() { + } + static [_a = (_baz_dec = [dec(baz, "")], baz)]() { + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __decorateElement(_init, 9, _b, _bar_dec, Foo2); + __decorateElement(_init, 9, _a, _baz_dec, Foo2); + __runInitializers(_init, 3, Foo2); + assertEq(() => Foo2.foo, old["foo"]); + assertEq(() => Foo2[bar], old[bar]); + assertEq(() => Foo2[baz], old[baz]); + }, + "Method decorators: Basic (private instance method)": () => { + var _foo_dec, _init, _Foo_instances, foo_fn; + let old; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "#foo"); + assertEq(() => ctx.kind, "method"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo2()), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(new Foo2()), $foo); + assertEq(() => "set" in ctx.access, false); + }; + old = fn; + }; + let $foo; + _init = [, , ,]; + _foo_dec = [dec]; + const _Foo = class _Foo { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + }; + _Foo_instances = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + $foo = __privateMethod(new _Foo(), _Foo_instances, foo_fn); + let Foo2 = _Foo; + assertEq(() => $foo, old); + lateAsserts(); + }, + "Method decorators: Basic (private static method)": () => { + var _foo_dec, _init, _Foo_static, foo_fn; + let old; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "#foo"); + assertEq(() => ctx.kind, "method"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo2), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(Foo2), $foo); + assertEq(() => "set" in ctx.access, false); + }; + old = fn; + }; + let $foo; + _init = [, , ,]; + _foo_dec = [dec]; + const _Foo = class _Foo { + }; + _Foo_static = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(_Foo, _Foo_static); + __runInitializers(_init, 3, _Foo); + $foo = __privateMethod(_Foo, _Foo_static, foo_fn); + let Foo2 = _Foo; + assertEq(() => $foo, old); + lateAsserts(); + }, + "Method decorators: Shim (instance method)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "bar", 123); + } + foo() { + return this.bar; + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + assertEq(() => Foo2.prototype.foo, bar); + assertEq(() => new Foo2().foo(), 124); + }, + "Method decorators: Shim (static method)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static foo() { + return this.bar; + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 123); + assertEq(() => Foo2.foo, bar); + assertEq(() => Foo2.foo(), 124); + }, + "Method decorators: Shim (private instance method)": () => { + var _foo_dec, _init, _Foo_instances, foo_fn; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + let $foo; + _init = [, , ,]; + _foo_dec = [dec]; + const _Foo = class _Foo { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __publicField(this, "bar", 123); + } + }; + _Foo_instances = new WeakSet(); + foo_fn = function() { + return this.bar; + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + $foo = __privateMethod(new _Foo(), _Foo_instances, foo_fn); + let Foo2 = _Foo; + assertEq(() => $foo, bar); + assertEq(() => bar.call(new Foo2()), 124); + }, + "Method decorators: Shim (private static method)": () => { + var _foo_dec, _init, _Foo_static, foo_fn; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + let $foo; + _init = [, , ,]; + _foo_dec = [dec]; + const _Foo = class _Foo { + }; + _Foo_static = new WeakSet(); + foo_fn = function() { + return this.bar; + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(_Foo, _Foo_static); + __runInitializers(_init, 3, _Foo); + __publicField(_Foo, "bar", 123); + $foo = __privateMethod(_Foo, _Foo_static, foo_fn); + let Foo2 = _Foo; + assertEq(() => $foo, bar); + assertEq(() => bar.call(Foo2), 124); + }, + "Method decorators: Order (instance method)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + foo() { + return log.push(6); + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + log.push(3); + new Foo2().foo(); + log.push(7); + assertEq(() => Foo2.prototype.foo, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Method decorators: Order (static method)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + static foo() { + return log.push(6); + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + log.push(3); + Foo2.foo(); + log.push(7); + assertEq(() => Foo2.foo, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Method decorators: Order (private instance method)": () => { + var _foo_dec, _init, _Foo_instances, foo_fn; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + let $foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + const _Foo = class _Foo { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + }; + _Foo_instances = new WeakSet(); + foo_fn = function() { + return log.push(6); + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + $foo = __privateMethod(new _Foo(), _Foo_instances, foo_fn); + let Foo2 = _Foo; + log.push(3); + $foo.call(new Foo2()); + log.push(7); + assertEq(() => $foo, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Method decorators: Order (private static method)": () => { + var _foo_dec, _init, _Foo_static, foo_fn; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + let $foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + const _Foo = class _Foo { + }; + _Foo_static = new WeakSet(); + foo_fn = function() { + return log.push(6); + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(_Foo, _Foo_static); + __runInitializers(_init, 3, _Foo); + $foo = __privateMethod(_Foo, _Foo_static, foo_fn); + let Foo2 = _Foo; + log.push(3); + $foo.call(Foo2); + log.push(7); + assertEq(() => $foo, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Method decorators: Return null (instance method)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + foo() { + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Method decorators: Return null (static method)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static foo() { + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Method decorators: Return null (private instance method)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_fn; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + }, TypeError); + }, + "Method decorators: Return null (private static method)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_fn; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Method decorators: Return object (instance method)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + foo() { + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Method decorators: Return object (static method)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static foo() { + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Method decorators: Return object (private instance method)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_fn; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + }, TypeError); + }, + "Method decorators: Return object (private static method)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_fn; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Method decorators: Extra initializer (instance method)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + foo() { + } + } + __decorateElement(_init, 1, "foo", _foo_dec, Foo2); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Method decorators: Extra initializer (static method)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + static foo() { + } + } + __decorateElement(_init, 9, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + "Method decorators: Extra initializer (private instance method)": () => { + var _foo_dec, _init, _Foo_instances, foo_fn; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 17, "#foo", _foo_dec, _Foo_instances, foo_fn); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Method decorators: Extra initializer (private static method)": () => { + var _foo_dec, _init, _Foo_static, foo_fn; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_fn = function() { + }; + foo_fn = __decorateElement(_init, 25, "#foo", _foo_dec, _Foo_static, foo_fn); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Field decorators + "Field decorators: Basic (instance field)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key) => (value, ctx) => { + assertEq(() => value, void 0); + assertEq(() => ctx.kind, "field"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => { + const obj = {}; + ctx.access.set(obj, 321); + return obj[key]; + }, 321); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + _foo_dec = [dec("foo")], _b = (_bar_dec = [dec(bar)], bar), _a = (_baz_dec = [dec(baz)], baz); + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + __publicField(this, _b, __runInitializers(_init, 10, 123)), __runInitializers(_init, 13, this); + __publicField(this, _a, __runInitializers(_init, 14, 123)), __runInitializers(_init, 17, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + __decorateElement(_init, 5, _b, _bar_dec, Foo2); + __decorateElement(_init, 5, _a, _baz_dec, Foo2); + assertEq(() => new Foo2().foo, 123); + assertEq(() => new Foo2()[bar], 123); + assertEq(() => new Foo2()[baz], 123); + }, + "Field decorators: Basic (static field)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key) => (value, ctx) => { + assertEq(() => value, void 0); + assertEq(() => ctx.kind, "field"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => { + const obj = {}; + ctx.access.set(obj, 321); + return obj[key]; + }, 321); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + _foo_dec = [dec("foo")], _b = (_bar_dec = [dec(bar)], bar), _a = (_baz_dec = [dec(baz)], baz); + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __decorateElement(_init, 13, _b, _bar_dec, Foo2); + __decorateElement(_init, 13, _a, _baz_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + __publicField(Foo2, _b, __runInitializers(_init, 10, 123)), __runInitializers(_init, 13, Foo2); + __publicField(Foo2, _a, __runInitializers(_init, 14, 123)), __runInitializers(_init, 17, Foo2); + assertEq(() => Foo2.foo, 123); + assertEq(() => Foo2[bar], 123); + assertEq(() => Foo2[baz], 123); + }, + "Field decorators: Basic (private instance field)": () => { + var _foo_dec, _init, _foo; + let lateAsserts; + const dec = (value, ctx) => { + assertEq(() => value, void 0); + assertEq(() => ctx.kind, "field"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo2()), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(new Foo2()), 123); + assertEq(() => { + const obj = new Foo2(); + ctx.access.set(obj, 321); + return get$foo(obj); + }, 321); + }; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + get$foo = (x) => __privateGet(x, _foo); + assertEq(() => get$foo(new Foo2()), 123); + lateAsserts(); + }, + "Field decorators: Basic (private static field)": () => { + var _foo_dec, _init, _foo; + let lateAsserts; + const dec = (value, ctx) => { + assertEq(() => value, void 0); + assertEq(() => ctx.kind, "field"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo2), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(Foo2), 123); + assertEq(() => { + ctx.access.set(Foo2, 321); + return get$foo(Foo2); + }, 321); + }; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + get$foo = (x) => __privateGet(x, _foo); + assertEq(() => get$foo(Foo2), 123); + lateAsserts(); + }, + "Field decorators: Shim (instance field)": () => { + var _bar_dec, _foo_dec, _init; + let log = []; + const dec = (value, ctx) => { + return (x) => log.push(x); + }; + _init = [, , ,]; + _foo_dec = [dec], _bar_dec = [dec]; + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + __publicField(this, "bar", __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + __decorateElement(_init, 5, "bar", _bar_dec, Foo2); + assertEq(() => log + "", ""); + var obj = new Foo2(); + assertEq(() => obj.foo, 1); + assertEq(() => obj.bar, 2); + assertEq(() => log + "", "123,"); + var obj = new Foo2(); + assertEq(() => obj.foo, 3); + assertEq(() => obj.bar, 4); + assertEq(() => log + "", "123,,123,"); + }, + "Field decorators: Shim (static field)": () => { + let log = []; + const dec = (value, ctx) => { + return (x) => log.push(x); + }; + const fn = (foo, bar) => { + var _bar_dec, _foo_dec, _init; + _init = [, , ,]; + _foo_dec = [dec], _bar_dec = [dec]; + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __decorateElement(_init, 13, "bar", _bar_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + __publicField(Foo2, "bar", __runInitializers(_init, 10)), __runInitializers(_init, 13, Foo2); + assertEq(() => Foo2.foo, foo); + assertEq(() => Foo2.bar, bar); + }; + assertEq(() => log + "", ""); + fn(1, 2); + assertEq(() => log + "", "123,"); + fn(3, 4); + assertEq(() => log + "", "123,,123,"); + }, + "Field decorators: Shim (private instance field)": () => { + var _bar_dec, _foo_dec, _init, _foo, _bar; + let log = []; + const dec = (value, ctx) => { + return (x) => log.push(x); + }; + let get$foo; + let get$bar; + _init = [, , ,]; + _foo_dec = [dec], _bar_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + __privateAdd(this, _bar, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + } + _foo = new WeakMap(); + _bar = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + __decorateElement(_init, 21, "#bar", _bar_dec, _bar); + get$foo = (x) => __privateGet(x, _foo); + get$bar = (x) => __privateGet(x, _bar); + assertEq(() => log + "", ""); + var obj = new Foo2(); + assertEq(() => get$foo(obj), 1); + assertEq(() => get$bar(obj), 2); + assertEq(() => log + "", "123,"); + var obj = new Foo2(); + assertEq(() => get$foo(obj), 3); + assertEq(() => get$bar(obj), 4); + assertEq(() => log + "", "123,,123,"); + }, + "Field decorators: Shim (private static field)": () => { + let log = []; + const dec = (value, ctx) => { + return (x) => log.push(x); + }; + const fn = (foo, bar) => { + var _bar_dec, _foo_dec, _init, _foo, _bar; + let get$foo; + let get$bar; + _init = [, , ,]; + _foo_dec = [dec], _bar_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + _bar = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __decorateElement(_init, 29, "#bar", _bar_dec, _bar); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + __privateAdd(Foo2, _bar, __runInitializers(_init, 10)), __runInitializers(_init, 13, Foo2); + get$foo = (x) => __privateGet(x, _foo); + get$bar = (x) => __privateGet(x, _bar); + assertEq(() => get$foo(Foo2), foo); + assertEq(() => get$bar(Foo2), bar); + }; + assertEq(() => log + "", ""); + fn(1, 2); + assertEq(() => log + "", "123,"); + fn(3, 4); + assertEq(() => log + "", "123,,123,"); + }, + "Field decorators: Order (instance field)": () => { + var _foo_dec, _init; + const log = []; + const dec1 = (value, ctx) => { + log.push(2); + return () => log.push(4); + }; + const dec2 = (value, ctx) => { + log.push(1); + return () => log.push(5); + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + log.push(3); + var obj = new Foo2(); + log.push(6); + assertEq(() => obj.foo, 6); + assertEq(() => log + "", "0,1,2,3,4,5,6"); + }, + "Field decorators: Order (static field)": () => { + var _foo_dec, _init; + const log = []; + const dec1 = (value, ctx) => { + log.push(2); + return () => log.push(3); + }; + const dec2 = (value, ctx) => { + log.push(1); + return () => log.push(4); + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + log.push(5); + assertEq(() => Foo2.foo, 5); + assertEq(() => log + "", "0,1,2,3,4,5"); + }, + "Field decorators: Order (private instance field)": () => { + var _foo_dec, _init, _foo; + const log = []; + const dec1 = (value, ctx) => { + log.push(2); + return () => log.push(4); + }; + const dec2 = (value, ctx) => { + log.push(1); + return () => log.push(5); + }; + log.push(0); + let get$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + get$foo = (x) => __privateGet(x, _foo); + log.push(3); + var obj = new Foo2(); + log.push(6); + assertEq(() => get$foo(obj), 6); + assertEq(() => log + "", "0,1,2,3,4,5,6"); + }, + "Field decorators: Order (private static field)": () => { + var _foo_dec, _init, _foo; + const log = []; + const dec1 = (value, ctx) => { + log.push(2); + return () => log.push(3); + }; + const dec2 = (value, ctx) => { + log.push(1); + return () => log.push(4); + }; + log.push(0); + let get$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + get$foo = (x) => __privateGet(x, _foo); + log.push(5); + assertEq(() => get$foo(Foo2), 5); + assertEq(() => log + "", "0,1,2,3,4,5"); + }, + "Field decorators: Return null (instance field)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (value, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Field decorators: Return null (static field)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (value, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Field decorators: Return null (private instance field)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (value, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + }, TypeError); + }, + "Field decorators: Return null (private static field)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (value, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Field decorators: Return object (instance field)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (value, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Field decorators: Return object (static field)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (value, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Field decorators: Return object (private instance field)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (value, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + }, TypeError); + }, + "Field decorators: Return object (private static field)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (value, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Field decorators: Extra initializer (instance field)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (value, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __publicField(this, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + __decorateElement(_init, 5, "foo", _foo_dec, Foo2); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Field decorators: Extra initializer (static field)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (value, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + __decorateElement(_init, 13, "foo", _foo_dec, Foo2); + __publicField(Foo2, "foo", __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + "Field decorators: Extra initializer (private instance field)": () => { + var _foo_dec, _init, _foo; + let oldAddInitializer; + let got; + const dec = (value, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 21, "#foo", _foo_dec, _foo); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Field decorators: Extra initializer (private static field)": () => { + var _foo_dec, _init, _foo; + let oldAddInitializer; + let got; + const dec = (value, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 29, "#foo", _foo_dec, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Getter decorators + "Getter decorators: Basic (instance getter)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "getter"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => "set" in ctx.access, false); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "bar", 123); + } + get foo() { + return this.bar; + } + get [(_foo_dec = [dec("foo", "get foo")], _b = (_bar_dec = [dec(bar, "get [bar]")], bar))]() { + return this.bar; + } + get [_a = (_baz_dec = [dec(baz, "get ")], baz)]() { + return this.bar; + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + __decorateElement(_init, 2, _b, _bar_dec, Foo2); + __decorateElement(_init, 2, _a, _baz_dec, Foo2); + assertEq(() => new Foo2().foo, 123); + assertEq(() => new Foo2()[bar], 123); + assertEq(() => new Foo2()[baz], 123); + }, + "Getter decorators: Basic (static getter)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "getter"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => "set" in ctx.access, false); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + static get foo() { + return this.bar; + } + static get [(_foo_dec = [dec("foo", "get foo")], _b = (_bar_dec = [dec(bar, "get [bar]")], bar))]() { + return this.bar; + } + static get [_a = (_baz_dec = [dec(baz, "get ")], baz)]() { + return this.bar; + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __decorateElement(_init, 10, _b, _bar_dec, Foo2); + __decorateElement(_init, 10, _a, _baz_dec, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 123); + assertEq(() => Foo2.foo, 123); + assertEq(() => Foo2[bar], 123); + assertEq(() => Foo2[baz], 123); + }, + "Getter decorators: Basic (private instance getter)": () => { + var _foo_dec, _bar, _init, _Foo_instances, foo_get; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "get #foo"); + assertEq(() => ctx.kind, "getter"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo2()), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(new Foo2()), 123); + assertEq(() => "set" in ctx.access, false); + }; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __privateAdd(this, _bar, 123); + } + } + _bar = new WeakMap(); + _Foo_instances = new WeakSet(); + foo_get = function() { + return __privateGet(this, _bar); + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + get$foo = (x) => __privateGet(x, _Foo_instances, foo_get); + assertEq(() => get$foo(new Foo2()), 123); + lateAsserts(); + }, + "Getter decorators: Basic (private static getter)": () => { + var _foo_dec, _bar, _init, _Foo_static, foo_get; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "get #foo"); + assertEq(() => ctx.kind, "getter"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo2), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(Foo2), 123); + assertEq(() => "set" in ctx.access, false); + }; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _bar = new WeakMap(); + _Foo_static = new WeakSet(); + foo_get = function() { + return __privateGet(this, _bar); + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + __privateAdd(Foo2, _bar, 123); + get$foo = (x) => __privateGet(x, _Foo_static, foo_get); + assertEq(() => get$foo(Foo2), 123); + lateAsserts(); + }, + "Getter decorators: Shim (instance getter)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "bar", 123); + } + get foo() { + return this.bar; + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").get, bar); + assertEq(() => new Foo2().foo, 124); + }, + "Getter decorators: Shim (static getter)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static get foo() { + return this.bar; + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 123); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").get, bar); + assertEq(() => Foo2.foo, 124); + }, + "Getter decorators: Shim (private instance getter)": () => { + var _foo_dec, _bar, _init, _Foo_instances, foo_get; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __privateAdd(this, _bar, 123); + } + } + _bar = new WeakMap(); + _Foo_instances = new WeakSet(); + foo_get = function() { + return __privateGet(this, _bar); + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + get$foo = (x) => __privateGet(x, _Foo_instances, foo_get); + assertEq(() => get$foo(new Foo2()), 124); + }, + "Getter decorators: Shim (private static getter)": () => { + var _foo_dec, _bar, _init, _Foo_static, foo_get; + let bar; + const dec = (fn, ctx) => { + bar = function() { + return fn.call(this) + 1; + }; + return bar; + }; + let get$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _bar = new WeakMap(); + _Foo_static = new WeakSet(); + foo_get = function() { + return __privateGet(this, _bar); + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + __privateAdd(Foo2, _bar, 123); + get$foo = (x) => __privateGet(x, _Foo_static, foo_get); + assertEq(() => get$foo(Foo2), 124); + }, + "Getter decorators: Order (instance getter)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + get foo() { + return log.push(6); + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + log.push(3); + new Foo2().foo; + log.push(7); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").get, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Getter decorators: Order (static getter)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + static get foo() { + return log.push(6); + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + log.push(3); + Foo2.foo; + log.push(7); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").get, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Getter decorators: Order (private instance getter)": () => { + var _foo_dec, _init, _Foo_instances, foo_get; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + let get$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_get = function() { + return log.push(6); + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + get$foo = (x) => __privateGet(x, _Foo_instances, foo_get); + log.push(3); + assertEq(() => get$foo(new Foo2()), 7); + log.push(7); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Getter decorators: Order (private static getter)": () => { + var _foo_dec, _init, _Foo_static, foo_get; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function() { + log.push(4); + return fn.call(this); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function() { + log.push(5); + return fn.call(this); + }; + return baz; + }; + log.push(0); + let get$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_get = function() { + return log.push(6); + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + get$foo = (x) => __privateGet(x, _Foo_static, foo_get); + log.push(3); + assertEq(() => get$foo(Foo2), 7); + log.push(7); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Getter decorators: Return null (instance getter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + get foo() { + return; + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Getter decorators: Return null (static getter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static get foo() { + return; + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Getter decorators: Return null (private instance getter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_get; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + }, TypeError); + }, + "Getter decorators: Return null (private static getter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_get; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Getter decorators: Return object (instance getter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + get foo() { + return; + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Getter decorators: Return object (static getter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static get foo() { + return; + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Getter decorators: Return object (private instance getter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_get; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + }, TypeError); + }, + "Getter decorators: Return object (private static getter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_get; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Getter decorators: Extra initializer (instance getter)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + get foo() { + return; + } + } + __decorateElement(_init, 2, "foo", _foo_dec, Foo2); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Getter decorators: Extra initializer (static getter)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + static get foo() { + return; + } + } + __decorateElement(_init, 10, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + "Getter decorators: Extra initializer (private instance getter)": () => { + var _foo_dec, _init, _Foo_instances, foo_get; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 18, "#foo", _foo_dec, _Foo_instances, foo_get); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Getter decorators: Extra initializer (private static getter)": () => { + var _foo_dec, _init, _Foo_static, foo_get; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_get = function() { + return; + }; + foo_get = __decorateElement(_init, 26, "#foo", _foo_dec, _Foo_static, foo_get); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Setter decorators + "Setter decorators: Basic (instance setter)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "setter"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => "get" in ctx.access, false); + const obj2 = {}; + ctx.access.set(obj2, 123); + assertEq(() => obj2[key], 123); + assertEq(() => "bar" in obj2, false); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "bar", 0); + } + set foo(x) { + this.bar = x; + } + set [(_foo_dec = [dec("foo", "set foo")], _b = (_bar_dec = [dec(bar, "set [bar]")], bar))](x) { + this.bar = x; + } + set [_a = (_baz_dec = [dec(baz, "set ")], baz)](x) { + this.bar = x; + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + __decorateElement(_init, 3, _b, _bar_dec, Foo2); + __decorateElement(_init, 3, _a, _baz_dec, Foo2); + var obj = new Foo2(); + obj.foo = 321; + assertEq(() => obj.bar, 321); + obj[bar] = 4321; + assertEq(() => obj.bar, 4321); + obj[baz] = 54321; + assertEq(() => obj.bar, 54321); + }, + "Setter decorators: Basic (static setter)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init; + const dec = (key, name) => (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, name); + assertEq(() => ctx.kind, "setter"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => "get" in ctx.access, false); + const obj = {}; + ctx.access.set(obj, 123); + assertEq(() => obj[key], 123); + assertEq(() => "bar" in obj, false); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + class Foo2 { + static set foo(x) { + this.bar = x; + } + static set [(_foo_dec = [dec("foo", "set foo")], _b = (_bar_dec = [dec(bar, "set [bar]")], bar))](x) { + this.bar = x; + } + static set [_a = (_baz_dec = [dec(baz, "set ")], baz)](x) { + this.bar = x; + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __decorateElement(_init, 11, _b, _bar_dec, Foo2); + __decorateElement(_init, 11, _a, _baz_dec, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 0); + Foo2.foo = 321; + assertEq(() => Foo2.bar, 321); + Foo2[bar] = 4321; + assertEq(() => Foo2.bar, 4321); + Foo2[baz] = 54321; + assertEq(() => Foo2.bar, 54321); + }, + "Setter decorators: Basic (private instance setter)": () => { + var _foo_dec, _init, _Foo_instances, foo_set; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "set #foo"); + assertEq(() => ctx.kind, "setter"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo2()), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => "get" in ctx.access, false); + assertEq(() => { + const obj2 = new Foo2(); + ctx.access.set(obj2, 123); + return obj2.bar; + }, 123); + }; + }; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __publicField(this, "bar", 0); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + this.bar = x; + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + set$foo = (x, y) => { + __privateSet(x, _Foo_instances, y, foo_set); + }; + lateAsserts(); + var obj = new Foo2(); + assertEq(() => set$foo(obj, 321), void 0); + assertEq(() => obj.bar, 321); + }, + "Setter decorators: Basic (private static setter)": () => { + var _foo_dec, _init, _Foo_static, foo_set; + let lateAsserts; + const dec = (fn, ctx) => { + assertEq(() => typeof fn, "function"); + assertEq(() => fn.name, "set #foo"); + assertEq(() => ctx.kind, "setter"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo2), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => "get" in ctx.access, false); + assertEq(() => { + ctx.access.set(Foo2, 123); + return Foo2.bar; + }, 123); + }; + }; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + this.bar = x; + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 0); + set$foo = (x, y) => { + __privateSet(x, _Foo_static, y, foo_set); + }; + lateAsserts(); + assertEq(() => set$foo(Foo2, 321), void 0); + assertEq(() => Foo2.bar, 321); + }, + "Setter decorators: Shim (instance setter)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function(x) { + fn.call(this, x + 1); + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "bar", 123); + } + set foo(x) { + this.bar = x; + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").set, bar); + var obj = new Foo2(); + obj.foo = 321; + assertEq(() => obj.bar, 322); + }, + "Setter decorators: Shim (static setter)": () => { + var _foo_dec, _init; + let bar; + const dec = (fn, ctx) => { + bar = function(x) { + fn.call(this, x + 1); + }; + return bar; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static set foo(x) { + this.bar = x; + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 123); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").set, bar); + Foo2.foo = 321; + assertEq(() => Foo2.bar, 322); + }, + "Setter decorators: Shim (private instance setter)": () => { + var _foo_dec, _init, _Foo_instances, foo_set; + let bar; + const dec = (fn, ctx) => { + bar = function(x) { + fn.call(this, x + 1); + }; + return bar; + }; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __publicField(this, "bar", 123); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + this.bar = x; + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + set$foo = (x, y) => { + __privateSet(x, _Foo_instances, y, foo_set); + }; + var obj = new Foo2(); + assertEq(() => set$foo(obj, 321), void 0); + assertEq(() => obj.bar, 322); + }, + "Setter decorators: Shim (private static setter)": () => { + var _foo_dec, _init, _Foo_static, foo_set; + let bar; + const dec = (fn, ctx) => { + bar = function(x) { + fn.call(this, x + 1); + }; + return bar; + }; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + this.bar = x; + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "bar", 123); + set$foo = (x, y) => { + __privateSet(x, _Foo_static, y, foo_set); + }; + assertEq(() => set$foo(Foo2, 321), void 0); + assertEq(() => Foo2.bar, 322); + }, + "Setter decorators: Order (instance setter)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function(x) { + log.push(4); + fn.call(this, x); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function(x) { + log.push(5); + fn.call(this, x); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + set foo(x) { + log.push(6); + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + log.push(3); + new Foo2().foo = 123; + log.push(7); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").set, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Setter decorators: Order (static setter)": () => { + var _foo_dec, _init; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function(x) { + log.push(4); + fn.call(this, x); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function(x) { + log.push(5); + fn.call(this, x); + }; + return baz; + }; + log.push(0); + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + static set foo(x) { + log.push(6); + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + log.push(3); + Foo2.foo = 123; + log.push(7); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").set, bar); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Setter decorators: Order (private instance setter)": () => { + var _foo_dec, _init, _Foo_instances, foo_set; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function(x) { + log.push(4); + fn.call(this, x); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function(x) { + log.push(5); + fn.call(this, x); + }; + return baz; + }; + log.push(0); + let set$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + log.push(6); + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + set$foo = (x, y) => { + __privateSet(x, _Foo_instances, y, foo_set); + }; + log.push(3); + assertEq(() => set$foo(new Foo2(), 123), void 0); + log.push(7); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Setter decorators: Order (private static setter)": () => { + var _foo_dec, _init, _Foo_static, foo_set; + const log = []; + let bar; + let baz; + const dec1 = (fn, ctx) => { + log.push(2); + bar = function(x) { + log.push(4); + fn.call(this, x); + }; + return bar; + }; + const dec2 = (fn, ctx) => { + log.push(1); + baz = function(x) { + log.push(5); + fn.call(this, x); + }; + return baz; + }; + log.push(0); + let set$foo; + _init = [, , ,]; + _foo_dec = [dec1, dec2]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + log.push(6); + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + set$foo = (x, y) => { + __privateSet(x, _Foo_static, y, foo_set); + }; + log.push(3); + assertEq(() => set$foo(Foo2, 123), void 0); + log.push(7); + assertEq(() => log + "", "0,1,2,3,4,5,6,7"); + }, + "Setter decorators: Return null (instance setter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + set foo(x) { + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Setter decorators: Return null (static setter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static set foo(x) { + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Setter decorators: Return null (private instance setter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_set; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + }, TypeError); + }, + "Setter decorators: Return null (private static setter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_set; + const dec = (fn, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Setter decorators: Return object (instance setter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + set foo(x) { + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + }, TypeError); + }, + "Setter decorators: Return object (static setter)": () => { + assertThrows(() => { + var _foo_dec, _init; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + static set foo(x) { + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Setter decorators: Return object (private instance setter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_instances, foo_set; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + }, TypeError); + }, + "Setter decorators: Return object (private static setter)": () => { + assertThrows(() => { + var _foo_dec, _init, _Foo_static, foo_set; + const dec = (fn, ctx) => { + return {}; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + }, TypeError); + }, + "Setter decorators: Extra initializer (instance setter)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + } + set foo(x) { + } + } + __decorateElement(_init, 3, "foo", _foo_dec, Foo2); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Setter decorators: Extra initializer (static setter)": () => { + var _foo_dec, _init; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + static set foo(x) { + } + } + __decorateElement(_init, 11, "foo", _foo_dec, Foo2); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + "Setter decorators: Extra initializer (private instance setter)": () => { + var _foo_dec, _init, _Foo_instances, foo_set; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + } + } + _Foo_instances = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 19, "#foo", _foo_dec, _Foo_instances, foo_set); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Setter decorators: Extra initializer (private static setter)": () => { + var _foo_dec, _init, _Foo_static, foo_set; + let oldAddInitializer; + let got; + const dec = (fn, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _Foo_static = new WeakSet(); + foo_set = function(x) { + }; + foo_set = __decorateElement(_init, 27, "#foo", _foo_dec, _Foo_static, foo_set); + __privateAdd(Foo2, _Foo_static); + __runInitializers(_init, 3, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Auto-accessor decorators + "Auto-accessor decorators: Basic (instance auto-accessor)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init, _foo, __b, __a; + const dec = (key, getName, setName) => (target, ctx) => { + assertEq(() => typeof target.get, "function"); + assertEq(() => typeof target.set, "function"); + assertEq(() => target.get.name, getName); + assertEq(() => target.set.name, setName); + assertEq(() => ctx.kind, "accessor"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => { + const obj2 = {}; + ctx.access.set(obj2, 123); + return obj2[key]; + }, 123); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + _foo_dec = [dec("foo", "get foo", "set foo")], _b = (_bar_dec = [dec(bar, "get [bar]", "set [bar]")], bar), _a = (_baz_dec = [dec(baz, "get ", "set ")], baz); + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6, 0)), __runInitializers(_init, 9, this); + __privateAdd(this, __b, __runInitializers(_init, 10, 0)), __runInitializers(_init, 13, this); + __privateAdd(this, __a, __runInitializers(_init, 14, 0)), __runInitializers(_init, 17, this); + } + } + _foo = new WeakMap(); + __b = new WeakMap(); + __a = new WeakMap(); + __decorateElement(_init, 4, "foo", _foo_dec, Foo2, _foo); + __decorateElement(_init, 4, _b, _bar_dec, Foo2, __b); + __decorateElement(_init, 4, _a, _baz_dec, Foo2, __a); + var obj = new Foo2(); + obj.foo = 321; + assertEq(() => obj.foo, 321); + obj[bar] = 4321; + assertEq(() => obj[bar], 4321); + obj[baz] = 54321; + assertEq(() => obj[baz], 54321); + }, + "Auto-accessor decorators: Basic (static auto-accessor)": () => { + var _baz_dec, _a, _bar_dec, _b, _foo_dec, _init, _foo, __b, __a; + const dec = (key, getName, setName) => (target, ctx) => { + assertEq(() => typeof target.get, "function"); + assertEq(() => typeof target.set, "function"); + assertEq(() => target.get.name, getName); + assertEq(() => target.set.name, setName); + assertEq(() => ctx.kind, "accessor"); + assertEq(() => ctx.name, key); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, false); + assertEq(() => ctx.access.has({ [key]: false }), true); + assertEq(() => ctx.access.has({ bar: true }), false); + assertEq(() => ctx.access.get({ [key]: 123 }), 123); + assertEq(() => { + const obj = {}; + ctx.access.set(obj, 123); + return obj[key]; + }, 123); + }; + const bar = Symbol("bar"); + const baz = Symbol(); + _init = [, , ,]; + _foo_dec = [dec("foo", "get foo", "set foo")], _b = (_bar_dec = [dec(bar, "get [bar]", "set [bar]")], bar), _a = (_baz_dec = [dec(baz, "get ", "set ")], baz); + class Foo2 { + } + _foo = new WeakMap(); + __b = new WeakMap(); + __a = new WeakMap(); + __decorateElement(_init, 12, "foo", _foo_dec, Foo2, _foo); + __decorateElement(_init, 12, _b, _bar_dec, Foo2, __b); + __decorateElement(_init, 12, _a, _baz_dec, Foo2, __a); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 0)), __runInitializers(_init, 9, Foo2); + __privateAdd(Foo2, __b, __runInitializers(_init, 10, 0)), __runInitializers(_init, 13, Foo2); + __privateAdd(Foo2, __a, __runInitializers(_init, 14, 0)), __runInitializers(_init, 17, Foo2); + Foo2.foo = 321; + assertEq(() => Foo2.foo, 321); + Foo2[bar] = 4321; + assertEq(() => Foo2[bar], 4321); + Foo2[baz] = 54321; + assertEq(() => Foo2[baz], 54321); + }, + "Auto-accessor decorators: Basic (private instance auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_instances; + let lateAsserts; + const dec = (target, ctx) => { + assertEq(() => typeof target.get, "function"); + assertEq(() => typeof target.set, "function"); + assertEq(() => target.get.name, "get #foo"); + assertEq(() => target.set.name, "set #foo"); + assertEq(() => ctx.kind, "accessor"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, false); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo2()), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(new Foo2()), 0); + assertEq(() => { + const obj2 = new Foo2(); + ctx.access.set(obj2, 123); + return get$foo(obj2); + }, 123); + }; + }; + let get$foo; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _Foo_instances); + __privateAdd(this, _foo, __runInitializers(_init, 6, 0)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + _Foo_instances = new WeakSet(); + _a = __decorateElement(_init, 20, "#foo", _foo_dec, _Foo_instances, _foo), foo_get = _a.get, foo_set = _a.set; + get$foo = (x) => __privateGet(x, _Foo_instances, foo_get); + set$foo = (x, y) => { + __privateSet(x, _Foo_instances, y, foo_set); + }; + lateAsserts(); + var obj = new Foo2(); + assertEq(() => set$foo(obj, 321), void 0); + assertEq(() => get$foo(obj), 321); + }, + "Auto-accessor decorators: Basic (private static auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_static; + let lateAsserts; + const dec = (target, ctx) => { + assertEq(() => typeof target.get, "function"); + assertEq(() => typeof target.set, "function"); + assertEq(() => target.get.name, "get #foo"); + assertEq(() => target.set.name, "set #foo"); + assertEq(() => ctx.kind, "accessor"); + assertEq(() => ctx.name, "#foo"); + assertEq(() => ctx.static, true); + assertEq(() => ctx.private, true); + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo2), true); + assertEq(() => ctx.access.has({}), false); + assertEq(() => ctx.access.get(Foo2), 0); + assertEq(() => { + ctx.access.set(Foo2, 123); + return get$foo(Foo2); + }, 123); + }; + }; + let get$foo; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + _Foo_static = new WeakSet(); + _a = __decorateElement(_init, 28, "#foo", _foo_dec, _Foo_static, _foo), foo_get = _a.get, foo_set = _a.set; + __privateAdd(Foo2, _Foo_static); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 0)), __runInitializers(_init, 9, Foo2); + get$foo = (x) => __privateGet(x, _Foo_static, foo_get); + set$foo = (x, y) => { + __privateSet(x, _Foo_static, y, foo_set); + }; + lateAsserts(); + assertEq(() => set$foo(Foo2, 321), void 0); + assertEq(() => get$foo(Foo2), 321); + }, + "Auto-accessor decorators: Shim (instance auto-accessor)": () => { + var _foo_dec, _init, _foo; + let get; + let set; + const dec = (target, ctx) => { + const init = (x) => x + 1; + get = function() { + return target.get.call(this) * 10; + }; + set = function(x) { + target.set.call(this, x * 2); + }; + return { get, set, init }; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 4, "foo", _foo_dec, Foo2, _foo); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").get, get); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2.prototype, "foo").set, set); + var obj = new Foo2(); + assertEq(() => obj.foo, (123 + 1) * 10); + obj.foo = 321; + assertEq(() => obj.foo, 321 * 2 * 10); + }, + "Auto-accessor decorators: Shim (static auto-accessor)": () => { + var _foo_dec, _init, _foo; + let get; + let set; + const dec = (target, ctx) => { + const init = (x) => x + 1; + get = function() { + return target.get.call(this) * 10; + }; + set = function(x) { + target.set.call(this, x * 2); + }; + return { get, set, init }; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 12, "foo", _foo_dec, Foo2, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").get, get); + assertEq(() => Object.getOwnPropertyDescriptor(Foo2, "foo").set, set); + assertEq(() => Foo2.foo, (123 + 1) * 10); + Foo2.foo = 321; + assertEq(() => Foo2.foo, 321 * 2 * 10); + }, + "Auto-accessor decorators: Shim (private instance auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_instances; + let get; + let set; + const dec = (target, ctx) => { + const init = (x) => x + 1; + get = function() { + return target.get.call(this) * 10; + }; + set = function(x) { + target.set.call(this, x * 2); + }; + return { get, set, init }; + }; + let get$foo; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _Foo_instances); + __privateAdd(this, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + _Foo_instances = new WeakSet(); + _a = __decorateElement(_init, 20, "#foo", _foo_dec, _Foo_instances, _foo), foo_get = _a.get, foo_set = _a.set; + get$foo = (x) => __privateGet(x, _Foo_instances, foo_get); + set$foo = (x, y) => { + __privateSet(x, _Foo_instances, y, foo_set); + }; + var obj = new Foo2(); + assertEq(() => get$foo(obj), (123 + 1) * 10); + assertEq(() => set$foo(obj, 321), void 0); + assertEq(() => get$foo(obj), 321 * 2 * 10); + }, + "Auto-accessor decorators: Shim (private static auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_static; + let get; + let set; + const dec = (target, ctx) => { + const init = (x) => x + 1; + get = function() { + return target.get.call(this) * 10; + }; + set = function(x) { + target.set.call(this, x * 2); + }; + return { get, set, init }; + }; + let get$foo; + let set$foo; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + _Foo_static = new WeakSet(); + _a = __decorateElement(_init, 28, "#foo", _foo_dec, _Foo_static, _foo), foo_get = _a.get, foo_set = _a.set; + __privateAdd(Foo2, _Foo_static); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6, 123)), __runInitializers(_init, 9, Foo2); + get$foo = (x) => __privateGet(x, _Foo_static, foo_get); + set$foo = (x, y) => { + __privateSet(x, _Foo_static, y, foo_set); + }; + assertEq(() => get$foo(Foo2), (123 + 1) * 10); + assertEq(() => set$foo(Foo2, 321), void 0); + assertEq(() => get$foo(Foo2), 321 * 2 * 10); + }, + "Auto-accessor decorators: Return null (instance auto-accessor)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (target, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 4, "foo", _foo_dec, Foo2, _foo); + }, TypeError); + }, + "Auto-accessor decorators: Return null (static auto-accessor)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo; + const dec = (target, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 12, "foo", _foo_dec, Foo2, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Auto-accessor decorators: Return null (private instance auto-accessor)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_instances; + const dec = (target, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + constructor() { + __privateAdd(this, _Foo_instances); + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + _Foo_instances = new WeakSet(); + _a = __decorateElement(_init, 20, "#foo", _foo_dec, _Foo_instances, _foo), foo_get = _a.get, foo_set = _a.set; + }, TypeError); + }, + "Auto-accessor decorators: Return null (private static auto-accessor)": () => { + assertThrows(() => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_static; + const dec = (target, ctx) => { + return null; + }; + _init = [, , ,]; + _foo_dec = [dec]; + class Foo2 { + } + _foo = new WeakMap(); + _Foo_static = new WeakSet(); + _a = __decorateElement(_init, 28, "#foo", _foo_dec, _Foo_static, _foo), foo_get = _a.get, foo_set = _a.set; + __privateAdd(Foo2, _Foo_static); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + }, TypeError); + }, + "Auto-accessor decorators: Extra initializer (instance auto-accessor)": () => { + var _foo_dec, _init, _foo; + let oldAddInitializer; + let got; + const dec = (target, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + __decorateElement(_init, 4, "foo", _foo_dec, Foo2, _foo); + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Auto-accessor decorators: Extra initializer (static auto-accessor)": () => { + var _foo_dec, _init, _foo; + let oldAddInitializer; + let got; + const dec = (target, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _foo = new WeakMap(); + __decorateElement(_init, 12, "foo", _foo_dec, Foo2, _foo); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + "Auto-accessor decorators: Extra initializer (private instance auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_instances; + let oldAddInitializer; + let got; + const dec = (target, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + constructor() { + __privateAdd(this, _Foo_instances); + __privateAdd(this, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, this); + } + } + _foo = new WeakMap(); + _Foo_instances = new WeakSet(); + _a = __decorateElement(_init, 20, "#foo", _foo_dec, _Foo_instances, _foo), foo_get = _a.get, foo_set = _a.set; + assertEq(() => got, void 0); + const instance = new Foo2(); + assertEq(() => got.this, instance); + assertEq(() => got.args.length, 0); + }, + "Auto-accessor decorators: Extra initializer (private static auto-accessor)": () => { + var _foo_dec, _init, _foo, _a, foo_get, foo_set, _Foo_static; + let oldAddInitializer; + let got; + const dec = (target, ctx) => { + ctx.addInitializer(function(...args) { + got = { this: this, args }; + }); + if (oldAddInitializer) assertThrows(() => oldAddInitializer(() => { + }), TypeError); + assertThrows(() => ctx.addInitializer({}), TypeError); + oldAddInitializer = ctx.addInitializer; + }; + _init = [, , ,]; + _foo_dec = [dec, dec]; + class Foo2 { + } + _foo = new WeakMap(); + _Foo_static = new WeakSet(); + _a = __decorateElement(_init, 28, "#foo", _foo_dec, _Foo_static, _foo), foo_get = _a.get, foo_set = _a.set; + __privateAdd(Foo2, _Foo_static); + __privateAdd(Foo2, _foo, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + assertEq(() => got.this, Foo2); + assertEq(() => got.args.length, 0); + }, + // Decorator list evaluation + "Decorator list evaluation: Computed names (class statement)": () => { + var _dec, _a, _dec2, _b, _dec3, _c, _dec4, _d, _dec5, _e, _dec6, _f, _dec7, _g, _dec8, _h, _dec9, _i, _dec10, _j, _Foo_decorators, _init, __b, __a; + const log = []; + const foo = (n) => { + log.push(n); + return () => { + }; + }; + const computed = { + get method() { + log.push(log.length); + return Symbol("method"); + }, + get field() { + log.push(log.length); + return Symbol("field"); + }, + get getter() { + log.push(log.length); + return Symbol("getter"); + }, + get setter() { + log.push(log.length); + return Symbol("setter"); + }, + get accessor() { + log.push(log.length); + return Symbol("accessor"); + } + }; + _init = [, , ,]; + _Foo_decorators = [foo(0)]; + class Foo2 extends (foo(1), Object) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, _h, __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, __b, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + [_j = (_dec10 = [foo(2)], computed.method)]() { + } + static [_i = (_dec9 = [foo(4)], computed.method)]() { + } + get [(_h = (_dec8 = [foo(6)], computed.field), _g = (_dec7 = [foo(8)], computed.field), _f = (_dec6 = [foo(10)], computed.getter))]() { + return; + } + static get [_e = (_dec5 = [foo(12)], computed.getter)]() { + return; + } + set [_d = (_dec4 = [foo(14)], computed.setter)](x) { + } + static set [(_c = (_dec3 = [foo(16)], computed.setter), _b = (_dec2 = [foo(18)], computed.accessor), _a = (_dec = [foo(20)], computed.accessor), _c)](x) { + } + } + __b = new WeakMap(); + __a = new WeakMap(); + __decorateElement(_init, 9, _i, _dec9, Foo2); + __decorateElement(_init, 10, _e, _dec5, Foo2); + __decorateElement(_init, 11, _c, _dec3, Foo2); + __decorateElement(_init, 12, _a, _dec, Foo2, __a); + __decorateElement(_init, 1, _j, _dec10, Foo2); + __decorateElement(_init, 2, _f, _dec6, Foo2); + __decorateElement(_init, 3, _d, _dec4, Foo2); + __decorateElement(_init, 4, _b, _dec2, Foo2, __b); + __decorateElement(_init, 13, _g, _dec7, Foo2); + __decorateElement(_init, 5, _h, _dec8, Foo2); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, _g, __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, __a, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + __runInitializers(_init, 1, Foo2); + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21"); + }, + "Decorator list evaluation: Computed names (class expression)": () => { + var _dec, _a, _dec2, _b, _dec3, _c, _dec4, _d, _dec5, _e, _dec6, _f, _dec7, _g, _dec8, _h, _dec9, _i, _dec10, _j, _class_decorators, _init, _k, __b, __a; + const log = []; + const foo = (n) => { + log.push(n); + return () => { + }; + }; + const computed = { + get method() { + log.push(log.length); + return Symbol("method"); + }, + get field() { + log.push(log.length); + return Symbol("field"); + }, + get getter() { + log.push(log.length); + return Symbol("getter"); + }, + get setter() { + log.push(log.length); + return Symbol("setter"); + }, + get accessor() { + log.push(log.length); + return Symbol("accessor"); + } + }; + _init = [, , ,], _class_decorators = [foo(0)], _k = class extends (foo(1), Object) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, _h, __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, __b, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + [_j = (_dec10 = [foo(2)], computed.method)]() { + } + static [_i = (_dec9 = [foo(4)], computed.method)]() { + } + get [(_h = (_dec8 = [foo(6)], computed.field), _g = (_dec7 = [foo(8)], computed.field), _f = (_dec6 = [foo(10)], computed.getter))]() { + return; + } + static get [_e = (_dec5 = [foo(12)], computed.getter)]() { + return; + } + set [_d = (_dec4 = [foo(14)], computed.setter)](x) { + } + static set [(_c = (_dec3 = [foo(16)], computed.setter), _b = (_dec2 = [foo(18)], computed.accessor), _a = (_dec = [foo(20)], computed.accessor), _c)](x) { + } + }, __b = new WeakMap(), __a = new WeakMap(), __decorateElement(_init, 9, _i, _dec9, _k), __decorateElement(_init, 10, _e, _dec5, _k), __decorateElement(_init, 11, _c, _dec3, _k), __decorateElement(_init, 12, _a, _dec, _k, __a), __decorateElement(_init, 1, _j, _dec10, _k), __decorateElement(_init, 2, _f, _dec6, _k), __decorateElement(_init, 3, _d, _dec4, _k), __decorateElement(_init, 4, _b, _dec2, _k, __b), __decorateElement(_init, 13, _g, _dec7, _k), __decorateElement(_init, 5, _h, _dec8, _k), _k = __decorateElement(_init, 0, "", _class_decorators, _k), __runInitializers(_init, 3, _k), __publicField(_k, _g, __runInitializers(_init, 14)), __runInitializers(_init, 17, _k), __privateAdd(_k, __a, __runInitializers(_init, 6)), __runInitializers(_init, 9, _k), __runInitializers(_init, 1, _k), _k; + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21"); + }, + 'Decorator list evaluation: "this" (class statement)': () => { + const log = []; + const dummy = () => { + }; + const ctx = { + foo(n) { + log.push(n); + } + }; + function wrapper() { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _Foo_decorators, _init, _accessor, _accessor2; + _init = [, , ,]; + _Foo_decorators = [(assertEq(() => this.foo(0), void 0), dummy)]; + class Foo2 extends (_a = (assertEq(() => this.foo(1), void 0), Object), _method_dec2 = [(assertEq(() => this.foo(2), void 0), dummy)], _method_dec = [(assertEq(() => this.foo(3), void 0), dummy)], _field_dec2 = [(assertEq(() => this.foo(4), void 0), dummy)], _field_dec = [(assertEq(() => this.foo(5), void 0), dummy)], _getter_dec2 = [(assertEq(() => this.foo(6), void 0), dummy)], _getter_dec = [(assertEq(() => this.foo(7), void 0), dummy)], _setter_dec2 = [(assertEq(() => this.foo(8), void 0), dummy)], _setter_dec = [(assertEq(() => this.foo(9), void 0), dummy)], _accessor_dec2 = [(assertEq(() => this.foo(10), void 0), dummy)], _accessor_dec = [(assertEq(() => this.foo(11), void 0), dummy)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + } + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, Foo2); + __decorateElement(_init, 10, "getter", _getter_dec, Foo2); + __decorateElement(_init, 11, "setter", _setter_dec, Foo2); + __decorateElement(_init, 12, "accessor", _accessor_dec, Foo2, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, Foo2); + __decorateElement(_init, 2, "getter", _getter_dec2, Foo2); + __decorateElement(_init, 3, "setter", _setter_dec2, Foo2); + __decorateElement(_init, 4, "accessor", _accessor_dec2, Foo2, _accessor); + __decorateElement(_init, 13, "field", _field_dec, Foo2); + __decorateElement(_init, 5, "field", _field_dec2, Foo2); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + __runInitializers(_init, 1, Foo2); + } + wrapper.call(ctx); + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + 'Decorator list evaluation: "this" (class expression)': () => { + const log = []; + const dummy = () => { + }; + const ctx = { + foo(n) { + log.push(n); + } + }; + function wrapper() { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _class_decorators, _init, _b, _accessor, _accessor2; + _init = [, , ,], _class_decorators = [(assertEq(() => this.foo(0), void 0), dummy)], _b = class extends (_a = (assertEq(() => this.foo(1), void 0), Object), _method_dec2 = [(assertEq(() => this.foo(2), void 0), dummy)], _method_dec = [(assertEq(() => this.foo(3), void 0), dummy)], _field_dec2 = [(assertEq(() => this.foo(4), void 0), dummy)], _field_dec = [(assertEq(() => this.foo(5), void 0), dummy)], _getter_dec2 = [(assertEq(() => this.foo(6), void 0), dummy)], _getter_dec = [(assertEq(() => this.foo(7), void 0), dummy)], _setter_dec2 = [(assertEq(() => this.foo(8), void 0), dummy)], _setter_dec = [(assertEq(() => this.foo(9), void 0), dummy)], _accessor_dec2 = [(assertEq(() => this.foo(10), void 0), dummy)], _accessor_dec = [(assertEq(() => this.foo(11), void 0), dummy)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _b), __decorateElement(_init, 10, "getter", _getter_dec, _b), __decorateElement(_init, 11, "setter", _setter_dec, _b), __decorateElement(_init, 12, "accessor", _accessor_dec, _b, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _b), __decorateElement(_init, 2, "getter", _getter_dec2, _b), __decorateElement(_init, 3, "setter", _setter_dec2, _b), __decorateElement(_init, 4, "accessor", _accessor_dec2, _b, _accessor), __decorateElement(_init, 13, "field", _field_dec, _b), __decorateElement(_init, 5, "field", _field_dec2, _b), _b = __decorateElement(_init, 0, "", _class_decorators, _b), __runInitializers(_init, 3, _b), __publicField(_b, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _b), __privateAdd(_b, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _b), __runInitializers(_init, 1, _b), _b; + } + wrapper.call(ctx); + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + 'Decorator list evaluation: "await" (class statement)': async () => { + const log = []; + const dummy = () => { + }; + async function wrapper() { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _Foo_decorators, _init, _accessor, _accessor2; + _init = [, , ,]; + _Foo_decorators = [(log.push(await Promise.resolve(0)), dummy)]; + class Foo2 extends (_a = (log.push(await Promise.resolve(1)), Object), _method_dec2 = [(log.push(await Promise.resolve(2)), dummy)], _method_dec = [(log.push(await Promise.resolve(3)), dummy)], _field_dec2 = [(log.push(await Promise.resolve(4)), dummy)], _field_dec = [(log.push(await Promise.resolve(5)), dummy)], _getter_dec2 = [(log.push(await Promise.resolve(6)), dummy)], _getter_dec = [(log.push(await Promise.resolve(7)), dummy)], _setter_dec2 = [(log.push(await Promise.resolve(8)), dummy)], _setter_dec = [(log.push(await Promise.resolve(9)), dummy)], _accessor_dec2 = [(log.push(await Promise.resolve(10)), dummy)], _accessor_dec = [(log.push(await Promise.resolve(11)), dummy)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + } + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, Foo2); + __decorateElement(_init, 10, "getter", _getter_dec, Foo2); + __decorateElement(_init, 11, "setter", _setter_dec, Foo2); + __decorateElement(_init, 12, "accessor", _accessor_dec, Foo2, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, Foo2); + __decorateElement(_init, 2, "getter", _getter_dec2, Foo2); + __decorateElement(_init, 3, "setter", _setter_dec2, Foo2); + __decorateElement(_init, 4, "accessor", _accessor_dec2, Foo2, _accessor); + __decorateElement(_init, 13, "field", _field_dec, Foo2); + __decorateElement(_init, 5, "field", _field_dec2, Foo2); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + __runInitializers(_init, 1, Foo2); + } + await wrapper(); + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + 'Decorator list evaluation: "await" (class expression)': async () => { + const log = []; + const dummy = () => { + }; + async function wrapper() { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _class_decorators, _init, _b, _accessor, _accessor2; + _init = [, , ,], _class_decorators = [(log.push(await Promise.resolve(0)), dummy)], _b = class extends (_a = (log.push(await Promise.resolve(1)), Object), _method_dec2 = [(log.push(await Promise.resolve(2)), dummy)], _method_dec = [(log.push(await Promise.resolve(3)), dummy)], _field_dec2 = [(log.push(await Promise.resolve(4)), dummy)], _field_dec = [(log.push(await Promise.resolve(5)), dummy)], _getter_dec2 = [(log.push(await Promise.resolve(6)), dummy)], _getter_dec = [(log.push(await Promise.resolve(7)), dummy)], _setter_dec2 = [(log.push(await Promise.resolve(8)), dummy)], _setter_dec = [(log.push(await Promise.resolve(9)), dummy)], _accessor_dec2 = [(log.push(await Promise.resolve(10)), dummy)], _accessor_dec = [(log.push(await Promise.resolve(11)), dummy)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _b), __decorateElement(_init, 10, "getter", _getter_dec, _b), __decorateElement(_init, 11, "setter", _setter_dec, _b), __decorateElement(_init, 12, "accessor", _accessor_dec, _b, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _b), __decorateElement(_init, 2, "getter", _getter_dec2, _b), __decorateElement(_init, 3, "setter", _setter_dec2, _b), __decorateElement(_init, 4, "accessor", _accessor_dec2, _b, _accessor), __decorateElement(_init, 13, "field", _field_dec, _b), __decorateElement(_init, 5, "field", _field_dec2, _b), _b = __decorateElement(_init, 0, "", _class_decorators, _b), __runInitializers(_init, 3, _b), __publicField(_b, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _b), __privateAdd(_b, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _b), __runInitializers(_init, 1, _b), _b; + } + await wrapper(); + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + "Decorator list evaluation: Outer private name (class statement)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _Foo_decorators, _init, _accessor, _accessor2; + const log = []; + class Dummy { + static #foo(n) { + log.push(n); + return () => { + }; + } + static { + const dummy = this; + _init = [, , ,]; + _Foo_decorators = [dummy.#foo(0)]; + class Foo2 extends (_a = (dummy.#foo(1), Object), _method_dec2 = [dummy.#foo(2)], _method_dec = [dummy.#foo(3)], _field_dec2 = [dummy.#foo(4)], _field_dec = [dummy.#foo(5)], _getter_dec2 = [dummy.#foo(6)], _getter_dec = [dummy.#foo(7)], _setter_dec2 = [dummy.#foo(8)], _setter_dec = [dummy.#foo(9)], _accessor_dec2 = [dummy.#foo(10)], _accessor_dec = [dummy.#foo(11)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + } + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, Foo2); + __decorateElement(_init, 10, "getter", _getter_dec, Foo2); + __decorateElement(_init, 11, "setter", _setter_dec, Foo2); + __decorateElement(_init, 12, "accessor", _accessor_dec, Foo2, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, Foo2); + __decorateElement(_init, 2, "getter", _getter_dec2, Foo2); + __decorateElement(_init, 3, "setter", _setter_dec2, Foo2); + __decorateElement(_init, 4, "accessor", _accessor_dec2, Foo2, _accessor); + __decorateElement(_init, 13, "field", _field_dec, Foo2); + __decorateElement(_init, 5, "field", _field_dec2, Foo2); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + __publicField(Foo2, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + __runInitializers(_init, 1, Foo2); + } + } + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + "Decorator list evaluation: Outer private name (class expression)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _class_decorators, _init, _b, _accessor, _accessor2; + const log = []; + class Dummy { + static #foo(n) { + log.push(n); + return () => { + }; + } + static { + const dummy = this; + _init = [, , ,], _class_decorators = [dummy.#foo(0)], _b = class extends (_a = (dummy.#foo(1), Object), _method_dec2 = [dummy.#foo(2)], _method_dec = [dummy.#foo(3)], _field_dec2 = [dummy.#foo(4)], _field_dec = [dummy.#foo(5)], _getter_dec2 = [dummy.#foo(6)], _getter_dec = [dummy.#foo(7)], _setter_dec2 = [dummy.#foo(8)], _setter_dec = [dummy.#foo(9)], _accessor_dec2 = [dummy.#foo(10)], _accessor_dec = [dummy.#foo(11)], _a) { + constructor() { + super(...arguments); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _b), __decorateElement(_init, 10, "getter", _getter_dec, _b), __decorateElement(_init, 11, "setter", _setter_dec, _b), __decorateElement(_init, 12, "accessor", _accessor_dec, _b, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _b), __decorateElement(_init, 2, "getter", _getter_dec2, _b), __decorateElement(_init, 3, "setter", _setter_dec2, _b), __decorateElement(_init, 4, "accessor", _accessor_dec2, _b, _accessor), __decorateElement(_init, 13, "field", _field_dec, _b), __decorateElement(_init, 5, "field", _field_dec2, _b), _b = __decorateElement(_init, 0, "", _class_decorators, _b), __runInitializers(_init, 3, _b), __publicField(_b, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _b), __privateAdd(_b, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _b), __runInitializers(_init, 1, _b), _b; + } + } + assertEq(() => "" + log, "0,1,2,3,4,5,6,7,8,9,10,11"); + }, + "Decorator list evaluation: Inner private name (class statement)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _Foo_decorators, _foo, _init, _accessor, _accessor2; + const fns = []; + const capture = (fn) => { + fns.push(fn); + return () => { + }; + }; + class Dummy { + static #foo = NaN; + static { + _init = [, , ,]; + _Foo_decorators = [capture(() => new Foo2().#foo + 0)], _method_dec2 = [capture(() => __privateGet(new _Foo(), _foo) + 1)], _method_dec = [capture(() => __privateGet(new _Foo(), _foo) + 2)], _field_dec2 = [capture(() => __privateGet(new _Foo(), _foo) + 3)], _field_dec = [capture(() => __privateGet(new _Foo(), _foo) + 4)], _getter_dec2 = [capture(() => __privateGet(new _Foo(), _foo) + 5)], _getter_dec = [capture(() => __privateGet(new _Foo(), _foo) + 6)], _setter_dec2 = [capture(() => __privateGet(new _Foo(), _foo) + 7)], _setter_dec = [capture(() => __privateGet(new _Foo(), _foo) + 8)], _accessor_dec2 = [capture(() => __privateGet(new _Foo(), _foo) + 9)], _accessor_dec = [capture(() => __privateGet(new _Foo(), _foo) + 10)]; + let _Foo = class _Foo { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _foo, 10); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }; + _foo = new WeakMap(); + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, _Foo); + __decorateElement(_init, 10, "getter", _getter_dec, _Foo); + __decorateElement(_init, 11, "setter", _setter_dec, _Foo); + __decorateElement(_init, 12, "accessor", _accessor_dec, _Foo, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, _Foo); + __decorateElement(_init, 2, "getter", _getter_dec2, _Foo); + __decorateElement(_init, 3, "setter", _setter_dec2, _Foo); + __decorateElement(_init, 4, "accessor", _accessor_dec2, _Foo, _accessor); + __decorateElement(_init, 13, "field", _field_dec, _Foo); + __decorateElement(_init, 5, "field", _field_dec2, _Foo); + _Foo = __decorateElement(_init, 0, "Foo", _Foo_decorators, _Foo); + __runInitializers(_init, 3, _Foo); + __publicField(_Foo, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _Foo); + __privateAdd(_Foo, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _Foo); + __runInitializers(_init, 1, _Foo); + let Foo2 = _Foo; + } + } + const firstFn = fns.shift(); + assertEq(() => { + try { + firstFn(); + throw new Error("Expected a TypeError to be thrown"); + } catch (err) { + if (err instanceof TypeError) return true; + throw err; + } + }, true); + const log = []; + for (const fn of fns) log.push(fn()); + assertEq(() => "" + log, "11,12,13,14,15,16,17,18,19,20"); + }, + "Decorator list evaluation: Inner private name (class expression)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _Foo_decorators, _foo, _init, _a, _accessor, _accessor2; + const fns = []; + const capture = (fn) => { + fns.push(fn); + return () => { + }; + }; + class Outer { + static #foo = 0; + static { + _init = [, , ,], _Foo_decorators = [capture(() => Outer.#foo + 0)], _method_dec2 = [capture(() => __privateGet(new _a(), _foo) + 1)], _method_dec = [capture(() => __privateGet(new _a(), _foo) + 2)], _field_dec2 = [capture(() => __privateGet(new _a(), _foo) + 3)], _field_dec = [capture(() => __privateGet(new _a(), _foo) + 4)], _getter_dec2 = [capture(() => __privateGet(new _a(), _foo) + 5)], _getter_dec = [capture(() => __privateGet(new _a(), _foo) + 6)], _setter_dec2 = [capture(() => __privateGet(new _a(), _foo) + 7)], _setter_dec = [capture(() => __privateGet(new _a(), _foo) + 8)], _accessor_dec2 = [capture(() => __privateGet(new _a(), _foo) + 9)], _accessor_dec = [capture(() => __privateGet(new _a(), _foo) + 10)], _a = class { + constructor() { + __runInitializers(_init, 5, this); + __privateAdd(this, _foo, 10); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _foo = new WeakMap(), _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _a), __decorateElement(_init, 10, "getter", _getter_dec, _a), __decorateElement(_init, 11, "setter", _setter_dec, _a), __decorateElement(_init, 12, "accessor", _accessor_dec, _a, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _a), __decorateElement(_init, 2, "getter", _getter_dec2, _a), __decorateElement(_init, 3, "setter", _setter_dec2, _a), __decorateElement(_init, 4, "accessor", _accessor_dec2, _a, _accessor), __decorateElement(_init, 13, "field", _field_dec, _a), __decorateElement(_init, 5, "field", _field_dec2, _a), _a = __decorateElement(_init, 0, "Foo", _Foo_decorators, _a), __runInitializers(_init, 3, _a), __publicField(_a, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _a), __privateAdd(_a, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _a), __runInitializers(_init, 1, _a), _a; + } + } + const log = []; + for (const fn of fns) log.push(fn()); + assertEq(() => "" + log, "0,11,12,13,14,15,16,17,18,19,20"); + }, + "Decorator list evaluation: Class binding (class statement)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _Foo_decorators, _init, _accessor, _accessor2; + const fns = []; + const capture = (fn) => { + fns.push(fn); + assertThrows(() => fn(), ReferenceError); + return () => { + }; + }; + _init = [, , ,]; + _Foo_decorators = [capture(() => Foo2)], _method_dec2 = [capture(() => _Foo)], _method_dec = [capture(() => _Foo)], _field_dec2 = [capture(() => _Foo)], _field_dec = [capture(() => _Foo)], _getter_dec2 = [capture(() => _Foo)], _getter_dec = [capture(() => _Foo)], _setter_dec2 = [capture(() => _Foo)], _setter_dec = [capture(() => _Foo)], _accessor_dec2 = [capture(() => _Foo)], _accessor_dec = [capture(() => _Foo)]; + let _Foo = class _Foo { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }; + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, _Foo); + __decorateElement(_init, 10, "getter", _getter_dec, _Foo); + __decorateElement(_init, 11, "setter", _setter_dec, _Foo); + __decorateElement(_init, 12, "accessor", _accessor_dec, _Foo, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, _Foo); + __decorateElement(_init, 2, "getter", _getter_dec2, _Foo); + __decorateElement(_init, 3, "setter", _setter_dec2, _Foo); + __decorateElement(_init, 4, "accessor", _accessor_dec2, _Foo, _accessor); + __decorateElement(_init, 13, "field", _field_dec, _Foo); + __decorateElement(_init, 5, "field", _field_dec2, _Foo); + _Foo = __decorateElement(_init, 0, "Foo", _Foo_decorators, _Foo); + __runInitializers(_init, 3, _Foo); + __publicField(_Foo, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _Foo); + __privateAdd(_Foo, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _Foo); + __runInitializers(_init, 1, _Foo); + let Foo2 = _Foo; + const originalFoo = Foo2; + for (const fn of fns) { + assertEq(() => fn(), originalFoo); + } + Foo2 = null; + const firstFn = fns.shift(); + assertEq(() => firstFn(), null); + for (const fn of fns) { + assertEq(() => fn(), originalFoo); + } + }, + "Decorator list evaluation: Class binding (class expression)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _originalFoo_decorators, _init, _a, _accessor, _accessor2; + const fns = []; + const capture = (fn) => { + fns.push(fn); + return () => { + }; + }; + const originalFoo = (_init = [, , ,], _originalFoo_decorators = [capture(() => Foo)], _method_dec2 = [capture(() => _a)], _method_dec = [capture(() => _a)], _field_dec2 = [capture(() => _a)], _field_dec = [capture(() => _a)], _getter_dec2 = [capture(() => _a)], _getter_dec = [capture(() => _a)], _setter_dec2 = [capture(() => _a)], _setter_dec = [capture(() => _a)], _accessor_dec2 = [capture(() => _a)], _accessor_dec = [capture(() => _a)], _a = class { + constructor() { + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _a), __decorateElement(_init, 10, "getter", _getter_dec, _a), __decorateElement(_init, 11, "setter", _setter_dec, _a), __decorateElement(_init, 12, "accessor", _accessor_dec, _a, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _a), __decorateElement(_init, 2, "getter", _getter_dec2, _a), __decorateElement(_init, 3, "setter", _setter_dec2, _a), __decorateElement(_init, 4, "accessor", _accessor_dec2, _a, _accessor), __decorateElement(_init, 13, "field", _field_dec, _a), __decorateElement(_init, 5, "field", _field_dec2, _a), _a = __decorateElement(_init, 0, "originalFoo", _originalFoo_decorators, _a), __runInitializers(_init, 3, _a), __publicField(_a, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _a), __privateAdd(_a, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _a), __runInitializers(_init, 1, _a), _a); + const firstFn = fns.shift(); + assertThrows(() => firstFn(), ReferenceError); + for (const fn of fns) { + assertEq(() => fn(), originalFoo); + } + }, + // Initializer order + "Initializer order (public members, class statement)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _Foo_decorators, _init, _accessor, _accessor2; + const log = []; + const classDec1 = (cls, ctxClass) => { + log.push("c2"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c5")); + ctxClass.addInitializer(() => log.push("c6")); + }; + const classDec2 = (cls, ctxClass) => { + log.push("c1"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c3")); + ctxClass.addInitializer(() => log.push("c4")); + }; + const methodDec1 = (fn, ctxMethod) => { + log.push("m2"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m5")); + ctxMethod.addInitializer(() => log.push("m6")); + }; + const methodDec2 = (fn, ctxMethod) => { + log.push("m1"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m3")); + ctxMethod.addInitializer(() => log.push("m4")); + }; + const staticMethodDec1 = (fn, ctxStaticMethod) => { + log.push("M2"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M5")); + ctxStaticMethod.addInitializer(() => log.push("M6")); + }; + const staticMethodDec2 = (fn, ctxStaticMethod) => { + log.push("M1"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M3")); + ctxStaticMethod.addInitializer(() => log.push("M4")); + }; + const fieldDec1 = (value, ctxField) => { + log.push("f2"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f5")); + ctxField.addInitializer(() => log.push("f6")); + return () => { + log.push("f7"); + }; + }; + const fieldDec2 = (value, ctxField) => { + log.push("f1"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f3")); + ctxField.addInitializer(() => log.push("f4")); + return () => { + log.push("f8"); + }; + }; + const staticFieldDec1 = (value, ctxStaticField) => { + log.push("F2"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F5")); + ctxStaticField.addInitializer(() => log.push("F6")); + return () => { + log.push("F7"); + }; + }; + const staticFieldDec2 = (value, ctxStaticField) => { + log.push("F1"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F3")); + ctxStaticField.addInitializer(() => log.push("F4")); + return () => { + log.push("F8"); + }; + }; + const getterDec1 = (fn, ctxGetter) => { + log.push("g2"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g5")); + ctxGetter.addInitializer(() => log.push("g6")); + }; + const getterDec2 = (fn, ctxGetter) => { + log.push("g1"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g3")); + ctxGetter.addInitializer(() => log.push("g4")); + }; + const staticGetterDec1 = (fn, ctxStaticGetter) => { + log.push("G2"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G5")); + ctxStaticGetter.addInitializer(() => log.push("G6")); + }; + const staticGetterDec2 = (fn, ctxStaticGetter) => { + log.push("G1"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G3")); + ctxStaticGetter.addInitializer(() => log.push("G4")); + }; + const setterDec1 = (fn, ctxSetter) => { + log.push("s2"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s5")); + ctxSetter.addInitializer(() => log.push("s6")); + }; + const setterDec2 = (fn, ctxSetter) => { + log.push("s1"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s3")); + ctxSetter.addInitializer(() => log.push("s4")); + }; + const staticSetterDec1 = (fn, ctxStaticSetter) => { + log.push("S2"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S5")); + ctxStaticSetter.addInitializer(() => log.push("S6")); + }; + const staticSetterDec2 = (fn, ctxStaticSetter) => { + log.push("S1"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S3")); + ctxStaticSetter.addInitializer(() => log.push("S4")); + }; + const accessorDec1 = (target, ctxAccessor) => { + log.push("a2"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a5")); + ctxAccessor.addInitializer(() => log.push("a6")); + return { init() { + log.push("a7"); + } }; + }; + const accessorDec2 = (target, ctxAccessor) => { + log.push("a1"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a3")); + ctxAccessor.addInitializer(() => log.push("a4")); + return { init() { + log.push("a8"); + } }; + }; + const staticAccessorDec1 = (target, ctxStaticAccessor) => { + log.push("A2"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A5")); + ctxStaticAccessor.addInitializer(() => log.push("A6")); + return { init() { + log.push("A7"); + } }; + }; + const staticAccessorDec2 = (target, ctxStaticAccessor) => { + log.push("A1"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A3")); + ctxStaticAccessor.addInitializer(() => log.push("A4")); + return { init() { + log.push("A8"); + } }; + }; + log.push("start"); + _init = [, , ,]; + _Foo_decorators = [classDec1, classDec2]; + class Foo2 extends (_a = (log.push("extends"), Object), _method_dec2 = [methodDec1, methodDec2], _method_dec = [staticMethodDec1, staticMethodDec2], _field_dec2 = [fieldDec1, fieldDec2], _field_dec = [staticFieldDec1, staticFieldDec2], _getter_dec2 = [getterDec1, getterDec2], _getter_dec = [staticGetterDec1, staticGetterDec2], _setter_dec2 = [setterDec1, setterDec2], _setter_dec = [staticSetterDec1, staticSetterDec2], _accessor_dec2 = [accessorDec1, accessorDec2], _accessor_dec = [staticAccessorDec1, staticAccessorDec2], _a) { + constructor() { + log.push("ctor:start"); + super(); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + log.push("ctor:end"); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + } + _accessor = new WeakMap(); + _accessor2 = new WeakMap(); + __decorateElement(_init, 9, "method", _method_dec, Foo2); + __decorateElement(_init, 10, "getter", _getter_dec, Foo2); + __decorateElement(_init, 11, "setter", _setter_dec, Foo2); + __decorateElement(_init, 12, "accessor", _accessor_dec, Foo2, _accessor2); + __decorateElement(_init, 1, "method", _method_dec2, Foo2); + __decorateElement(_init, 2, "getter", _getter_dec2, Foo2); + __decorateElement(_init, 3, "setter", _setter_dec2, Foo2); + __decorateElement(_init, 4, "accessor", _accessor_dec2, Foo2, _accessor); + __decorateElement(_init, 13, "field", _field_dec, Foo2); + __decorateElement(_init, 5, "field", _field_dec2, Foo2); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + log.push("static:start"); + __publicField(Foo2, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + log.push("static:end"); + __runInitializers(_init, 1, Foo2); + log.push("after"); + new Foo2(); + log.push("end"); + assertEq(() => log + "", "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"); + }, + "Initializer order (public members, class expression)": () => { + var _accessor_dec, _accessor_dec2, _setter_dec, _setter_dec2, _getter_dec, _getter_dec2, _field_dec, _field_dec2, _method_dec, _method_dec2, _a, _Foo_decorators, _init, _b, _accessor, _accessor2; + const log = []; + const classDec1 = (cls, ctxClass) => { + log.push("c2"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c5")); + ctxClass.addInitializer(() => log.push("c6")); + }; + const classDec2 = (cls, ctxClass) => { + log.push("c1"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c3")); + ctxClass.addInitializer(() => log.push("c4")); + }; + const methodDec1 = (fn, ctxMethod) => { + log.push("m2"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m5")); + ctxMethod.addInitializer(() => log.push("m6")); + }; + const methodDec2 = (fn, ctxMethod) => { + log.push("m1"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m3")); + ctxMethod.addInitializer(() => log.push("m4")); + }; + const staticMethodDec1 = (fn, ctxStaticMethod) => { + log.push("M2"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M5")); + ctxStaticMethod.addInitializer(() => log.push("M6")); + }; + const staticMethodDec2 = (fn, ctxStaticMethod) => { + log.push("M1"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M3")); + ctxStaticMethod.addInitializer(() => log.push("M4")); + }; + const fieldDec1 = (value, ctxField) => { + log.push("f2"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f5")); + ctxField.addInitializer(() => log.push("f6")); + return () => { + log.push("f7"); + }; + }; + const fieldDec2 = (value, ctxField) => { + log.push("f1"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f3")); + ctxField.addInitializer(() => log.push("f4")); + return () => { + log.push("f8"); + }; + }; + const staticFieldDec1 = (value, ctxStaticField) => { + log.push("F2"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F5")); + ctxStaticField.addInitializer(() => log.push("F6")); + return () => { + log.push("F7"); + }; + }; + const staticFieldDec2 = (value, ctxStaticField) => { + log.push("F1"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F3")); + ctxStaticField.addInitializer(() => log.push("F4")); + return () => { + log.push("F8"); + }; + }; + const getterDec1 = (fn, ctxGetter) => { + log.push("g2"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g5")); + ctxGetter.addInitializer(() => log.push("g6")); + }; + const getterDec2 = (fn, ctxGetter) => { + log.push("g1"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g3")); + ctxGetter.addInitializer(() => log.push("g4")); + }; + const staticGetterDec1 = (fn, ctxStaticGetter) => { + log.push("G2"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G5")); + ctxStaticGetter.addInitializer(() => log.push("G6")); + }; + const staticGetterDec2 = (fn, ctxStaticGetter) => { + log.push("G1"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G3")); + ctxStaticGetter.addInitializer(() => log.push("G4")); + }; + const setterDec1 = (fn, ctxSetter) => { + log.push("s2"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s5")); + ctxSetter.addInitializer(() => log.push("s6")); + }; + const setterDec2 = (fn, ctxSetter) => { + log.push("s1"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s3")); + ctxSetter.addInitializer(() => log.push("s4")); + }; + const staticSetterDec1 = (fn, ctxStaticSetter) => { + log.push("S2"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S5")); + ctxStaticSetter.addInitializer(() => log.push("S6")); + }; + const staticSetterDec2 = (fn, ctxStaticSetter) => { + log.push("S1"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S3")); + ctxStaticSetter.addInitializer(() => log.push("S4")); + }; + const accessorDec1 = (target, ctxAccessor) => { + log.push("a2"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a5")); + ctxAccessor.addInitializer(() => log.push("a6")); + return { init() { + log.push("a7"); + } }; + }; + const accessorDec2 = (target, ctxAccessor) => { + log.push("a1"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a3")); + ctxAccessor.addInitializer(() => log.push("a4")); + return { init() { + log.push("a8"); + } }; + }; + const staticAccessorDec1 = (target, ctxStaticAccessor) => { + log.push("A2"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A5")); + ctxStaticAccessor.addInitializer(() => log.push("A6")); + return { init() { + log.push("A7"); + } }; + }; + const staticAccessorDec2 = (target, ctxStaticAccessor) => { + log.push("A1"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A3")); + ctxStaticAccessor.addInitializer(() => log.push("A4")); + return { init() { + log.push("A8"); + } }; + }; + log.push("start"); + const Foo2 = (_init = [, , ,], _Foo_decorators = [classDec1, classDec2], _b = class extends (_a = (log.push("extends"), Object), _method_dec2 = [methodDec1, methodDec2], _method_dec = [staticMethodDec1, staticMethodDec2], _field_dec2 = [fieldDec1, fieldDec2], _field_dec = [staticFieldDec1, staticFieldDec2], _getter_dec2 = [getterDec1, getterDec2], _getter_dec = [staticGetterDec1, staticGetterDec2], _setter_dec2 = [setterDec1, setterDec2], _setter_dec = [staticSetterDec1, staticSetterDec2], _accessor_dec2 = [accessorDec1, accessorDec2], _accessor_dec = [staticAccessorDec1, staticAccessorDec2], _a) { + constructor() { + log.push("ctor:start"); + super(); + __runInitializers(_init, 5, this); + __publicField(this, "field", __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + log.push("ctor:end"); + } + method() { + } + static method() { + } + get getter() { + return; + } + static get getter() { + return; + } + set setter(x) { + } + static set setter(x) { + } + }, _accessor = new WeakMap(), _accessor2 = new WeakMap(), __decorateElement(_init, 9, "method", _method_dec, _b), __decorateElement(_init, 10, "getter", _getter_dec, _b), __decorateElement(_init, 11, "setter", _setter_dec, _b), __decorateElement(_init, 12, "accessor", _accessor_dec, _b, _accessor2), __decorateElement(_init, 1, "method", _method_dec2, _b), __decorateElement(_init, 2, "getter", _getter_dec2, _b), __decorateElement(_init, 3, "setter", _setter_dec2, _b), __decorateElement(_init, 4, "accessor", _accessor_dec2, _b, _accessor), __decorateElement(_init, 13, "field", _field_dec, _b), __decorateElement(_init, 5, "field", _field_dec2, _b), _b = __decorateElement(_init, 0, "Foo", _Foo_decorators, _b), __runInitializers(_init, 3, _b), log.push("static:start"), __publicField(_b, "field", __runInitializers(_init, 14)), __runInitializers(_init, 17, _b), __privateAdd(_b, _accessor2, __runInitializers(_init, 6)), __runInitializers(_init, 9, _b), log.push("static:end"), __runInitializers(_init, 1, _b), _b); + log.push("after"); + new Foo2(); + log.push("end"); + assertEq(() => log + "", "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"); + }, + "Initializer order (private members, class statement)": () => { + var _staticAccessor_dec, _accessor_dec, _staticSetter_dec, _setter_dec, _staticGetter_dec, _getter_dec, _staticField_dec, _field_dec, _staticMethod_dec, _method_dec, _a, _Foo_decorators, _init, _Foo_instances, method_fn, _Foo_static, staticMethod_fn, _field, _staticField, getter_get, staticGetter_get, setter_set, staticSetter_set, _accessor, _b, accessor_get, accessor_set, _staticAccessor, _c, staticAccessor_get, staticAccessor_set; + const log = []; + const classDec1 = (cls, ctxClass) => { + log.push("c2"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c5")); + ctxClass.addInitializer(() => log.push("c6")); + }; + const classDec2 = (cls, ctxClass) => { + log.push("c1"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c3")); + ctxClass.addInitializer(() => log.push("c4")); + }; + const methodDec1 = (fn, ctxMethod) => { + log.push("m2"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m5")); + ctxMethod.addInitializer(() => log.push("m6")); + }; + const methodDec2 = (fn, ctxMethod) => { + log.push("m1"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m3")); + ctxMethod.addInitializer(() => log.push("m4")); + }; + const staticMethodDec1 = (fn, ctxStaticMethod) => { + log.push("M2"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M5")); + ctxStaticMethod.addInitializer(() => log.push("M6")); + }; + const staticMethodDec2 = (fn, ctxStaticMethod) => { + log.push("M1"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M3")); + ctxStaticMethod.addInitializer(() => log.push("M4")); + }; + const fieldDec1 = (value, ctxField) => { + log.push("f2"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f5")); + ctxField.addInitializer(() => log.push("f6")); + return () => { + log.push("f7"); + }; + }; + const fieldDec2 = (value, ctxField) => { + log.push("f1"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f3")); + ctxField.addInitializer(() => log.push("f4")); + return () => { + log.push("f8"); + }; + }; + const staticFieldDec1 = (value, ctxStaticField) => { + log.push("F2"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F5")); + ctxStaticField.addInitializer(() => log.push("F6")); + return () => { + log.push("F7"); + }; + }; + const staticFieldDec2 = (value, ctxStaticField) => { + log.push("F1"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F3")); + ctxStaticField.addInitializer(() => log.push("F4")); + return () => { + log.push("F8"); + }; + }; + const getterDec1 = (fn, ctxGetter) => { + log.push("g2"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g5")); + ctxGetter.addInitializer(() => log.push("g6")); + }; + const getterDec2 = (fn, ctxGetter) => { + log.push("g1"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g3")); + ctxGetter.addInitializer(() => log.push("g4")); + }; + const staticGetterDec1 = (fn, ctxStaticGetter) => { + log.push("G2"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G5")); + ctxStaticGetter.addInitializer(() => log.push("G6")); + }; + const staticGetterDec2 = (fn, ctxStaticGetter) => { + log.push("G1"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G3")); + ctxStaticGetter.addInitializer(() => log.push("G4")); + }; + const setterDec1 = (fn, ctxSetter) => { + log.push("s2"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s5")); + ctxSetter.addInitializer(() => log.push("s6")); + }; + const setterDec2 = (fn, ctxSetter) => { + log.push("s1"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s3")); + ctxSetter.addInitializer(() => log.push("s4")); + }; + const staticSetterDec1 = (fn, ctxStaticSetter) => { + log.push("S2"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S5")); + ctxStaticSetter.addInitializer(() => log.push("S6")); + }; + const staticSetterDec2 = (fn, ctxStaticSetter) => { + log.push("S1"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S3")); + ctxStaticSetter.addInitializer(() => log.push("S4")); + }; + const accessorDec1 = (target, ctxAccessor) => { + log.push("a2"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a5")); + ctxAccessor.addInitializer(() => log.push("a6")); + return { init() { + log.push("a7"); + } }; + }; + const accessorDec2 = (target, ctxAccessor) => { + log.push("a1"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a3")); + ctxAccessor.addInitializer(() => log.push("a4")); + return { init() { + log.push("a8"); + } }; + }; + const staticAccessorDec1 = (target, ctxStaticAccessor) => { + log.push("A2"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A5")); + ctxStaticAccessor.addInitializer(() => log.push("A6")); + return { init() { + log.push("A7"); + } }; + }; + const staticAccessorDec2 = (target, ctxStaticAccessor) => { + log.push("A1"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A3")); + ctxStaticAccessor.addInitializer(() => log.push("A4")); + return { init() { + log.push("A8"); + } }; + }; + log.push("start"); + _init = [, , ,]; + _Foo_decorators = [classDec1, classDec2]; + class Foo2 extends (_a = (log.push("extends"), Object), _method_dec = [methodDec1, methodDec2], _staticMethod_dec = [staticMethodDec1, staticMethodDec2], _field_dec = [fieldDec1, fieldDec2], _staticField_dec = [staticFieldDec1, staticFieldDec2], _getter_dec = [getterDec1, getterDec2], _staticGetter_dec = [staticGetterDec1, staticGetterDec2], _setter_dec = [setterDec1, setterDec2], _staticSetter_dec = [staticSetterDec1, staticSetterDec2], _accessor_dec = [accessorDec1, accessorDec2], _staticAccessor_dec = [staticAccessorDec1, staticAccessorDec2], _a) { + constructor() { + log.push("ctor:start"); + super(); + __runInitializers(_init, 5, this); + __privateAdd(this, _Foo_instances); + __privateAdd(this, _field, __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + log.push("ctor:end"); + } + } + _Foo_instances = new WeakSet(); + method_fn = function() { + }; + _Foo_static = new WeakSet(); + staticMethod_fn = function() { + }; + _field = new WeakMap(); + _staticField = new WeakMap(); + getter_get = function() { + return; + }; + staticGetter_get = function() { + return; + }; + setter_set = function(x) { + }; + staticSetter_set = function(x) { + }; + _accessor = new WeakMap(); + _staticAccessor = new WeakMap(); + staticMethod_fn = __decorateElement(_init, 25, "#staticMethod", _staticMethod_dec, _Foo_static, staticMethod_fn); + staticGetter_get = __decorateElement(_init, 26, "#staticGetter", _staticGetter_dec, _Foo_static, staticGetter_get); + staticSetter_set = __decorateElement(_init, 27, "#staticSetter", _staticSetter_dec, _Foo_static, staticSetter_set); + _c = __decorateElement(_init, 28, "#staticAccessor", _staticAccessor_dec, _Foo_static, _staticAccessor), staticAccessor_get = _c.get, staticAccessor_set = _c.set; + method_fn = __decorateElement(_init, 17, "#method", _method_dec, _Foo_instances, method_fn); + getter_get = __decorateElement(_init, 18, "#getter", _getter_dec, _Foo_instances, getter_get); + setter_set = __decorateElement(_init, 19, "#setter", _setter_dec, _Foo_instances, setter_set); + _b = __decorateElement(_init, 20, "#accessor", _accessor_dec, _Foo_instances, _accessor), accessor_get = _b.get, accessor_set = _b.set; + __decorateElement(_init, 29, "#staticField", _staticField_dec, _staticField); + __decorateElement(_init, 21, "#field", _field_dec, _field); + __privateAdd(Foo2, _Foo_static); + Foo2 = __decorateElement(_init, 0, "Foo", _Foo_decorators, Foo2); + __runInitializers(_init, 3, Foo2); + log.push("static:start"); + __privateAdd(Foo2, _staticField, __runInitializers(_init, 14)), __runInitializers(_init, 17, Foo2); + __privateAdd(Foo2, _staticAccessor, __runInitializers(_init, 6)), __runInitializers(_init, 9, Foo2); + log.push("static:end"); + __runInitializers(_init, 1, Foo2); + log.push("after"); + new Foo2(); + log.push("end"); + assertEq(() => log + "", "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"); + }, + "Initializer order (private members, class expression)": () => { + var _staticAccessor_dec, _accessor_dec, _staticSetter_dec, _setter_dec, _staticGetter_dec, _getter_dec, _staticField_dec, _field_dec, _staticMethod_dec, _method_dec, _a, _class_decorators, _init, _instances, method_fn, _static, _b, staticMethod_fn, _field, _staticField, getter_get, staticGetter_get, setter_set, staticSetter_set, _accessor, _c, accessor_get, accessor_set, _staticAccessor, _d, staticAccessor_get, staticAccessor_set; + const log = []; + const classDec1 = (cls, ctxClass) => { + log.push("c2"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c5")); + ctxClass.addInitializer(() => log.push("c6")); + }; + const classDec2 = (cls, ctxClass) => { + log.push("c1"); + if (!assertEq(() => typeof ctxClass.addInitializer, "function")) return; + ctxClass.addInitializer(() => log.push("c3")); + ctxClass.addInitializer(() => log.push("c4")); + }; + const methodDec1 = (fn, ctxMethod) => { + log.push("m2"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m5")); + ctxMethod.addInitializer(() => log.push("m6")); + }; + const methodDec2 = (fn, ctxMethod) => { + log.push("m1"); + if (!assertEq(() => typeof ctxMethod.addInitializer, "function")) return; + ctxMethod.addInitializer(() => log.push("m3")); + ctxMethod.addInitializer(() => log.push("m4")); + }; + const staticMethodDec1 = (fn, ctxStaticMethod) => { + log.push("M2"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M5")); + ctxStaticMethod.addInitializer(() => log.push("M6")); + }; + const staticMethodDec2 = (fn, ctxStaticMethod) => { + log.push("M1"); + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, "function")) return; + ctxStaticMethod.addInitializer(() => log.push("M3")); + ctxStaticMethod.addInitializer(() => log.push("M4")); + }; + const fieldDec1 = (value, ctxField) => { + log.push("f2"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f5")); + ctxField.addInitializer(() => log.push("f6")); + return () => { + log.push("f7"); + }; + }; + const fieldDec2 = (value, ctxField) => { + log.push("f1"); + if (!assertEq(() => typeof ctxField.addInitializer, "function")) return; + ctxField.addInitializer(() => log.push("f3")); + ctxField.addInitializer(() => log.push("f4")); + return () => { + log.push("f8"); + }; + }; + const staticFieldDec1 = (value, ctxStaticField) => { + log.push("F2"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F5")); + ctxStaticField.addInitializer(() => log.push("F6")); + return () => { + log.push("F7"); + }; + }; + const staticFieldDec2 = (value, ctxStaticField) => { + log.push("F1"); + if (!assertEq(() => typeof ctxStaticField.addInitializer, "function")) return; + ctxStaticField.addInitializer(() => log.push("F3")); + ctxStaticField.addInitializer(() => log.push("F4")); + return () => { + log.push("F8"); + }; + }; + const getterDec1 = (fn, ctxGetter) => { + log.push("g2"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g5")); + ctxGetter.addInitializer(() => log.push("g6")); + }; + const getterDec2 = (fn, ctxGetter) => { + log.push("g1"); + if (!assertEq(() => typeof ctxGetter.addInitializer, "function")) return; + ctxGetter.addInitializer(() => log.push("g3")); + ctxGetter.addInitializer(() => log.push("g4")); + }; + const staticGetterDec1 = (fn, ctxStaticGetter) => { + log.push("G2"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G5")); + ctxStaticGetter.addInitializer(() => log.push("G6")); + }; + const staticGetterDec2 = (fn, ctxStaticGetter) => { + log.push("G1"); + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, "function")) return; + ctxStaticGetter.addInitializer(() => log.push("G3")); + ctxStaticGetter.addInitializer(() => log.push("G4")); + }; + const setterDec1 = (fn, ctxSetter) => { + log.push("s2"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s5")); + ctxSetter.addInitializer(() => log.push("s6")); + }; + const setterDec2 = (fn, ctxSetter) => { + log.push("s1"); + if (!assertEq(() => typeof ctxSetter.addInitializer, "function")) return; + ctxSetter.addInitializer(() => log.push("s3")); + ctxSetter.addInitializer(() => log.push("s4")); + }; + const staticSetterDec1 = (fn, ctxStaticSetter) => { + log.push("S2"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S5")); + ctxStaticSetter.addInitializer(() => log.push("S6")); + }; + const staticSetterDec2 = (fn, ctxStaticSetter) => { + log.push("S1"); + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, "function")) return; + ctxStaticSetter.addInitializer(() => log.push("S3")); + ctxStaticSetter.addInitializer(() => log.push("S4")); + }; + const accessorDec1 = (target, ctxAccessor) => { + log.push("a2"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a5")); + ctxAccessor.addInitializer(() => log.push("a6")); + return { init() { + log.push("a7"); + } }; + }; + const accessorDec2 = (target, ctxAccessor) => { + log.push("a1"); + if (!assertEq(() => typeof ctxAccessor.addInitializer, "function")) return; + ctxAccessor.addInitializer(() => log.push("a3")); + ctxAccessor.addInitializer(() => log.push("a4")); + return { init() { + log.push("a8"); + } }; + }; + const staticAccessorDec1 = (target, ctxStaticAccessor) => { + log.push("A2"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A5")); + ctxStaticAccessor.addInitializer(() => log.push("A6")); + return { init() { + log.push("A7"); + } }; + }; + const staticAccessorDec2 = (target, ctxStaticAccessor) => { + log.push("A1"); + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, "function")) return; + ctxStaticAccessor.addInitializer(() => log.push("A3")); + ctxStaticAccessor.addInitializer(() => log.push("A4")); + return { init() { + log.push("A8"); + } }; + }; + log.push("start"); + const Foo2 = (_init = [, , ,], _class_decorators = [classDec1, classDec2], _b = class extends (_a = (log.push("extends"), Object), _method_dec = [methodDec1, methodDec2], _staticMethod_dec = [staticMethodDec1, staticMethodDec2], _field_dec = [fieldDec1, fieldDec2], _staticField_dec = [staticFieldDec1, staticFieldDec2], _getter_dec = [getterDec1, getterDec2], _staticGetter_dec = [staticGetterDec1, staticGetterDec2], _setter_dec = [setterDec1, setterDec2], _staticSetter_dec = [staticSetterDec1, staticSetterDec2], _accessor_dec = [accessorDec1, accessorDec2], _staticAccessor_dec = [staticAccessorDec1, staticAccessorDec2], _a) { + constructor() { + log.push("ctor:start"); + super(); + __runInitializers(_init, 5, this); + __privateAdd(this, _instances); + __privateAdd(this, _field, __runInitializers(_init, 18)), __runInitializers(_init, 21, this); + __privateAdd(this, _accessor, __runInitializers(_init, 10)), __runInitializers(_init, 13, this); + log.push("ctor:end"); + } + }, _instances = new WeakSet(), method_fn = function() { + }, _static = new WeakSet(), staticMethod_fn = function() { + }, _field = new WeakMap(), _staticField = new WeakMap(), getter_get = function() { + return; + }, staticGetter_get = function() { + return; + }, setter_set = function(x) { + }, staticSetter_set = function(x) { + }, _accessor = new WeakMap(), _staticAccessor = new WeakMap(), staticMethod_fn = __decorateElement(_init, 25, "#staticMethod", _staticMethod_dec, _static, staticMethod_fn), staticGetter_get = __decorateElement(_init, 26, "#staticGetter", _staticGetter_dec, _static, staticGetter_get), staticSetter_set = __decorateElement(_init, 27, "#staticSetter", _staticSetter_dec, _static, staticSetter_set), _d = __decorateElement(_init, 28, "#staticAccessor", _staticAccessor_dec, _static, _staticAccessor), staticAccessor_get = _d.get, staticAccessor_set = _d.set, method_fn = __decorateElement(_init, 17, "#method", _method_dec, _instances, method_fn), getter_get = __decorateElement(_init, 18, "#getter", _getter_dec, _instances, getter_get), setter_set = __decorateElement(_init, 19, "#setter", _setter_dec, _instances, setter_set), _c = __decorateElement(_init, 20, "#accessor", _accessor_dec, _instances, _accessor), accessor_get = _c.get, accessor_set = _c.set, __decorateElement(_init, 29, "#staticField", _staticField_dec, _staticField), __decorateElement(_init, 21, "#field", _field_dec, _field), __privateAdd(_b, _static), _b = __decorateElement(_init, 0, "", _class_decorators, _b), __runInitializers(_init, 3, _b), log.push("static:start"), __privateAdd(_b, _staticField, __runInitializers(_init, 14)), __runInitializers(_init, 17, _b), __privateAdd(_b, _staticAccessor, __runInitializers(_init, 6)), __runInitializers(_init, 9, _b), log.push("static:end"), __runInitializers(_init, 1, _b), _b); + log.push("after"); + new Foo2(); + log.push("end"); + assertEq(() => log + "", "start,extends,M1,M2,G1,G2,S1,S2,A1,A2,m1,m2,g1,g2,s1,s2,a1,a2,F1,F2,f1,f2,c1,c2,M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,static:start,F7,F8,F3,F4,F5,F6,A7,A8,A3,A4,A5,A6,static:end,c3,c4,c5,c6,after,ctor:start,m3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,f7,f8,f3,f4,f5,f6,a7,a8,a3,a4,a5,a6,ctor:end,end"); + } +}; +function prettyPrint(x) { + if (x && x.prototype && x.prototype.constructor === x) return "class"; + if (typeof x === "string") return JSON.stringify(x); + return x; +} +function assertEq(callback, expected) { + let details; + try { + let x = callback(); + if (x === expected) return true; + details = ` Expected: ${prettyPrint(expected)} + Observed: ${prettyPrint(x)}`; + } catch (error) { + details = ` Throws: ${error}`; + } + const code = callback.toString().replace(/^\(\) => /, "").replace(/\s+/g, " "); + console.log(`\u274C ${testName} + Code: ${code} +${details} +`); + failures++; + return false; +} +function assertThrows(callback, expected) { + let details; + try { + let x = callback(); + details = ` Expected: throws instanceof ${expected.name} + Observed: returns ${prettyPrint(x)}`; + } catch (error) { + if (error instanceof expected) return true; + details = ` Expected: throws instanceof ${expected.name} + Observed: throws ${error}`; + } + const code = callback.toString().replace(/^\(\) => /, "").replace(/\s+/g, " "); + console.log(`\u274C ${testName} + Code: ${code} +${details} +`); + failures++; + return false; +} +let testName; +let failures = 0; +async function run() { + for (const [name, test] of Object.entries(tests)) { + testName = name; + try { + await test(); + } catch (err) { + console.log(`\u274C ${name} + Throws: ${err} +`); + failures++; + } + } + if (failures > 0) { + console.log(`\u274C ${failures} checks failed`); + } else { + console.log(`\u2705 All checks passed`); + } +} +const promise = run(); diff --git a/scripts/decorator-tests.ts b/scripts/decorator-tests.ts new file mode 100644 index 00000000000..9e0adf75951 --- /dev/null +++ b/scripts/decorator-tests.ts @@ -0,0 +1,3714 @@ +const tests: Record Promise | void> = { + // Class decorators + 'Class decorators: Basic statement': () => { + let old: { new(): Foo } + const dec = (cls: { new(): Foo }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + @dec class Foo { } + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Anonymous': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, '') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, '') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + const Foo = (x => x)(@dec class { }) + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Property value': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + const obj = { + Foo: @dec class { }, + } + assertEq(() => obj.Foo, old!) + }, + 'Class decorators: Basic expression: Variable initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + const Foo = @dec class { } + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Array binding': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + const [Foo = @dec class { }] = [] + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Object binding': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + const { Foo = @dec class { } } = {} + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Assignment initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + let Foo: { new(): unknown } + Foo = @dec class { } + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Assignment array binding': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + let Foo: { new(): unknown }; + [Foo = @dec class { }] = [] + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Assignment object binding': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + let Foo: { new(): unknown }; + ({ Foo = @dec class { } } = {}) + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Instance field initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + class Class { + Foo = @dec class { } + } + const Foo = new Class().Foo + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Static field initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + class Class { + static Foo = @dec class { } + } + assertEq(() => Class.Foo, old!) + }, + 'Class decorators: Basic expression: Instance auto-accessor initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + class Class { + accessor Foo = @dec class { } + } + const Foo = new Class().Foo + assertEq(() => Foo, old!) + }, + 'Class decorators: Basic expression: Static auto-accessor initializer': () => { + let old: { new(): unknown } + const dec = (cls: { new(): unknown }, ctx: ClassDecoratorContext) => { + assertEq(() => typeof cls, 'function') + assertEq(() => cls.name, 'Foo') + assertEq(() => ctx.kind, 'class') + assertEq(() => ctx.name, 'Foo') + assertEq(() => 'static' in ctx, false) + assertEq(() => 'private' in ctx, false) + assertEq(() => 'access' in ctx, false) + old = cls + } + class Class { + static accessor Foo = @dec class { } + } + assertEq(() => Class.Foo, old!) + }, + 'Class decorators: Order': () => { + const log: number[] = [] + let Bar: { new(): Foo } + let Baz: { new(): Foo } + const dec1 = (cls: { new(): Foo }, ctx: ClassDecoratorContext) => { + log.push(2) + Bar = function () { + log.push(4) + return new cls + } as any + return Bar + } + const dec2 = (cls: { new(): Foo }, ctx: ClassDecoratorContext) => { + log.push(1) + Baz = function () { + log.push(5) + return new cls + } as any + return Baz + } + log.push(0) + @dec1 @dec2 class Foo { + constructor() { log.push(6) } + } + log.push(3) + new Foo + log.push(7) + assertEq(() => Foo, Bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Class decorators: Return null': () => { + assertThrows(() => { + const dec = (cls: { new(): Foo }, ctx: ClassDecoratorContext): any => { + return null + } + @dec class Foo { } + }, TypeError) + }, + 'Class decorators: Return object': () => { + assertThrows(() => { + const dec = (cls: { new(): Foo }, ctx: ClassDecoratorContext): any => { + return {} + } + @dec class Foo { } + }, TypeError) + }, + 'Class decorators: Extra initializer': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (cls: { new(): Foo }, ctx: ClassDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + @dec @dec class Foo { } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Method decorators + 'Method decorators: Basic (instance method)': () => { + const old: Record void> = {} + const dec = (key: PropertyKey, name: string) => + (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'method') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => (ctx.access.get as any)({ [key]: 123 }), 123) + assertEq(() => 'set' in ctx.access, false) + old[key] = fn + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo', 'foo') foo() { } + @dec(bar, '[bar]') [bar]() { } + @dec(baz, '') [baz]() { } + } + assertEq(() => Foo.prototype.foo, old['foo']) + assertEq(() => Foo.prototype[bar], old[bar]) + assertEq(() => Foo.prototype[baz], old[baz]) + }, + 'Method decorators: Basic (static method)': () => { + const old: Record void> = {} + const dec = (key: PropertyKey, name: string) => + (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'method') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => (ctx.access.get as any)({ [key]: 123 }), 123) + assertEq(() => 'set' in ctx.access, false) + old[key] = fn + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo', 'foo') static foo() { } + @dec(bar, '[bar]') static [bar]() { } + @dec(baz, '') static [baz]() { } + } + assertEq(() => Foo.foo, old['foo']) + assertEq(() => Foo[bar], old[bar]) + assertEq(() => Foo[baz], old[baz]) + }, + 'Method decorators: Basic (private instance method)': () => { + let old: (this: Foo) => void + let lateAsserts: () => void + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, '#foo') + assertEq(() => ctx.kind, 'method') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(new Foo), $foo) + assertEq(() => 'set' in ctx.access, false) + } + old = fn + } + let $foo: Function + class Foo { + @dec #foo() { } + static { $foo = new Foo().#foo } + } + assertEq(() => $foo, old!) + lateAsserts!() + }, + 'Method decorators: Basic (private static method)': () => { + let old: (this: typeof Foo) => void + let lateAsserts: () => void + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, '#foo') + assertEq(() => ctx.kind, 'method') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(Foo), $foo) + assertEq(() => 'set' in ctx.access, false) + } + old = fn + } + let $foo: Function + class Foo { + @dec static #foo() { } + static { $foo = this.#foo } + } + assertEq(() => $foo, old!) + lateAsserts!() + }, + 'Method decorators: Shim (instance method)': () => { + let bar: (this: Foo) => number + const dec = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + class Foo { + bar = 123 + @dec foo() { return this.bar } + } + assertEq(() => Foo.prototype.foo, bar!) + assertEq(() => new Foo().foo(), 124) + }, + 'Method decorators: Shim (static method)': () => { + let bar: (this: typeof Foo) => number + const dec = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + class Foo { + static bar = 123 + @dec static foo() { return this.bar } + } + assertEq(() => Foo.foo, bar!) + assertEq(() => Foo.foo(), 124) + }, + 'Method decorators: Shim (private instance method)': () => { + let bar: (this: Foo) => number + const dec = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + let $foo: (this: Foo) => number + class Foo { + bar = 123 + @dec #foo() { return this.bar } + static { $foo = new Foo().#foo } + } + assertEq(() => $foo, bar!) + assertEq(() => bar.call(new Foo), 124) + }, + 'Method decorators: Shim (private static method)': () => { + let bar: (this: typeof Foo) => number + const dec = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + let $foo: (this: Foo) => number + class Foo { + static bar = 123 + @dec static #foo() { return this.bar } + static { $foo = this.#foo } + } + assertEq(() => $foo, bar!) + assertEq(() => bar.call(Foo), 124) + }, + 'Method decorators: Order (instance method)': () => { + const log: number[] = [] + let bar: (this: Foo) => number + let baz: (this: Foo) => number + const dec1 = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 foo() { return log.push(6) } + } + log.push(3) + new Foo().foo() + log.push(7) + assertEq(() => Foo.prototype.foo, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Method decorators: Order (static method)': () => { + const log: number[] = [] + let bar: (this: typeof Foo) => number + let baz: (this: typeof Foo) => number + const dec1 = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 static foo() { return log.push(6) } + } + log.push(3) + Foo.foo() + log.push(7) + assertEq(() => Foo.foo, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Method decorators: Order (private instance method)': () => { + const log: number[] = [] + let bar: (this: Foo) => number + let baz: (this: Foo) => number + const dec1 = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + let $foo: Function + class Foo { + @dec1 @dec2 #foo() { return log.push(6) } + static { $foo = new Foo().#foo } + } + log.push(3) + $foo.call(new Foo) + log.push(7) + assertEq(() => $foo, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Method decorators: Order (private static method)': () => { + const log: number[] = [] + let bar: (this: typeof Foo) => number + let baz: (this: typeof Foo) => number + const dec1 = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: typeof Foo) => number, ctx: ClassMethodDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + let $foo: (this: Foo) => number + class Foo { + @dec1 @dec2 static #foo() { return log.push(6) } + static { $foo = Foo.#foo } + } + log.push(3) + $foo.call(Foo) + log.push(7) + assertEq(() => $foo, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Method decorators: Return null (instance method)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return null + } + class Foo { @dec foo() { } } + }, TypeError) + }, + 'Method decorators: Return null (static method)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return null + } + class Foo { @dec static foo() { } } + }, TypeError) + }, + 'Method decorators: Return null (private instance method)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return null + } + class Foo { @dec #foo() { } } + }, TypeError) + }, + 'Method decorators: Return null (private static method)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return null + } + class Foo { @dec static #foo() { } } + }, TypeError) + }, + 'Method decorators: Return object (instance method)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return {} + } + class Foo { @dec foo() { } } + }, TypeError) + }, + 'Method decorators: Return object (static method)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return {} + } + class Foo { @dec static foo() { } } + }, TypeError) + }, + 'Method decorators: Return object (private instance method)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return {} + } + class Foo { @dec #foo() { } } + }, TypeError) + }, + 'Method decorators: Return object (private static method)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + return {} + } + class Foo { @dec static #foo() { } } + }, TypeError) + }, + 'Method decorators: Extra initializer (instance method)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec foo() { } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Method decorators: Extra initializer (static method)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static foo() { } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + 'Method decorators: Extra initializer (private instance method)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo) => void, ctx: ClassMethodDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec #foo() { } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Method decorators: Extra initializer (private static method)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo) => void, ctx: ClassMethodDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static #foo() { } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Field decorators + 'Field decorators: Basic (instance field)': () => { + const dec = (key: PropertyKey) => + (value: undefined, ctx: ClassFieldDecoratorContext) => { + assertEq(() => value, undefined) + assertEq(() => ctx.kind, 'field') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => { + const obj: any = {} + ctx.access.set(obj, 321) + return obj[key] + }, 321) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo') foo = 123 + @dec(bar) [bar] = 123 + @dec(baz) [baz] = 123 + } + assertEq(() => new Foo().foo, 123) + assertEq(() => new Foo()[bar], 123) + assertEq(() => new Foo()[baz], 123) + }, + 'Field decorators: Basic (static field)': () => { + const dec = (key: PropertyKey) => + (value: undefined, ctx: ClassFieldDecoratorContext) => { + assertEq(() => value, undefined) + assertEq(() => ctx.kind, 'field') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => { + const obj: any = {} + ctx.access.set(obj, 321) + return obj[key] + }, 321) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo') static foo = 123 + @dec(bar) static [bar] = 123 + @dec(baz) static [baz] = 123 + } + assertEq(() => Foo.foo, 123) + assertEq(() => Foo[bar], 123) + assertEq(() => Foo[baz], 123) + }, + 'Field decorators: Basic (private instance field)': () => { + let lateAsserts: () => void + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + assertEq(() => value, undefined) + assertEq(() => ctx.kind, 'field') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(new Foo), 123) + assertEq(() => { + const obj = new Foo + ctx.access.set(obj, 321) + return get$foo(obj) + }, 321) + } + } + let get$foo: (x: Foo) => number + class Foo { + @dec #foo = 123 + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(new Foo()), 123) + lateAsserts!() + }, + 'Field decorators: Basic (private static field)': () => { + let lateAsserts: () => void + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + assertEq(() => value, undefined) + assertEq(() => ctx.kind, 'field') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(Foo), 123) + assertEq(() => { + ctx.access.set(Foo, 321) + return get$foo(Foo) + }, 321) + } + } + let get$foo: (x: typeof Foo) => number + class Foo { + @dec static #foo = 123 + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(Foo), 123) + lateAsserts!() + }, + 'Field decorators: Shim (instance field)': () => { + let log: number[] = [] + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + return (x: number) => log.push(x) + } + class Foo { + @dec foo = 123 + @dec bar!: number + } + assertEq(() => log + '', '') + var obj = new Foo + assertEq(() => obj.foo, 1) + assertEq(() => obj.bar, 2) + assertEq(() => log + '', '123,') + var obj = new Foo + assertEq(() => obj.foo, 3) + assertEq(() => obj.bar, 4) + assertEq(() => log + '', '123,,123,') + }, + 'Field decorators: Shim (static field)': () => { + let log: number[] = [] + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + return (x: number) => log.push(x) + } + const fn = (foo: number, bar: number) => { + class Foo { + @dec static foo = 123 + @dec static bar: number + } + assertEq(() => Foo.foo, foo) + assertEq(() => Foo.bar, bar) + } + assertEq(() => log + '', '') + fn(1, 2) + assertEq(() => log + '', '123,') + fn(3, 4) + assertEq(() => log + '', '123,,123,') + }, + 'Field decorators: Shim (private instance field)': () => { + let log: number[] = [] + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + return (x: number) => log.push(x) + } + let get$foo: (x: Foo) => number + let get$bar: (x: Foo) => number + class Foo { + @dec #foo = 123 + @dec #bar!: number + static { + get$foo = x => x.#foo + get$bar = x => x.#bar + } + } + assertEq(() => log + '', '') + var obj = new Foo + assertEq(() => get$foo(obj), 1) + assertEq(() => get$bar(obj), 2) + assertEq(() => log + '', '123,') + var obj = new Foo + assertEq(() => get$foo(obj), 3) + assertEq(() => get$bar(obj), 4) + assertEq(() => log + '', '123,,123,') + }, + 'Field decorators: Shim (private static field)': () => { + let log: number[] = [] + const dec = (value: undefined, ctx: ClassFieldDecoratorContext) => { + return (x: number) => log.push(x) + } + const fn = (foo: number, bar: number) => { + let get$foo: (x: typeof Foo) => number + let get$bar: (x: typeof Foo) => number + class Foo { + @dec static #foo = 123 + @dec static #bar: number + static { + get$foo = x => x.#foo + get$bar = x => x.#bar + } + } + assertEq(() => get$foo(Foo), foo) + assertEq(() => get$bar(Foo), bar) + } + assertEq(() => log + '', '') + fn(1, 2) + assertEq(() => log + '', '123,') + fn(3, 4) + assertEq(() => log + '', '123,,123,') + }, + 'Field decorators: Order (instance field)': () => { + const log: number[] = [] + const dec1 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(2) + return () => log.push(4) + } + const dec2 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(1) + return () => log.push(5) + } + log.push(0) + class Foo { + @dec1 @dec2 foo = 123 + } + log.push(3) + var obj = new Foo() + log.push(6) + assertEq(() => obj.foo, 6) + assertEq(() => log + '', '0,1,2,3,4,5,6') + }, + 'Field decorators: Order (static field)': () => { + const log: number[] = [] + const dec1 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(2) + return () => log.push(3) + } + const dec2 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(1) + return () => log.push(4) + } + log.push(0) + class Foo { + @dec1 @dec2 static foo = 123 + } + log.push(5) + assertEq(() => Foo.foo, 5) + assertEq(() => log + '', '0,1,2,3,4,5') + }, + 'Field decorators: Order (private instance field)': () => { + const log: number[] = [] + const dec1 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(2) + return () => log.push(4) + } + const dec2 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(1) + return () => log.push(5) + } + log.push(0) + let get$foo: (x: Foo) => number + class Foo { + @dec1 @dec2 #foo = 123 + static { get$foo = x => x.#foo } + } + log.push(3) + var obj = new Foo() + log.push(6) + assertEq(() => get$foo(obj), 6) + assertEq(() => log + '', '0,1,2,3,4,5,6') + }, + 'Field decorators: Order (private static field)': () => { + const log: number[] = [] + const dec1 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(2) + return () => log.push(3) + } + const dec2 = (value: undefined, ctx: ClassFieldDecoratorContext) => { + log.push(1) + return () => log.push(4) + } + log.push(0) + let get$foo: (x: typeof Foo) => number + class Foo { + @dec1 @dec2 static #foo = 123 + static { get$foo = x => x.#foo } + } + log.push(5) + assertEq(() => get$foo(Foo), 5) + assertEq(() => log + '', '0,1,2,3,4,5') + }, + 'Field decorators: Return null (instance field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return null + } + class Foo { @dec foo: undefined } + }, TypeError) + }, + 'Field decorators: Return null (static field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return null + } + class Foo { @dec static foo: undefined } + }, TypeError) + }, + 'Field decorators: Return null (private instance field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return null + } + class Foo { @dec #foo: undefined } + }, TypeError) + }, + 'Field decorators: Return null (private static field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return null + } + class Foo { @dec static #foo: undefined } + }, TypeError) + }, + 'Field decorators: Return object (instance field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return {} + } + class Foo { @dec foo: undefined } + }, TypeError) + }, + 'Field decorators: Return object (static field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return {} + } + class Foo { @dec static foo: undefined } + }, TypeError) + }, + 'Field decorators: Return object (private instance field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return {} + } + class Foo { @dec #foo: undefined } + }, TypeError) + }, + 'Field decorators: Return object (private static field)': () => { + assertThrows(() => { + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + return {} + } + class Foo { @dec static #foo: undefined } + }, TypeError) + }, + 'Field decorators: Extra initializer (instance field)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec foo: undefined } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Field decorators: Extra initializer (static field)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static foo: undefined } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + 'Field decorators: Extra initializer (private instance field)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec #foo: undefined } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Field decorators: Extra initializer (private static field)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (value: undefined, ctx: ClassFieldDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static #foo: undefined } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Getter decorators + 'Getter decorators: Basic (instance getter)': () => { + const dec = (key: PropertyKey, name: string) => + (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'getter') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => 'set' in ctx.access, false) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + bar = 123 + @dec('foo', 'get foo') get foo() { return this.bar } + @dec(bar, 'get [bar]') get [bar]() { return this.bar } + @dec(baz, 'get ') get [baz]() { return this.bar } + } + assertEq(() => new Foo().foo, 123) + assertEq(() => new Foo()[bar], 123) + assertEq(() => new Foo()[baz], 123) + }, + 'Getter decorators: Basic (static getter)': () => { + const dec = (key: PropertyKey, name: string) => + (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'getter') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => 'set' in ctx.access, false) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + static bar = 123 + @dec('foo', 'get foo') static get foo() { return this.bar } + @dec(bar, 'get [bar]') static get [bar]() { return this.bar } + @dec(baz, 'get ') static get [baz]() { return this.bar } + } + assertEq(() => Foo.foo, 123) + assertEq(() => Foo[bar], 123) + assertEq(() => Foo[baz], 123) + }, + 'Getter decorators: Basic (private instance getter)': () => { + let lateAsserts: () => void + const dec = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, 'get #foo') + assertEq(() => ctx.kind, 'getter') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(new Foo), 123) + assertEq(() => 'set' in ctx.access, false) + } + } + let get$foo: (x: Foo) => number + class Foo { + #bar = 123 + @dec get #foo() { return this.#bar } + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(new Foo), 123) + lateAsserts!() + }, + 'Getter decorators: Basic (private static getter)': () => { + let lateAsserts: () => void + const dec = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, 'get #foo') + assertEq(() => ctx.kind, 'getter') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(Foo), 123) + assertEq(() => 'set' in ctx.access, false) + } + } + let get$foo: (x: typeof Foo) => number + class Foo { + static #bar = 123 + @dec static get #foo() { return this.#bar } + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(Foo), 123) + lateAsserts!() + }, + 'Getter decorators: Shim (instance getter)': () => { + let bar: (this: Foo) => number + const dec = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + class Foo { + bar = 123 + @dec get foo() { return this.bar } + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.get, bar!) + assertEq(() => new Foo().foo, 124) + }, + 'Getter decorators: Shim (static getter)': () => { + let bar: (this: typeof Foo) => number + const dec = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + class Foo { + static bar = 123 + @dec static get foo() { return this.bar } + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.get, bar!) + assertEq(() => Foo.foo, 124) + }, + 'Getter decorators: Shim (private instance getter)': () => { + let bar: (this: Foo) => number + const dec = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + let get$foo: (x: Foo) => number + class Foo { + #bar = 123 + @dec get #foo() { return this.#bar } + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(new Foo), 124) + }, + 'Getter decorators: Shim (private static getter)': () => { + let bar: (this: typeof Foo) => number + const dec = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + bar = function () { return fn.call(this) + 1 } + return bar + } + let get$foo: (x: typeof Foo) => number + class Foo { + static #bar = 123 + @dec static get #foo() { return this.#bar } + static { get$foo = x => x.#foo } + } + assertEq(() => get$foo(Foo), 124) + }, + 'Getter decorators: Order (instance getter)': () => { + const log: number[] = [] + let bar: (this: Foo) => number + let baz: (this: Foo) => number + const dec1 = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 get foo() { return log.push(6) } + } + log.push(3) + new Foo().foo + log.push(7) + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.get, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Getter decorators: Order (static getter)': () => { + const log: number[] = [] + let bar: (this: typeof Foo) => number + let baz: (this: typeof Foo) => number + const dec1 = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 static get foo() { return log.push(6) } + } + log.push(3) + Foo.foo + log.push(7) + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.get, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Getter decorators: Order (private instance getter)': () => { + const log: number[] = [] + let bar: (this: Foo) => number + let baz: (this: Foo) => number + const dec1 = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + let get$foo: (x: Foo) => number + class Foo { + @dec1 @dec2 get #foo() { return log.push(6) } + static { get$foo = x => x.#foo } + } + log.push(3) + assertEq(() => get$foo(new Foo), 7) + log.push(7) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Getter decorators: Order (private static getter)': () => { + const log: number[] = [] + let bar: (this: typeof Foo) => number + let baz: (this: typeof Foo) => number + const dec1 = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(2) + bar = function () { + log.push(4) + return fn.call(this) + } + return bar + } + const dec2 = (fn: (this: typeof Foo) => number, ctx: ClassGetterDecoratorContext) => { + log.push(1) + baz = function () { + log.push(5) + return fn.call(this) + } + return baz + } + log.push(0) + let get$foo: (x: typeof Foo) => number + class Foo { + @dec1 @dec2 static get #foo() { return log.push(6) } + static { get$foo = x => x.#foo } + } + log.push(3) + assertEq(() => get$foo(Foo), 7) + log.push(7) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Getter decorators: Return null (instance getter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return null + } + class Foo { @dec get foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return null (static getter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return null + } + class Foo { @dec static get foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return null (private instance getter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return null + } + class Foo { @dec get #foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return null (private static getter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return null + } + class Foo { @dec static get #foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return object (instance getter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return {} + } + class Foo { @dec get foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return object (static getter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return {} + } + class Foo { @dec static get foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return object (private instance getter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return {} + } + class Foo { @dec get #foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Return object (private static getter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + return {} + } + class Foo { @dec static get #foo(): undefined { return } } + }, TypeError) + }, + 'Getter decorators: Extra initializer (instance getter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec get foo(): undefined { return } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Getter decorators: Extra initializer (static getter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static get foo(): undefined { return } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + 'Getter decorators: Extra initializer (private instance getter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec get #foo(): undefined { return } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Getter decorators: Extra initializer (private static getter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo) => undefined, ctx: ClassGetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static get #foo(): undefined { return } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Setter decorators + 'Setter decorators: Basic (instance setter)': () => { + const dec = (key: PropertyKey, name: string) => + (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'setter') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => 'get' in ctx.access, false) + const obj: any = {} + ctx.access.set(obj, 123) + assertEq(() => obj[key], 123) + assertEq(() => 'bar' in obj, false) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + bar = 0 + @dec('foo', 'set foo') set foo(x: number) { this.bar = x } + @dec(bar, 'set [bar]') set [bar](x: number) { this.bar = x } + @dec(baz, 'set ') set [baz](x: number) { this.bar = x } + } + var obj = new Foo + obj.foo = 321 + assertEq(() => obj.bar, 321) + obj[bar] = 4321 + assertEq(() => obj.bar, 4321) + obj[baz] = 54321 + assertEq(() => obj.bar, 54321) + }, + 'Setter decorators: Basic (static setter)': () => { + const dec = (key: PropertyKey, name: string) => + (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, name) + assertEq(() => ctx.kind, 'setter') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => 'get' in ctx.access, false) + const obj: any = {} + ctx.access.set(obj, 123) + assertEq(() => obj[key], 123) + assertEq(() => 'bar' in obj, false) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + static bar = 0 + @dec('foo', 'set foo') static set foo(x: number) { this.bar = x } + @dec(bar, 'set [bar]') static set [bar](x: number) { this.bar = x } + @dec(baz, 'set ') static set [baz](x: number) { this.bar = x } + } + Foo.foo = 321 + assertEq(() => Foo.bar, 321) + Foo[bar] = 4321 + assertEq(() => Foo.bar, 4321) + Foo[baz] = 54321 + assertEq(() => Foo.bar, 54321) + }, + 'Setter decorators: Basic (private instance setter)': () => { + let lateAsserts: () => void + const dec = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, 'set #foo') + assertEq(() => ctx.kind, 'setter') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => 'get' in ctx.access, false) + assertEq(() => { + const obj = new Foo + ctx.access.set(obj, 123) + return obj.bar + }, 123) + } + } + let set$foo: (x: Foo, y: number) => void + class Foo { + bar = 0 + @dec set #foo(x: number) { this.bar = x } + static { set$foo = (x, y) => { x.#foo = y } } + } + lateAsserts!() + var obj = new Foo + assertEq(() => set$foo(obj, 321), undefined) + assertEq(() => obj.bar, 321) + }, + 'Setter decorators: Basic (private static setter)': () => { + let lateAsserts: () => void + const dec = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + assertEq(() => typeof fn, 'function') + assertEq(() => fn.name, 'set #foo') + assertEq(() => ctx.kind, 'setter') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => 'get' in ctx.access, false) + assertEq(() => { + ctx.access.set(Foo, 123) + return Foo.bar + }, 123) + } + } + let set$foo: (x: typeof Foo, y: number) => void + class Foo { + static bar = 0 + @dec static set #foo(x: number) { this.bar = x } + static { set$foo = (x, y) => { x.#foo = y } } + } + lateAsserts!() + assertEq(() => set$foo(Foo, 321), undefined) + assertEq(() => Foo.bar, 321) + }, + 'Setter decorators: Shim (instance setter)': () => { + let bar: (this: Foo, x: number) => void + const dec = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + bar = function (x) { fn.call(this, x + 1) } + return bar + } + class Foo { + bar = 123 + @dec set foo(x: number) { this.bar = x } + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.set, bar!) + var obj = new Foo + obj.foo = 321 + assertEq(() => obj.bar, 322) + }, + 'Setter decorators: Shim (static setter)': () => { + let bar: (this: typeof Foo, x: number) => void + const dec = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + bar = function (x) { fn.call(this, x + 1) } + return bar + } + class Foo { + static bar = 123 + @dec static set foo(x: number) { this.bar = x } + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.set, bar!) + Foo.foo = 321 + assertEq(() => Foo.bar, 322) + }, + 'Setter decorators: Shim (private instance setter)': () => { + let bar: (this: Foo, x: number) => void + const dec = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + bar = function (x) { fn.call(this, x + 1) } + return bar + } + let set$foo: (x: Foo, y: number) => void + class Foo { + bar = 123 + @dec set #foo(x: number) { this.bar = x } + static { set$foo = (x, y) => { x.#foo = y } } + } + var obj = new Foo + assertEq(() => set$foo(obj, 321), undefined) + assertEq(() => obj.bar, 322) + }, + 'Setter decorators: Shim (private static setter)': () => { + let bar: (this: typeof Foo, x: number) => void + const dec = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + bar = function (x) { fn.call(this, x + 1) } + return bar + } + let set$foo: (x: typeof Foo, y: number) => void + class Foo { + static bar = 123 + @dec static set #foo(x: number) { this.bar = x } + static { set$foo = (x, y) => { x.#foo = y } } + } + assertEq(() => set$foo(Foo, 321), undefined) + assertEq(() => Foo.bar, 322) + }, + 'Setter decorators: Order (instance setter)': () => { + const log: number[] = [] + let bar: (this: Foo, x: number) => void + let baz: (this: Foo, x: number) => void + const dec1 = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(2) + bar = function (x) { + log.push(4) + fn.call(this, x) + } + return bar + } + const dec2 = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(1) + baz = function (x) { + log.push(5) + fn.call(this, x) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 set foo(x: number) { log.push(6) } + } + log.push(3) + new Foo().foo = 123 + log.push(7) + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.set, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Setter decorators: Order (static setter)': () => { + const log: number[] = [] + let bar: (this: typeof Foo, x: number) => void + let baz: (this: typeof Foo, x: number) => void + const dec1 = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(2) + bar = function (x) { + log.push(4) + fn.call(this, x) + } + return bar + } + const dec2 = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(1) + baz = function (x) { + log.push(5) + fn.call(this, x) + } + return baz + } + log.push(0) + class Foo { + @dec1 @dec2 static set foo(x: number) { log.push(6) } + } + log.push(3) + Foo.foo = 123 + log.push(7) + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.set, bar!) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Setter decorators: Order (private instance setter)': () => { + const log: number[] = [] + let bar: (this: Foo, x: number) => void + let baz: (this: Foo, x: number) => void + const dec1 = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(2) + bar = function (x) { + log.push(4) + fn.call(this, x) + } + return bar + } + const dec2 = (fn: (this: Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(1) + baz = function (x) { + log.push(5) + fn.call(this, x) + } + return baz + } + log.push(0) + let set$foo: (x: Foo, y: number) => void + class Foo { + @dec1 @dec2 set #foo(x: number) { log.push(6) } + static { set$foo = (x, y) => { x.#foo = y } } + } + log.push(3) + assertEq(() => set$foo(new Foo, 123), undefined) + log.push(7) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Setter decorators: Order (private static setter)': () => { + const log: number[] = [] + let bar: (this: typeof Foo, x: number) => void + let baz: (this: typeof Foo, x: number) => void + const dec1 = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(2) + bar = function (x) { + log.push(4) + fn.call(this, x) + } + return bar + } + const dec2 = (fn: (this: typeof Foo, x: number) => void, ctx: ClassSetterDecoratorContext) => { + log.push(1) + baz = function (x) { + log.push(5) + fn.call(this, x) + } + return baz + } + log.push(0) + let set$foo: (x: typeof Foo, y: number) => void + class Foo { + @dec1 @dec2 static set #foo(x: number) { log.push(6) } + static { set$foo = (x, y) => { x.#foo = y } } + } + log.push(3) + assertEq(() => set$foo(Foo, 123), undefined) + log.push(7) + assertEq(() => log + '', '0,1,2,3,4,5,6,7') + }, + 'Setter decorators: Return null (instance setter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return null + } + class Foo { @dec set foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return null (static setter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return null + } + class Foo { @dec static set foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return null (private instance setter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return null + } + class Foo { @dec set #foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return null (private static setter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return null + } + class Foo { @dec static set #foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return object (instance setter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return {} + } + class Foo { @dec set foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return object (static setter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return {} + } + class Foo { @dec static set foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return object (private instance setter)': () => { + assertThrows(() => { + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return {} + } + class Foo { @dec set #foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Return object (private static setter)': () => { + assertThrows(() => { + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + return {} + } + class Foo { @dec static set #foo(x: undefined) { } } + }, TypeError) + }, + 'Setter decorators: Extra initializer (instance setter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec set foo(x: undefined) { } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Setter decorators: Extra initializer (static setter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static set foo(x: undefined) { } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + 'Setter decorators: Extra initializer (private instance setter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec set #foo(x: undefined) { } } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Setter decorators: Extra initializer (private static setter)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (fn: (this: typeof Foo, x: undefined) => void, ctx: ClassSetterDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static set #foo(x: undefined) { } } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Auto-accessor decorators + 'Auto-accessor decorators: Basic (instance auto-accessor)': () => { + const dec = (key: PropertyKey, getName: string, setName: string) => + (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext) => { + assertEq(() => typeof target.get, 'function') + assertEq(() => typeof target.set, 'function') + assertEq(() => target.get.name, getName) + assertEq(() => target.set.name, setName) + assertEq(() => ctx.kind, 'accessor') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => { + const obj: any = {} + ctx.access.set(obj, 123) + return obj[key] + }, 123) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo', 'get foo', 'set foo') accessor foo = 0 + @dec(bar, 'get [bar]', 'set [bar]') accessor [bar] = 0 + @dec(baz, 'get ', 'set ') accessor [baz] = 0 + } + var obj = new Foo + obj.foo = 321 + assertEq(() => obj.foo, 321) + obj[bar] = 4321 + assertEq(() => obj[bar], 4321) + obj[baz] = 54321 + assertEq(() => obj[baz], 54321) + }, + 'Auto-accessor decorators: Basic (static auto-accessor)': () => { + const dec = (key: PropertyKey, getName: string, setName: string) => + (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext) => { + assertEq(() => typeof target.get, 'function') + assertEq(() => typeof target.set, 'function') + assertEq(() => target.get.name, getName) + assertEq(() => target.set.name, setName) + assertEq(() => ctx.kind, 'accessor') + assertEq(() => ctx.name, key) + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, false) + assertEq(() => ctx.access.has({ [key]: false }), true) + assertEq(() => ctx.access.has({ bar: true }), false) + assertEq(() => ctx.access.get({ [key]: 123 }), 123) + assertEq(() => { + const obj: any = {} + ctx.access.set(obj, 123) + return obj[key] + }, 123) + } + const bar = Symbol('bar') + const baz = Symbol() + class Foo { + @dec('foo', 'get foo', 'set foo') static accessor foo = 0 + @dec(bar, 'get [bar]', 'set [bar]') static accessor [bar] = 0 + @dec(baz, 'get ', 'set ') static accessor [baz] = 0 + } + Foo.foo = 321 + assertEq(() => Foo.foo, 321) + Foo[bar] = 4321 + assertEq(() => Foo[bar], 4321) + Foo[baz] = 54321 + assertEq(() => Foo[baz], 54321) + }, + 'Auto-accessor decorators: Basic (private instance auto-accessor)': () => { + let lateAsserts: () => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext) => { + assertEq(() => typeof target.get, 'function') + assertEq(() => typeof target.set, 'function') + assertEq(() => target.get.name, 'get #foo') + assertEq(() => target.set.name, 'set #foo') + assertEq(() => ctx.kind, 'accessor') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, false) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(new Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(new Foo), 0) + assertEq(() => { + const obj = new Foo + ctx.access.set(obj, 123) + return get$foo(obj) + }, 123) + } + } + let get$foo: (x: Foo) => number + let set$foo: (x: Foo, y: number) => void + class Foo { + @dec accessor #foo = 0 + static { + get$foo = x => x.#foo + set$foo = (x, y) => { x.#foo = y } + } + } + lateAsserts!() + var obj = new Foo + assertEq(() => set$foo(obj, 321), undefined) + assertEq(() => get$foo(obj), 321) + }, + 'Auto-accessor decorators: Basic (private static auto-accessor)': () => { + let lateAsserts: () => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext) => { + assertEq(() => typeof target.get, 'function') + assertEq(() => typeof target.set, 'function') + assertEq(() => target.get.name, 'get #foo') + assertEq(() => target.set.name, 'set #foo') + assertEq(() => ctx.kind, 'accessor') + assertEq(() => ctx.name, '#foo') + assertEq(() => ctx.static, true) + assertEq(() => ctx.private, true) + lateAsserts = () => { + assertEq(() => ctx.access.has(Foo), true) + assertEq(() => ctx.access.has({}), false) + assertEq(() => ctx.access.get(Foo), 0) + assertEq(() => { + ctx.access.set(Foo, 123) + return get$foo(Foo) + }, 123) + } + } + let get$foo: (x: typeof Foo) => number + let set$foo: (x: typeof Foo, y: number) => void + class Foo { + @dec static accessor #foo = 0 + static { + get$foo = x => x.#foo + set$foo = (x, y) => { x.#foo = y } + } + } + lateAsserts!() + assertEq(() => set$foo(Foo, 321), undefined) + assertEq(() => get$foo(Foo), 321) + }, + 'Auto-accessor decorators: Shim (instance auto-accessor)': () => { + let get: (this: Foo) => number + let set: (this: Foo, x: number) => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): ClassAccessorDecoratorResult => { + const init = (x: number) => x + 1 + get = function () { return target.get.call(this) * 10 } + set = function (x) { target.set.call(this, x * 2) } + return { get, set, init } + } + class Foo { + @dec accessor foo = 123 + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.get, get!) + assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo')!.set, set!) + var obj = new Foo + assertEq(() => obj.foo, (123 + 1) * 10) + obj.foo = 321 + assertEq(() => obj.foo, (321 * 2) * 10) + }, + 'Auto-accessor decorators: Shim (static auto-accessor)': () => { + let get: (this: typeof Foo) => number + let set: (this: typeof Foo, x: number) => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): ClassAccessorDecoratorResult => { + const init = (x: number) => x + 1 + get = function () { return target.get.call(this) * 10 } + set = function (x) { target.set.call(this, x * 2) } + return { get, set, init } + } + class Foo { + @dec static accessor foo = 123 + } + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.get, get!) + assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo')!.set, set!) + assertEq(() => Foo.foo, (123 + 1) * 10) + Foo.foo = 321 + assertEq(() => Foo.foo, (321 * 2) * 10) + }, + 'Auto-accessor decorators: Shim (private instance auto-accessor)': () => { + let get: (this: Foo) => number + let set: (this: Foo, x: number) => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): ClassAccessorDecoratorResult => { + const init = (x: number) => x + 1 + get = function () { return target.get.call(this) * 10 } + set = function (x) { target.set.call(this, x * 2) } + return { get, set, init } + } + let get$foo: (x: Foo) => number + let set$foo: (x: Foo, y: number) => void + class Foo { + @dec accessor #foo = 123 + static { + get$foo = x => x.#foo + set$foo = (x, y) => { x.#foo = y } + } + } + var obj = new Foo + assertEq(() => get$foo(obj), (123 + 1) * 10) + assertEq(() => set$foo(obj, 321), undefined) + assertEq(() => get$foo(obj), (321 * 2) * 10) + }, + 'Auto-accessor decorators: Shim (private static auto-accessor)': () => { + let get: (this: typeof Foo) => number + let set: (this: typeof Foo, x: number) => void + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): ClassAccessorDecoratorResult => { + const init = (x: number) => x + 1 + get = function () { return target.get.call(this) * 10 } + set = function (x) { target.set.call(this, x * 2) } + return { get, set, init } + } + let get$foo: (x: typeof Foo) => number + let set$foo: (x: typeof Foo, y: number) => void + class Foo { + @dec static accessor #foo = 123 + static { + get$foo = x => x.#foo + set$foo = (x, y) => { x.#foo = y } + } + } + assertEq(() => get$foo(Foo), (123 + 1) * 10) + assertEq(() => set$foo(Foo, 321), undefined) + assertEq(() => get$foo(Foo), (321 * 2) * 10) + }, + 'Auto-accessor decorators: Return null (instance auto-accessor)': () => { + assertThrows(() => { + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + return null + } + class Foo { @dec accessor foo: undefined } + }, TypeError) + }, + 'Auto-accessor decorators: Return null (static auto-accessor)': () => { + assertThrows(() => { + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + return null + } + class Foo { @dec static accessor foo: undefined } + }, TypeError) + }, + 'Auto-accessor decorators: Return null (private instance auto-accessor)': () => { + assertThrows(() => { + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + return null + } + class Foo { @dec accessor #foo: undefined } + }, TypeError) + }, + 'Auto-accessor decorators: Return null (private static auto-accessor)': () => { + assertThrows(() => { + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + return null + } + class Foo { @dec static accessor #foo: undefined } + }, TypeError) + }, + 'Auto-accessor decorators: Extra initializer (instance auto-accessor)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec accessor foo: undefined } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Auto-accessor decorators: Extra initializer (static auto-accessor)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static accessor foo: undefined } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + 'Auto-accessor decorators: Extra initializer (private instance auto-accessor)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec accessor #foo: undefined } + assertEq(() => got, undefined) + const instance = new Foo + assertEq(() => got.this, instance) + assertEq(() => got.args.length, 0) + }, + 'Auto-accessor decorators: Extra initializer (private static auto-accessor)': () => { + let oldAddInitializer: DecoratorContext['addInitializer'] | null + let got: { this: any, args: any[] } + const dec = (target: ClassAccessorDecoratorTarget, ctx: ClassAccessorDecoratorContext): any => { + ctx.addInitializer(function (...args) { + got = { this: this, args } + }) + if (oldAddInitializer) assertThrows(() => oldAddInitializer!(() => { }), TypeError) + assertThrows(() => ctx.addInitializer({} as any), TypeError) + oldAddInitializer = ctx.addInitializer + } + class Foo { @dec @dec static accessor #foo: undefined } + assertEq(() => got.this, Foo) + assertEq(() => got.args.length, 0) + }, + + // Decorator list evaluation + 'Decorator list evaluation: Computed names (class statement)': () => { + const log: number[] = [] + const foo = (n: number): Function => { + log.push(n) + return () => { } + } + + const computed: { + readonly method: unique symbol, + readonly field: unique symbol, + readonly getter: unique symbol, + readonly setter: unique symbol, + readonly accessor: unique symbol, + } = { + get method() { log.push(log.length); return Symbol('method') }, + get field() { log.push(log.length); return Symbol('field') }, + get getter() { log.push(log.length); return Symbol('getter') }, + get setter() { log.push(log.length); return Symbol('setter') }, + get accessor() { log.push(log.length); return Symbol('accessor') }, + } as any + + @foo(0) class Foo + extends (foo(1), Object) + { + @foo(2) [computed.method]() { } + @foo(4) static [computed.method]() { } + + @foo(6) [computed.field]: undefined + @foo(8) static [computed.field]: undefined + + @foo(10) get [computed.getter](): undefined { return } + @foo(12) static get [computed.getter](): undefined { return } + + @foo(14) set [computed.setter](x: undefined) { } + @foo(16) static set [computed.setter](x: undefined) { } + + @foo(18) accessor [computed.accessor]: undefined + @foo(20) static accessor [computed.accessor]: undefined + } + + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21') + }, + 'Decorator list evaluation: Computed names (class expression)': () => { + const log: number[] = [] + const foo = (n: number): Function => { + log.push(n) + return () => { } + } + + const computed: { + readonly method: unique symbol, + readonly field: unique symbol, + readonly getter: unique symbol, + readonly setter: unique symbol, + readonly accessor: unique symbol, + } = { + get method() { log.push(log.length); return Symbol('method') }, + get field() { log.push(log.length); return Symbol('field') }, + get getter() { log.push(log.length); return Symbol('getter') }, + get setter() { log.push(log.length); return Symbol('setter') }, + get accessor() { log.push(log.length); return Symbol('accessor') }, + } as any + + (@foo(0) class + extends (foo(1), Object) + { + @foo(2) [computed.method]() { } + @foo(4) static [computed.method]() { } + + @foo(6) [computed.field]: undefined + @foo(8) static [computed.field]: undefined + + @foo(10) get [computed.getter](): undefined { return } + @foo(12) static get [computed.getter](): undefined { return } + + @foo(14) set [computed.setter](x: undefined) { } + @foo(16) static set [computed.setter](x: undefined) { } + + @foo(18) accessor [computed.accessor]: undefined + @foo(20) static accessor [computed.accessor]: undefined + }) + + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21') + }, + 'Decorator list evaluation: "this" (class statement)': () => { + const log: number[] = [] + const dummy: Function = () => { } + const ctx = { + foo(n: number) { + log.push(n) + } + } + + function wrapper(this: typeof ctx) { + @(assertEq(() => this.foo(0), undefined), dummy) class Foo + extends (assertEq(() => this.foo(1), undefined), Object) + { + @(assertEq(() => this.foo(2), undefined), dummy) method() { } + @(assertEq(() => this.foo(3), undefined), dummy) static method() { } + + @(assertEq(() => this.foo(4), undefined), dummy) field: undefined + @(assertEq(() => this.foo(5), undefined), dummy) static field: undefined + + @(assertEq(() => this.foo(6), undefined), dummy) get getter(): undefined { return } + @(assertEq(() => this.foo(7), undefined), dummy) static get getter(): undefined { return } + + @(assertEq(() => this.foo(8), undefined), dummy) set setter(x: undefined) { } + @(assertEq(() => this.foo(9), undefined), dummy) static set setter(x: undefined) { } + + @(assertEq(() => this.foo(10), undefined), dummy) accessor accessor: undefined + @(assertEq(() => this.foo(11), undefined), dummy) static accessor accessor: undefined + } + } + + wrapper.call(ctx) + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: "this" (class expression)': () => { + const log: number[] = [] + const dummy: Function = () => { } + const ctx = { + foo(n: number) { + log.push(n) + } + } + + function wrapper(this: typeof ctx) { + (@(assertEq(() => this.foo(0), undefined), dummy) class + extends (assertEq(() => this.foo(1), undefined), Object) + { + @(assertEq(() => this.foo(2), undefined), dummy) method() { } + @(assertEq(() => this.foo(3), undefined), dummy) static method() { } + + @(assertEq(() => this.foo(4), undefined), dummy) field: undefined + @(assertEq(() => this.foo(5), undefined), dummy) static field: undefined + + @(assertEq(() => this.foo(6), undefined), dummy) get getter(): undefined { return } + @(assertEq(() => this.foo(7), undefined), dummy) static get getter(): undefined { return } + + @(assertEq(() => this.foo(8), undefined), dummy) set setter(x: undefined) { } + @(assertEq(() => this.foo(9), undefined), dummy) static set setter(x: undefined) { } + + @(assertEq(() => this.foo(10), undefined), dummy) accessor accessor: undefined + @(assertEq(() => this.foo(11), undefined), dummy) static accessor accessor: undefined + }) + } + + wrapper.call(ctx) + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: "await" (class statement)': async () => { + const log: number[] = [] + const dummy: Function = () => { } + + async function wrapper() { + @(log.push(await Promise.resolve(0)), dummy) class Foo + extends (log.push(await Promise.resolve(1)), Object) + { + @(log.push(await Promise.resolve(2)), dummy) method() { } + @(log.push(await Promise.resolve(3)), dummy) static method() { } + + @(log.push(await Promise.resolve(4)), dummy) field: undefined + @(log.push(await Promise.resolve(5)), dummy) static field: undefined + + @(log.push(await Promise.resolve(6)), dummy) get getter(): undefined { return } + @(log.push(await Promise.resolve(7)), dummy) static get getter(): undefined { return } + + @(log.push(await Promise.resolve(8)), dummy) set setter(x: undefined) { } + @(log.push(await Promise.resolve(9)), dummy) static set setter(x: undefined) { } + + @(log.push(await Promise.resolve(10)), dummy) accessor accessor: undefined + @(log.push(await Promise.resolve(11)), dummy) static accessor accessor: undefined + } + } + + await wrapper() + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: "await" (class expression)': async () => { + const log: number[] = [] + const dummy: Function = () => { } + + async function wrapper() { + (@(log.push(await Promise.resolve(0)), dummy) class + extends (log.push(await Promise.resolve(1)), Object) + { + @(log.push(await Promise.resolve(2)), dummy) method() { } + @(log.push(await Promise.resolve(3)), dummy) static method() { } + + @(log.push(await Promise.resolve(4)), dummy) field: undefined + @(log.push(await Promise.resolve(5)), dummy) static field: undefined + + @(log.push(await Promise.resolve(6)), dummy) get getter(): undefined { return } + @(log.push(await Promise.resolve(7)), dummy) static get getter(): undefined { return } + + @(log.push(await Promise.resolve(8)), dummy) set setter(x: undefined) { } + @(log.push(await Promise.resolve(9)), dummy) static set setter(x: undefined) { } + + @(log.push(await Promise.resolve(10)), dummy) accessor accessor: undefined + @(log.push(await Promise.resolve(11)), dummy) static accessor accessor: undefined + }) + } + + await wrapper() + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: Outer private name (class statement)': () => { + const log: number[] = [] + + class Dummy { + static #foo(n: number): Function { + log.push(n) + return () => { } + } + + static { + const dummy = this + @(dummy.#foo(0)) class Foo + extends (dummy.#foo(1), Object) + { + @(dummy.#foo(2)) method() { } + @(dummy.#foo(3)) static method() { } + + @(dummy.#foo(4)) field: undefined + @(dummy.#foo(5)) static field: undefined + + @(dummy.#foo(6)) get getter(): undefined { return } + @(dummy.#foo(7)) static get getter(): undefined { return } + + @(dummy.#foo(8)) set setter(x: undefined) { } + @(dummy.#foo(9)) static set setter(x: undefined) { } + + @(dummy.#foo(10)) accessor accessor: undefined + @(dummy.#foo(11)) static accessor accessor: undefined + } + } + } + + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: Outer private name (class expression)': () => { + const log: number[] = [] + + class Dummy { + static #foo(n: number): Function { + log.push(n) + return () => { } + } + + static { + const dummy = this; + (@(dummy.#foo(0)) class + extends (dummy.#foo(1), Object) + { + @(dummy.#foo(2)) method() { } + @(dummy.#foo(3)) static method() { } + + @(dummy.#foo(4)) field: undefined + @(dummy.#foo(5)) static field: undefined + + @(dummy.#foo(6)) get getter(): undefined { return } + @(dummy.#foo(7)) static get getter(): undefined { return } + + @(dummy.#foo(8)) set setter(x: undefined) { } + @(dummy.#foo(9)) static set setter(x: undefined) { } + + @(dummy.#foo(10)) accessor accessor: undefined + @(dummy.#foo(11)) static accessor accessor: undefined + }) + } + } + + assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11') + }, + 'Decorator list evaluation: Inner private name (class statement)': () => { + const fns: (() => number)[] = [] + const capture = (fn: () => number): Function => { + fns.push(fn) + return () => { } + } + + class Dummy { + static #foo = NaN + + static { + @(capture(() => (new Foo() as any).#foo + 0)) + class Foo { + #foo = 10 + + @(capture(() => new Foo().#foo + 1)) method() { } + @(capture(() => new Foo().#foo + 2)) static method() { } + + @(capture(() => new Foo().#foo + 3)) field: undefined + @(capture(() => new Foo().#foo + 4)) static field: undefined + + @(capture(() => new Foo().#foo + 5)) get getter(): undefined { return } + @(capture(() => new Foo().#foo + 6)) static get getter(): undefined { return } + + @(capture(() => new Foo().#foo + 7)) set setter(x: undefined) { } + @(capture(() => new Foo().#foo + 8)) static set setter(x: undefined) { } + + @(capture(() => new Foo().#foo + 9)) accessor accessor: undefined + @(capture(() => new Foo().#foo + 10)) static accessor accessor: undefined + } + } + } + + // Accessing "#foo" in the class decorator should fail. The "#foo" should + // refer to the outer "#foo", not the inner "#foo". + const firstFn = fns.shift()! + assertEq(() => { + try { + firstFn() + throw new Error('Expected a TypeError to be thrown') + } catch (err) { + if (err instanceof TypeError) return true + throw err + } + }, true) + + // Accessing "#foo" from any of the class element decorators should succeed. + // Each "#foo" should refer to the inner "#foo", not the outer "#foo". + const log: number[] = [] + for (const fn of fns) log.push(fn()) + assertEq(() => '' + log, '11,12,13,14,15,16,17,18,19,20') + }, + 'Decorator list evaluation: Inner private name (class expression)': () => { + const fns: (() => number)[] = [] + const capture = (fn: () => number): Function => { + fns.push(fn) + return () => { } + } + + class Outer { + static #foo = 0 + + static { + (@(capture(() => Outer.#foo + 0)) + class Foo { + #foo = 10 + + @(capture(() => new Foo().#foo + 1)) method() { } + @(capture(() => new Foo().#foo + 2)) static method() { } + + @(capture(() => new Foo().#foo + 3)) field: undefined + @(capture(() => new Foo().#foo + 4)) static field: undefined + + @(capture(() => new Foo().#foo + 5)) get getter(): undefined { return } + @(capture(() => new Foo().#foo + 6)) static get getter(): undefined { return } + + @(capture(() => new Foo().#foo + 7)) set setter(x: undefined) { } + @(capture(() => new Foo().#foo + 8)) static set setter(x: undefined) { } + + @(capture(() => new Foo().#foo + 9)) accessor accessor: undefined + @(capture(() => new Foo().#foo + 10)) static accessor accessor: undefined + }) + } + } + + // Accessing the outer "#foo" on "Outer" from within the class decorator + // should succeed. Class decorators are evaluated in the outer private + // environment before entering "ClassDefinitionEvaluation". + // + // Accessing the inner "#foo" on "Foo" from within any of the class element + // decorators should also succeed. Class element decorators are evaluated + // in the inner private environment inside "ClassDefinitionEvaluation". + const log: number[] = [] + for (const fn of fns) log.push(fn()) + assertEq(() => '' + log, '0,11,12,13,14,15,16,17,18,19,20') + }, + 'Decorator list evaluation: Class binding (class statement)': () => { + const fns: (() => typeof Foo)[] = [] + + const capture = (fn: () => typeof Foo): Function => { + fns.push(fn) + + // Note: As far as I can tell, early reference to the class name should + // throw a reference error because: + // + // 1. Class decorators run first in the top-level scope before entering + // BindingClassDeclarationEvaluation. + // + // 2. Class element decorators run in ClassDefinitionEvaluation, which + // runs ClassElementEvaluation for each class element before eventually + // running classEnv.InitializeBinding(classBinding, F). + // + assertThrows(() => fn(), ReferenceError) + return () => { } + } + + @(capture(() => Foo)) class Foo { + @(capture(() => Foo)) method() { } + @(capture(() => Foo)) static method() { } + + @(capture(() => Foo)) field: undefined + @(capture(() => Foo)) static field: undefined + + @(capture(() => Foo)) get getter(): undefined { return } + @(capture(() => Foo)) static get getter(): undefined { return } + + @(capture(() => Foo)) set setter(x: undefined) { } + @(capture(() => Foo)) static set setter(x: undefined) { } + + @(capture(() => Foo)) accessor accessor: undefined + @(capture(() => Foo)) static accessor accessor: undefined + } + + const originalFoo = Foo + + // Once we get here, these should all reference the now-initialized class, + // either through classBinding (for class element decorators) or through + // className (for decorators on the class itself). + for (const fn of fns) { + assertEq(() => fn(), originalFoo) + } + + // Mutating a class binding is allowed in JavaScript. Let's test what + // happens when we do this. + (Foo as any) = null as any + + // As far as I can tell, class decorators should observe this change because + // they are evaluated in the top-level scope. + const firstFn = fns.shift()! + assertEq(() => firstFn(), null) + + // But I believe class element decorators should not observe this change + // because they are evaluated in the environment that exists for the + // duration of ClassDefinitionEvaluation (i.e. classEnv, not env). + for (const fn of fns) { + assertEq(() => fn(), originalFoo) + } + }, + 'Decorator list evaluation: Class binding (class expression)': () => { + const fns: (() => { new(): Object })[] = [] + + const capture = (fn: () => { new(): Object }): Function => { + fns.push(fn) + return () => { } + } + + const originalFoo = (@(capture(() => Foo)) class Foo { + @(capture(() => Foo)) method() { } + @(capture(() => Foo)) static method() { } + + @(capture(() => Foo)) field: undefined + @(capture(() => Foo)) static field: undefined + + @(capture(() => Foo)) get getter(): undefined { return } + @(capture(() => Foo)) static get getter(): undefined { return } + + @(capture(() => Foo)) set setter(x: undefined) { } + @(capture(() => Foo)) static set setter(x: undefined) { } + + @(capture(() => Foo)) accessor accessor: undefined + @(capture(() => Foo)) static accessor accessor: undefined + }) + + // Decorators on the class itself should reference a global called "Foo", + // which should still be a reference error. This is because a class + // expression runs "DecoratorListEvaluation" in the outer environment and + // then passes the evaluated decorators to "ClassDefinitionEvaluation". + const firstFn = fns.shift()! + assertThrows(() => firstFn(), ReferenceError) + + // All other decorators should reference the classBinding called "Foo", + // which should now be initialized. This is because all other decorators + // are evaluated within "ClassDefinitionEvaluation" while the running + // execution context's environment is the nested class environment. + for (const fn of fns) { + assertEq(() => fn(), originalFoo) + } + }, + + // Initializer order + 'Initializer order (public members, class statement)': () => { + const log: string[] = [] + + // Class decorators + const classDec1 = (cls: { new(): Foo }, ctxClass: ClassDecoratorContext) => { + log.push('c2') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c5')) + ctxClass.addInitializer(() => log.push('c6')) + } + const classDec2 = (cls: { new(): Foo }, ctxClass: ClassDecoratorContext) => { + log.push('c1') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c3')) + ctxClass.addInitializer(() => log.push('c4')) + } + + // Method decorators + const methodDec1 = (fn: (this: Foo) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m2') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m5')) + ctxMethod.addInitializer(() => log.push('m6')) + } + const methodDec2 = (fn: (this: Foo) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m1') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m3')) + ctxMethod.addInitializer(() => log.push('m4')) + } + const staticMethodDec1 = (fn: (this: Foo) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M2') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M5')) + ctxStaticMethod.addInitializer(() => log.push('M6')) + } + const staticMethodDec2 = (fn: (this: Foo) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M1') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M3')) + ctxStaticMethod.addInitializer(() => log.push('M4')) + } + + // Field decorators + const fieldDec1 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Foo, value: undefined) => undefined) | undefined => { + log.push('f2') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f5')) + ctxField.addInitializer(() => log.push('f6')) + return () => { log.push('f7') } + } + const fieldDec2 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Foo, value: undefined) => undefined) | undefined => { + log.push('f1') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f3')) + ctxField.addInitializer(() => log.push('f4')) + return () => { log.push('f8') } + } + const staticFieldDec1 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: typeof Foo, value: undefined) => undefined) | undefined => { + log.push('F2') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F5')) + ctxStaticField.addInitializer(() => log.push('F6')) + return () => { log.push('F7') } + } + const staticFieldDec2 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: typeof Foo, value: undefined) => undefined) | undefined => { + log.push('F1') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F3')) + ctxStaticField.addInitializer(() => log.push('F4')) + return () => { log.push('F8') } + } + + // Getter decorators + const getterDec1 = (fn: (this: Foo) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g2') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g5')) + ctxGetter.addInitializer(() => log.push('g6')) + } + const getterDec2 = (fn: (this: Foo) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g1') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g3')) + ctxGetter.addInitializer(() => log.push('g4')) + } + const staticGetterDec1 = (fn: (this: Foo) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G2') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G5')) + ctxStaticGetter.addInitializer(() => log.push('G6')) + } + const staticGetterDec2 = (fn: (this: Foo) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G1') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G3')) + ctxStaticGetter.addInitializer(() => log.push('G4')) + } + + // Setter decorators + const setterDec1 = (fn: (this: Foo, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s2') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s5')) + ctxSetter.addInitializer(() => log.push('s6')) + } + const setterDec2 = (fn: (this: Foo, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s1') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s3')) + ctxSetter.addInitializer(() => log.push('s4')) + } + const staticSetterDec1 = (fn: (this: Foo, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S2') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S5')) + ctxStaticSetter.addInitializer(() => log.push('S6')) + } + const staticSetterDec2 = (fn: (this: Foo, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S1') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S3')) + ctxStaticSetter.addInitializer(() => log.push('S4')) + } + + // Auto-accessor decorators + const accessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a2') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a5')) + ctxAccessor.addInitializer(() => log.push('a6')) + return { init() { log.push('a7') } } + } + const accessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a1') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a3')) + ctxAccessor.addInitializer(() => log.push('a4')) + return { init() { log.push('a8') } } + } + const staticAccessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A2') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A5')) + ctxStaticAccessor.addInitializer(() => log.push('A6')) + return { init() { log.push('A7') } } + } + const staticAccessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A1') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A3')) + ctxStaticAccessor.addInitializer(() => log.push('A4')) + return { init() { log.push('A8') } } + } + + log.push('start') + @classDec1 @classDec2 class Foo extends (log.push('extends'), Object) { + static { log.push('static:start') } + + constructor() { + log.push('ctor:start') + super() + log.push('ctor:end') + } + + @methodDec1 @methodDec2 method() { } + @staticMethodDec1 @staticMethodDec2 static method() { } + + @fieldDec1 @fieldDec2 field: undefined + @staticFieldDec1 @staticFieldDec2 static field: undefined + + @getterDec1 @getterDec2 get getter(): undefined { return } + @staticGetterDec1 @staticGetterDec2 static get getter(): undefined { return } + + @setterDec1 @setterDec2 set setter(x: undefined) { } + @staticSetterDec1 @staticSetterDec2 static set setter(x: undefined) { } + + @accessorDec1 @accessorDec2 accessor accessor: undefined + @staticAccessorDec1 @staticAccessorDec2 static accessor accessor: undefined + + static { log.push('static:end') } + } + log.push('after') + new Foo + log.push('end') + assertEq(() => log + '', 'start,extends,' + + 'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field + 'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field + 'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field + 'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field + 'c1,c2,' + // ApplyDecoratorsToClassDefinition + 'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers + 'static:start,' + // For each element elementRecord of staticElements + 'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'static:end,' + // For each element elementRecord of staticElements + 'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers + 'after,' + + 'ctor:start,' + + 'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers) + 'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'ctor:end,' + + 'end') + }, + 'Initializer order (public members, class expression)': () => { + const log: string[] = [] + + // Class decorators + const classDec1 = (cls: { new(): Object }, ctxClass: ClassDecoratorContext) => { + log.push('c2') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c5')) + ctxClass.addInitializer(() => log.push('c6')) + } + const classDec2 = (cls: { new(): Object }, ctxClass: ClassDecoratorContext) => { + log.push('c1') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c3')) + ctxClass.addInitializer(() => log.push('c4')) + } + + // Method decorators + const methodDec1 = (fn: (this: Object) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m2') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m5')) + ctxMethod.addInitializer(() => log.push('m6')) + } + const methodDec2 = (fn: (this: Object) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m1') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m3')) + ctxMethod.addInitializer(() => log.push('m4')) + } + const staticMethodDec1 = (fn: (this: Object) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M2') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M5')) + ctxStaticMethod.addInitializer(() => log.push('M6')) + } + const staticMethodDec2 = (fn: (this: Object) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M1') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M3')) + ctxStaticMethod.addInitializer(() => log.push('M4')) + } + + // Field decorators + const fieldDec1 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('f2') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f5')) + ctxField.addInitializer(() => log.push('f6')) + return () => { log.push('f7') } + } + const fieldDec2 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('f1') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f3')) + ctxField.addInitializer(() => log.push('f4')) + return () => { log.push('f8') } + } + const staticFieldDec1 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('F2') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F5')) + ctxStaticField.addInitializer(() => log.push('F6')) + return () => { log.push('F7') } + } + const staticFieldDec2 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('F1') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F3')) + ctxStaticField.addInitializer(() => log.push('F4')) + return () => { log.push('F8') } + } + + // Getter decorators + const getterDec1 = (fn: (this: Object) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g2') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g5')) + ctxGetter.addInitializer(() => log.push('g6')) + } + const getterDec2 = (fn: (this: Object) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g1') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g3')) + ctxGetter.addInitializer(() => log.push('g4')) + } + const staticGetterDec1 = (fn: (this: Object) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G2') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G5')) + ctxStaticGetter.addInitializer(() => log.push('G6')) + } + const staticGetterDec2 = (fn: (this: Object) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G1') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G3')) + ctxStaticGetter.addInitializer(() => log.push('G4')) + } + + // Setter decorators + const setterDec1 = (fn: (this: Object, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s2') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s5')) + ctxSetter.addInitializer(() => log.push('s6')) + } + const setterDec2 = (fn: (this: Object, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s1') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s3')) + ctxSetter.addInitializer(() => log.push('s4')) + } + const staticSetterDec1 = (fn: (this: Object, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S2') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S5')) + ctxStaticSetter.addInitializer(() => log.push('S6')) + } + const staticSetterDec2 = (fn: (this: Object, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S1') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S3')) + ctxStaticSetter.addInitializer(() => log.push('S4')) + } + + // Auto-accessor decorators + const accessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a2') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a5')) + ctxAccessor.addInitializer(() => log.push('a6')) + return { init() { log.push('a7') } } + } + const accessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a1') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a3')) + ctxAccessor.addInitializer(() => log.push('a4')) + return { init() { log.push('a8') } } + } + const staticAccessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A2') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A5')) + ctxStaticAccessor.addInitializer(() => log.push('A6')) + return { init() { log.push('A7') } } + } + const staticAccessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A1') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A3')) + ctxStaticAccessor.addInitializer(() => log.push('A4')) + return { init() { log.push('A8') } } + } + + log.push('start') + const Foo = @classDec1 @classDec2 class extends (log.push('extends'), Object) { + static { log.push('static:start') } + + constructor() { + log.push('ctor:start') + super() + log.push('ctor:end') + } + + @methodDec1 @methodDec2 method() { } + @staticMethodDec1 @staticMethodDec2 static method() { } + + @fieldDec1 @fieldDec2 field: undefined + @staticFieldDec1 @staticFieldDec2 static field: undefined + + @getterDec1 @getterDec2 get getter(): undefined { return } + @staticGetterDec1 @staticGetterDec2 static get getter(): undefined { return } + + @setterDec1 @setterDec2 set setter(x: undefined) { } + @staticSetterDec1 @staticSetterDec2 static set setter(x: undefined) { } + + @accessorDec1 @accessorDec2 accessor accessor: undefined + @staticAccessorDec1 @staticAccessorDec2 static accessor accessor: undefined + + static { log.push('static:end') } + } + log.push('after') + new Foo + log.push('end') + assertEq(() => log + '', 'start,extends,' + + 'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field + 'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field + 'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field + 'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field + 'c1,c2,' + // ApplyDecoratorsToClassDefinition + 'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers + 'static:start,' + // For each element elementRecord of staticElements + 'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'static:end,' + // For each element elementRecord of staticElements + 'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers + 'after,' + + 'ctor:start,' + + 'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers) + 'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'ctor:end,' + + 'end') + }, + 'Initializer order (private members, class statement)': () => { + const log: string[] = [] + + // Class decorators + const classDec1 = (cls: { new(): Foo }, ctxClass: ClassDecoratorContext) => { + log.push('c2') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c5')) + ctxClass.addInitializer(() => log.push('c6')) + } + const classDec2 = (cls: { new(): Foo }, ctxClass: ClassDecoratorContext) => { + log.push('c1') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c3')) + ctxClass.addInitializer(() => log.push('c4')) + } + + // Method decorators + const methodDec1 = (fn: (this: Foo) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m2') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m5')) + ctxMethod.addInitializer(() => log.push('m6')) + } + const methodDec2 = (fn: (this: Foo) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m1') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m3')) + ctxMethod.addInitializer(() => log.push('m4')) + } + const staticMethodDec1 = (fn: (this: Foo) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M2') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M5')) + ctxStaticMethod.addInitializer(() => log.push('M6')) + } + const staticMethodDec2 = (fn: (this: Foo) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M1') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M3')) + ctxStaticMethod.addInitializer(() => log.push('M4')) + } + + // Field decorators + const fieldDec1 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Foo, value: undefined) => undefined) | undefined => { + log.push('f2') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f5')) + ctxField.addInitializer(() => log.push('f6')) + return () => { log.push('f7') } + } + const fieldDec2 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Foo, value: undefined) => undefined) | undefined => { + log.push('f1') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f3')) + ctxField.addInitializer(() => log.push('f4')) + return () => { log.push('f8') } + } + const staticFieldDec1 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: typeof Foo, value: undefined) => undefined) | undefined => { + log.push('F2') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F5')) + ctxStaticField.addInitializer(() => log.push('F6')) + return () => { log.push('F7') } + } + const staticFieldDec2 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: typeof Foo, value: undefined) => undefined) | undefined => { + log.push('F1') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F3')) + ctxStaticField.addInitializer(() => log.push('F4')) + return () => { log.push('F8') } + } + + // Getter decorators + const getterDec1 = (fn: (this: Foo) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g2') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g5')) + ctxGetter.addInitializer(() => log.push('g6')) + } + const getterDec2 = (fn: (this: Foo) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g1') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g3')) + ctxGetter.addInitializer(() => log.push('g4')) + } + const staticGetterDec1 = (fn: (this: Foo) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G2') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G5')) + ctxStaticGetter.addInitializer(() => log.push('G6')) + } + const staticGetterDec2 = (fn: (this: Foo) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G1') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G3')) + ctxStaticGetter.addInitializer(() => log.push('G4')) + } + + // Setter decorators + const setterDec1 = (fn: (this: Foo, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s2') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s5')) + ctxSetter.addInitializer(() => log.push('s6')) + } + const setterDec2 = (fn: (this: Foo, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s1') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s3')) + ctxSetter.addInitializer(() => log.push('s4')) + } + const staticSetterDec1 = (fn: (this: Foo, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S2') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S5')) + ctxStaticSetter.addInitializer(() => log.push('S6')) + } + const staticSetterDec2 = (fn: (this: Foo, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S1') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S3')) + ctxStaticSetter.addInitializer(() => log.push('S4')) + } + + // Auto-accessor decorators + const accessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a2') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a5')) + ctxAccessor.addInitializer(() => log.push('a6')) + return { init() { log.push('a7') } } + } + const accessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a1') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a3')) + ctxAccessor.addInitializer(() => log.push('a4')) + return { init() { log.push('a8') } } + } + const staticAccessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A2') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A5')) + ctxStaticAccessor.addInitializer(() => log.push('A6')) + return { init() { log.push('A7') } } + } + const staticAccessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A1') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A3')) + ctxStaticAccessor.addInitializer(() => log.push('A4')) + return { init() { log.push('A8') } } + } + + log.push('start') + @classDec1 @classDec2 class Foo extends (log.push('extends'), Object) { + static { log.push('static:start') } + + constructor() { + log.push('ctor:start') + super() + log.push('ctor:end') + } + + @methodDec1 @methodDec2 #method() { } + @staticMethodDec1 @staticMethodDec2 static #staticMethod() { } + + @fieldDec1 @fieldDec2 #field: undefined + @staticFieldDec1 @staticFieldDec2 static #staticField: undefined + + @getterDec1 @getterDec2 get #getter(): undefined { return } + @staticGetterDec1 @staticGetterDec2 static get #staticGetter(): undefined { return } + + @setterDec1 @setterDec2 set #setter(x: undefined) { } + @staticSetterDec1 @staticSetterDec2 static set #staticSetter(x: undefined) { } + + @accessorDec1 @accessorDec2 accessor #accessor: undefined + @staticAccessorDec1 @staticAccessorDec2 static accessor #staticAccessor: undefined + + static { log.push('static:end') } + } + log.push('after') + new Foo + log.push('end') + assertEq(() => log + '', 'start,extends,' + + 'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field + 'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field + 'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field + 'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field + 'c1,c2,' + // ApplyDecoratorsToClassDefinition + 'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers + 'static:start,' + // For each element elementRecord of staticElements + 'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'static:end,' + // For each element elementRecord of staticElements + 'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers + 'after,' + + 'ctor:start,' + + 'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers) + 'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'ctor:end,' + + 'end') + }, + 'Initializer order (private members, class expression)': () => { + const log: string[] = [] + + // Class decorators + const classDec1 = (cls: { new(): Object }, ctxClass: ClassDecoratorContext) => { + log.push('c2') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c5')) + ctxClass.addInitializer(() => log.push('c6')) + } + const classDec2 = (cls: { new(): Object }, ctxClass: ClassDecoratorContext) => { + log.push('c1') + if (!assertEq(() => typeof ctxClass.addInitializer, 'function')) return + ctxClass.addInitializer(() => log.push('c3')) + ctxClass.addInitializer(() => log.push('c4')) + } + + // Method decorators + const methodDec1 = (fn: (this: Object) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m2') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m5')) + ctxMethod.addInitializer(() => log.push('m6')) + } + const methodDec2 = (fn: (this: Object) => void, ctxMethod: ClassMethodDecoratorContext) => { + log.push('m1') + if (!assertEq(() => typeof ctxMethod.addInitializer, 'function')) return + ctxMethod.addInitializer(() => log.push('m3')) + ctxMethod.addInitializer(() => log.push('m4')) + } + const staticMethodDec1 = (fn: (this: Object) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M2') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M5')) + ctxStaticMethod.addInitializer(() => log.push('M6')) + } + const staticMethodDec2 = (fn: (this: Object) => void, ctxStaticMethod: ClassMethodDecoratorContext) => { + log.push('M1') + if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function')) return + ctxStaticMethod.addInitializer(() => log.push('M3')) + ctxStaticMethod.addInitializer(() => log.push('M4')) + } + + // Field decorators + const fieldDec1 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('f2') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f5')) + ctxField.addInitializer(() => log.push('f6')) + return () => { log.push('f7') } + } + const fieldDec2 = ( + value: undefined, + ctxField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('f1') + if (!assertEq(() => typeof ctxField.addInitializer, 'function')) return + ctxField.addInitializer(() => log.push('f3')) + ctxField.addInitializer(() => log.push('f4')) + return () => { log.push('f8') } + } + const staticFieldDec1 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('F2') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F5')) + ctxStaticField.addInitializer(() => log.push('F6')) + return () => { log.push('F7') } + } + const staticFieldDec2 = ( + value: undefined, + ctxStaticField: ClassFieldDecoratorContext, + ): ((this: Object, value: undefined) => undefined) | undefined => { + log.push('F1') + if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function')) return + ctxStaticField.addInitializer(() => log.push('F3')) + ctxStaticField.addInitializer(() => log.push('F4')) + return () => { log.push('F8') } + } + + // Getter decorators + const getterDec1 = (fn: (this: Object) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g2') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g5')) + ctxGetter.addInitializer(() => log.push('g6')) + } + const getterDec2 = (fn: (this: Object) => undefined, ctxGetter: ClassGetterDecoratorContext) => { + log.push('g1') + if (!assertEq(() => typeof ctxGetter.addInitializer, 'function')) return + ctxGetter.addInitializer(() => log.push('g3')) + ctxGetter.addInitializer(() => log.push('g4')) + } + const staticGetterDec1 = (fn: (this: Object) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G2') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G5')) + ctxStaticGetter.addInitializer(() => log.push('G6')) + } + const staticGetterDec2 = (fn: (this: Object) => undefined, ctxStaticGetter: ClassGetterDecoratorContext) => { + log.push('G1') + if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function')) return + ctxStaticGetter.addInitializer(() => log.push('G3')) + ctxStaticGetter.addInitializer(() => log.push('G4')) + } + + // Setter decorators + const setterDec1 = (fn: (this: Object, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s2') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s5')) + ctxSetter.addInitializer(() => log.push('s6')) + } + const setterDec2 = (fn: (this: Object, x: undefined) => void, ctxSetter: ClassSetterDecoratorContext) => { + log.push('s1') + if (!assertEq(() => typeof ctxSetter.addInitializer, 'function')) return + ctxSetter.addInitializer(() => log.push('s3')) + ctxSetter.addInitializer(() => log.push('s4')) + } + const staticSetterDec1 = (fn: (this: Object, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S2') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S5')) + ctxStaticSetter.addInitializer(() => log.push('S6')) + } + const staticSetterDec2 = (fn: (this: Object, x: undefined) => void, ctxStaticSetter: ClassSetterDecoratorContext) => { + log.push('S1') + if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function')) return + ctxStaticSetter.addInitializer(() => log.push('S3')) + ctxStaticSetter.addInitializer(() => log.push('S4')) + } + + // Auto-accessor decorators + const accessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a2') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a5')) + ctxAccessor.addInitializer(() => log.push('a6')) + return { init() { log.push('a7') } } + } + const accessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('a1') + if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function')) return + ctxAccessor.addInitializer(() => log.push('a3')) + ctxAccessor.addInitializer(() => log.push('a4')) + return { init() { log.push('a8') } } + } + const staticAccessorDec1 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A2') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A5')) + ctxStaticAccessor.addInitializer(() => log.push('A6')) + return { init() { log.push('A7') } } + } + const staticAccessorDec2 = ( + target: ClassAccessorDecoratorTarget, + ctxStaticAccessor: ClassAccessorDecoratorContext, + ): ClassAccessorDecoratorResult | undefined => { + log.push('A1') + if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function')) return + ctxStaticAccessor.addInitializer(() => log.push('A3')) + ctxStaticAccessor.addInitializer(() => log.push('A4')) + return { init() { log.push('A8') } } + } + + log.push('start') + const Foo = @classDec1 @classDec2 class extends (log.push('extends'), Object) { + static { log.push('static:start') } + + constructor() { + log.push('ctor:start') + super() + log.push('ctor:end') + } + + @methodDec1 @methodDec2 #method() { } + @staticMethodDec1 @staticMethodDec2 static #staticMethod() { } + + @fieldDec1 @fieldDec2 #field: undefined + @staticFieldDec1 @staticFieldDec2 static #staticField: undefined + + @getterDec1 @getterDec2 get #getter(): undefined { return } + @staticGetterDec1 @staticGetterDec2 static get #staticGetter(): undefined { return } + + @setterDec1 @setterDec2 set #setter(x: undefined) { } + @staticSetterDec1 @staticSetterDec2 static set #staticSetter(x: undefined) { } + + @accessorDec1 @accessorDec2 accessor #accessor: undefined + @staticAccessorDec1 @staticAccessorDec2 static accessor #staticAccessor: undefined + + static { log.push('static:end') } + } + log.push('after') + new Foo + log.push('end') + assertEq(() => log + '', 'start,extends,' + + 'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field + 'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field + 'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field + 'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field + 'c1,c2,' + // ApplyDecoratorsToClassDefinition + 'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers + 'static:start,' + // For each element elementRecord of staticElements + 'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'static:end,' + // For each element elementRecord of staticElements + 'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers + 'after,' + + 'ctor:start,' + + 'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers) + 'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]] + 'ctor:end,' + + 'end') + }, +} + +function prettyPrint(x: any): any { + if (x && x.prototype && x.prototype.constructor === x) return 'class' + if (typeof x === 'string') return JSON.stringify(x) + return x +} + +function assertEq(callback: () => T, expected: T): boolean { + let details: string + try { + let x: any = callback() + if (x === expected) return true + details = ` Expected: ${prettyPrint(expected)}\n Observed: ${prettyPrint(x)}` + } catch (error) { + details = ` Throws: ${error}` + } + + const code = callback.toString().replace(/^\(\) => /, '').replace(/\s+/g, ' ') + console.log(`❌ ${testName}\n Code: ${code}\n${details}\n`) + failures++ + return false +} + +function assertThrows(callback: () => void, expected: T): boolean { + let details: string + try { + let x: any = callback() + details = ` Expected: throws instanceof ${expected.name}\n Observed: returns ${prettyPrint(x)}` + } catch (error) { + if (error instanceof expected) return true + details = ` Expected: throws instanceof ${expected.name}\n Observed: throws ${error}` + } + + const code = callback.toString().replace(/^\(\) => /, '').replace(/\s+/g, ' ') + console.log(`❌ ${testName}\n Code: ${code}\n${details}\n`) + failures++ + return false +} + +let testName: string +let failures = 0 + +async function run() { + for (const [name, test] of Object.entries(tests)) { + testName = name + try { + await test() + } catch (err) { + console.log(`❌ ${name}\n Throws: ${err}\n`) + failures++ + } + } + + if (failures > 0) { + console.log(`❌ ${failures} checks failed`) + } else { + console.log(`✅ All checks passed`) + } +} + +const promise = run()