diff --git a/features.txt b/features.txt index 6004cca4bf..d911d88a4d 100644 --- a/features.txt +++ b/features.txt @@ -77,6 +77,10 @@ regexp-v-flag # https://github.com/tc39/proposal-decorators decorators +# Decorator Metadata +# https://github.com/tc39/proposal-decorator-metadata +decorator-metadata + # Duplicate named capturing groups # https://github.com/tc39/proposal-duplicate-named-capturing-groups regexp-duplicate-named-groups diff --git a/test/built-ins/Function/prototype/Symbol.metadata.js b/test/built-ins/Function/prototype/Symbol.metadata.js new file mode 100644 index 0000000000..f604ae19b4 --- /dev/null +++ b/test/built-ins/Function/prototype/Symbol.metadata.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-function.prototype-@@metadata +description: Function.prototype[Symbol.metadata] property descriptor +info: | + The initial value of the @@metadata property is null. + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: false }. +includes: [propertyHelper.js] +features: [decorator-metadata] +---*/ + +verifyProperty(Function.prototype, Symbol.metadata, { + value: null, + writable: false, + enumerable: false, + configurable: false +}); diff --git a/test/built-ins/Symbol/metadata/cross-realm.js b/test/built-ins/Symbol/metadata/cross-realm.js new file mode 100644 index 0000000000..59e4c0dded --- /dev/null +++ b/test/built-ins/Symbol/metadata/cross-realm.js @@ -0,0 +1,14 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-symbol.metadata +description: Value shared by all realms +info: | + Unless otherwise specified, well-known symbols values are shared by all + realms. +features: [cross-realm, decorator-metadata] +---*/ + +var OSymbol = $262.createRealm().global.Symbol; + +assert.sameValue(Symbol.metadata, OSymbol.metadata); diff --git a/test/built-ins/Symbol/metadata/prop-desc.js b/test/built-ins/Symbol/metadata/prop-desc.js new file mode 100644 index 0000000000..897f9a9ecd --- /dev/null +++ b/test/built-ins/Symbol/metadata/prop-desc.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-symbol.metadata +description: > + `Symbol.metadata` property descriptor +info: | + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: false }. +includes: [propertyHelper.js] +features: [decorator-metadata] +---*/ + +assert.sameValue(typeof Symbol.metadata, 'symbol'); +verifyNotEnumerable(Symbol, 'metadata'); +verifyNotWritable(Symbol, 'metadata'); +verifyNotConfigurable(Symbol, 'metadata'); diff --git a/test/language/expressions/class/decorator/metadata/context-metadata-prop-desc.js b/test/language/expressions/class/decorator/metadata/context-metadata-prop-desc.js new file mode 100644 index 0000000000..44278f0152 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/context-metadata-prop-desc.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +void @dec class C {}; +assert.sameValue(typeof contextObj.metadata, "object"); +verifyNotEnumerable(contextObj, "metadata"); +verifyWritable(contextObj, "metadata"); +verifyConfigurable(contextObj, "metadata"); diff --git a/test/language/expressions/class/decorator/metadata/context-metadata.js b/test/language/expressions/class/decorator/metadata/context-metadata.js new file mode 100644 index 0000000000..12146481d3 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/context-metadata.js @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [deepEqual.js] +features: [decorators, decorator-metadata] +---*/ + +var kinds = { + "class": false, + "public method": false, + "public getter": false, + "public setter": false, + "public field": false, + "public accessor": false, + "private method": false, + "private getter": false, + "private setter": false, + "private field": false, + "private accessor": false, +}; +function dec(_, context) { + const key = `${context.private ? "private" : "public"} ${context.kind}`; + kinds[key] = typeof context.metadata === "object"; +} + +void @dec class C { + @dec method() {} + @dec get getter() {} + @dec set setter(x) {} + @dec field; + @dec accessor accessor; + @dec #method() {} + @dec get #getter() {} + @dec set #setter(x) {} + @dec #field; + @dec accessor #accessor; +}; + +assert.deepEqual(kinds, { + "class": true, + "public method": true, + "public getter": true, + "public setter": true, + "public field": true, + "public accessor": true, + "private method": true, + "private getter": true, + "private setter": true, + "private field": true, + "private accessor": true, +}); diff --git a/test/language/expressions/class/decorator/metadata/metadata-attached-when-class-is-decorated.js b/test/language/expressions/class/decorator/metadata/metadata-attached-when-class-is-decorated.js new file mode 100644 index 0000000000..558ea5c6f4 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-attached-when-class-is-decorated.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata is only attached when a class or class element is decorated. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +let C1 = @dec class C1 {}; +assert.sameValue(typeof C1[Symbol.metadata], "object"); + +let C2 = class C2 { @dec method() {} }; +assert.sameValue(typeof C2[Symbol.metadata], "object"); + +let C3 = class C3 {}; +assert.sameValue(typeof C3[Symbol.metadata], "undefined"); diff --git a/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js new file mode 100644 index 0000000000..4d133108e8 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata on a derived class inherits from the metadata of the declared super class. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +class UndecoratedBase {} + +let Base = @dec class Base {}; +const baseMetadata = Base[Symbol.metadata]; + +let Derived = @dec class Derived extends Base { }; +Object.setPrototypeOf(Derived, UndecoratedBase); + +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js new file mode 100644 index 0000000000..458bd0d65c --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata on a derived class inherits from the metadata of the declared super class. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +class UndecoratedBase {} + +let Base = @dec class Base {}; +const baseMetadata = Base[Symbol.metadata]; + +let Derived = @dec class Derived extends Base { }; +Base[Symbol.metadata] = {}; + +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/expressions/class/decorator/metadata/metadata-inheritance.js b/test/language/expressions/class/decorator/metadata/metadata-inheritance.js new file mode 100644 index 0000000000..3af3b9fd88 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance.js @@ -0,0 +1,42 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata is only attached when a class or class element is decorated. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +let Base = @dec class Base {}; +const baseMetadata = Base[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(baseMetadata), null); + +let Derived = @dec class Derived extends Base { }; +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/expressions/class/decorator/metadata/metadata-is-plain-object.js b/test/language/expressions/class/decorator/metadata/metadata-is-plain-object.js new file mode 100644 index 0000000000..ba795e7266 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-is-plain-object.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + The metadata object is a regular, extensible JS object. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +let C = @dec class C {}; +const metadata = C[Symbol.metadata]; +assert(Object.isExtensible(metadata)); diff --git a/test/language/expressions/class/decorator/metadata/metadata-is-same-as-context-metadata.js b/test/language/expressions/class/decorator/metadata/metadata-is-same-as-context-metadata.js new file mode 100644 index 0000000000..53fce57f72 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-is-same-as-context-metadata.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + The metadata object provided to a decorator is the same one installed on the class. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. + + ClassDefinitionEvaluation + ClassTail : ClassHeritage_opt { ClassBody_opt } + + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +let C = @dec class C {}; +assert.sameValue(C[Symbol.metadata], contextObj.metadata); diff --git a/test/language/expressions/class/decorator/metadata/metadata-prop-desc.js b/test/language/expressions/class/decorator/metadata/metadata-prop-desc.js new file mode 100644 index 0000000000..41c95e0f1f --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-prop-desc.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +let C = @dec class C {}; +assert.sameValue(typeof contextObj.metadata, "object"); +verifyNotEnumerable(contextObj, "metadata"); +verifyWritable(contextObj, "metadata"); +verifyConfigurable(contextObj, "metadata"); diff --git a/test/language/statements/class/decorator/metadata/context-metadata-prop-desc.js b/test/language/statements/class/decorator/metadata/context-metadata-prop-desc.js new file mode 100644 index 0000000000..2113eceb0f --- /dev/null +++ b/test/language/statements/class/decorator/metadata/context-metadata-prop-desc.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +@dec class C {} +assert.sameValue(typeof contextObj.metadata, "object"); +verifyNotEnumerable(contextObj, "metadata"); +verifyWritable(contextObj, "metadata"); +verifyConfigurable(contextObj, "metadata"); diff --git a/test/language/statements/class/decorator/metadata/context-metadata.js b/test/language/statements/class/decorator/metadata/context-metadata.js new file mode 100644 index 0000000000..0ba7776556 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/context-metadata.js @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [deepEqual.js] +features: [decorators, decorator-metadata] +---*/ + +var kinds = { + "class": false, + "public method": false, + "public getter": false, + "public setter": false, + "public field": false, + "public accessor": false, + "private method": false, + "private getter": false, + "private setter": false, + "private field": false, + "private accessor": false, +}; +function dec(_, context) { + const key = `${context.private ? "private" : "public"} ${context.kind}`; + kinds[key] = typeof context.metadata === "object"; +} + +@dec class C { + @dec method() {} + @dec get getter() {} + @dec set setter(x) {} + @dec field; + @dec accessor accessor; + @dec #method() {} + @dec get #getter() {} + @dec set #setter(x) {} + @dec #field; + @dec accessor #accessor; +} + +assert.deepEqual(kinds, { + "class": true, + "public method": true, + "public getter": true, + "public setter": true, + "public field": true, + "public accessor": true, + "private method": true, + "private getter": true, + "private setter": true, + "private field": true, + "private accessor": true, +}); diff --git a/test/language/statements/class/decorator/metadata/metadata-attached-when-class-is-decorated.js b/test/language/statements/class/decorator/metadata/metadata-attached-when-class-is-decorated.js new file mode 100644 index 0000000000..5136d41bf3 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-attached-when-class-is-decorated.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata is only attached when a class or class element is decorated. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +@dec class C1 {} +assert.sameValue(typeof C1[Symbol.metadata], "object"); + +class C2 { @dec method() {} } +assert.sameValue(typeof C2[Symbol.metadata], "object"); + +class C3 {} +assert.sameValue(typeof C3[Symbol.metadata], "undefined"); diff --git a/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js new file mode 100644 index 0000000000..daf2793204 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata on a derived class inherits from the metadata of the declared super class. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +class UndecoratedBase {} + +@dec class Base {} +const baseMetadata = Base[Symbol.metadata]; + +@dec class Derived extends Base { } +Object.setPrototypeOf(Derived, UndecoratedBase); + +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js new file mode 100644 index 0000000000..8a2a464c0a --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata on a derived class inherits from the metadata of the declared super class. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +class UndecoratedBase {} + +@dec class Base {} +const baseMetadata = Base[Symbol.metadata]; + +@dec class Derived extends Base { } +Base[Symbol.metadata] = {}; + +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/statements/class/decorator/metadata/metadata-inheritance.js b/test/language/statements/class/decorator/metadata/metadata-inheritance.js new file mode 100644 index 0000000000..8c1f2ec5f5 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance.js @@ -0,0 +1,42 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + Metadata is only attached when a class or class element is decorated. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +@dec class Base {} +const baseMetadata = Base[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(baseMetadata), null); + +@dec class Derived extends Base { } +const derivedMetadata = Derived[Symbol.metadata]; +assert.sameValue(Object.getPrototypeOf(derivedMetadata), baseMetadata); diff --git a/test/language/statements/class/decorator/metadata/metadata-is-plain-object.js b/test/language/statements/class/decorator/metadata/metadata-is-plain-object.js new file mode 100644 index 0000000000..b5e176e48e --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-is-plain-object.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + The metadata object is a regular, extensible JS object. +info: | + ClassTail : ClassHeritage_opt { ClassBody_opt } + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +@dec class C {} +const metadata = C[Symbol.metadata]; +assert(Object.isExtensible(metadata)); diff --git a/test/language/statements/class/decorator/metadata/metadata-is-same-as-context-metadata.js b/test/language/statements/class/decorator/metadata/metadata-is-same-as-context-metadata.js new file mode 100644 index 0000000000..020f3a1bc0 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-is-same-as-context-metadata.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-runtime-semantics-classdefinitionevaluation +description: > + The metadata object provided to a decorator is the same one installed on the class. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. + + ClassDefinitionEvaluation + ClassTail : ClassHeritage_opt { ClassBody_opt } + + 21. Let hasDecorators be false. + 22. If decorators is not empty, set hasDecorators to true. + [...] + 25. For each ClassElement e of elements, do + [...] + e. If element is a ClassElementDefinition Record, then + i. If e.[[Decorators]] is not empty, set hasDecorators to true. + [...] + [...] + 29. Let metadataObj be empty. + 30. If hasDecorators is true, then + a. If ClassHeritage is present, [...] + b. Else, let metadataParent be null. + c. Set metadataObj to OrdinaryObjectCreate(metadataParent). + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + 1. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 1. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +@dec class C {} +assert.sameValue(C[Symbol.metadata], contextObj.metadata); diff --git a/test/language/statements/class/decorator/metadata/metadata-prop-desc.js b/test/language/statements/class/decorator/metadata/metadata-prop-desc.js new file mode 100644 index 0000000000..2113eceb0f --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-prop-desc.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-createdecoratorcontextobject +description: > + Property descriptor for metadata property of decorator context object. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 13. Perform ! CreateDataPropertyOrThrow(contextObj, "metadata", metadata). + 14. Return contextObj. +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +var contextObj; +function dec(_, context) { + contextObj = context; +} + +@dec class C {} +assert.sameValue(typeof contextObj.metadata, "object"); +verifyNotEnumerable(contextObj, "metadata"); +verifyWritable(contextObj, "metadata"); +verifyConfigurable(contextObj, "metadata");