diff --git a/features.txt b/features.txt index 6004cca4bf8..d911d88a4d4 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 00000000000..f604ae19b49 --- /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 00000000000..59e4c0ddedc --- /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 00000000000..95201ae1513 --- /dev/null +++ b/test/built-ins/Symbol/metadata/prop-desc.js @@ -0,0 +1,20 @@ +// 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'); +verifyProperty(Symbol, 'metadata', { + writable: false, + enumerable: false, + configurable: false, +}); + 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 00000000000..3b3a68c9d25 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/context-metadata-prop-desc.js @@ -0,0 +1,27 @@ +// 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"); +verifyProperty(contextObj, 'metadata', { + enumerable: false, + writable: true, + configurable: true, +}); 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 00000000000..b026dc21626 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/context-metadata.js @@ -0,0 +1,60 @@ +// 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 = { + __proto__: null, + "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 00000000000..a921fabda06 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-attached-when-class-is-decorated.js @@ -0,0 +1,46 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. Return ? _setMetadataResult_. + [...] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +let C1 = @dec class C1 {}; +assert.sameValue(typeof C1[Symbol.metadata], "object"); +assert.notSameValue(C1[Symbol.metadata], null); + +let C2 = class C2 { @dec method() {} }; +assert.sameValue(typeof C2[Symbol.metadata], "object"); +assert.notSameValue(C2[Symbol.metadata], null); + +let C3 = class C3 {}; +assert.sameValue(C3[Symbol.metadata], null); // inherited from Function.prototype[Symbol.metadata] 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 00000000000..bb47ea83150 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js @@ -0,0 +1,46 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..a74ff5bdab4 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js @@ -0,0 +1,46 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..46f99ee3d1f --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-inheritance.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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..ba795e7266e --- /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 00000000000..c63cdf1ae74 --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-is-same-as-context-metadata.js @@ -0,0 +1,48 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..ed7a9dfd82f --- /dev/null +++ b/test/language/expressions/class/decorator/metadata/metadata-prop-desc.js @@ -0,0 +1,28 @@ +// 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: > + Property descriptor for Symbol.metadata property of decorated class expression. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { + [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. Return ? _setMetadataResult_. + [...] +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +let C = @dec class C {}; +verifyProperty(C, Symbol.metadata, { + enumerable: false, + writable: true, + configurable: true, +}); 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 00000000000..06f66bc18d7 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/context-metadata-prop-desc.js @@ -0,0 +1,27 @@ +// 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"); +verifyProperty(contextObj, 'metadata', { + enumerable: false, + writable: true, + configurable: true, +}); 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 00000000000..8ba984bc0f1 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/context-metadata.js @@ -0,0 +1,60 @@ +// 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 = { + __proto__: null, + "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 00000000000..3a18fce5f33 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-attached-when-class-is-decorated.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 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"); +assert.notSameValue(C1[Symbol.metadata], null); + +class C2 { @dec method() {} } +assert.sameValue(typeof C2[Symbol.metadata], "object"); +assert.notSameValue(C2[Symbol.metadata], null); + +class C3 {} +assert.sameValue(C3[Symbol.metadata], null); // inherited from Function.prototype[Symbol.metadata] 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 00000000000..27eb86ffbf4 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-by-setPrototype-on-class.js @@ -0,0 +1,46 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..c46972796d5 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance-not-affected-when-base-metadata-replaced.js @@ -0,0 +1,46 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..af80781c240 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-inheritance.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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..b5e176e48e1 --- /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 00000000000..91c22d9b3f8 --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-is-same-as-context-metadata.js @@ -0,0 +1,48 @@ +// 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 })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. 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 00000000000..495cc38863d --- /dev/null +++ b/test/language/statements/class/decorator/metadata/metadata-prop-desc.js @@ -0,0 +1,29 @@ +// 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: > + Property descriptor for Symbol.metadata property of decorated class declaration. +info: | + CreateDecoratorContextObject ( kind, name, initializers, decorationState, metadataObj [ , isStatic ] ) + + [...] + 41. If metadataObj is not empty, then + a. Let setMetadataResult be Completion(DefinePropertyOrThrow(F, @@metadata, PropertyDescriptor { + [[Value]]: metadataObj, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true })). + i. If _setMetadataResult_ is an abrupt completion, then + 1. Set the running execution context's PrivateEnvironment to _outerPrivateEnvironment_. + 2. Return ? _setMetadataResult_. + [...] +includes: [propertyHelper.js] +features: [decorators, decorator-metadata] +---*/ + +function dec() {} + +@dec class C {}; +verifyProperty(C, Symbol.metadata, { + enumerable: false, + writable: true, + configurable: true, +});