Stage 2 Draft / September 6, 2018

Decorators proposal

Introduction

This proposal adds decorators to JavaScript. It incorporates features needed to make decorators work with the class fields and private methods. See the explainer for an overview.

This document is phrased as a diff against the previous private methods proposal, which is in turn a diff against the class fields proposal.

1Syntax

1.1New Productions

DecoratorList[Yield, Await]:DecoratorList[?Yield, ?Await]optDecorator[?Yield, ?Await] Decorator[Yield, Await]:@DecoratorMemberExpression[?Yield, ?Await] @DecoratorCallExpression[?Yield, ?Await] DecoratorMemberExpression[Yield, Await]:IdentifierReference[?Yield, ?Await] DecoratorMemberExpression[?Yield, ?Await].IdentifierName (Expression[+In, ?Yield, ?Await]) DecoratorCallExpression[Yield, Await]:DecoratorMemberExpressionArguments[?Yield, ?Await]

1.2Updated Productions

ClassElement[Yield, Await]:DecoratorList[?Yield, ?Await]optMethodDefinition[?Yield, ?Await] DecoratorList[?Yield, ?Await]optstaticMethodDefinition[?Yield, ?Await] DecoratorList[?Yield, ?Await]optFieldDefinition[?Yield, ?Await]; DecoratorList[?Yield, ?Await]optstaticFieldDefinition[?Yield, ?Await]; ClassDeclaration[Yield, Await, Default]:DecoratorList[?Yield, ?Await]optclassBindingIdentifier[?Yield, ?Await]ClassTail[?Yield, ?Await] [+Default]DecoratorList[?Yield, ?Await]optclassClassTail[?Yield, ?Await] ClassExpression[Yield, Await]:DecoratorList[?Yield, ?Await]optclassBindingIdentifier[?Yield, ?Await]optClassTail[?Yield, ?Await] ExportDeclaration:export*FromClause; exportExportClauseFromClause; exportExportClause; exportVariableStatement[~Yield, ~Await] exportDeclaration[~Yield, ~Await] exportdefaultHoistableDeclaration[~Yield, ~Await, +Default] exportdefaultClassDeclaration[~Yield, ~Await, +Default] exportdefault[lookahead ∉ { function, async [no LineTerminator here] function, class, @ }]AssignmentExpression[+In, ~Yield, ~Await];

2New ECMAScript Specification Types

2.1Classes Specification Types

2.1.1The ElementDescriptor Specification Type

The ElementDescriptor is a Record used to represent class elements at runtime. Values of the ElementDescriptor type are Record values whose fields are defined as by Table 1. Unless otherwise specified, every field is always present.

Table 1: ElementDescriptor fields
Field Name Value
[[Kind]] One of "method" or "field"
[[Key]] A Property Key or %PrivateName% object
[[Descriptor]] A Property Descriptor
[[Placement]] One of "static", "prototype", or "own"
[[Initializer]] A function or empty. This field can be absent.
[[Decorators]] A List of ECMAScript language values. This field can be absent.

In addition, given an ElementDescriptor element, the following conditions are always respected:

  • If element.[[Kind]] is "method", then element.[[Initializer]] is not present.
  • If element.[[Kind]] is "field", then
    • element.[[Initializer]] is present.
    • element.[[Descriptor]]'s [[Get]], [[Set]], and [[Value]] slots are absent.
  • If element.[[Key]] is a Private Name, then
    • element.[[Placement]] is "own".
    • element.[[Descriptor]].[[Enumerable]] is false.
    • element.[[Descriptor]].[[Configurable]] is false.

3Modified class algorithms

3.1Runtime Semantics: ClassDefinitionEvaluation

With parameters className and decorators.

ClassTail:ClassHeritageopt{ClassBodyopt}
  1. Let lex be the LexicalEnvironment of the running execution context.
  2. Let classScope be NewDeclarativeEnvironment(lex).
  3. Let classScopeEnvRec be classScope's EnvironmentRecord.
  4. If className is not undefined, then
    1. Perform classScopeEnvRec.CreateImmutableBinding(className, true).
  5. Let outerPrivateEnvironment be the PrivateNameEnvironment of the running execution context.
  6. Let classPrivateEnvironment be NewDeclarativeEnvironment(outerPrivateEnvironment).
  7. Let classPrivateEnvRec be classPrivateEnvironment's EnvironmentRecord.
  8. If ClassBodyopt is present, then
    1. For each element dn of the PrivateBoundNames of ClassBodyopt,
      1. Perform classPrivateEnvRec.CreateImmutableBinding(dn, true).
  9. If ClassHeritageopt is not present, then
    1. Let protoParent be the intrinsic object %ObjectPrototype%.
    2. Let constructorParent be the intrinsic object %FunctionPrototype%.
  10. Else,
    1. Set the running execution context's LexicalEnvironment to classScope.
    2. Let superclass be the result of evaluating ClassHeritage.
    3. Set the running execution context's LexicalEnvironment to lex.
    4. ReturnIfAbrupt(superclass).
    5. If superclass is null, then
      1. Let protoParent be null.
      2. Let constructorParent be the intrinsic object %FunctionPrototype%.
    6. Else if IsConstructor(superclass) is false, throw a TypeError exception.
    7. Else,
      1. Let protoParent be ? Get(superclass, "prototype").
      2. If Type(protoParent) is neither Object nor Null, throw a TypeError exception.
      3. Let constructorParent be superclass.
  11. Let proto be ObjectCreate(protoParent).
  12. If ClassBodyopt is not present, let constructor be empty.
  13. Else, let constructor be ConstructorMethod of ClassBody.
  14. If constructor is empty, then
    1. If ClassHeritageopt is present and protoParent is not null, then
      1. Let constructor be the result of parsing the source text
        constructor(... args){ super (...args);}
        using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
    2. Else,
      1. Let constructor be the result of parsing the source text
        constructor( ){ }
        using the syntactic grammar with the goal symbol MethodDefinition[~Yield].
  15. Set the running execution context's LexicalEnvironment to classScope.
  16. Set the running execution context's PrivateNameEnvironment to classPrivateEnvironment.
  17. Let constructorInfo be the result of performing DefineMethod for constructor with arguments proto and constructorParent as the optional functionPrototype argument.
  18. Assert: constructorInfo is not an abrupt completion.
  19. Let F be constructorInfo.[[Closure]].
  20. If ClassHeritageopt is present and protoParent is not null, then set F.[[ConstructorKind]] to "derived".
  21. Perform MakeConstructor(F, false, proto).
  22. Perform MakeClassConstructor(F).
  23. Perform CreateMethodProperty(proto, "constructor", F).
  24. If ClassBodyopt is not present, let methodselements be a new empty List.
  25. Else, let methodsdefinitions be NonConstructorMethodDefinitionsNonConstructorElementDefinitions of ClassBody. NOTE: Simply renaming this internal algorithm will be enough; it includes fields.
  26. Let elements be a new empty List.
  27. For each ClassElement md in order from methodsdefinitions,
    1. If IsStatic of m is false, then
      1. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments proto and false.
    2. Else,
      1. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments F and false.
    3. Let newElements be the result of performing ClassElementEvaluation for d with arguments F, true, and empty.
    4. If statusnewElements is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to lex.
      2. Set the running execution context's PrivateNameEnvironment to outerPrivateEnvironment.
      3. Return Completion(status).
    5. Append newElements to elements
  28. Let elements be CoalesceClassElements(elements).
  29. If decorators is not provided, let decorators be a new empty List.
  30. Let decorated be ? DecorateClass(elements, decorators).
  31. Set the running execution context's LexicalEnvironment to lex.
  32. Set the running execution context's PrivateNameEnvironment to outerPrivateEnvironment.
  33. If className is not undefined, then
    1. Perform classScopeEnvRec.InitializeBinding(className, F).
  34. Set the value of F's [[Elements]] internal slot to decorated.[[Elements]].
  35. Perform ? InitializeClassElements(F, proto).
  36. Return F.
  37. Return ? RunClassFinishers(F, decorated.[[Finishers]]).

3.2Runtime Semantics: ClassElementEvaluation

With parameters homeObject, enumerable and placement.

ClassElementEvaluation returns a List of ElementDescriptor Records.

ClassElement:DecoratorListoptMethodDefinition
  1. If DecoratorList is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. ReturnLet elements be ? ClassElementEvaluation of MethodDefinition with arguments ! Get(homeObject, "prototype"),enumerable, and "prototype".
  3. If DecoratorList is present, for element in elements, set element.[[Decorators]] to decorators.
  4. Return elements.
ClassElement:DecoratorListoptstaticMethodDefinition
  1. If DecoratorList is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. ReturnLet elements be ? ClassElementEvaluation of MethodDefinition with arguments homeObject, enumerable and "static".
  3. If DecoratorList is present, for element in elements, set element.[[Decorators]] to decorators.
  4. Return elements.
ClassElement:DecoratorListoptstaticFieldDefinition;
  1. If DecoratorList is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. ReturnLet elements be ? ClassFieldDefinitionEvaluation of FieldDefinition with parameters "static" and homeObject.
  3. If DecoratorList is present, for element in elements, set element.[[Decorators]] to decorators.
  4. Return elements.
ClassElement:DecoratorListoptFieldDefinition;
  1. If DecoratorList is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. ReturnLet elements be ? ClassFieldDefinitionEvaluation of FieldDefinition with parameters "own" and ! Get(homeObject, "prototype").
  3. If DecoratorList is present, for element in elements, set element.[[Decorators]] to decorators.
  4. Return elements.

3.3Runtime Semantics: BindingClassDeclarationEvaluation

ClassDeclaration:DecoratorListoptclassBindingIdentifierClassTail
  1. If DecoratorListopt is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. Otherwise, let decorators be a new empty List.
  3. Let className be StringValue of BindingIdentifier.
  4. Let value be the result of ClassDefinitionEvaluation of ClassTail with arguments className and _decorators.
  5. ReturnIfAbrupt(value).
  6. Let hasNameProperty be ? HasOwnProperty(value, "name").
  7. If hasNameProperty is false, perform SetFunctionName(value, className).
  8. Let env be the running execution context's LexicalEnvironment.
  9. Perform ? InitializeBoundName(className, value, env).
  10. Return value.
ClassDeclaration:DecoratorListoptclassClassTail
  1. If DecoratorList is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. Otherwise, let decorators be a new empty List.
  3. Return the result of ClassDefinitionEvaluation of ClassTail with arguments undefined and decorators.
Note

ClassDeclaration:classClassTail only occurs as part of an ExportDeclaration and the setting of a name property and establishing its binding are handled as part of the evaluation action for that production. See 15.2.3.9.

3.4Runtime Semantics: Evaluation

ClassDeclaration:DecoratorListoptclassBindingIdentifierClassTail
  1. Perform ? BindingClassDeclarationEvaluation of this ClassDeclaration.
  2. Return NormalCompletion(empty).
Note 1

ClassDeclaration:classClassTail only occurs as part of an ExportDeclaration and is never directly evaluated.

ClassExpression:DecoratorListoptclassBindingIdentifieroptClassTail
  1. If DecoratorListopt is present, let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
  2. Otherwise, let decorators be a new empty List.
  3. If BindingIdentifieropt is not present, let className be undefined.
  4. Else, let className be StringValue of BindingIdentifier.
  5. Let value be the result of ClassDefinitionEvaluation of ClassTail with argument className and decorators.
  6. ReturnIfAbrupt(value).
  7. If className is not undefined, then
    1. Let hasNameProperty be ? HasOwnProperty(value, "name").
    2. If hasNameProperty is false, then
      1. Perform SetFunctionName(value, className).
  8. Return NormalCompletion(value).
Note 2

If the class definition included a name static method then that method is not over-written with a name data property for the class name.

3.5CoalesceGetterSetter ( element, other )

  1. Assert: element and other are both ElementDescriptor Records.
  2. Assert: IsAccessorDescriptor(other.[[Descriptor]]) and IsAccessorDescriptor(element.[[Descriptor]]) are both true,
  3. If element.[[Descriptor]] has a [[Get]] field,
    1. Set other.[[Descriptor]].[[Get]] to element.[[Descriptor]].[[Get]].
  4. Otherwise,
    1. Assert: element.[[Descriptor]] has a [[Set]] field.
    2. Set other.[[Descriptor]].[[Set]] to element.[[Descriptor]].[[Set]].

3.6CoalesceClassElements ( elements )

  1. Assert: elements is a List of ElementDescriptor Records.
  2. Let newElements be an empty List.
  3. For element in elements,
    1. If element.[[Kind]] is "method" and newElements contains a Record other where other.[[Kind]] is "method", SameValue(other.[[Key]], element.[[Key]]) is true, and other.[[Placement]] is element.[[Placement]],
      1. If IsDataDescriptor(element.[[Descriptor]]) is true or IsDataDescriptor(other.[[Descriptor]]) is true, then
        1. Assert: element.[[Key]] is not a Private Name.
        2. Assert: element.[[Descriptor]].[[Configurable]] is true, and other.[[Descriptor]].[[Configurable]] is true.
        3. If element.[[Decorators]] is present or other.[[Decorators]] is present, throw a ReferenceError exception.
        4. Set other.[[Descriptor]] to element.[[Descriptor]].
      2. Else,
        1. If element.[[Decorators]] is present,
          1. If other.[[Decorators]] is present, throw a ReferenceError exception.
          2. Set other.[[Decorators]] to element.[[Decorators]].
        2. Perform ! CoalesceGetterSetter(element, other).
    2. Otherwise, append element to newElements.
  4. Return newElements.
Note
In the case of public class elements, coalescing corresponds in semantics to ValidateAndApplyPropertyDescriptor. Note that this algorithm only coalesces method and accessor declarations, and it leaves field declarations as is.

4Private Names and references

Editor's Note
This section refers to Private Name values, as defined in the class fields proposal.

4.1Private Name Objects

4.1.1The %PrivateName% Constructor

The Private Name constructor is the %PrivateName% intrinsic object. The %PrivateName% intrinsic does not have a global name or appear as a property of the global object.

The PrivateName object is deeply frozen, in the sense that it is frozen, all objects reachable from it are frozen, and PrivateName instances are frozen as well. See the logic in CreateIntrinsics for details.

The value of the [[Prototype]] internal slot of %PrivateName% is null.

4.1.1.1%PrivateName% ( )

When %PrivateName% is called, the following steps are taken:

  1. Throw a TypeError exception.
Note
New PrivateName instances can be created by decorating private class elements.

4.1.1.2PrivateNameObject ( name )

When PrivateNameObject is called with Private Name name, the following steps are taken:

  1. Let O be ? ObjectCreate(%PrivateNamePrototype%, « [[PrivateName]] »).
  2. Set O.[[PrivateNameData]] to name.
  3. Perform ! SetIntegrityLevel(O, "frozen").
  4. Return O.

4.1.1.3Properties of the %PrivateNamePrototype% Object

The %PrivateNamePrototype% object is an ordinary object. It is not a %PrivateName% instance and does not have a [[PrivateNameData]] internal slot.

The value of the [[Prototype]] internal slot of %PrivateNamePrototype% is null.

4.1.1.3.1%PrivateName%.prototype.constructor

The initial value of PrivateName.prototype.constructor is the intrinsic object %PrivateName%.

4.1.1.3.2%PrivateName%.prototype.get ( object )

When invoked, the following steps are taken:

  1. Let O be the this value.
  2. Let pn be ? GetPrivateName(O).
  3. If Type(object) is not Object, throw a TypeError exception.
  4. Return ? PrivateFieldGet(pn, object).

4.1.1.3.3%PrivateName%.prototype.set ( object, value )

%PrivateNameSet% is a per-realm built-in function object. When invoked, the following steps are taken:

  1. Let O be the this value.
  2. Let pn be ? GetPrivateName(O).
  3. If Type(object) is not Object, throw a TypeError exception.
  4. Return ? PrivateFieldSet(pn, object, value).

4.1.1.3.4get %PrivateName%.prototype.description ( )

The following steps are taken:

  1. Let O be the this value.
  2. Let pn be ? GetPrivateName(O).
  3. Let desc be pn's [[Description]] value.
  4. If desc is undefined, return the empty string.
  5. Otherwise, return desc.

4.1.1.3.5%PrivateName%.prototype.toString ( )

The following steps are taken:

  1. Throw a TypeError exception.
Note
Because conversion to a string throws, ToPropertyKey applied to a %PrivateName% object throws as well. This property is important to ensure that Private Names are not incorrectly used by decorators using property access, rather than with their get and set methods.

4.1.1.3.6PrivateName.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "PrivateName".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

4.1.1.3.7GetPrivateName ( O )

  1. If Type(O) is not Object, throw a TypeError exception.
  2. If O does not have a [[PrivateNameData]] internal slot, throw a TypeError exception.
  3. Return O.[[PrivateNameData]].

4.1.1.4Properties of PrivateName Instances

PrivateName instances are ordinary objects that inherit properties from the PrivateName prototype object. PrivateName instances have a [[PrivateNameData]] internal slot. The [[PrivateNameData]] internal slot is the Private Name value represented by this Private Name object.

4.2CreateIntrinsics ( realmRec )

The abstract operation CreateIntrinsics with argument realmRec performs the following steps:

  1. Let intrinsics be a new Record.
  2. Set realmRec.[[Intrinsics]] to intrinsics.
  3. Let objProto be ObjectCreate(null).
  4. Set intrinsics.[[%ObjectPrototype%]] to objProto.
  5. Let throwerSteps be the algorithm steps specified in 9.2.7.1 for the %ThrowTypeError% function.
  6. Let thrower be CreateBuiltinFunction(throwerSteps, « », realmRec, null).
  7. Set intrinsics.[[%ThrowTypeError%]] to thrower.
  8. Let noSteps be an empty sequence of algorithm steps.
  9. Let funcProto be CreateBuiltinFunction(noSteps, « », realmRec, objProto).
  10. Set intrinsics.[[%FunctionPrototype%]] to funcProto.
  11. Call thrower.[[SetPrototypeOf]](funcProto).
  12. Perform AddRestrictedFunctionProperties(funcProto, realmRec).
  13. Set fields of intrinsics with the values listed in Table 7 that have not already been handled above. The field names are the names listed in column one of the table. The value of each field is a new object value fully and recursively populated with property values as defined by the specification of each object in clauses 18-26. All object property values are newly created object values. All values that are built-in function objects are created by performing CreateBuiltinFunction(<steps>, <slots>, realmRec, <prototype>) where <steps> is the definition of that function provided by this specification, <slots> is a list of the names, if any, of the function's specified internal slots, and <prototype> is the specified value of the function's [[Prototype]] internal slot. The creation of the intrinsics and their properties must be ordered to avoid any dependencies upon objects that have not yet been created.
  14. For each property of %PrivateNamePrototype%, do
    1. Let desc be the data property descriptor for this property.
    2. If desc has a [[Get]], [[Set]], or [[Value]] field, then for each value value of such fields,
      1. Perform ! SetIntegrityLevel(value, "frozen").
  15. Perform ! SetIntegrityLevel(%PrivateNamePrototype%, "frozen").
  16. Assert: The only own property of %PrivateName% is the "prototype" property, whose [[Value]] is %PrivateNamePrototype%.
  17. Perform ! SetIntegrityLevel(%PrivateName%, "frozen").
  18. Return intrinsics.

5Decorator semantics

5.1Decorator Functions

A decorator function is a function that takes and returns either a element descriptor or a class descriptor. The body of a decorator function modifies and returns the descriptor it receives to change the semantics of the decorated entity. Descriptor types can be differentiated by their kind property, which is either "method", "field" or "class". Descriptors also have a @@toStringTag property which has the value "Descriptor"; this property helps differentiate them from other objects.

5.1.1Element Descriptors

An element descriptor describes an element of a class or object literal and has the following shape:

interface ElementDesciptor {
  kind: "method" or "field"
  key: String, Symbol or Private Name,
  placement: "static", "prototype", or "own"
  descriptor: PropertyDescriptor,
  initializer?: Function
  extras?: ElementDescriptor[]
  finisher?: (klass): undefined or constructor;
}
The finisher and extra fields are only present when returning from user code, and are not given as an argument to them, or logically part of the descriptor.

5.1.2Class Descriptors

A class descriptor describes a class and has the following shape:

interface ClassDesciptor {
  kind: "class"
  elements: ElementDescriptor[]
  finisher?: (klass): undefined or constructor;
}

5.2Runtime Semantics: DecoratorEvaluation

Decorator:@DecoratorMemberExpression[?Yield]
  1. Let expr be the MemberExpression that is covered by DecoratorMemberExpression.
  2. Let ref be the result of evaluating expr.
  3. Let value be ? GetValue(ref).
  4. Return value.
Decorator:@DecoratorCallExpression[?Yield]
  1. Let expr be the result of reparsing DecoratorCallExpression as a CallMemberExpression.
  2. Let ref be the result of evaluating expr.
  3. Let value be ? GetValue(ref).
  4. Return value.

5.3Runtime Semantics: DecoratorListEvaluation

DecoratorList:DecoratorList[?Yield]optDecorator[?Yield]
  1. If DecoratorList is present, then let leftValue be ? DecoratorListEvaluation(DecoratorList).
  2. Else, let leftValue be a new empty List.
  3. Let rightValue be ? DecoratorEvaluation(Decorator).
  4. Append rightValue to the end of leftValue.
  5. Return leftValue.

5.4RunClassFinishers ( constructor, finishers )

  1. For each finisher in finishers, do
    1. Let newConstructor be Call( finisher, undefined, « constructor »).
    2. If newConstructor is not undefined,
      1. If IsConstructor(newConstructor) is false, throw a TypeError exception.
      2. Let constructor be newConstructor.
  2. Return constructor.

5.5DecorateClass ( elements, decorators )

  1. Let newElements and finishers each be a new empty List.
  2. Let placements be the Record { [[StaticKeys]]: « », [[PrototypeKeys]]: « », [[OwnKeys]]: « » }.
  3. For each element in elements, do
    1. Perform ? AddElementPlacement(element, placements, true).
  4. For each element in elements, do
    1. Let elementFinishersExtras be ? DecorateElement(element, placements).
    2. Append elementFinishersExtras.[[Element]] to newElements.
    3. Concatenate elementFinishersExtras.[[Extras]] onto newElements.
    4. Concatenate elementFinishersExtras.[[Finishers]] onto finishers.
  5. Let result be ? DecorateConstructor(newElements, decorators).
  6. Set result.[[Finishers]] to the concatenation of finishers and result.[[Finishers]].
  7. Return result.

5.6DecorateElement ( element, placements )

With parameters element, a Class Element, and placements:

  1. Let extras be a new empty List.
  2. Let finishers be a new empty List.
  3. For each decorator in element.[[Decorators]], in reverse list order do
    1. Perform RemoveElementPlacement(element, placements).
    2. Let elementObject be ? FromElementDescriptor(element).
    3. Let elementFinisherExtrasObject be ? Call(decorator, undefined, « elementObject »).
    4. If elementFinisherExtrasObject is undefined,
      1. Let elementFinisherExtrasObject be elementObject.
    5. Let elementFinisherExtras be ? ToElementFinisherExtras(elementFinisherExtrasObject).
    6. Let element be elementFinisherExtras.[[Element]].
    7. Perform ? AddElementPlacement(element, placements).
    8. If elementFinisherExtras.[[Finisher]] is not undefined, then
      1. Append elementFinisherExtras.[[Finisher]] to the end of finishers.
      2. NOTE: Finishers are not passed forward to the next decorator.
    9. Let newExtras be elementFinisherExtras.[[Extras]]
    10. If newExtras is not undefined, then
      1. For each extra of newExtras, do
        1. Perform ? AddElementPlacement(extra, placements).
      2. Concatenate newExtras onto extras.
  4. Return the Record {[[Element]]: element, [[Extras]]: extras, [[Finishers]]: finishers}.

5.7DecorateConstructor ( elements, decorators )

With parameters elements, a List of Class Elements, and decorators, a List of decorator functions.

  1. Let finishers be a new empty List.
  2. For each decorator in decorators, in reverse list order do
    1. Let obj be FromClassDescriptor(elements).
    2. Let result be ? Call(decorator, undefined, « obj »).
    3. If result is undefined, let result be obj.
    4. Let elementsAndFinisher be ? ToClassDescriptor(result).
    5. If elementsAndFinisher.[[Finisher]] is not undefined,
      1. Append elementsAndFinisher.[[Finisher]] to finishers.
    6. If elementsAndFinisher.[[Elements]] is not undefined,
      1. Set elements to elementsAndFinisher.[[Elements]].
      2. If there are two class elements a and b in elements such that a.[[Key]] is b.[[Key]] and a.[[Placement]] is b.[[Placement]], throw a TypeError exception.
  3. Return the Record { [[Elements]]: elements, [[Finishers]]: finishers }.

5.8AddElementPlacement (element, placements [, silent])

  1. If element.[[Placement]] is "own", then
    1. Let keys be placements.[[OwnKeys]].
  2. Else if element.[[Placement]] is "static", then
    1. Let keys be placements.[[StaticKeys]].
  3. Else,
    1. Assert: element.[[Placement]] is "prototype".
    2. Let keys be placements.[[PrototypeKeys]].
  4. If element.[[Key]] is an element of keys, then
    1. If silent is not present or is false, throw a TypeError exception.
  5. Otherwise, append element.[[Key]] to keys.

5.9RemoveElementPlacement (element, placements)

  1. If element.[[Placement]] is "own", then
    1. Let keys be placements.[[OwnKeys]].
  2. Else if element.[[Placement]] is "static", then
    1. Let keys be placements.[[StaticKeys]].
  3. Else,
    1. Assert: element.[[Placement]] is "prototype".
    2. Let keys be placements.[[PrototypeKeys]].
  4. Assert: element.[[Key]] is an element of keys.
  5. Remove element.[[Key]] from keys.

5.10FromElementDescriptors ( elements )

  1. Assert: elements is a List of ElementDescriptor Records.
  2. Let elementObjects be a new empty List.
  3. For each element in elements, do
    1. Append ! FromElementDescriptor(element) to elementObjects.
  4. Return ! CreateArrayFromList(elementObjects).

5.11FromElementDescriptor ( element )

  1. Assert: element is an ElementDescriptor Record.
  2. Let obj be ! ObjectCreate(%ObjectPrototype%).
  3. Let desc be PropertyDescriptor{ [[Value]]: "Descriptor", [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
  4. Perform ! DefinePropertyOrThrow(obj, @@toStringTag, desc).
  5. Perform ! CreateDataPropertyOrThrow(obj, "kind", element.[[Kind]]).
  6. Let key be element.[[Key]].
  7. If key is a Private Name, set key to ? PrivateNameObject(key).
  8. Perform ! CreateDataPropertyOrThrow(obj, "key", key).
  9. Perform ! CreateDataPropertyOrThrow(obj, "placement", element.[[Placement]]).
  10. Perform ! CreateDataPropertyOrThrow(obj, "descriptor", ! FromPropertyDescriptor(element.[[Descriptor]])).
  11. If element.[[Kind]] is "field",
    1. Let initializer be element.[[Initializer]].
    2. If initializer is empty, set initializer to undefined.
    3. Perform ! CreateDataPropertyOrThrow(obj, "initializer", initializer).
  12. Return obj.

5.12ToElementDescriptors ( elementObjects )

  1. Assert: elementObject is an ECMAScript language value.
  2. If elementObjects is undefined, return undefined.
  3. Let elements be a new empty List.
  4. Let elementObjectsList be ? IterableToList(elementObjects).
  5. For each elementObject in elementObjectsList, do
    1. Let element be ? ToElementDescriptor(elementObject).
    2. Let finisher be ? Get(elementObject, "finisher").
    3. If finisher is not undefined, throw a TypeError exception.
    4. Let extras be ? Get(elementObject, "extras").
    5. If extras is not undefined, throw a TypeError exception.
    6. Append element to elements.
  6. Return elements.

5.13ToElementDescriptor ( elementObject )

With parameter elementObject, returns an ElementDescriptor.

  1. Assert: elementObject is an ECMAScript language value.
  2. Let kind be ? ToString(? Get(elementObject, "kind")).
  3. If kind is not one of "method" or "field", throw a TypeError exception.
  4. Let key be ? Get(elementObject, "key").
  5. Set key to ? ToPrimitive(key, hint String).
  6. If key has a [[PrivateName]] internal slot, set key to key.[[PrivateName]].
  7. Otherwise, set key to ? ToPropertyKey(key).
  8. Let placement be ? ToString(? Get(elementObject, "placement")).
  9. If placement is not one of "static", "prototype", or "own", throw a TypeError exception.
  10. Let descriptor be ? ToPropertyDescriptor(? Get(elementObject, "descriptor")).
  11. Let initializer be ? Get(elementObject, "initializer").
  12. Let elements be ? Get(elementObject, "elements").
  13. If elements is not undefined, throw a TypeError exception.
  14. If kind not "field",
    1. If initializer is not undefined, throw a TypeError exception.
  15. If key is a Private Name,
    1. If descriptor.[[Enumerable]] is true, throw a TypeError exception.
    2. If descriptor.[[Configurable]] is true, throw a TypeError exception.
    3. If placement is "prototype" or "static", throw a TypeError exception.
  16. If kind is "field",
    1. If descriptor has a [[Get]], [[Set]] or [[Value]] internal slot, throw a TypeError exception.
  17. Let element be the ElementDescriptor { [[Kind]]: kind, [[Key]]: key, [[Placement]]: placement, [[Descriptor]]: descriptor }.
  18. If kind is "field", set element.[[Initializer]] to initializer.
  19. Return element.

5.14ToElementFinisherExtras ( elementObject )

With parameter elementObject, returns a Record containing three values: { [[Element]]: ElementDescriptor, [[Extras]]: an iterable of other Element Descriptors, [[Finisher]]: a Function or undefined }.

  1. Let element be ? ToElementDescriptor(elementObject).
  2. Let finisher be ? Get(elementObject, "finisher").
  3. If IsCallable(finisher) is false and finisher is not undefined, throw a TypeError exception.
  4. Let extrasObject be ? Get(elementObject, "extras").
  5. Let extras be ? ToElementDescriptors(extrasObject).
  6. Return the Record { [[Element]]: element, [[Finisher]]: finisher, [[Extras]]: extras }.

5.15FromClassDescriptor ( elements )

  1. Assert: elements is a List of ElementDescriptor Records.
  2. Let elementsObjects be FromElementDescriptors(elements).
  3. Let obj be ! ObjectCreate(%ObjectPrototype%).
  4. Let desc be PropertyDescriptor{ [[Value]]: "Descriptor", [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
  5. Perform ! DefinePropertyOrThrow(obj, @@toStringTag, desc).
  6. Perform ! CreateDataPropertyOrThrow(obj, "kind", "class").
  7. Perform ! CreateDataPropertyOrThrow(obj, "elements", elementsObjects).
  8. Return obj.

5.16ToClassDescriptor ( classDescriptor )

  1. Let kind be ? ToString(? Get(classDescriptor, "kind").
  2. If kind is not "class", throw a TypeError exception.
  3. Let key be ? Get(classDescriptor, "key").
  4. If key is not undefined, throw a TypeError exception.
  5. Let placement be ? Get(classDescriptor, "placement").
  6. If placement is not undefined, throw a TypeError exception.
  7. Let descriptor be ? Get(classDescriptor, "descriptor").
  8. If descriptor is not undefined, throw a TypeError exception.
  9. Let initializer be ? Get(classDescriptor, "initializer").
  10. If initializer is not undefined, throw a TypeError exception.
  11. Let extras be ? Get(classDescriptor, "extras").
  12. If extras is not undefined, throw a TypeError exception.
  13. Let finisher be ? Get(classDescriptor, "finisher").
  14. If finisher is not undefined,
    1. If IsCallable(finisher) is false, throw a TypeError exception.
  15. Let elementsObject be ? Get(classDescriptor, "elements").
  16. Let elements be ? ToElementDescriptors(elementsObject).
  17. Return the Record { [[Elements]]: elements, [[Finisher]]: finisher }.

ACopyright & Software License

Copyright Notice

© 2018 Daniel Ehrenberg, Jeff Morrison, Kevin Smith, Kevin Gibbons, Yehuda Katz, Brian Terlson

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.