Stage 1 Draft / February 13, 2017

Public Class Fields

Introduction#

Today ES classes are currently limited to declarative specification of methods, but declarative specification of fields is left to ad-hoc expando mutations on class instances in various places.

This proposal aims to provide a declarative mechanism for specifying fields intended to be placed on classes. Such a mechanism is useful for both developers and tooling alike as it provides a place to specify the intended properties.

1New Productions#

1.1PublicFieldDefinition#

PublicFieldDefinition:PropertyName[?Yield]Initializeropt

1.2Static Semantics: ClassPublicFields#

ClassElementList:ClassElement
  1. If ClassElement is the production ClassElement:PublicFieldDefinition , return a List containing ClassElement.
  2. If ClassElement is the production ClassElement:staticPublicFieldDefinition , return a List containing ClassElement.
  3. Return a new empty List.
ClassElementList:ClassElementListClassElement
  1. Let list be ClassPublicFields of ClassElementList.
  2. If ClassElement is the production ClassElement:PublicFieldDefinition , append ClassElement to the end of list.
  3. If ClassElement is the production ClassElement:staticPublicFieldDefinition , append ClassElement to the end of list.
  4. Return list.

1.3ClassPublicFieldDefinitionEvaluation#

With parameters isStatic and homeObject.

PublicFieldDefinition:PropertyName[?Yield]Initializeropt
  1. Let fieldName be the result of performing PropName of PropertyName.
  2. If Initializeropt is present,
    1. Let lex be the Lexical Environment of the running execution context.
    2. Let initializer be FunctionCreate(Method, empty, Initializeropt, lex, true).
    3. Perform MakeMethod(initializer, homeObject).
  3. Else, let initializer be empty.
  4. Return Record{ [[name]]: fieldName, [[initializer]]: initializer, [[static]]: isStatic, }.

1.4Runtime Semantics: EvaluateBody#

With parameter functionObject.

Initializer[In, Yield]:=AssignmentExpression[?In, ?Yield]
  1. Return the result of evaluating AssignmentExpression.

1.5InitializePublicStaticFields(F)#

  1. Assert: Type(F) is Object.
  2. Assert: F is an ECMAScript function object.
  3. Let publicFieldRecords be the value of F's [[PublicFields]] internal slot.
  4. For each item fieldRecord in order from publicFieldRecords,
    1. If fieldRecord.[[static]] is true, then
      1. Let fieldName be fieldRecord.[[name]].
      2. Let initializer be fieldRecord.[[initializer]].
      3. If initializer is not empty, then
        1. Let initValue be ? Call(initializer, undefined).
      4. Else, let initValue be undefined.
      5. Let desc be PropertyDescriptor { [[configurable]]: false, [[enumerable]]: true, [[writable]]: true, [[value]]: initValue }.
      6. Perform ?DefinePropertyOrThrow (F, fieldName, desc).
  5. Return.

1.6InitializePublicInstanceFields ( O, constructor )#

TODO: Factor out the lexical environment wrangling and do that at the callsites of this procedure.

Note We likely want to execute public and private fields their coalesced order of definition, so when the two proposals merge we'll probably want to merge their initialization procedures as well.
  1. Assert: Type ( O ) is Object.
  2. Assert: Assert constructor is an ECMAScript function object.
  3. Let publicFieldRecords be the value of constructor's [[PublicFields]] internal slot.
  4. For each item fieldRecord in order from publicFieldRecords,
    1. If fieldRecord.[[static]] is false, then
      1. Let fieldName be fieldRecord.[[name]]
      2. Let initializer be fieldRecord.[[initializer]].
      3. If initializer is not empty, then
        1. Let initValue be ? Call(initializer, O).
      4. Else,
        1. Let initValue be undefined.
      5. Let desc be PropertyDescriptor { [[configurable]]: false, [[enumerable]]: true, [[writable]]: true, [[value]]: initValue }
      6. Perform ? DefinePropertyOrThrow (O, fieldName, desc)
  5. Set the running execution context's LexicalEnvironment to lex.
  6. Return.

2Modified Productions#

2.1Class Definitions#

2.1.1Static Semantics: IsStatic#

ClassElement:PublicFieldDefinition
  1. Return false.
ClassElement:staticPublicFieldDefinition
  1. Return true.

2.1.2ClassElement#

ClassElement:MethodDefinition[?Yield] staticMethodDefinition[?Yield] PublicFieldDefinition[?Yield] staticPublicFieldDefinition[?Yield] ;

2.1.3Runtime 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
      1. Set the running execution context's LexicalEnvironment to lex.
      2. Return Completion(status).
  22. Set the running execution context's LexicalEnvironment to lex.
  23. If className is not undefined, then
    1. Perform classScopeEnvRec.InitializeBinding(className, F).
  24. If ClassBodyopt is not present, let publicFields be a new empty List.
  25. Else, let publicFields be the result of performing ClassPublicFields of ClassBody.
  26. Let publicFieldRecords be a new empty List.
  27. For each item publicField in order from publicFields,
    1. Let isStatic be the result of performing IsStatic of publicField.
    2. If isStatic, let homeObject be F, otherwise let homeObject be proto.
    3. Let fieldRecord be the result of performing ClassPublicFieldDefinitionEvaluation for publicField with arguments isStatic and homeObject.
    4. Append fieldRecord to publicFieldRecords.
  28. Set the value of F's [[PublicFields]] internal slot to publicFieldRecords.
  29. Set the running execution context's LexicalEnvironment to classScope.
  30. Let result be InitializePublicStaticFields(F).
  31. If result is an abrupt completion, then
    1. Set the running execution context's LexicalEnvironment to lex.
    2. Return Completion(result)
  32. Set the running execution context's LexicalEnvironment to lex.
  33. Return F.

2.2[[Construct]] ( argumentsList, newTarget)#

The [[Construct]] internal method for an ECMAScript Function object F is called with parameters argumentsList and newTarget. argumentsList is a possibly empty List of ECMAScript language values. The following steps are taken:

  1. Assert: F is an ECMAScript function object.
  2. Assert: Type(newTarget) is Object.
  3. Let callerContext be the running execution context.
  4. Let kind be F.[[ConstructorKind]].
  5. If kind is "base", then
    1. Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
  6. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
  7. Assert: calleeContext is now the running execution context.
  8. If kind is "base", then
    1. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
    2. Let result be InitializePublicInstanceFields(thisArgument, F).
    3. If result is an abrupt completion, then
      1. Remove calleeContext from execution context stack and restore callerContext as the running execution context.
      2. Return Completion(result).
  9. Let constructorEnv be the LexicalEnvironment of calleeContext.
  10. Let envRec be constructorEnv's EnvironmentRecord.
  11. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
  12. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
  13. If result.[[Type]] is return, then
    1. If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]).
    2. If kind is "base", return NormalCompletion(thisArgument).
    3. If result.[[Value]] is not undefined, throw a TypeError exception.
  14. Else, ReturnIfAbrupt(result).
  15. Return ? envRec.GetThisBinding().

2.3The super Keyword#

2.3.1Runtime Semantics: Evaluation#

SuperCall:superArguments
  1. Let newTarget be GetNewTarget().
  2. If newTarget is undefined, throw a ReferenceError exception.
  3. Let func be ? GetSuperConstructor().
  4. Let argList be ArgumentListEvaluation of Arguments.
  5. ReturnIfAbrupt(argList).
  6. Let result be ? Construct(func, argList, newTarget).
  7. Let thisER be GetThisEnvironment( ).
  8. Perform ? InitializePublicInstanceFields(thisValue, F).
  9. Return ? thisER.BindThisValue(result).

ACopyright & Software License#

Copyright Notice

© 2017 Jeff Morrison

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.