Stage 3 Draft / October 26, 2017

Integrated public and private fields proposal

Introduction

This document attempts to integrate the public fields and private fields proposals into a single, coherent, orthogonal whole. See the explainer for an overview.

1Syntax

1.1New Productions

ClassElementName[Yield, Await]:PropertyName[?Yield, ?Await] PrivateName FieldDefinition[Yield, Await]:ClassElementName[?Yield, ?Await]Initializer[In, ~Yield, ~Await]opt PrivateName::#IdentifierName

1.2Updated Productions

ClassElement[Yield, Await]:MethodDefinition[?Yield, ?Await] staticMethodDefinition[?Yield, ?Await] FieldDefinition[?Yield, ?Await]; staticFieldDefinition[?Yield, ?Await]; ; MemberExpression[Yield, Await]:PrimaryExpression[?Yield, ?Await] MemberExpression[?Yield, ?Await][Expression[+In, ?Yield, ?Await]] MemberExpression[?Yield, ?Await].IdentifierName MemberExpression[?Yield, ?Await]TemplateLiteral[?Yield, ?Await] SuperProperty[?Yield, ?Await] MetaProperty newMemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await] MemberExpression[?Yield, ?Await].PrivateName CallExpression[Yield, Await]:CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] SuperCall[?Yield, ?Await] CallExpression[?Yield, ?Await]Arguments[?Yield, ?Await] CallExpression[?Yield, ?Await][Expression[+In, ?Yield, ?Await]] CallExpression[?Yield, ?Await].IdentifierName CallExpression[?Yield, ?Await]TemplateLiteral[?Yield, ?Await] CallExpression[?Yield, ?Await].PrivateName

1.3Static Semantics: Early Errors

FieldDefinition:ClassElementNameInitializeropt ClassBody:ClassElementList ClassElement:staticFieldDefinition;
  • It is a Syntax Error if PropName of FieldDefinition is "prototype" or "constructor".
ClassElement:FieldDefinition; ClassElementName:PrivateName;
  • It is a Syntax Error if StringValue of PrivateName is "#constructor".
Note 1

These static rules have been modified to produce an early error if the delete operator is applied to a private reference.

UnaryExpression:deleteUnaryExpression Note 2

The last rule means that expressions such as delete (((foo))) produce early errors because of recursive application of the first rule.

Editor's Note
Private fields may not be deleted in any case. There are only ever private names defined inside class bodies. Inside of a class body, code will be in strict mode, and the above rule applies. Outside of a class body, there will be no private names defined, so the below rule will apply.
ScriptBody:StatementList ModuleBody:ModuleItemList Editor's Note
References to PrivateNames which are not lexically present cause an early error.

1.3.1Static Semantics: AllPrivateNamesValid

AllPrivateNamesValid is an abstract operation which takes names as an argument. MemberExpression[Yield, Await]:MemberExpression[?Yield, ?Await].PrivateName
  1. If StringValue of PrivateName is in names, return true.
  2. Return false.
CallExpression[Yield, Await]:CallExpression[?Yield, ?Await].PrivateName
  1. If StringValue of PrivateName is in names, return true.
  2. Return false.
ClassBody[Yield, Await]:ClassElementList[?Yield, ?Await]
  1. Let newNames be the concatenation of names with PrivateBoundNames of ClassBody.
  2. Return AllPrivateNamesValid of ClassElementList with the argument newNames.
For all other grammatical productions, recurse on subexpressions/substatements, passing in the names of the caller. If all pieces return true, then return true. If any returns false, return false. Editor's Note
TODO: Elaborate the preceding paragraph with spec text inserted in each relevant place

1.3.2Runtime Semantics: PerformEval ( x, evalRealm, strictCaller, direct )

1.3.2.1Additional Early Error Rules for Eval Inside Initializer

These static semantics are applied by PerformEval when a direct eval call occurs inside a class field initializer.

ScriptBody:StatementList

2Modified algorithms

2.1Static Semantics: PropName

FieldDefinition:ClassElementNameInitializeropt
  1. Return PropName of ClassElementName.
ClassElementName[Yield, Await]:PropertyName[?Yield, ?Await]
  1. Return PropName of PropertyName.
ClassElementName[Yield, Await]:PrivateName
  1. Return empty.

2.2Runtime Semantics: Evaluation

ClassElementName[Yield, Await]:PropertyName[?Yield, ?Await]
  1. Return the result of evaluating PropertyName.
ClassElementName[Yield, Await]:PrivateName
  1. Let bindingName be StringValue of PrivateName.
  2. Let field be NewPrivateName(bindingName).
  3. Let scope be the running execution context's PrivateNameEnvironment.
  4. Let scopeEnvRec be scope's EnvironmentRecord.
  5. Perform ! scopeEnvRec.InitializeBinding(bindingName, field).
  6. Return field.
Editor's Note
Each time a class declaration executes, distinct internal Private Names are created. This means, that they cannot directly access each other's private state if a method of one is called with the other as a receiver.

2.3Runtime Semantics: ClassElementEvaluation

With parameters object and enumerable.

ClassElement:staticFieldDefinition;
  1. Return ClassFieldDefinitionEvaluation of FieldDefinition with parameter true and object.
ClassElement:FieldDefinition;
  1. Return ClassFieldDefinitionEvaluation of FieldDefinition with parameter false and object.
ClassElement:MethodDefinition ClassElement:staticMethodDefinition
  1. Perform ? PropertyDefinitionEvaluation with parameters object and enumerable.
  2. Return an empty List.

2.4Runtime Semantics: ClassFieldDefinitionEvaluation

With parameters isStatic and homeObject.

FieldDefinition:ClassElementNameInitializeropt
  1. Let fieldName be the result of evaluating ClassElementName.
  2. ReturnIfAbrupt(fieldName).
  3. If Initializeropt is present,
    1. Let lex be the Lexical Environment of the running execution context.
    2. Let formalParameterList be an instance of the production FormalParameters:[empty] .
    3. Let initializer be FunctionCreate(Method, formalParameterList, Initializer, lex, true).
    4. Perform MakeMethod(initializer, homeObject).
    5. Let isAnonymousFunctionDefinition be IsAnonymousFunctionDefinition(Initializer).
  4. Else,
    1. Let initializer be empty.
    2. Let isAnonymousFunctionDeclaration be false.
  5. Return a List containing Record { [[Name]]: fieldName, [[Initializer]]: initializer, [[Static]]: isStatic, [[IsAnonymousFunctionDefinition]]: isAnonymousFunctionDefinition }.

2.5Runtime Semantics: EvaluateBody

With parameter functionObject and List argumentsList.

Initializer:=AssignmentExpression
  1. Assert: argumentsList is empty.
  2. Return the result of evaluating AssignmentExpression.
Note
FunctionDeclarationInstantiation would not have any observable behaviour here, so its call is omitted.

2.6SetFunctionName ( F, name [ , prefix ] )

The abstract operation SetFunctionName requires a Function argument F, a String, Symbol, or Private Name argument name and optionally a String argument prefix. This operation adds a name property to F by performing the following steps:

  1. Assert: F is an extensible object that does not have a name own property.
  2. Assert: Type(name) is either Symbol, String, or Private Name.
  3. Assert: If prefix was passed, then Type(prefix) is String.
  4. If Type(name) is Symbol, then
    1. Let description be name's [[Description]] value.
    2. If description is undefined, set name to the empty String.
    3. Else, set name to the concatenation of "[", description, and "]".
  5. Else if name is a Private Name,
    1. Set name to name's [[Description]] value.
  6. If prefix was passed, then
    1. Set name to the concatenation of prefix, code unit 0x0020 (SPACE), and name.
  7. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).

2.7DefineField(receiver, fieldRecord)

  1. Assert: Type(receiver) is Object.
  2. Assert: fieldRecord is a Record as created by ClassFieldDefinitionEvaluation.
  3. Let fieldName be fieldRecord.[[Name]].
  4. Let initializer be fieldRecord.[[Initializer]].
  5. If initializer is not empty, then
    1. Let initValue be ? Call(initializer, receiver).
  6. Else, let initValue be undefined.
  7. If fieldRecord.[[IsAnonymousFunctionDefinition]] is true, then
    1. Let hasNameProperty be ? HasOwnProperty(initValue, "name").
    2. If hasNameProperty is false, perform SetFunctionName(initValue, fieldName).
  8. If fieldName is a Private Name,
    1. Perform ? PrivateFieldAdd(fieldName, receiver, initValue).
  9. Else,
    1. Assert: IsPropertyKey(fieldName) is true.
    2. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).
Editor's Note
TODO: Set the running execution context's PrivateNameEnvironment when calling a function (Issue #40).

2.8InitializeStaticFields(F)

  1. Assert: Type(F) is Object.
  2. Assert: F is an ECMAScript function object.
  3. Let fieldRecords be the value of F's [[Fields]] internal slot.
  4. For each item fieldRecord in order from fieldRecords,
    1. If fieldRecord.[[static]] is true, then
      1. Perform ? DefineField(F, fieldRecord).
  5. Return.

2.9InitializeInstanceFields ( O, constructor )

  1. Assert: Type ( O ) is Object.
  2. Assert: Assert constructor is an ECMAScript function object.
  3. Let fieldRecords be the value of constructor's [[Fields]] internal slot.
  4. For each item fieldRecord in order from fieldRecords,
    1. If fieldRecord.[[static]] is false, then
      1. Perform ? DefineField(O, fieldRecord).
  5. Return.
Editor's Note
Private fields are added to the object one by one, interspersed with evaluation of the initializers, following the construction of the receiver. These semantics allow for a later initializer to refer to a previously private field.

2.10Static Semantics: StringValue

PrivateName::#IdentifierName
  1. Return the String value consisting of the sequence of code units corresponding to PrivateName. In determining the sequence any occurrences of \ UnicodeEscapeSequence are first replaced with the code point represented by the UnicodeEscapeSequence and then the code points of the entire PrivateName are converted to code units by UTF16Encoding (10.1.1) each code point.

2.11Static Semantics: PrivateBoundNames

FieldDefinition:ClassElementNameInitializeropt
  1. Return PrivateBoundNames of ClassElementName
ClassElementName:PrivateName
  1. Return a new List containing the StringValue of PrivateName.
ClassElementName:PropertyName
  1. Return a new empty List.
ClassElement:MethodDefinition; ClassElement:staticMethodDefinition; ClassElement:;
  1. Return a new empty List.
ClassElementList:ClassElement
  1. Return PrivateBoundNames of ClassElement.
ClassElementList:ClassElementListClassElement
  1. Let names be PrivateBoundNames of ClassElementList.
  2. Append to names the elements of PrivateBoundNames of ClassElement.
  3. Return names.
ClassBody:ClassElementList
  1. Return PrivateBoundNames of ClassElementList.

2.12Static Semantics: ContainsArguments

IdentifierReference:Identifier
  1. If the StringValue of Identifier is "arguments", return true.
  2. Else, return false.
FunctionDeclaration:functionBindingIdentifier(FormalParameters){FunctionBody} FunctionDeclaration:function(FormalParameters){FunctionBody} FunctionExpression:functionBindingIdentifieropt(FormalParameters){FunctionBody}
  1. Return false.
MethodDefinition:PropertyName(UniqueFormalParameters){FunctionBody} getPropertyName(){FunctionBody} setPropertyName(PropertySetParameterList){FunctionBody}
  1. Return the result of ContainsArguments for PropertyName.
GeneratorMethod:*PropertyName(UniqueFormalParameters){GeneratorBody}
  1. Return the result of ContainsArguments for PropertyName.
GeneratorDeclaration:function*BindingIdentifier(FormalParameters){GeneratorBody} GeneratorDeclaration:function*(FormalParameters){GeneratorBody} GeneratorExpression:function*BindingIdentifieropt(FormalParameters){GeneratorBody}
  1. Return false.
ClassBody:ClassElementList
  1. Return false.
AsyncMethod:async[no LineTerminator here]PropertyName(UniqueFormalParameters){AsyncFunctionBody}
  1. Return the result of ContainsArguments for PropertyName.
AsyncFunctionDeclaration:async[no LineTerminator here]functionBindingIdentifier(FormalParameters){AsyncFunctionBody} AsyncFunctionDeclaration:async[no LineTerminator here]function(FormalParameters){AsyncFunctionBody} AsyncFunctionExpression:async[no LineTerminator here]function(FormalParameters){AsyncFunctionBody} AsyncFunctionExpression:async[no LineTerminator here]functionBindingIdentifier(FormalParameters){AsyncFunctionBody}
  1. Return false.
For all other grammatical productions, recurse on all nonterminals. If any piece returns true, then return true. Otherwise return false. Editor's Note
TODO: Elaborate the preceding paragraph with spec text inserted in each relevant place

2.13Class Definitions

2.13.1Static Semantics: IsStatic

ClassElement:FieldDefinition
  1. Return false.
ClassElement:staticFieldDefinition
  1. Return true.

2.13.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. 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. NOTE: The running execution context's PrivateNameEnvironment is outerPrivateEnvironment when evaluating ClassHeritage.
    3. Let superclass be the result of evaluating ClassHeritage.
    4. Set the running execution context's LexicalEnvironment to lex.
    5. ReturnIfAbrupt(superclass).
    6. If superclass is null, then
      1. Let protoParent be null.
      2. Let constructorParent be the intrinsic object %FunctionPrototype%.
    7. Else if IsConstructor(superclass) is false, throw a TypeError exception.
    8. 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 methods be a new empty List.
  25. Else, let methodselements be NonConstructorMethodDefinitions of ClassBody.
  26. Let fieldRecords be a new empty List.
  27. For each ClassElement me in order from methodselements
    1. If IsStatic of me is false, then
      1. Let fields be the result of performing PropertyDefinitionEvaluation for mClassElementEvaluation for e with arguments proto and false.
    2. Else,
      1. Let fields be the result of performing PropertyDefinitionEvaluation for mClassElementEvaluation for e with arguments F and false.
    3. If fields 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).
    4. Append to fieldRecords the elements of fields.
  28. Set the running execution context's LexicalEnvironment to lex.
  29. If className is not undefined, then
    1. Perform classScopeEnvRec.InitializeBinding(className, F).
  30. Set the value of F's [[Fields]] internal slot to fieldRecords.
  31. Set the running execution context's LexicalEnvironment to classScope.
  32. Set the running execution context's PrivateNameEnvironment to outerPrivateEnvironment.
  33. Let result be InitializeStaticFields(F).
  34. If result is an abrupt completion, then
    1. Set the running execution context's LexicalEnvironment to lex.
    2. Return Completion(result).
  35. Set the running execution context's LexicalEnvironment to lex.
  36. Return F.

2.14[[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 InitializeInstanceFields(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().
Editor's Note
Fields are added by the base class constructor when the super chain reaches up to that, rather than by the subclass constructor when creating the object, in order to be analogous to ES2015 subclassable builtins. See this GitHub thread for more discussion.

2.15The super Keyword

2.15.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. Let F be thisER.[[FunctionObject]].
  9. Assert: F is an ECMAScript function object.
  10. Perform ? InitializeInstanceFields(result, F).
  11. Return ? thisER.BindThisValue(result).

2.16The delete Operator

2.16.1Runtime Semantics: Evaluation

UnaryExpression:deleteUnaryExpression
  1. Let ref be the result of evaluating UnaryExpression.
  2. ReturnIfAbrupt(ref).
  3. If Type(ref) is not Reference, return true.
  4. If IsUnresolvableReference(ref) is true, then
    1. Assert: IsStrictReference(ref) is false.
    2. Return true.
  5. If IsPropertyReference(ref) is true, then
    1. Assert: IsPrivateReference(ref) is false.
    2. If IsSuperReference(ref), throw a ReferenceError exception.
    3. Let baseObj be ! ToObject(GetBase(ref)).
    4. Let deleteStatus be ? baseObj.[[Delete]](GetReferencedName(ref)).
    5. If deleteStatus is false and IsStrictReference(ref) is true, throw a TypeError exception.
    6. Return deleteStatus.
  6. Else ref is a Reference to an Environment Record binding,
    1. Let bindings be GetBase(ref).
    2. Return ? bindings.DeleteBinding(GetReferencedName(ref)).
Note

When a delete operator occurs within strict mode code, a SyntaxError exception is thrown if its UnaryExpression is a direct reference to a variable, function argument, or function name. In addition, if a delete operator occurs within strict mode code and the property to be deleted has the attribute { [[Configurable]]: false }, a TypeError exception is thrown.

2.17Runtime Semantics: CreateDynamicFunction( constructor, newTarget, kind, args )

The abstract operation CreateDynamicFunction is called with arguments constructor, newTarget, kind, and args. constructor is the constructor function that is performing this action, newTarget is the constructor that new was initially applied to, kind is either "normal", "generator", or "async", and args is a List containing the actual argument values that were passed to constructor. The following steps are taken:

  1. Assert: The execution context stack has at least two elements.
  2. Let callerContext be the second to top element of the execution context stack.
  3. Let callerRealm be callerContext's Realm.
  4. Let calleeRealm be the current Realm Record.
  5. Perform ? HostEnsureCanCompileStrings(callerRealm, calleeRealm).
  6. If newTarget is undefined, set newTarget to constructor.
  7. If kind is "normal", then
    1. Let goal be the grammar symbol FunctionBody[~Yield, ~Await].
    2. Let parameterGoal be the grammar symbol FormalParameters[~Yield, ~Await].
    3. Let fallbackProto be "%FunctionPrototype%".
  8. Else if kind is "generator", then
    1. Let goal be the grammar symbol GeneratorBody.
    2. Let parameterGoal be the grammar symbol FormalParameters[+Yield, ~Await].
    3. Let fallbackProto be "%Generator%".
  9. Else,
    1. Assert: kind is "async".
    2. Let goal be the grammar symbol AsyncFunctionBody.
    3. Let parameterGoal be the grammar symbol FormalParameters[~Yield, +Await].
    4. Let fallbackProto be "%AsyncFunctionPrototype%".
  10. Let argCount be the number of elements in args.
  11. Let P be the empty String.
  12. If argCount = 0, let bodyText be the empty String.
  13. Else if argCount = 1, let bodyText be args[0].
  14. Else argCount > 1,
    1. Let firstArg be args[0].
    2. Set P to ? ToString(firstArg).
    3. Let k be 1.
    4. Repeat, while k < argCount-1
      1. Let nextArg be args[k].
      2. Let nextArgString be ? ToString(nextArg).
      3. Set P to the result of concatenating the previous value of P, the String "," (a comma), and nextArgString.
      4. Increase k by 1.
    5. Let bodyText be args[k].
  15. Set bodyText to ? ToString(bodyText).
  16. Let parameters be the result of parsing P, interpreted as UTF-16 encoded Unicode text as described in 6.1.4, using parameterGoal as the goal symbol. Throw a SyntaxError exception if the parse fails.
  17. Let body be the result of parsing bodyText, interpreted as UTF-16 encoded Unicode text as described in 6.1.4, using goal as the goal symbol. Throw a SyntaxError exception if the parse fails.
  18. Let strict be ContainsUseStrict of body.
  19. If any static semantics errors are detected for parameters or body, throw a SyntaxError or a ReferenceError exception, depending on the type of the error. If strict is true, the Early Error rules for UniqueFormalParameters:FormalParameters are applied. Parsing and early error detection may be interweaved in an implementation-dependent manner.
  20. If strict is true and IsSimpleParameterList of parameters is false, throw a SyntaxError exception.
  21. If any element of the BoundNames of parameters also occurs in the LexicallyDeclaredNames of body, throw a SyntaxError exception.
  22. If body Contains SuperCall is true, throw a SyntaxError exception.
  23. If parameters Contains SuperCall is true, throw a SyntaxError exception.
  24. If body Contains SuperProperty is true, throw a SyntaxError exception.
  25. If parameters Contains SuperProperty is true, throw a SyntaxError exception.
  26. If kind is "generator", then
    1. If parameters Contains YieldExpression is true, throw a SyntaxError exception.
  27. If kind is "async", then
    1. If parameters Contains AwaitExpression is true, throw a SyntaxError exception.
  28. If strict is true, then
    1. If BoundNames of parameters contains any duplicate elements, throw a SyntaxError exception.
  29. Let privateNames be an empty List.
  30. If AllPrivateNamesValid of body with the argument privateNames is false, throw a SyntaxError exception.
  31. If AllPrivateNamesValid of parameters with the argument privateNames is false, throw a SyntaxError exception.
  32. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
  33. Let F be FunctionAllocate(proto, strict, kind).
  34. Let realmF be F.[[Realm]].
  35. Let scope be realmF.[[GlobalEnv]].
  36. Perform FunctionInitialize(F, Normal, parameters, body, scope).
  37. If kind is "generator", then
    1. Let prototype be ObjectCreate(%GeneratorPrototype%).
    2. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
  38. Else if kind is "normal", perform MakeConstructor(F).
  39. NOTE: Async functions are not constructable and do not have a [[Construct]] internal method or a "prototype" property.
  40. Perform SetFunctionName(F, "anonymous").
  41. Return F.
Note

A prototype property is created for every non-async function created using CreateDynamicFunction to provide for the possibility that the function will be used as a constructor.

3eval ( x )

3.1Runtime Semantics: EvalDeclarationInstantiation( body, varEnv, lexEnv, strict )

When the abstract operation EvalDeclarationInstantiation is called with arguments body, varEnv, lexEnv, and strict, the following steps are taken:

  1. Let varNames be the VarDeclaredNames of body.
  2. Let varDeclarations be the VarScopedDeclarations of body.
  3. Let lexEnvRec be lexEnv's EnvironmentRecord.
  4. Let varEnvRec be varEnv's EnvironmentRecord.
  5. If strict is false, then
    1. If varEnvRec is a global Environment Record, then
      1. For each name in varNames, do
        1. If varEnvRec.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
        2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
    2. Let thisLex be lexEnv.
    3. Assert: The following loop will terminate.
    4. Repeat while thisLex is not the same as varEnv,
      1. Let thisEnvRec be thisLex's EnvironmentRecord.
      2. If thisEnvRec is not an object Environment Record, then
        1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
        2. For each name in varNames, do
          1. If thisEnvRec.HasBinding(name) is true, then
            1. Throw a SyntaxError exception.
            2. NOTE: Annex B.3.5 defines alternate semantics for the above step.
          2. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
      3. Let thisLex be thisLex's outer environment reference.
  6. Let privateNames be an empty List.
  7. Let privateEnv be the running execution context's PrivateNameEnvironment.
  8. Repeat while privateEnv is not null,
    1. For each binding named N in privateEnv,
      1. If privateNames does not contain N, append N to privateNames.
    2. Let privateEnv be privateEnv's outer environment reference.
  9. If AllPrivateNamesValid of body with the argument privateNames is false, throw a SyntaxError exception.
  10. Let functionsToInitialize be a new empty List.
  11. Let declaredFunctionNames be a new empty List.
  12. For each d in varDeclarations, in reverse list order do
    1. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
      1. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, or an AsyncFunctionDeclaration.
      2. NOTE If there are multiple function declarations for the same name, the last declaration is used.
      3. Let fn be the sole element of the BoundNames of d.
      4. If fn is not an element of declaredFunctionNames, then
        1. If varEnvRec is a global Environment Record, then
          1. Let fnDefinable be ? varEnvRec.CanDeclareGlobalFunction(fn).
          2. If fnDefinable is false, throw a TypeError exception.
        2. Append fn to declaredFunctionNames.
        3. Insert d as the first element of functionsToInitialize.
  13. NOTE: Annex B.3.3.3 adds additional steps at this point.
  14. Let declaredVarNames be a new empty List.
  15. For each d in varDeclarations, do
    1. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
      1. For each String vn in the BoundNames of d, do
        1. If vn is not an element of declaredFunctionNames, then
          1. If varEnvRec is a global Environment Record, then
            1. Let vnDefinable be ? varEnvRec.CanDeclareGlobalVar(vn).
            2. If vnDefinable is false, throw a TypeError exception.
          2. If vn is not an element of declaredVarNames, then
            1. Append vn to declaredVarNames.
  16. NOTE: No abnormal terminations occur after this algorithm step unless varEnvRec is a global Environment Record and the global object is a Proxy exotic object.
  17. Let lexDeclarations be the LexicallyScopedDeclarations of body.
  18. For each element d in lexDeclarations do
    1. NOTE Lexically declared names are only instantiated here but not initialized.
    2. For each element dn of the BoundNames of d do
      1. If IsConstantDeclaration of d is true, then
        1. Perform ? lexEnvRec.CreateImmutableBinding(dn, true).
      2. Else,
        1. Perform ? lexEnvRec.CreateMutableBinding(dn, false).
  19. For each Parse Node f in functionsToInitialize, do
    1. Let fn be the sole element of the BoundNames of f.
    2. Let fo be the result of performing InstantiateFunctionObject for f with argument lexEnv.
    3. If varEnvRec is a global Environment Record, then
      1. Perform ? varEnvRec.CreateGlobalFunctionBinding(fn, fo, true).
    4. Else,
      1. Let bindingExists be varEnvRec.HasBinding(fn).
      2. If bindingExists is false, then
        1. Let status be ! varEnvRec.CreateMutableBinding(fn, true).
        2. Assert: status is not an abrupt completion because of validation preceding step 12.
        3. Perform ! varEnvRec.InitializeBinding(fn, fo).
      3. Else,
        1. Perform ! varEnvRec.SetMutableBinding(fn, fo, false).
  20. For each String vn in declaredVarNames, in list order do
    1. If varEnvRec is a global Environment Record, then
      1. Perform ? varEnvRec.CreateGlobalVarBinding(vn, true).
    2. Else,
      1. Let bindingExists be varEnvRec.HasBinding(vn).
      2. If bindingExists is false, then
        1. Let status be ! varEnvRec.CreateMutableBinding(vn, true).
        2. Assert: status is not an abrupt completion because of validation preceding step 12.
        3. Perform ! varEnvRec.InitializeBinding(vn, undefined).
  21. Return NormalCompletion(empty).

4Private Names and references

The Private Name specification type is used to describe a globally unique identifier which represents a private field name. A private name may be installed on any ECMAScript object with the PrivateFieldAdd internal algorithm, and then read or written using PrivateFieldGet and PrivateFieldSet.

Each PrivateName value immutably holds an associated value called [[Description]] that is a String value.

All ECMAScript objects have a new additional internal slot, [[PrivateFieldValues]], which is a List of Records of the form { [[PrivateName]]: Private Name, [[PrivateFieldValue]]: ECMAScript value }. This List represents the values of the private fields for the object. All objects, including Proxies and all host environment-provided objects, have this internal slot, but primitives such as Numbers do not.

Editor's Note

Private fields are designed to have semantics analogous to WeakMaps. However, the implied garbage collection semantics are weaker: If all the references to a WeakMap are inaccessible, but there is still a reference to a key which was in the WeakMap, one would expect the value to be eventually collected. However, PrivateNameIdentifiers specifically do not have this connotation: because the reference from the Identifier to the Value is in a Record which the Object points to, the value would not be collected, even if nothing else points to the identifier.

Private Names are a specification type here, not directly observable to ECMAScript code. However, in a decorator integration strawman, an object wrapping Private Names would be exposed to allow greater metaprogramming.

Editor's Note
Private fields are deliberately inaccessible outside of the class body. It is proposed that there could be an "escape hatch" to access them though some sort of reflective mechanism; see the GitHub thread. This proposal deliberately omits any such escape hatch.

4.1ObjectCreate (proto [ , internalSlotsList ])

The abstract operation ObjectCreate with argument proto (an object or null) is used to specify the runtime creation of new ordinary objects. The optional argument internalSlotsList is a List of the names of additional internal slots that must be defined as part of the object. If the list is not provided, a new empty List is used. This abstract operation performs the following steps:

  1. If internalSlotsList was not provided, let internalSlotsList be a new empty List.
  2. Let obj be a newly created object with an internal slot for each name in internalSlotsList.
  3. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1.
  4. Set obj.[[Prototype]] to proto.
  5. Set obj.[[Extensible]] to true.
  6. Set obj.[[PrivateFieldValues]] to an empty List.
  7. Return obj.
Editor's Note
TODO: All ECMAScript objects, including Proxies, and any user exotic object, should have a [[PrivateFieldValues]] internal slot initialized to an empty List.

4.2NewPrivateName ( description )

  1. Return a new unique Private Name value whose [[Description]] value is description.

4.3PrivateFieldFind (P, O)

  1. Assert: P is a Private Name value.
  2. Assert: O is an object with a [[PrivateFieldValues]] internal slot.
  3. For each element entry in O.[[PrivateFieldValues]],
    1. If entry.[[PrivateName]] is P, return entry.
  4. Return empty.

4.4PrivateFieldAdd (P, O, value)

  1. Assert: P is a Private Name value.
  2. If O is not an object, throw a TypeError exception.
  3. Let entry be PrivateFieldFind(P, O).
  4. If entry is not empty, throw a TypeError exception.
  5. Append { [[PrivateName]]: P, [[PrivateFieldValue]]: value } to O.[[PrivateFieldValues]].

4.5PrivateFieldGet (P, O )

  1. Assert: P is a Private Name value.
  2. If O is not an object, throw a TypeError exception.
  3. Let entry be PrivateFieldFind(P, O).
  4. If entry is empty, throw a TypeError exception.
  5. Return entry.[[PrivateFieldValue]].

4.6PrivateFieldSet (P, O, value )

  1. Assert: P is a Private Name value.
  2. If O is not an object, throw a TypeError exception.
  3. Let entry be PrivateFieldFind(P, O).
  4. If entry is empty, throw a TypeError exception.
  5. Set entry.[[PrivateFieldValue]] to value.

4.7Runtime Semantics: Evaluation

MemberExpression:MemberExpression.PrivateName
  1. Let baseReference be the result of evaluating MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let bv be ? RequireObjectCoercible(baseValue).
  4. Let fieldNameString be the StringValue of PrivateName.
  5. Return MakePrivateReference(bv, fieldNameString).
CallExpression:CallExpression.PrivateName
  1. Let baseReference be the result of evaluating CallExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let bv be ? RequireObjectCoercible(baseValue).
  4. Let fieldNameString be the StringValue of PrivateName.
  5. Return MakePrivateReference(bv, fieldNameString).

4.8The Reference Specification Type

Note

The Reference type is used to explain the behaviour of such operators as delete, typeof, the assignment operators, the super keyword and other language features. For example, the left-hand operand of an assignment is expected to produce a reference.

A Reference is a resolved name or property binding. A Reference consists of four components, the base value, the referenced name, the Boolean valued strict reference flag, and the Boolean valued private reference flag. The base value is either undefined, an Object, a Boolean, a String, a Symbol, a Number, or an Environment Record (8.1.1). A base value of undefined indicates that the Reference could not be resolved to a binding. The referenced name is a String value or a Symbol value.

A Super Reference is a Reference that is used to represent a name binding that was expressed using the super keyword. A Super Reference has an additional thisValue component and its base value will never be an Environment Record.

The following abstract operations are used in this specification to access the components of references:

  • GetBase(V). Returns the base value component of the reference V.
  • GetReferencedName(V). Returns the referenced name component of the reference V.
  • IsStrictReference(V). Returns the strict reference flag component of the reference V.
  • HasPrimitiveBase(V). Returns true if Type(base) is Boolean, String, Symbol, or Number.
  • IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.
  • IsUnresolvableReference(V). Returns true if the base value is undefined and false otherwise.
  • IsSuperReference(V). Returns true if this reference has a thisValue component.
  • IsPrivateReference(V). Returns the private reference flag component of the reference V.

The following abstract operations are used in this specification to operate on references:

4.8.1GetValue ( V )

  1. ReturnIfAbrupt(V).
  2. If Type(V) is not Reference, return V.
  3. Let base be GetBase(V).
  4. If IsUnresolvableReference(V), throw a ReferenceError exception.
  5. If IsPropertyReference(V), then
    1. If HasPrimitiveBase(V), then
      1. Assert: In this case, base will never be null or undefined.
      2. Let base be ToObject(base).
    2. If IsPrivateReference(V), then
      1. Let env be the running execution context's PrivateNameEnvironment.
      2. Let field be ? ResolveBinding(GetReferencedName(V), env).
      3. Assert: field is a Private Name.
      4. Return ? PrivateFieldGet(field, base).
    3. Return ? base.[[Get]](GetReferencedName(V), GetThisValue(V)).
  6. Else base must be an Environment Record,
    1. Return ? base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)) (see 8.1.1).
Note

The object that may be created in step 5.a.ii is not accessible outside of the above abstract operation and the ordinary object [[Get]] internal method. An implementation might choose to avoid the actual creation of the object.

4.8.2PutValue ( V, W )

  1. ReturnIfAbrupt(V).
  2. ReturnIfAbrupt(W).
  3. If Type(V) is not Reference, throw a ReferenceError exception.
  4. Let base be GetBase(V).
  5. If IsUnresolvableReference(V), then
    1. If IsStrictReference(V) is true, then
      1. Throw a ReferenceError exception.
    2. Let globalObj be GetGlobalObject().
    3. Return ? Set(globalObj, GetReferencedName(V), W, false).
  6. Else if IsPropertyReference(V), then
    1. If HasPrimitiveBase(V) is true, then
      1. Assert: In this case, base will never be null or undefined.
      2. Set base to ToObject(base).
    2. If IsPrivateReference(V), then
      1. Let env be the running execution context's PrivateNameEnvironment.
      2. Let field be ? ResolveBinding(GetReferencedName(V), env).
      3. Assert: field is a Private Name.
      4. Perform ? PrivateFieldSet(field, base, W).
    3. Else,
      1. Let succeeded be ? base.[[Set]](GetReferencedName(V), W, GetThisValue(V)).
      2. If succeeded is false and IsStrictReference(V) is true, throw a TypeError exception.
    4. Return.
  7. Else base must be an Environment Record.
    1. Return ? base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)) (see 8.1.1).
Note

The object that may be created in step 6.a.ii is not accessible outside of the above algorithm and the ordinary object [[Set]] internal method. An implementation might choose to avoid the actual creation of that object.

4.8.3MakePrivateReference ( baseValue, fieldName )

  1. Return a value of type Reference whose base value is baseValue, whose referenced name is fieldName, whose strict reference flag is true, and whose private reference component is true.

4.9Execution Contexts

Table 1: Additional State Components for ECMAScript Code Execution Contexts
Component Purpose
LexicalEnvironment Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
VariableEnvironment Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by VariableStatements within this execution context.
PrivateNameEnvironment Identifies the Lexical Environment whose EnvironmentRecord holds internal private names created by FieldDefinitions.
Note
The PrivateNameEnvironment Lexical Context is always a chain of Declaration Contexts. Each name begins with "#".
Editor's Note
Private names could be specified by lumping it all into the LexicalEnvironment. However, this would create false conflicts with object environment records that would need to be resolved. Further, it seems logically cleaner to separate out the distinct namespace into a distinct object.
When a new execution context is created for an ECMAScript code execution context, the PrivateNameIdentifiers value is inherited from the running execution context, or if none exists, a new Declaration Context with a null parent. Editor's Note
TODO: Elaborate the preceding paragraph with spec text inserted in each relevant place

ACopyright & Software License

Copyright Notice

© 2017 Daniel Ehrenberg, Jeff Morrison, Kevin Smith, Kevin Gibbons

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.