Stage 2 Draft / January 4, 2017

Decorators

Introduction

Decorators offer a convenient declarative syntax to modify the shape of class declarations. This capability can be used for myriad purposes including modifying the descriptor of the declared member (@nonconfigurable/@enumerable), adding metadata (as used by Angular), and more.

Syntax

As of this proposal, decorators can be attached to either classes or class members. An example of such a class might be:

Example (Informative)
@frozen class Foo {
  @configurable(false) @enumerable(true) method() {}
}

Prior to the class keyword and prior to method definitions are a new DecoratorList production - essentially, a list of Decorator productions. The Decorator production is @ followed by a sequence of dotted identifiers (no [] access is allowed due to grammar difficulties) and an argument list. Thus, @foo.bar() is valid, while @foo().bar() (and @foo[bar]) are syntax errors.

Editor's Note

The syntax here can be pretty much anything, but this proposal takes a maximally conservative approach. Square brackets can be made unambiguous with a clever cover grammar but the syntax is confusing and error-prone.

Elements of the DecoratorList are evaluated in-order but their resulting decorator functions are evaluated in a bottom-up fashion. The value returned by a decorator becomes the input of the next decorator.

Decorator Functions

A member decorator function is a function which takes a member descriptor and which returns a member descriptor. A member descriptor is similar to a property descriptor and has the following shape:

interface MemberDesciptor {
  kind: "Property"
  key: string,
  isStatic: boolean,
  descriptor: PropertyDescriptor,
  extras?: MemberDescriptor[]
  finisher?: (klass): void;
}

A class decorator function is a function which takes a constructor, heritage (parent class), and an array of MemberDescriptors that represent the instance and static members of the class.

There are a number of examples of real-world usage based on previous drafts of this proposal.

Example (Informative)

The core-decorators library includes a number of standalone decorators that are intended to work in vanilla JavaScript code.

An @deprecate decorator:

import { deprecate } from 'core-decorators';

class Person {
  @deprecate
  facepalm() {}

  @deprecate('We stopped facepalming')
  facepalmHard() {}

  @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
  facepalmHarder() {}
}

let person = new Person();

person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.

person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming

person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
//     See http://knowyourmeme.com/memes/facepalm for more details.
//

A throttling decorator:

import { throttle } from 'core-decorators';

class Editor {

  content = '';

  @throttle(500, { leading: false })
  updateContent(content) {
    this.content = content;
  }
}

A mixin class decorator:

import { mixin } from 'core-decorators';

const SingerMixin = {
  sing(sound) {
    alert(sound);
  }
};

const FlyMixin = {
  // All types of property descriptors are supported
  get speed() {},
  fly() {},
  land() {}
};

@mixin(SingerMixin, FlyMixin)
class Bird {
  singMatingCall() {
    this.sing('tweet tweet');
  }
}

var bird = new Bird();
bird.singMatingCall();
// alerts "tweet tweet"

A decorator using console.time to time a function:

class Bird {
  @time('sing')
  sing() {
  }
}

var bird = new Bird();
bird.sing(); // console.time label will be 'sing-0'
bird.sing(); // console.time label will be 'sing-1'

Frameworks

Several frameworks have also adopted decorators in various forms.

Example 1 (Informative)
Aurelia @computedFrom decorator
var x = 1;

import { computedFrom } from 'aurelia-framework';

export class Welcome{
  firstName = 'John';
  lastName = 'Doe';

  @computedFrom('firstName', 'lastName')
  get fullName(){
    return `${this.firstName} ${this.lastName}`;
  }
}
Example 2 (Informative)
Aurelia @customElement, @useShadowDOM, and @bindable decorators
import { customElement, useShadowDOM, bindable } from 'aurelia-framework';

@customElement('nav-bar')
export class NavBar {}

@useShadowDOM
@customElement('my-expander')
export class Expander {
  @bindable isExpanded = false;
  @bindable header;
}
Example 3 (Informative)
Ember @computed decorator
import Ember from 'ember';
import computed from 'ember-computed-decorators';

export default class extends Ember.Component {
  @computed('first', 'last')
  name(first, last) {
    return `${first} ${last}`;
  }
};
Example 4 (Informative)
Ember @readOnly decorator
import Ember from 'ember';
import computed, { readOnly } from 'ember-computed-decorators';

export default class extends Ember.Component {
  @readOnly
  @computed('first', 'last')
  name(first, last) {
    return `${first} ${last}`;
  }
};
Example 5 (Informative)
Ember Data entity relationship decorators
import DS from 'ember-data';
import {
  attr,
  hasMany
} from "ember-computed-decorators/ember-data";

export default class extends DS.Model {
  @attr firstName,
  @hasMany users
};
Example 6 (Informative)
Ember @alias decorator
import Ember from 'ember';
import { alias } from 'ember-computed-decorators';
export default class extends Ember.Component {
  person: {
    first: 'Joe'
  }

  @alias('person.first') firstName
};
Example 7 (Informative)
Ember @intersect decorator
import Ember from 'ember';
import { intersect } from 'ember-computed-decorators';

export default class extends Ember.Component {
  likes = [ 'tacos', 'puppies', 'pizza' ];
  foods = [ 'tacos', 'pizza' ];

  @intersect('likes', 'foods') favoriteFoods; // ['tacos', 'pizza']
};
Example 8 (Informative)
Angular & TypeScript
@Component({
  selector: 'talk-cmp',
  directives: [FormattedRating, WatchButton, RateButton],
  templateUrl: 'talk_cmp.html'
})
class TalkCmp {
  @Input() talk: Talk;
  @Output() rate: EventEmitter;
  // ...
}

1Syntax

1.1New Productions

DecoratorList[Yield]:DecoratorList[?Yield]optDecorator[?Yield] Decorator[Yield]:@DecoratorMemberExpression[?Yield] @DecoratorCallExpression[?Yield] DecoratorMemberExpression[Yield]:IdentifierReference[?Yield] DecoratorMemberExpression[?Yield].IdentifierName DecoratorCallExpression[Yield]:DecoratorMemberExpressionArguments[?Yield]

1.2Updated Productions

PropertyDefinition:MethodDefinition gets an optional DecoratorList.

PropertyDefinition[Yield]:IdentifierReference[?Yield] CoverInitializedName[?Yield] PropertyName[?Yield]:AssignmentExpression[+In, ?Yield] DecoratorList[?Yield]optMethodDefinition[?Yield]

ClassDeclaration:classBindingIdentifierClassTail and ClassDeclaration:[+Default]classClassTail get an optional DecoratorList.

ClassDeclaration[Yield, Default]:DecoratorList[?Yield]optclassBindingIdentifier[?Yield]ClassTail[?Yield] [+Default]DecoratorList[?Yield]optclassClassTail[?Yield]

ClassExpression:classBindingIdentifieroptClassTail gets an optional DecoratorList.

ClassExpression[Yield]:DecoratorList[?Yield]optclassBindingIdentifier[?Yield]optClassTail[?Yield]

ClassElement:MethodDefinition and ClassElement:staticMethodDefinition gets an optional DecoratorList.

ClassElement[Yield]:DecoratorList[?Yield]optMethodDefinition[?Yield] DecoratorList[?Yield]optstaticMethodDefinition[?Yield]

2Semantics

2.1Updated Operations

2.1.1Runtime Semantics: DefineMethod

With parameters object and optional parameter functionPrototype.

MethodDefinition:PropertyName(StrictFormalParameters){FunctionBody}
  1. Let propKey be the result of evaluating PropertyName.
  2. ReturnIfAbrupt(propKey).
  3. If the function code for this MethodDefinition is strict mode code, let strict be true. Otherwise let strict be false.
  4. Let scope be the running execution context's LexicalEnvironment.
  5. If functionPrototype was passed as a parameter, let kind be Normal; otherwise let kind be Method.
  6. Let closure be FunctionCreate(kind, StrictFormalParameters, FunctionBody, scope, strict). If functionPrototype was passed as a parameter, then pass its value as the prototype optional argument of FunctionCreate.
  7. Perform MakeMethod(closure, object).
  8. Return the Record{[[Key]]: propKey, [[Closure]]: closure}.
  9. Let desc be the PropertyDescriptor{[[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  10. Return the Record{[[Key]]: propKey, [[Descriptor]]: desc}.

2.1.2Runtime Semantics: ClassDefinitionEvaluation

With parameter className.

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. If ClassHeritageopt is not present, then
    1. Let protoParent be the intrinsic object %ObjectPrototype%.
    2. Let constructorParent be the intrinsic object %FunctionPrototype%.
  6. 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.
  7. Let proto be ObjectCreate(protoParent).
  8. If ClassBodyopt is not present, let constructor be empty.
  9. Else, let constructor be ConstructorMethod of ClassBody.
  10. 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].
  11. Set the running execution context's LexicalEnvironment to classScope.
  12. Let constructorInfo be the result of performing DefineMethod for constructor with arguments proto and constructorParent as the optional functionPrototype argument.
  13. Assert: constructorInfo is not an abrupt completion.
  14. Let F be constructorInfo.[[Closure]].
  15. If ClassHeritageopt is present and protoParent is not null, then set F.[[ConstructorKind]] to "derived".
  16. Perform MakeConstructor(F, false, proto).
  17. Perform MakeClassConstructor(F).
  18. Perform CreateMethodProperty(proto, "constructor", F).
  19. If ClassBodyopt is not present, let methods be a new empty List.
  20. Else, let methods be NonConstructorMethodDefinitions of ClassBody.
  21. For each ClassElement m in order from methods
    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. If status is an abrupt completion, then
    4. Set the running execution context's LexicalEnvironment to lex.
    5. Return Completion(status).
    6. Let static be IsStatic of m.
    7. If m's DecoratorList is present, then let decorators be the result of performing DecoratorListEvaluation of DecoratorList.
    8. Else, let decorators be a new empty List.
    9. Let finishers be a new empty List.
    10. ReturnIfAbrupt(decorators).
    11. Let el be the result of performing PropertyDefinitionEvaluation for m with arguments proto, false.
    12. If el is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to lex.
      2. Return Completion(el).
    13. Else, let descriptor be descriptor.[[value]].
    14. Let element be the Record{[[Kind]]: "property", [[Static]]: static, [[Key]]: descriptor.[[Key]], [[Descriptor]]: descriptor.[[Descriptor]], [[Decorators]]: decorators}.
    15. Append element to the end of elements.
  22. Let coalesced be the new List created by combining getters and setters with the same [[Key]]. If both a getter and setter with the same [[Key]] have a non-empty [[Decorators]], throw a new Error.
  23. Let elementDescriptors be a new empty List.
  24. For each element in order from coalesced
    1. Let decorated be ? DecorateElement(element).
    2. Append decorated.[[Descriptor]] to the end of elementDescriptors.
    3. Append all elements of decorated.[[Finishers]] to the end of finishers.
    4. For each descriptor in order from decorated.[[Extras]].
      1. Append descriptor to the end of elementDescriptors.
  25. Let constructorInfo be the result of performing DefineMethod for constructor with constructorParent as the optional functionPrototype argument.
  26. Assert: constructorInfo is not an abrupt completion.
  27. Let originalF be constructorInfo.[[Closure]].
  28. Let F be MakeUninitializedConstructor(originalF).
  29. Let result be ? DecorateClass(F, constructorParent, elementDescriptors).
  30. Append all elements of result.[[Finishers]] to the end of finishers.
  31. Let constructor be result.[[Constructor]].
  32. Let elements be result.[[Elements]].
  33. If |ClassHeritage?| is present and protoParent is not null, then set constructor's [[ConstructorKind]] internal slot to "derived".
  34. Perform MakeConstructor(constructor, false, proto).
  35. Perform MakeClassConstructor(constructor).
  36. Perform CreateMethodProperty(proto, "constructor", constructor).
  37. For each descriptor in order from elements, do
    1. Assert: descriptor.[[Kind]] is "property"
    2. If descriptor.[[Static]] is false, then
      1. Let status be DefinePropertyOrThrow(proto, descriptor.[[Key]], descriptor.[[Descriptor]])
    3. Else,
    4. Let status be DefinePropertyOrThrow(constructor, descriptor.[[Key]], descriptor.[[Descriptor]])
    5. If status is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to lex.
      2. Return Completion(status).
  38. Perform MakeFunctionInitialized(constructor).
  39. Perform MakeClassElementsInitialized(elementDescriptors).
  40. Set the running execution context's LexicalEnvironment to lex.
  41. If className is not undefined, then
    1. Perform classScopeEnvRec.InitializeBinding(className, Fconstructor).
  42. For each finisher in order from finishers, do
    1. Perform ? Call(finisher, undefined, « _constructor »).
  43. Return Fconstructor.
Editor's Note

We need to make a decision about what validations we need on the extras list, and when to apply it.

At minimum, if one decorator marks a property as non-configurable, another decorator will not be allowed to modify it.

There are several options:

  • Imperatively apply the extras, and let them naturally merge (or fail in the configurability case).
  • Validate the extras as they are produced.
  • Validate the entire list of extras right before they are applied.

We would prefer to avoid the first option, because it is useful for users of decorators to know if they have caused a name conflict.

Note: The second and third option are observably different in terms of timing.

In terms of validations, we would prefer to disallow naming conflicts caused by extras, which would otherwise become confusing silent errors.

We would also prefer to disallow, as a validation rule, a property made non-configurable by one decorator to be modified by a future one (rather than causing it to fail at application time).

It seems important to allow decorators on a single element to be reordered flexibly, so that for example a @nonconfigurable decorator could be applied before or after an @enumerable decorator. On the other hand, decorators on separate elements that end up affecting class properties of the same name are almost always going to be bugs (analogous to name collisions arising from unhygienic macros). For both of these reasons, it seems important for a single element's decorators to be combined permissively, whereas the extras produced by distinct elements should be validated not to have conflicts.

2.1.3Runtime Semantics: PropertyDefinitionEvaluation

With parameters object and enumerable.

Note
The modifications to PropertyDefinitionEvaluation delays actually building the object by returning a record instead of doing the DefineProperty at this point. We also make any closures uninitialized.
MethodDefinition:PropertyName(StrictFormalParameters){FunctionBody}
  1. Let methodDef be DefineMethod of MethodDefinition with argument object.
  2. ReturnIfAbrupt(methodDef).
  3. Perform SetFunctionName(methodDef.[[Closure]], methodDef.[[Key]]).
  4. Let desc be the PropertyDescriptor{[[Value]]: methodDef.[[Closure]], [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  5. Return ? DefinePropertyOrThrow(object, methodDef.[[Key]], desc).
  6. Let closure be MakeFunctionUninitialized(methodDef.[[Closure]]).
  7. Let desc be the PropertyDescriptor{[[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  8. Return the Record{[[Key]]: methodDef.[[Key]], [[Desc]]: desc, [[Decorators]]: decorators}.
MethodDefinition:getPropertyName(){FunctionBody}
  1. Let propKey be the result of evaluating PropertyName.
  2. ReturnIfAbrupt(propKey).
  3. If the function code for this MethodDefinition is strict mode code, let strict be true. Otherwise let strict be false.
  4. Let scope be the running execution context's LexicalEnvironment.
  5. Let formalParameterList be the production FormalParameters:[empty] .
  6. Let closure be FunctionCreate(Method, formalParameterList, FunctionBody, scope, strict).
  7. Perform MakeMethod(closure, object).
  8. Perform SetFunctionName(closure, propKey, "get").
  9. Let closure be MakeFunctionUninitialized(closure).
  10. Let desc be the PropertyDescriptor{[[Get]]: closure, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  11. Return ? DefinePropertyOrThrow(object, propKey, desc).
  12. Return the Record{[[Key]]: propKey, [[Desc]]: desc}.
MethodDefinition:setPropertyName(PropertySetParameterList){FunctionBody}
  1. Let propKey be the result of evaluating PropertyName.
  2. ReturnIfAbrupt(propKey).
  3. If the function code for this MethodDefinition is strict mode code, let strict be true. Otherwise let strict be false.
  4. Let scope be the running execution context's LexicalEnvironment.
  5. Let closure be FunctionCreate(Method, PropertySetParameterList, FunctionBody, scope, strict).
  6. Perform MakeMethod(closure, object).
  7. Perform SetFunctionName(closure, propKey, "set").
  8. Let closure be MakeFunctionUninitialized(closure).
  9. Let desc be the PropertyDescriptor{[[Set]]: closure, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  10. Return ? DefinePropertyOrThrow(object, propKey, desc).
  11. Return the Record{[[Key]]: propKey, [[Desc]]: desc}.
GeneratorMethod:*PropertyName(StrictFormalParameters){GeneratorBody}
  1. Let propKey be the result of evaluating PropertyName.
  2. ReturnIfAbrupt(propKey).
  3. If the function code for this GeneratorMethod is strict mode code, let strict be true. Otherwise let strict be false.
  4. Let scope be the running execution context's LexicalEnvironment.
  5. Let closure be GeneratorFunctionCreate(Method, StrictFormalParameters, GeneratorBody, scope, strict).
  6. Perform MakeMethod(closure, object).
  7. Let prototype be ObjectCreate(%GeneratorPrototype%).
  8. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
  9. Perform SetFunctionName(closure, propKey).
  10. Let closure be MakeFunctionUninitialized(closure).
  11. Let desc be the PropertyDescriptor{[[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  12. Return ? DefinePropertyOrThrow(object, propKey, desc).
  13. Return the Record{[[Key]]: propKey, [[Desc]]: desc}.

2.2New Operations

2.2.1Runtime Semantics: DecoratorEvaluation

Decorator:@DecoratorMemberExpression[?Yield]
  1. Let _ref be the result of evaluating DecoratorMemberExpression.
  2. Let _value be ? GetValue(ref).
  3. Return value.
Decorator:@DecoratorCallExpression[?Yield]
  1. Let _ref be the result of evaluating DecoratorCallExpression.
  2. Let _value be ? GetValue(ref).
  3. Return value.

2.2.2Runtime 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.

2.2.3Runtime Semantics: MakeFunctionUninitialized

With parameter function.

Editor's Note

The semantics of functions leaked to JavaScript during decoration was contentious in Munich. The exact semantics will be driven by implementation considerations.

2.2.4Runtime Semantics: MakeFunctionInitialized

With parameter function.

Editor's Note

The semantics of functions leaked to JavaScript during decoration was contentious in Munich. The exact semantics will be driven by implementation considerations.

2.2.5Runtime Semantics: IsUninitializedFunction

With parameter function.

Editor's Note

The semantics of functions leaked to JavaScript during decoration was contentious in Munich. The exact semantics will be driven by implementation considerations.

2.2.6Runtime Semantics: MakeClassElementsInitialized

With parameter elements.

  1. For each element in order from elements
    1. Let desc be element.[[Descriptor]].
    2. If desc has a [[Set]] field, MakeFunctionInitialized(desc.[[Set]]).
    3. If desc has a [[Get]] field, MakeFunctionInitialized(desc.[[Get]]).
    4. If desc has a [[Value]] field, and IsUninitializedFunction(desc.[[Value]]) is true, MakeFunctionInitialized(desc.[[Value]]).
Note

The semantics of functions leaked to JavaScript during decoration was contentious in Munich. The exact semantics will be driven by implementation considerations.

2.2.7DecorateElement

With parameter element.

  1. Assert: element.[[Kind]] is "property".
  2. Let extras be a new empty List.
  3. Let finishers be a new empty List.
  4. Let previousDescriptor be ? FromElementDescriptor(element).
  5. For each decorator in element_.[[Decorators]], in reverse list order do
    1. Let result be ? Call(decorator, undefined, « previousDescriptor »).
    2. Let currentDescriptor be ? Get(result, "descriptor").
    3. Let current be ? ToElementDescriptor(result).
    4. If current.[[Finisher]] is not undefined, then
      1. Append current.[[Finisher]] to the end of finishers.
      2. Set current.[[Finisher]] to undefined.
      3. Note: Finishers are not passed forward to the next decorator.
    5. Let previousDescriptor be ? FromElementDescriptor(current).
    6. Let extrasObject be ? Get(result, "extras").
    7. If extrasObject is not undefined, then:
      1. Let iterator be ? GetIterator(extrasObject).
      2. Repeat
        1. Let next be ? IteratorStep(iterator).
        2. If next is false, then break.
        3. Let nextValue be ? IteratorValue(next).
        4. Append nextValue to the end of extras.
  6. Let extras be the result of merging any extras with the same [[Key]] and [[Static]].
  7. Return the Record {[[Descriptor]]: previousDescriptor, [[Extras]]: extras, [[Finishers]]: finishers}.
Note

Future versions of the spec will create element descriptors with other [[Kind]]s.

Editor's Note

There is a decision-point about what validation rules should be applied to the merging that happens right before merging.

2.2.8DecorateClass

With parameters decorators, constructor, heritage and elementDescriptors.

  1. Let elements be a new empty List.
  2. Let finishers be a new empty List.
  3. Let previousConstructor be constructor.
  4. Let previousDescriptors be elementDescriptors.
  5. For each decorator in decorators, in reverse list order do
    1. Let result be ? Call(decorator, undefined, « previousConstructor, heritage, previousDescriptors »).
    2. Let previousConstructor be ? Get(result, "constructor").
    3. Let finisher be ? Get(result, "finisher").
    4. If finisher is not undefined, then append finisher to the end of finishers.
    5. Let elementsObject be ? Get(result, "elements").
    6. If elementsObject is not undefined, then
      1. Let iterator be ? GetIterator(extrasObject).
      2. Repeat
        1. Let next be ? IteratorStep(iterator).
        2. If next is false, then break.
        3. Let nextValue be ? IteratorValue(next).
        4. Append nextValue to the end of elements.
  6. Let elements be the result of merging any elements with the same [[Key]] and [[Static]].
  7. Return the Record {[[Constructor]]: previousConstructor, [[Elements]]: elements, [[Finishers]]: finishers}.
Editor's Note

There is a decision-point about what validation rules should be applied to the merging.

We also need to decide whether to produce an error if the returned constructor from a decorator has the wrong prototype. We think it should be an error.

2.2.9FromElementDescriptor

With parameter element.

  1. Let obj be ! ObjectCreate(%ObjectPrototype%).
  2. Perform ! CreateDataPropertyOrThrow(obj, "kind", element.[[Kind]]).
  3. Perform ! CreateDataPropertyOrThrow(obj, "isStatic", element.[[Static]]).
  4. Perform ! CreateDataPropertyOrThrow(obj, "key", element.[[Key]]).
  5. Perform ! CreateDataPropertyOrThrow(obj, "descriptor", ! FromPropertyDescriptor(element.[[Descriptor]])).
  6. If element.[[Finisher]] is not undefined, then
    1. Perform ! CreateDataPropertyOrThrow(obj, "finisher", element.[[Finisher]]).
  7. Return obj.

2.2.10ToElementDescriptor

With parameter descriptor.

  1. Let kind be ? Get(descriptor, "kind").
  2. If kind is not equal to the string value "property", throw a TypeError exception.
  3. Let static be ToBoolean(? Get(descriptor, "isStatic")).
  4. Let key be ? ToString(? Get(descriptor, "key")).
  5. Let descriptor be ? ToPropertyDescriptor(? Get(descriptor, "descriptor")).
  6. Let hasFinisher be ? HasProperty(descriptor, 'finisher').
  7. If hasFinisher is true, let finisher be ? Get(descriptor, "key").
  8. Else, let finisher be undefined.
  9. Return the Record {[[Kind]]: kind, [[Static]]: static, [[Key]]: key, [[Descriptor]]: descriptor, [[Finisher]]: finisher}.
Editor's Note

Todo: Should probably add validations for having kind, static, and key fields.

ACopyright & Software License

Copyright Notice

© 2017 Yehuda Katz, Brian Terlson, Ecma International

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 http://www.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.