Stage 2 Draft / November 14, 2024

JavaScript Structs

Structs, Shared Structs, Unsafe Blocks, and Synchronization Primitives

We extend the JS language with fixed-layout objects both for unshared and shared uses, unsafe blocks to syntactically limit the scope of where racy accesses may be performed, and high-level synchronization primitive APIs. This spec draft is organized by logical feature.

1 Structs

Structs are fixed-layout objects. They are constructed with the integrity level sealed, and have all declared fields initialized before the instance is made available to user code. They may only extend other structs. Their instance methods are non-generic, and throw TypeError exceptions when the this value is not either an instance of the struct declaration within which the method was declared, or a subclass of that struct declaration.

1.1 Syntax

StructDeclaration[Yield, Await, Default] : struct [no LineTerminator here] BindingIdentifier[?Yield, ?Await] StructTail[?Yield, ?Await] [+Default] struct [no LineTerminator here] StructTail[?Yield, ?Await] StructTail[Yield, Await] : ClassHeritage[?Yield, ?Await]opt { StructBody[?Yield, ?Await]opt } StructBody[Yield, Await] : ClassElementList[?Yield, ?Await] Note

A struct definition is always strict mode code.

1.1.1 Static Semantics: Early Errors

StructBody : ClassElementList

1.1.2 DefineStructField ( receiver, fieldRecord )

The abstract operation DefineStructField takes arguments receiver (an Object) and fieldRecord (a ClassFieldDefinition Record) and returns unused. It performs the following steps when called:

  1. Let fieldName be fieldRecord.[[Name]].
  2. If fieldName is a Private Name, then
    1. Perform ! PrivateFieldAdd(receiver, fieldName, undefined).
  3. Else,
    1. Assert: fieldName is a property key.
    2. Perform ! DefinePropertyOrThrow(receiver, fieldName, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }).
  4. Return unused.

1.1.3 InitializeStructInstanceFieldsAndBrand ( receiver, constructor )

The abstract operation InitializeStructInstanceFieldsAndBrand takes arguments receiver (an Object) and constructor (an ECMAScript function object) and returns unused. It performs the following steps when called:

  1. If constructor.[[ConstructorKind]] is derived, then
    1. Let parent be ! constructor.[[GetPrototypeOf]]().
    2. Perform InitializeStructInstanceFieldsAndBrand(receiver, parent).
  2. If constructor has a [[StructBrand]] internal slot, then
    1. Prepend constructor.[[StructBrand]] to receiver.[[StructBrands]].
    2. NOTE: Shared Struct constructors do not have a [[StructBrand]] internal slot because per-Realm prototypes is currently an open design question and are not included in this draft. Without per-Realm prototypes, Shared Structs cannot have methods, and there are no users of Shared Struct brands.
  3. Let methods be the value of constructor.[[PrivateMethods]].
  4. For each PrivateElement method of methods, do
    1. Perform ! PrivateMethodOrAccessorAdd(receiver, method).
  5. Let fields be the value of constructor.[[Fields]].
  6. For each element fieldRecord of fields, do
    1. Perform DefineStructField(receiver, fieldRecord).
  7. Return unused.

1.1.4 RunFieldInitializer ( receiver, fieldRecord )

The abstract operation RunFieldInitializer takes arguments receiver (an Object) and fieldRecord (a ClassFieldDefinition Record) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  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, receiver).
    2. If fieldName is a Private Name, then
      1. Perform ? PrivateSet(receiver, fieldName, initValue).
    3. Else,
      1. Assert: fieldName is a property key.
      2. Perform ? DefinePropertyOrThrow(receiver, fieldName, PropertyDescriptor { [[Value]]: initValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }).
  4. Return unused.

1.1.5 RunStructInstanceFieldInitializers ( receiver, constructor )

The abstract operation RunStructInstanceFieldInitializers takes arguments receiver (an Object) and constructor (an ECMAScript function object) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. If constructor.[[ConstructorKind]] is derived, then
    1. Let parent be ! constructor.[[GetPrototypeOf]]().
    2. Perform ? RunStructInstanceFieldInitializers(receiver, parent).
  2. Let fields be the value of constructor.[[Fields]].
  3. For each element fieldRecord of fields, do
    1. Perform ? RunFieldInitializer(receiver, fieldRecord).
  4. Return unused.

1.1.6 Runtime Semantics: StructDefinitionEvaluation

The syntax-directed operation StructDefinitionEvaluation takes arguments structBinding (a String or undefined) and structName (a property key) and returns either a normal completion containing a function object or an abrupt completion. It is defined piecewise over the following productions:

StructTail : ClassHeritageopt { StructBodyopt }
  1. Let env be the LexicalEnvironment of the running execution context.
  2. Let structEnv be NewDeclarativeEnvironment(env).
  3. If structBinding is not undefined, then
    1. Perform ! structEnv.CreateImmutableBinding(structBinding, true).
  4. Let outerPrivateEnvironment be the running execution context's PrivateEnvironment.
  5. Let classPrivateEnvironment be NewPrivateEnvironment(outerPrivateEnvironment).
  6. If StructBody is present, then
    1. For each String dn of the PrivateBoundIdentifiers of StructBody, do
      1. If classPrivateEnvironment.[[Names]] contains a Private Name pn such that pn.[[Description]] is dn, then
        1. Assert: This is only possible for getter/setter pairs.
      2. Else,
        1. Let name be a new Private Name whose [[Description]] is dn.
        2. Append name to classPrivateEnvironment.[[Names]].
  7. If ClassHeritage is not present, then
    1. Let protoParent be %Object.prototype%.
    2. Let constructorParent be %Function.prototype%.
  8. Else,
    1. Set the running execution context's LexicalEnvironment to structEnv.
    2. NOTE: The running execution context's PrivateEnvironment is outerPrivateEnvironment when evaluating ClassHeritage.
    3. Let superclassRef be Completion(Evaluation of ClassHeritage).
    4. Set the running execution context's LexicalEnvironment to env.
    5. Let superclass be ? GetValue(? superclassRef).
    6. If superclass is null, then
      1. Let protoParent be null.
      2. Let constructorParent be %Function.prototype%.
    7. Else if superclass does not have a [[IsStructConstructor]] internal slot, then
      1. Throw a TypeError exception.
    8. Else,
      1. Let protoParent be ? Get(superclass, "prototype").
      2. If protoParent is not an Object and protoParent is not null, throw a TypeError exception.
      3. Let constructorParent be superclass.
  9. Let proto be OrdinaryObjectCreate(protoParent, « [[StructBrand]] »).
  10. Let structSerial be the value of GlobalStructSerial.
  11. Set proto.[[StructBrand]] to structSerial.
  12. Set GlobalStructSerial to GlobalStructSerial + 1.
  13. NOTE: GlobalStructSerial is a monotonically increasing integer that is globally available. It is shared by all realms. Prior to the evaluation of any ECMAScript code, it is initialized to 0.
  14. NOTE: Structs have one-shot construction, with the user-defined "constructor" method performing post-construction initialization. By the time ECMAScript code has access to a struct instance, it already has all of its declared fields as own properties.
  15. Set the running execution context's LexicalEnvironment to structEnv.
  16. Set the running execution context's PrivateEnvironment to classPrivateEnvironment.
  17. If StructBody is not present, let initializerParseNode be empty.
  18. Else, let initializerParseNode be ConstructorMethod of StructBody.
  19. If initializerParseNode is empty, then
    1. Let initializer be empty.
  20. Else,
    1. Let initializerInfo be ? DefineMethod of initializerParseNode with arguments proto and constructorParent.
    2. Let initializer be initializerInfo.[[Closure]].
    3. Perform SetFunctionName(initializer, structName).
  21. Let constructor be a new Abstract Closure with no parameters that captures initializer and structSerial and performs the following steps when called:
    1. Let args be the List of arguments that was passed to this function by [[Call]] or [[Construct]].
    2. Let F be the active function object.
    3. If NewTarget is not F, throw a TypeError exception.
    4. Let result be ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%", « [[StructBrands]] »).
    5. Set result.[[StructBrands]] to « structSerial ».
    6. Perform InitializeStructInstanceFieldsAndBrand(result, F).
    7. Perform ! result.[[PreventExtensions]]().
    8. Assert: ! TestIntegrityLevel(result, sealed) is true.
    9. Perform ? RunStructInstanceFieldInitializers(result, F).
    10. If initializer is not empty, then
      1. Perform ? Call(initializer, result).
    11. Return result.
  22. Let F be CreateBuiltinFunction(constructor, 0, structName, « [[ConstructorKind]], [[SourceText]], [[StructBrand]], [[StructInitializer]], [[IsStructConstructor]] », the current Realm Record, constructorParent).
  23. Perform MakeConstructor(F, false, proto).
  24. If ClassHeritage is present, set F.[[ConstructorKind]] to derived.
  25. Set F.[[StructInitializer]] to initializer.
  26. Set F.[[StructBrand]] to structSerial.
  27. Set F.[[IsStructConstructor]] to true.
  28. If StructBody is not present, let elements be a new empty List.
  29. Else, let elements be NonConstructorElements of StructBody.
  30. Let instancePrivateMethods be a new empty List.
  31. Let staticPrivateMethods be a new empty List.
  32. Let instanceFields be a new empty List.
  33. Let staticElements be a new empty List.
  34. For each ClassElement e of elements, do
    1. If IsStatic of e is false, then
      1. Let element be Completion(ClassElementEvaluation of e with argument proto).
    2. Else,
      1. Let element be Completion(ClassElementEvaluation of e with argument F).
    3. If element is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to env.
      2. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
      3. Return ? element.
    4. Set element to ! element.
    5. If element is a PrivateElement, then
      1. Assert: element.[[Kind]] is either method or accessor.
      2. If IsStatic of e is false, let container be instancePrivateMethods.
      3. Else, let container be staticPrivateMethods.
      4. If container contains a PrivateElement pe such that pe.[[Key]] is element.[[Key]], then
        1. Assert: element.[[Kind]] and pe.[[Kind]] are both accessor.
        2. If element.[[Get]] is undefined, then
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Get]]: pe.[[Get]], [[Set]]: element.[[Set]] }.
        3. Else,
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Get]]: element.[[Get]], [[Set]]: pe.[[Set]] }.
        4. Replace pe in container with combined.
      5. Else,
        1. Append element to container.
    6. Else if element is a ClassFieldDefinition Record, then
      1. If IsStatic of e is false, append element to instanceFields.
      2. Else, append element to staticElements.
    7. Else if element is a ClassStaticBlockDefinition Record, then
      1. Append element to staticElements.
  35. Set the running execution context's LexicalEnvironment to env.
  36. If structBinding is not undefined, then
    1. Perform ! structEnv.InitializeBinding(structBinding, F).
  37. Set F.[[PrivateMethods]] to instancePrivateMethods.
  38. Set F.[[Fields]] to instanceFields.
  39. For each PrivateElement method of staticPrivateMethods, do
    1. Perform ! PrivateMethodOrAccessorAdd(F, method).
  40. For each element elementRecord of staticElements, do
    1. If elementRecord is a ClassFieldDefinition Record, then
      1. Let result be Completion(DefineField(F, elementRecord)).
    2. Else,
      1. Assert: elementRecord is a ClassStaticBlockDefinition Record.
      2. Let result be Completion(Call(elementRecord.[[BodyFunction]], F)).
    3. If result is an abrupt completion, then
      1. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
      2. Return ? result.
  41. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
  42. Perform ! SetIntegrityLevel(proto, sealed).
  43. Return F.

1.1.7 Runtime Semantics: BindingStructDeclarationEvaluation

The syntax-directed operation BindingStructDeclarationEvaluation takes no arguments and returns either a normal completion containing a function object or an abrupt completion. It is defined piecewise over the following productions:

StructDeclaration : struct BindingIdentifier StructTail
  1. Let structName be the StringValue of BindingIdentifier.
  2. Let value be ? StructDefinitionEvaluation of StructTail with arguments structName and structName.
  3. Set value.[[SourceText]] to the source text matched by StructDeclaration.
  4. Let env be the running execution context's LexicalEnvironment.
  5. Perform ? InitializeBoundName(structName, value, env).
  6. Return value.
StructDeclaration : struct StructTail
  1. Let value be ? StructDefinitionEvaluation of StructTail with arguments undefined and "default".
  2. Set value.[[SourceText]] to the source text matched by StructDeclaration.
  3. Return value.

1.1.8 Runtime Semantics: Evaluation

StructDeclaration : struct BindingIdentifier StructTail
  1. Perform ? BindingStructDeclarationEvaluation of this StructDeclaration.
  2. Return empty.

1.2 Struct Method Exotic Objects

A struct method exotic object is an exotic object that wraps another method. A struct method exotic object is callable (it has a [[Call]] internal method). Calling a struct method exotic object checks if the this value is a struct instance constructed by the same struct declaration that defined the method, then in calls its wrapped method.

An object is a struct method exotic object if its [[Call]] internal method uses the following implementation, and its other essential internal methods use the definitions found in 4.5.1. These methods are installed in StructMethodCreate.

Struct method exotic objects do not have the internal slots of ECMAScript function objects listed in Table 30. Instead they have the internal slots listed in Table 1, in addition to [[Prototype]] and [[Extensible]].

Table 1: Internal Slots of Struct Method Exotic Objects
Internal Slot Type Description
[[BoundTargetMethod]] a callable Object The wrapped method object.

1.2.1 [[Call]] ( thisArgument, argumentsList )

The [[Call]] internal method of a struct method exotic object F takes arguments thisArgument (an ECMAScript language value) and argumentsList (a List of ECMAScript language values) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. Let target be F.[[BoundTargetMethod]].
  2. Let homeObject be target.[[HomeObject]].
  3. Assert: homeObject is not undefined.
  4. Assert: homeObject has a [[StructBrand]] internal slot.
  5. If thisArgument is not an Object, throw a TypeError exception.
  6. If thisArgument does not have a [[StructBrands]] internal slot, throw a TypeError exception.
  7. If thisArgument.[[StructBrands]] does not contain homeObject.[[StructBrand]], throw a TypeError exception.
  8. Return ? Call(target, thisArgument, argumentsList).

1.2.2 StructMethodCreate ( targetMethod )

The abstract operation StructMethodCreate takes argument targetMethod (a function object) and returns either a normal completion containing a function object or a throw completion. It is used to specify the creation of new struct method exotic objects. It performs the following steps when called:

  1. Let proto be ? targetMethod.[[GetPrototypeOf]]().
  2. Let internalSlotsList be the list-concatenation of « [[Prototype]], [[Extensible]] » and the internal slots listed in Table 1.
  3. Let obj be MakeBasicObject(internalSlotsList).
  4. Set obj.[[Prototype]] to proto.
  5. Set obj.[[Call]] as described in 1.2.1.
  6. Set obj.[[BoundTargetMethod]] to targetMethod.
  7. Return obj.

1.3 Changes to ECMAScript Language: Expressions

1.3.1 Runtime Semantics: Evaluation

SuperCall : super Arguments
  1. Let newTarget be GetNewTarget().
  2. Assert: newTarget is an Object.
  3. Let func be GetSuperConstructor().
  4. Let argList be ? ArgumentListEvaluation of Arguments.
  5. If IsConstructor(func) is false, throw a TypeError exception.
  6. If func has a [[StructInitializer]] internal slot, then
    1. If func.[[StructInitializer]] is not empty, then
      1. Let envRec be GetThisEnvironment().
      2. Let thisValue be envRec.GetThisBinding().
      3. Return ? Call(func.[[StructInitializer]], thisValue).
    2. Else,
      1. Return undefined.
  7. Let result be ? Construct(func, argList, newTarget).
  8. Let thisER be GetThisEnvironment().
  9. Perform ? thisER.BindThisValue(result).
  10. Let F be thisER.[[FunctionObject]].
  11. Assert: F is an ECMAScript function object.
  12. Perform ? InitializeInstanceElements(result, F).
  13. Return result.

1.4 Changes to ECMAScript Language: Functions and Classes

1.4.1 Runtime Semantics: DefineMethod

The syntax-directed operation DefineMethod takes argument object (an Object) and optional argument functionPrototype (an Object) and returns either a normal completion containing a Record with fields [[Key]] (a property key) and [[Closure]] (an ECMAScript function object) or an abrupt completion. It is defined piecewise over the following productions:

MethodDefinition : ClassElementName ( UniqueFormalParameters ) { FunctionBody }
  1. Let propKey be ? Evaluation of ClassElementName.
  2. Let env be the running execution context's LexicalEnvironment.
  3. Let privateEnv be the running execution context's PrivateEnvironment.
  4. If functionPrototype is present, then
    1. Let prototype be functionPrototype.
  5. Else,
    1. Let prototype be %Function.prototype%.
  6. Let sourceText be the source text matched by MethodDefinition.
  7. Let closure be OrdinaryFunctionCreate(prototype, sourceText, UniqueFormalParameters, FunctionBody, non-lexical-this, env, privateEnv).
  8. Perform MakeMethod(closure, object).
  9. If object has a [[StructBrand]] internal slot, then
    1. NOTE: Struct instance methods' home object have a [[StructBrand]] internal slot.
    2. Set closure to ? StructMethodCreate(closure).
  10. Return the Record { [[Key]]: propKey, [[Closure]]: closure }.

1.5 Changes to Modules

ExportDeclaration : export ExportFromClause FromClause ; export NamedExports ; export VariableStatement[~Yield, +Await] export Declaration[~Yield, +Await] export default HoistableDeclaration[~Yield, +Await, +Default] export default ClassDeclaration[~Yield, +Await, +Default] export default StructDeclaration[~Yield, +Await, +Default] export default [lookahead ∉ { function, async [no LineTerminator here] function, class }] AssignmentExpression[+In, ~Yield, +Await] ;

1.5.1 Runtime Semantics: Evaluation

ExportDeclaration : export default StructDeclaration
  1. Let value be ? BindingStructDeclarationEvaluation of StructDeclaration.
  2. Let structName be the sole element of the BoundNames of StructDeclaration.
  3. If structName is "*default*", then
    1. Let env be the running execution context's LexicalEnvironment.
    2. Perform ? InitializeBoundName("*default*", value, env).
  4. Return empty.

2 Shared Structs

2.1 Shared Struct Exotic Objects

Shared Structs are fixed-layout exotic objects that can be shared across agents and be accessed in parallel from multiple agents. They are like structs with more restricted behaviour so as to be possible to be shared across agents. They cannot contain methods or private fields. Their fields can only hold primitives or other shared values. Accessing their fields is unordered by default and is governed by the memory model. Such accesses can be made sequentially consistent by using newly overloaded Atomics methods.

An object is a Shared Struct if its [[GetOwnProperty]], [[DefineOwnProperty]], [[HasProperty]], [[Get]], [[Set]], and [[Delete]] internal methods use the definitions in this section, and its other essential internal methods use the definitions found in 4.5.1.

2.1.1 Critical Section for Shared Struct Creation

2.1.1.1 EnterSharedStructCreationCriticalSection ( )

The abstract operation EnterSharedStructCreationCriticalSection takes no arguments and returns unused. It performs the following steps when called:

  1. Assert: The surrounding agent is not in the critical section for Shared Struct creation.
  2. Wait until no agent is in the critical section for Shared Struct creation, then enter the critical section for Shared Struct creation (without allowing any other agent to enter).
  3. Return unused.

2.1.1.2 LeaveSharedStructCreationCriticalSection ( )

The abstract operation LeaveSharedStructCreationCriticalSection takes no arguments and returns unused. It performs the following steps when called:

  1. Assert: The surrounding agent is in the critical section for Shared Struct creation.
  2. Leave the critical section for Shared Struct creation.
  3. Return unused.
Note

This critical section is a specification semantic prescription of the memory model to prohibit the nondeterministic read in ReadSharedStructField from manifesting Shared Struct values that are partially initialized.

This critical section does not provide any ordering guarantees.

In implementations, this critical section is not needed. Implementations must not allow out-of-thin-air reads.

2.1.2 SharedStructCreate ( initializer [ , internalSlotsList ] )

The abstract operation SharedStructCreate takes argument initializer (an Abstract Closure with one parameter) and optional argument internalSlotsList (a List of internal slot names) and returns a Shared Struct. It performs the following steps when called:

  1. If internalSlotsList is not present, set internalSlotsList to a new empty List.
  2. Perform EnterSharedStructCreationCriticalSection().
  3. Let result be OrdinaryObjectCreate(null, internalSlotsList).
  4. Set result.[[GetOwnProperty]] as specified in 2.1.5.
  5. Set result.[[DefineOwnProperty]] as specified in 2.1.6.
  6. Set result.[[HasProperty]] as specified in 2.1.7.
  7. Set result.[[Get]] as specified in 2.1.8.
  8. Set result.[[Set]] as specified in 2.1.9.
  9. Set result.[[Delete]] as specified in 2.1.10.
  10. Perform initializer(result).
  11. Perform ! result.[[PreventExtensions]]().
  12. Perform LeaveSharedStructCreationCriticalSection().
  13. Assert: ! TestIntegrityLevel(result, sealed) is true.
  14. Return result.

2.1.3 ReadSharedStructField ( struct, field, order )

The abstract operation ReadSharedStructField takes arguments struct (a Shared Struct), field (a property key), and order (seq-cst or unordered) and returns an ECMAScript language value. It performs the following steps when called:

  1. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
  2. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
  3. Let storage be SharedStructStorage { [[Struct]]: struct, [[Field]]: field }.
  4. Perform EnterSharedStructCreationCriticalSection().
  5. Let rawLanguageValue be a nondeterministically chosen ECMAScript language value such that CanBeSharedAcrossAgents(rawLanguageValue) is true.
  6. Perform LeaveSharedStructCreationCriticalSection().
  7. NOTE: In implementations, rawLanguageValue is the result of a non-atomic or atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
  8. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: true, [[Storage]]: storage } to eventsRecord.[[EventList]].
  9. Append readEvent to eventsRecord.[[EventList]].
  10. NOTE: Shared struct field accesses can never tear.
  11. Append Chosen Value Record { [[Event]]: readEvent, [[ChosenValue]]: rawLanguageValue } to execution.[[ChosenValues]].
  12. Return rawLanguageValue.

2.1.4 WriteSharedStructField ( struct, field, value, order )

The abstract operation WriteSharedStructField takes arguments struct (a Shared Struct), field (a property key), value (an ECMAScript language value), and order (seq-cst, unordered, or init) and returns unused. It performs the following steps when called:

  1. Assert: CanBeSharedAcrossAgents(value) is true.
  2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
  3. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
  4. Let storage be SharedStructStorage { [[Struct]]: struct, [[Field]]: field }.
  5. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: true, [[Storage]]: storage, [[Payload]]: value } to eventsRecord.[[EventList]].
  6. NOTE: Shared struct field accesses can never tear.
  7. Return unused.

2.1.5 [[GetOwnProperty]] ( P )

The [[GetOwnProperty]] internal method of a Shared Struct O takes argument P (a property key) and returns a normal completion containing either a Property Descriptor or undefined. It performs the following steps when called:

  1. If O does not have an own property with key P, return undefined.
  2. Let D be a newly created Property Descriptor with no fields.
  3. Let X be O's own property whose key is P.
  4. Assert: X is a data property.
  5. Set D.[[Value]] to ReadSharedStructField(O, P, unordered).
  6. Set D.[[Writable]] to false.
  7. Set D.[[Enumerable]] to true.
  8. Set D.[[Configurable]] to false.
  9. Return D.

2.1.6 [[DefineOwnProperty]] ( P, Desc )

The [[DefineOwnProperty]] internal method of a Shared Struct O takes arguments P (a property key) and Desc (a Property Descriptor) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. Assert: ! TestIntegrityLevel(O, sealed) is true.
  2. Let current be ! O.[[GetOwnProperty]](P).
  3. If current is undefined, return false.
  4. Assert: IsDataDescriptor(current) is true.
  5. Assert: current.[[Enumerable]] is true.
  6. Assert: current.[[Configurable]] is false.
  7. Assert: current.[[Writable]] is true.
  8. If Desc has a [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
  9. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
  10. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
  11. If Desc has a [[Value]] field, then
    1. If CanBeSharedAcrossAgents(Desc.[[Value]]) is false, throw a TypeError exception.
    2. Perform WriteSharedStructField(O, P, Desc.[[Value]], unordered).
  12. Return true.

2.1.7 [[HasProperty]] ( P )

The [[HasProperty]] internal method of a Shared Struct O takes argument P (a property key) and returns a normal completion containing a Boolean. It performs the following steps when called:

  1. If O does not have an own property with key P, return false.
  2. NOTE: [[GetOwnPropertyDescriptor]] is not used to avoid an unnecessary ReadSharedMemory event.
  3. Return true.

2.1.8 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of a shared Struct O takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing undefined or a throw completion. It performs the following steps when called:

  1. If O does not have an own property with key P, return undefined.
  2. If unsafe is false, throw a TypeError exception.
  3. Let ownDesc be ! O.[[GetOwnProperty]](P).
  4. Assert: ownDesc is not undefined.
  5. Return _ownDesc.[[Value]].

2.1.9 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of a Shared Struct O takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If O does not have an own property with key P, return false.
  2. NOTE: [[GetOwnPropertyDescriptor]] is not used to avoid an unnecessary ReadSharedMemory event.
  3. If unsafe is false, throw a TypeError exception.
  4. Let desc be PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.
  5. Return ? O.[[DefineOwnProperty]](P, desc).

2.1.10 [[Delete]] ( P )

The [[Delete]] internal method of a Shared Struct O takes argument P (a property key) and returns a normal completion containing false. It performs the following steps when called:

  1. Return false.

2.2 Syntax

StructDeclaration[Yield, Await, Default] : shared [no LineTerminator here] struct [no LineTerminator here] BindingIdentifier[?Yield, ?Await] SharedStructTail[?Yield, ?Await] [+Default] shared [no LineTerminator here] struct [no LineTerminator here] SharedStructTail[?Yield, ?Await] SharedStructTail[Yield, Await] : ClassHeritage[?Yield, ?Await]opt { SharedStructBody[?Yield, ?Await]opt } SharedStructBody[Yield, Await] : ClassElementList[?Yield, ?Await] Note

A shared struct definition is always strict mode code.

2.2.1 Static Semantics: ContainsInstancePrivateIdentifier

The syntax-directed operation ContainsInstancePrivateIdentifier takes no arguments and returns a Boolean. It is defined piecewise over the following productions:

ClassElementList : ClassElement
  1. Return ContainsInstancePrivateIdentifier of ClassElement.
ClassElementList : ClassElementList ClassElement
  1. If ContainsInstancePrivateIdentifier of ClassElementList, return true.
  2. Return ContainsInstancePrivateIdentifier of ClassElement.
ClassElement : FieldDefinition ;
  1. Return FieldDefinition Contains PrivateIdentifier.
ClassElement : MethodDefinition
  1. Return MethodDefinition Contains PrivateIdentifier.
ClassElement : static FieldDefinition ; static MethodDefinition ClassStaticBlock ;
  1. Return false.

2.2.2 Static Semantics: ContainsInstanceMethod

The syntax-directed operation ContainsInstanceMethod takes no arguments and returns a Boolean. It is defined piecewise over the following productions:

ClassElementList : ClassElement
  1. Return ContainsInstanceMethod of ClassElement.
ClassElementList : ClassElementList ClassElement
  1. If ContainsInstanceMethod of ClassElementList, return true.
  2. Return ContainsInstanceMethod of ClassElement.
ClassElement : MethodDefinition
  1. If ClassElementKind of ClassElement is constructor-method, return false.
  2. Return true.
ClassElement : FieldDefinition ; static FieldDefinition ; static MethodDefinition ClassStaticBlock ;
  1. Return false.

2.2.3 Static Semantics: Early Errors

SharedStructBody : ClassElementList Note

EDITOR'S NOTE: Per-Realm prototypes, which is currently an open design question and not included in this draft, will allow methods.

2.2.4 CanBeSharedAcrossAgents ( val )

The abstract operation CanBeSharedAcrossAgents takes argument val (an ECMAScript language value) and returns a Boolean.

Returns whether ECMAScript language language values can be shared across agents. Primitives can always be shared.

It performs the following steps when called:

  1. If val is undefined, return true.
  2. If val is null, return true.
  3. If val is a String, return true.
  4. If val is a Boolean, return true.
  5. If val is a Number, return true.
  6. If val is a BigInt, return true.
  7. If val is a Symbol, return true.
  8. Assert: val is an Object.
  9. If val is a Shared Struct exotic object, return true.
  10. Return false.

2.2.5 DefineSharedStructField ( receiver, fieldRecord )

The abstract operation DefineSharedStructField takes arguments receiver (a Shared Struct) and fieldRecord (a ClassFieldDefinition Record) and returns unused. It performs the following steps when called:

  1. Assert: The surrounding agent is in the critical section for Shared Struct creation.
  2. Let fieldName be fieldRecord.[[Name]].
  3. Assert: fieldName is a property key.
  4. Create an own data property named fieldName of object receiver whose [[Value]] is undefined, [[Writable]] is true, [[Enumerable]] is true, and [[Configurable]] is false.
  5. Perform WriteSharedStructField(receiver, fieldName, undefined, init).
  6. Return unused.

2.2.6 Runtime Semantics: SharedStructDefinitionEvaluation

The syntax-directed operation SharedStructDefinitionEvaluation takes arguments structBinding (a String or undefined) and structName (a property key) and returns either a normal completion containing a function object or an abrupt completion. It is defined piecewise over the following productions:

SharedStructTail : ClassHeritageopt { SharedStructBodyopt }
  1. Let env be the LexicalEnvironment of the running execution context.
  2. Let structEnv be NewDeclarativeEnvironment(env).
  3. If structBinding is not undefined, then
    1. Perform ! structEnv.CreateImmutableBinding(structBinding, true).
  4. Let outerPrivateEnvironment be the running execution context's PrivateEnvironment.
  5. Let classPrivateEnvironment be NewPrivateEnvironment(outerPrivateEnvironment).
  6. If SharedStructBody is present, then
    1. For each String dn of the PrivateBoundIdentifiers of SharedStructBody, do
      1. If classPrivateEnvironment.[[Names]] contains a Private Name pn such that pn.[[Description]] is dn, then
        1. Assert: This is only possible for getter/setter pairs.
      2. Else,
        1. Let name be a new Private Name whose [[Description]] is dn.
        2. Append name to classPrivateEnvironment.[[Names]].
  7. If ClassHeritage is not present, then
    1. Let constructorParent be %Function.prototype%.
  8. Else,
    1. Set the running execution context's LexicalEnvironment to structEnv.
    2. Let superclassRef be Completion(Evaluation of ClassHeritage).
    3. Set the running execution context's LexicalEnvironment to env.
    4. Let superclass be ? GetValue(? superclassRef).
    5. If superclass is null, then
      1. Let constructorParent be %Function.prototype%.
    6. Else if superclass does not have a [[IsSharedStructConstructor]] internal slot, then
      1. Throw a TypeError exception.
    7. Else,
      1. Let constructorParent be superclass.
  9. Let proto be null.
  10. NOTE: Per-Realm prototypes, which is currently an open design question and not included in this draft, will allow prototypes.
  11. NOTE: Shared Structs have one-shot construction, with the user-defined "constructor" method performing post-construction initialization. By the time ECMAScript code has access to a Shared Struct instance, it already has all of its declared fields as own properties.
  12. Set the running execution context's LexicalEnvironment to structEnv.
  13. Set the running execution context's PrivateEnvironment to classPrivateEnvironment.
  14. If SharedStructBody is not present, let initializerParseNode be empty.
  15. Else, let initializerParseNode be ConstructorMethod of SharedStructBody.
  16. If initializerParseNode is empty, then
    1. Let initializer be empty.
  17. Else,
    1. Let initializerInfo be ? DefineMethod of initializerParseNode with arguments proto and constructorParent.
    2. Let initializer be initializerInfo.[[Closure]].
    3. Perform SetFunctionName(initializer, structName).
  18. Let constructor be a new Abstract Closure with no parameters that captures initializer and performs the following steps when called:
    1. Let args be the List of arguments that was passed to this function by [[Call]] or [[Construct]].
    2. Let F be the active function object.
    3. If NewTarget is not F, throw a TypeError exception.
    4. Let createInitializer be a new Abstract Closure with parameters (newSharedStruct) that captures F and performs the following steps when called:
      1. Perform InitializeStructInstanceFieldsAndBrand(newSharedStruct, F).
      2. Return unused.
    5. Let result be SharedStructCreate(createInitializer).
    6. Perform ? RunStructInstanceFieldInitializers(result, F).
    7. If initializer is not empty, then
      1. Perform ? Call(initializer, result).
    8. Return result.
  19. Let F be CreateBuiltinFunction(constructor, 0, structName, « [[ConstructorKind]], [[SourceText]], [[StructInitializer]], [[IsSharedStructConstructor]] », the current Realm Record, constructorParent).
  20. Perform MakeConstructor(F, false, proto).
  21. If ClassHeritage is present, set F.[[ConstructorKind]] to derived.
  22. Set F.[[StructInitializer]] to initializer.
  23. Set F.[[IsSharedStructConstructor]] to true.
  24. If StructBody is not present, let elements be a new empty List.
  25. Else, let elements be NonConstructorElements of SharedStructBody.
  26. Let staticPrivateMethods be a new empty List.
  27. Let instanceFields be a new empty List.
  28. Let staticElements be a new empty List.
  29. For each ClassElement e of elements, do
    1. If IsStatic of e is false, then
      1. Let element be Completion(ClassElementEvaluation of e with argument proto).
    2. Else,
      1. Let element be Completion(ClassElementEvaluation of e with argument F).
    3. If element is an abrupt completion, then
      1. Set the running execution context's LexicalEnvironment to env.
      2. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
      3. Return ? element.
    4. Set element to ! element.
    5. If element is a PrivateElement, then
      1. Assert: element.[[Kind]] is either method or accessor.
      2. Assert: IsStatic of e is true.
      3. Let container be staticPrivateMethods.
      4. If container contains a PrivateElement pe such that pe.[[Key]] is element.[[Key]], then
        1. Assert: element.[[Kind]] and pe.[[Kind]] are both accessor.
        2. If element.[[Get]] is undefined, then
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Get]]: pe.[[Get]], [[Set]]: element.[[Set]] }.
        3. Else,
          1. Let combined be PrivateElement { [[Key]]: element.[[Key]], [[Kind]]: accessor, [[Get]]: element.[[Get]], [[Set]]: pe.[[Set]] }.
        4. Replace pe in container with combined.
      5. Else,
        1. Append element to container.
    6. Else if element is a ClassFieldDefinition Record, then
      1. If IsStatic of e is false, append element to instanceFields.
      2. Else, append element to staticElements.
    7. Else if element is a ClassStaticBlockDefinition Record, then
      1. Append element to staticElements.
  30. Set the running execution context's LexicalEnvironment to env.
  31. If structBinding is not undefined, then
    1. Perform ! structEnv.InitializeBinding(structBinding, F).
  32. Set F.[[Fields]] to instanceFields.
  33. For each PrivateElement method of staticPrivateMethods, do
    1. Perform ! PrivateMethodOrAccessorAdd(F, method).
  34. For each element elementRecord of staticElements, do
    1. If elementRecord is a ClassFieldDefinition Record, then
      1. Let result be Completion(DefineField(F, elementRecord)).
    2. Else,
      1. Assert: elementRecord is a ClassStaticBlockDefinition Record.
      2. Let result be Completion(Call(elementRecord.[[BodyFunction]], F)).
    3. If result is an abrupt completion, then
      1. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
      2. Return ? result.
  35. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
  36. Perform ! SetIntegrityLevel(F, sealed).
  37. Return F.

2.2.7 Runtime Semantics: BindingStructDeclarationEvaluation

StructDeclaration : shared struct BindingIdentifier SharedStructTail
  1. Let structName be the StringValue of BindingIdentifier.
  2. Let value be ? SharedStructDefinitionEvaluation of StructTail with arguments structName and structName.
  3. Set value.[[SourceText]] to the source text matched by StructDeclaration.
  4. Let env be the running execution context's LexicalEnvironment.
  5. Perform ? InitializeBoundName(structName, value, env).
  6. Return value.
StructDeclaration : shared struct SharedStructTail
  1. Let value be ? SharedStructDefinitionEvaluation of SharedStructTail with arguments undefined and "default".
  2. Set value.[[SourceText]] to the source text matched by StructDeclaration.
  3. Return value.

2.2.8 Runtime Semantics: Evaluation

StructDeclaration : shared struct BindingIdentifier SharedStructTail
  1. Perform ? BindingStructDeclarationEvaluation of this StructDeclaration.
  2. Return empty.

2.3 Changes to the Atomics Object

2.3.1 AtomicCompareExchangeInSharedStruct ( struct, field, expectedValue, replacementValue )

The abstract operation AtomicCompareExchangeInSharedStruct takes arguments struct (a Shared Struct), field (a property key), expectedValue (an ECMAScript language value), and replacementValue (an ECMAScript language value) and returns an ECMAScript language value. It performs the following steps when called:

  1. Assert: CanBeSharedAcrossAgents(replacementValue) is true.
  2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
  3. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
  4. Perform EnterSharedStructCreationCriticalSection().
  5. Let rawLanguageValue be a nondeterministically chosen ECMAScript language value such that CanBeSharedAcrossAgents(rawLanguageValue) is true.
  6. Perform LeaveSharedStructCreationCriticalSection().
  7. NOTE: In implementations, rawLanguageValue is the result of a non-atomic or atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
  8. NOTE: The comparison of the expected value and the read value is performed outside of the read-modify-write modification function to avoid needlessly strong synchronization when the expected value is not equal to the read value.
  9. Let storage be SharedStructStorage { [[Struct]]: struct, [[Field]]: field }.
  10. If SameValue(expectedValue, replacementValue) is true, then
    1. Let second be a new read-modify-write modification function with parameters (oldValue, newValue) that captures nothing and performs the following steps atomically when called:
      1. Return newValue.
    2. Let event be ReadModifyWriteSharedMemory { [[Order]]: seq-cst, [[NoTear]]: true, [[Storage]]: storage, [[Payload]]: replacementValue, [[ModifyOp]]: second }.
  11. Else,
    1. Let event be ReadSharedMemory { [[Order]]: seq-cst, [[NoTear]]: true, [[Storage]]: storage }.
  12. Append event to eventsRecord.[[EventList]].
  13. Append Chosen Value Record { [[Event]]: event, [[ChosenValue]]: rawLanguageValue } to execution.[[ChosenValues]].
  14. Return rawLanguageValue.

2.3.2 AtomicReadModifyWriteInSharedStruct ( struct, field, value, op )

The abstract operation AtomicReadModifyWriteInSharedStruct takes arguments struct (a Shared Struct), field (an ECMAScript language value), value (an ECMAScript language value), and op (a read-modify-write modification function) and returns either a normal completion containing an ECMAScript language value, or a throw completion. It performs the following steps when called:

  1. If field is not a property key, throw a TypeError exception.
  2. If CanBeSharedAcrossAgents(value) is false, throw a TypeError exception.
  3. If struct does not have an own property with key field, throw a RangeError exception.
  4. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
  5. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
  6. Perform EnterSharedStructCreationCriticalSection().
  7. Let rawLanguageValue be a nondeterministically chosen ECMAScript language value such that CanBeSharedAcrossAgents(rawLanguageValue) is true.
  8. Perform LeaveSharedStructCreationCriticalSection().
  9. NOTE: In implementations, rawLanguageValue is the result of a non-atomic or atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
  10. Let storage be SharedStructStorage { [[Struct]]: struct, [[Field]]: field }.
  11. Let rmwEvent be ReadModifyWriteSharedMemory { [[Order]]: seq-cst, [[NoTear]]: true, [[Storage]]: storage, [[Payload]]: rawLanguageValue, [[ModifyOp]]: op }.
  12. Append rmwEvent to eventsRecord.[[EventList]].
  13. Append Chosen Value Record { [[Event]]: rmwEvent, [[ChosenValue]]: rawLanguageValue } to execution.[[ChosenValues]].
  14. Return rawLanguageValue.

2.3.3 Atomics.compareExchange ( typedArraytypedArrayOrStruct, indexindexOrField, expectedValue, replacementValue )

This function performs the following steps when called:

  1. If typedArrayOrStruct is a Shared Struct, then
    1. If indexOrField is not a property key, throw a TypeError exception.
    2. If CanBeSharedAcrossAgents(replacementValue) is false, throw a TypeError exception.
    3. If typedArrayOrStruct does not have an own property with key indexOrField, throw a RangeError exception.
    4. Return AtomicCompareExchangeInSharedStruct(typedArrayOrStruct, indexOrField, expectedValue, replacementValue).
  2. Let typedArray be typedArrayOrStruct.
  3. Let index be indexOrField.
  4. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).
  5. Let buffer be typedArray.[[ViewedArrayBuffer]].
  6. Let block be buffer.[[ArrayBufferData]].
  7. If typedArray.[[ContentType]] is bigint, then
    1. Let expected be ? ToBigInt(expectedValue).
    2. Let replacement be ? ToBigInt(replacementValue).
  8. Else,
    1. Let expected be 𝔽(? ToIntegerOrInfinity(expectedValue)).
    2. Let replacement be 𝔽(? ToIntegerOrInfinity(replacementValue)).
  9. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  10. Let elementType be TypedArrayElementType(typedArray).
  11. Let elementSize be TypedArrayElementSize(typedArray).
  12. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  13. Let expectedBytes be NumericToRawBytes(elementType, expected, isLittleEndian).
  14. Let replacementBytes be NumericToRawBytes(elementType, replacement, isLittleEndian).
  15. If IsSharedArrayBuffer(buffer) is true, then
    1. Let rawBytesRead be AtomicCompareExchangeInSharedBlock(block, byteIndexInBuffer, elementSize, expectedBytes, replacementBytes).
  16. Else,
    1. Let rawBytesRead be a List of length elementSize whose elements are the sequence of elementSize bytes starting with block[byteIndexInBuffer].
    2. If ByteListEqual(rawBytesRead, expectedBytes) is true, then
      1. Store the individual bytes of replacementBytes into block, starting at block[byteIndexInBuffer].
  17. Return RawBytesToNumeric(elementType, rawBytesRead, isLittleEndian).

2.3.4 Atomics.exchange ( typedArraytypedArrayOrStruct, indexindexOrField, value )

This function performs the following steps when called:

  1. Let second be a new read-modify-write modification function with parameters (oldBytesoldValue, newBytesnewValue) that captures nothing and performs the following steps atomically when called:
    1. Return newBytesnewValue.
  2. If _typedArrayOrStruct is a Shared Struct, then
    1. Return ? AtomicReadModifyWriteInSharedStruct(typedArrayOrStruct, indexOrField, value, second).
  3. Let typedArray be typedArrayOrStruct.
  4. Let index be indexOrField.
  5. Return ? AtomicReadModifyWrite(typedArray, index, value, second).

2.3.5 Atomics.load ( typedArraytypedArrayOrStruct, indexindexOrField )

This function performs the following steps when called:

  1. If typedArrayOrStruct is a Shared Struct, then
    1. If indexOrField is not a property key, throw a TypeError exception.
    2. If typedArrayOrStruct does not have an own property with key indexOrField, throw a RangeError exception.
    3. Return ReadSharedStructField(typedArrayOrStruct, indexOrField, seq-cst).
  2. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).
  3. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  4. Let buffer be typedArray.[[ViewedArrayBuffer]].
  5. Let elementType be TypedArrayElementType(typedArray).
  6. Return GetValueFromBuffer(buffer, byteIndexInBuffer, elementType, true, seq-cst).

2.3.6 Atomics.store ( typedArraytypedArrayOrStruct, indexindexOrField, value )

This function performs the following steps when called:

  1. If typedArrayOrStruct is a Shared Struct, then
    1. If indexOrField is not a property key, throw a TypeError exception.
    2. If CanBeSharedAcrossAgents(value) is false, throw a TypeError exception.
    3. If typedArrayOrStruct does not have an own property with key indexOrField, throw a RangeError exception.
    4. Perform WriteSharedStructField(typedArrayOrStruct, indexOrField, value, seq-cst).
    5. Return value.
  2. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).
  3. If typedArray.[[ContentType]] is bigint, let v be ? ToBigInt(value).
  4. Otherwise, let v be 𝔽(? ToIntegerOrInfinity(value)).
  5. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  6. Let buffer be typedArray.[[ViewedArrayBuffer]].
  7. Let elementType be TypedArrayElementType(typedArray).
  8. Perform SetValueInBuffer(buffer, byteIndexInBuffer, elementType, v, true, seq-cst).
  9. Return v.

2.4 Changes to the Reflect Object

2.4.1 Reflect.canBeShared ( val )

  1. Return CanBeSharedAcrossAgents(val).

2.5 Changes to the Memory Model

2.5.1 Memory Model Fundamentals

Shared memory accesses (reads and writes) are divided into two groups, atomic accesses and data accesses, defined below. Atomic accesses are sequentially consistent, i.e., there is a strict total ordering of events agreed upon by all agents in an agent cluster. Non-atomic accesses do not have a strict total ordering agreed upon by all agents, i.e., unordered.

Note

No orderings weaker than sequentially consistent and stronger than unordered, such as release-acquire, are supported.

A Shared Memory Storage Record is either a SharedBlockStorage or SharedStructStorage Record.

Table 2: SharedBlockStorage Fields
Field Name Value Meaning
[[Block]] a Shared Data Block The block the event operates on.
[[ByteIndex]] a non-negative integer The byte address of the access in [[Block]].
[[ElementSize]] a non-negative integer The size of the access.
Table 3: SharedStructStorage Fields
Field Name Value Meaning
[[Struct]] a Shared Struct The shared struct the event operates on.
[[Field]] a property key The field that is accessed in [[Struct]].

A Shared Data Block event is either a ReadSharedMemory, WriteSharedMemory, or ReadModifyWriteSharedMemory Record.

Table 4: ReadSharedMemory Event Fields
Field Name Value Meaning
[[Order]] seq-cst or unordered The weakest ordering guaranteed by the memory model for the event.
[[NoTear]] a Boolean Whether this event is allowed to read from multiple write events with equal range as this event.
[[Block]] a Shared Data Block The block the event operates on.
[[ByteIndex]] a non-negative integer The byte address of the read in [[Block]].
[[ElementSize]] a non-negative integer The size of the read.
[[Storage]] a Shared Memory Storage Record The storage of memory that is read.
Table 5: WriteSharedMemory Event Fields
Field Name Value Meaning
[[Order]] seq-cst, unordered, or init The weakest ordering guaranteed by the memory model for the event.
[[NoTear]] a Boolean Whether this event is allowed to be read from multiple read events with equal range as this event.
[[Block]] a Shared Data Block The block the event operates on.
[[ByteIndex]] a non-negative integer The byte address of the write in [[Block]].
[[ElementSize]] a non-negative integer The size of the write.
[[Storage]] a Shared Memory Storage Record The storage of memory that is written.
[[Payload]] a List of byte values The List of byte values to be read by other events.
Table 6: ReadModifyWriteSharedMemory Event Fields
Field Name Value Meaning
[[Order]] seq-cst Read-modify-write events are always sequentially consistent.
[[NoTear]] true Read-modify-write events cannot tear.
[[Block]] a Shared Data Block The block the event operates on.
[[ByteIndex]] a non-negative integer The byte address of the read-modify-write in [[Block]].
[[ElementSize]] a non-negative integer The size of the read-modify-write.
[[Storage]] a Shared Memory Storage Record The storage of memory of the read-modify-write.
[[Payload]] a List of byte values The List of byte values to be passed to [[ModifyOp]].
[[ModifyOp]] a read-modify-write modification function An abstract closure that returns a modified List of byte values from a read List of byte values and [[Payload]].

These events are introduced by abstract operations or by methods on the Atomics object.

Some operations may also introduce Synchronize events. A Synchronize event has no fields, and exists purely to directly constrain the permitted orderings of other events.

In addition to Shared Data Block and Synchronize events, there are host-specific events.

If the [[Storage]] field of a ReadSharedMemory, WriteSharedMemory, or ReadModifyWriteSharedMemory event is a SharedBlockStorage, then Llet theits range of a ReadSharedMemory, WriteSharedMemory, or ReadModifyWriteSharedMemory event be the Set of contiguous integers from its [[Storage]].[[ByteIndex]] to [[Storage]].[[ByteIndex]] + [[Storage]].[[ElementSize]] - 1. Two events' ranges are equal when the events have a SharedBlockStorage in their [[Storage]] field, have the same [[Storage]].[[Block]], and the ranges are element-wise equal. Two events' ranges are overlapping when the events have the same [[Storage]].[[Block]], the ranges are not equal and their intersection is non-empty. Two events' ranges are disjoint when the events do not both have a SharedBlockStorage in their [[Storage]] field, do not have the same [[Storage]].[[Block]], or their ranges are neither equal nor overlapping.

If the [[Storage]] field of a ReadSharedMemory, WriteSharedMemory, or ReadModifyWriteSharedMemory event is a SharedStructStorage, then let its range be the value of the [[Storage]] field. Two events' ranges are equal when the events have a SharedStructStorage in their [[Storage]] field, have the same [[Storage]].[[Struct]] and the same [[Storage]].[[Field]]. Two events' ranges that both have a SharedStructStorage in their [[Storage]] field are never overlapping. Two events' ranges are disjoint when the events do not both have a SharedStructStorage in their [[Storage]] Field, or do not have the same [[Storage]].[[Struct]] or the same [[Storage]].[[Field]].

For brevity, the refactoring of the memory model relations to use SharedStructStorage and the modified definition of event ranges is omitted.

3 Shared Array Object

Shared Arrays are a special case of Shared Structs with array indexed properties and an immutable "length" own property. Since they are Shared Structs, their layout, i.e. their length, is fixed at creation time.

3.1 The SharedArray Constructor

The SharedArray constructor:

  • is %SharedArray%.
  • is the initial value of the "SharedArray" property of the global object.
  • creates and initializes a new Shared Array when called as a constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • is a function whose behaviour differs based upon the number and types of its arguments.

3.1.1 SharedArrayCreate ( length )

The abstract operation SharedArrayCreate takes argument length (an non-negative integer) and returns a Shared Array. It is used to specify the creation of new Shared Arrays. It performs the following steps when called:

  1. Assert: length ≤ 232 - 1.
  2. Let createInitializer be a new Abstract Closure with parameters (newSharedArray) that captures length and performs the following steps when called:
    1. Let k be 0.
    2. Repeat, while k < length,
      1. Let Pk be ! ToString(𝔽(k)).
      2. Create an own data property named Pk of object newSharedArray whose [[Value]] is undefined, [[Writable]] is true, [[Enumerable]] is true, and [[Configurable]] is false.
      3. Perform WriteSharedStructField(newSharedArray, Pk, undefined, init).
    3. Create an own data property named "length" of object newSharedArray whose [[Value]] is 𝔽(length), [[Writable]] is false, [[Enumerable]] is false, and [[Configurable]] is false.
    4. Perform WriteSharedStructField(newSharedArray, "length", 𝔽(length), init).
    5. Return unused.
  3. Return SharedStructCreate(createInitializer).

3.1.2 SharedArray ( ...values )

This function performs the following steps when called:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. EDITOR'S NOTE: Per-Realm prototypes, which is currently an open design question and not included in this draft, will give Shared Arrays a per-Realm prototype with built-in methods.
  3. Let numberOfArgs be the number of elements in values.
  4. If numberOfArgs = 0, then
    1. Return SharedArrayCreate(0).
  5. Else if numberOfArgs = 1, then
    1. Let len be values[0].
    2. If len is not an integral Number, throw a TypeError exception.
    3. If len < 0, throw a RangeError exception.
    4. Let lenReal be (len).
    5. If lenReal > 232 - 1, throw a RangeError exception.
    6. Return SharedArrayCreate(lenReal).
  6. Else,
    1. Assert: numberOfArgs ≥ 2.
    2. Let array be SharedArrayCreate(numberOfArgs).
    3. Let k be 0.
    4. Repeat, while k < numberOfArgs,
      1. Let Pk be ! ToString(𝔽(k)).
      2. Let itemK be values[k].
      3. Perform ! Set(array, Pk, itemK, true).
      4. Set k to k + 1.
    5. Return array.

4 The unsafe Block

Syntax

BlockStatement[Yield, Await, Return] : Block[?Yield, ?Await, ?Return] UnsafeBlock[?Yield, ?Await, ?Return] UnsafeBlock[Yield, Await, Return] : unsafe [no LineTerminator here] Block[?Yield, ?Await, ?Return]

4.1 Static Semantics: IsUnsafe ( node )

The abstract operation IsUnsafe takes argument node (a Parse Node) and returns a Boolean. It performs the following steps when called:

  1. If node is contained within an UnsafeBlock, return true; else return false.

4.2 Runtime Semantics: Evaluation

UnsafeBlock : unsafe Block
  1. Return ? Evaluation of Block.

4.3 Changes to Reference Records

4.3.1 The Reference Record Specification Type

The Reference Record 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 Record.

A Reference Record is a resolved name or property binding; its fields are defined by Table 7.

Table 7: Reference Record Fields
Field Name Value Meaning
[[Base]] an ECMAScript language value, an Environment Record, or unresolvable The value or Environment Record which holds the binding. A [[Base]] of unresolvable indicates that the binding could not be resolved.
[[ReferencedName]] a String, a Symbol, or a Private Name The name of the binding. Always a String if [[Base]] value is an Environment Record.
[[Strict]] a Boolean true if the Reference Record originated in strict mode code, false otherwise.
[[Unsafe]] a Boolean true if the Reference Record originated in an UnsafeBlock, false otherwise.
[[ThisValue]] an ECMAScript language value or empty If not empty, the Reference Record represents a property binding that was expressed using the super keyword; it is called a Super Reference Record and its [[Base]] value will never be an Environment Record. In that case, the [[ThisValue]] field holds the this value at the time the Reference Record was created.

The following abstract operations are used in this specification to operate upon Reference Records:

4.3.1.1 GetValue ( V )

The abstract operation GetValue takes argument V (a Reference Record or an ECMAScript language value) and returns either a normal completion containing an ECMAScript language value or an abrupt completion. It performs the following steps when called:

  1. If V is not a Reference Record, return V.
  2. If IsUnresolvableReference(V) is true, throw a ReferenceError exception.
  3. If IsPropertyReference(V) is true, then
    1. Let baseObj be ? ToObject(V.[[Base]]).
    2. If IsPrivateReference(V) is true, then
      1. Return ? PrivateGet(baseObj, V.[[ReferencedName]]).
    3. Return ? baseObj.[[Get]](V.[[ReferencedName]], GetThisValue(V), V.[[Unsafe]]).
  4. Else,
    1. Let base be V.[[Base]].
    2. Assert: base is an Environment Record.
    3. Return ? base.GetBindingValue(V.[[ReferencedName]], V.[[Strict]]) (see 9.1).
Note

The object that may be created in step 3.a 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.3.1.2 PutValue ( V, W )

The abstract operation PutValue takes arguments V (a Reference Record or an ECMAScript language value) and W (an ECMAScript language value) and returns either a normal completion containing unused or an abrupt completion. It performs the following steps when called:

  1. If V is not a Reference Record, throw a ReferenceError exception.
  2. If IsUnresolvableReference(V) is true, then
    1. If V.[[Strict]] is true, throw a ReferenceError exception.
    2. Let globalObj be GetGlobalObject().
    3. Perform ? Set(globalObj, V.[[ReferencedName]], W, false).
    4. Return unused.
  3. If IsPropertyReference(V) is true, then
    1. Let baseObj be ? ToObject(V.[[Base]]).
    2. If IsPrivateReference(V) is true, then
      1. Return ? PrivateSet(baseObj, V.[[ReferencedName]], W).
    3. Let succeeded be ? baseObj.[[Set]](V.[[ReferencedName]], W, GetThisValue(V), V.[[Unsafe]]).
    4. If succeeded is false and V.[[Strict]] is true, throw a TypeError exception.
    5. Return unused.
  4. Else,
    1. Let base be V.[[Base]].
    2. Assert: base is an Environment Record.
    3. Return ? base.SetMutableBinding(V.[[ReferencedName]], W, V.[[Strict]]) (see 9.1).
Note

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

4.4 Changes to the Object Type

4.4.1 Object Internal Methods and Internal Slots

Table 8: Essential Internal Methods
Internal Method Signature Description
[[Get]] (propertyKey, Receiver, unsafe) any Return the value of the property whose key is propertyKey from this object. If any ECMAScript code must be executed to retrieve the property value, Receiver is used as the this value when evaluating the code. If this operation is to be performed from within an unsafe block, unsafe will be true; otherwise, unsafe will be false.
[[Set]] (propertyKey, value, Receiver, unsafe) Boolean Set the value of the property whose key is propertyKey to value. If any ECMAScript code must be executed to set the property value, Receiver is used as the this value when evaluating the code. If this operation is to be performed from within an unsafe block, unsafe will be true; otherwise, unsafe will be false. Returns true if the property value was set or false if it could not be set.
Note

DRAFT NOTE: Except where specified, it is assumed that existing invocations of [[Get]] or [[Set]] that do not currently pass a value for the unsafe parameter are assumed to pass false. These call sites will be updated when the fully integrated specification text becomes available during Stage 2.

4.5 Changes to Ordinary and Exotic Objects Behaviours

4.5.1 Ordinary Object Internal Methods and Internal Slots

4.5.1.1 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of an ordinary object O takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. Return ? OrdinaryGet(O, P, Receiver, unsafe).

4.5.1.1.1 OrdinaryGet ( O, P, Receiver, unsafe )

The abstract operation OrdinaryGet takes arguments O (an Object), P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. Let desc be ? O.[[GetOwnProperty]](P).
  2. If desc is undefined, then
    1. Let parent be ? O.[[GetPrototypeOf]]().
    2. If parent is null, return undefined.
    3. Return ? parent.[[Get]](P, Receiver, unsafe).
  3. If IsDataDescriptor(desc) is true, return desc.[[Value]].
  4. Assert: IsAccessorDescriptor(desc) is true.
  5. Let getter be desc.[[Get]].
  6. If getter is undefined, return undefined.
  7. Return ? Call(getter, Receiver).

4.5.1.2 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of an ordinary object O takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. Return ? OrdinarySet(O, P, V, Receiver, unsafe).

4.5.1.2.1 OrdinarySet ( O, P, V, Receiver, unsafe )

The abstract operation OrdinarySet takes arguments O (an Object), P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. Let ownDesc be ? O.[[GetOwnProperty]](P).
  2. Return ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc, unsafe).

4.5.1.2.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc, unsafe )

The abstract operation OrdinarySetWithOwnDescriptor takes arguments O (an Object), P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), ownDesc (a Property Descriptor or undefined), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If ownDesc is undefined, then
    1. Let parent be ? O.[[GetPrototypeOf]]().
    2. If parent is not null, then
      1. Return ? parent.[[Set]](P, V, Receiver, unsafe).
    3. Else,
      1. Set ownDesc to the PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
  2. If IsDataDescriptor(ownDesc) is true, then
    1. If ownDesc.[[Writable]] is false, return false.
    2. If Receiver is not an Object, return false.
    3. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
    4. If existingDescriptor is not undefined, then
      1. If IsAccessorDescriptor(existingDescriptor) is true, return false.
      2. If existingDescriptor.[[Writable]] is false, return false.
      3. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
      4. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
    5. Else,
      1. Assert: Receiver does not currently have a property P.
      2. Return ? CreateDataProperty(Receiver, P, V).
  3. Assert: IsAccessorDescriptor(ownDesc) is true.
  4. Let setter be ownDesc.[[Set]].
  5. If setter is undefined, return false.
  6. Perform ? Call(setter, Receiver, « V »).
  7. Return true.

4.5.2 Built-in Exotic Object Internal Methods and Slots

4.5.2.1 Arguments Exotic Objects

4.5.2.1.1 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of an arguments exotic object args takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. Let map be args.[[ParameterMap]].
  2. Let isMapped be ! HasOwnProperty(map, P).
  3. If isMapped is false, then
    1. Return ? OrdinaryGet(args, P, Receiver, unsafe).
  4. Else,
    1. Assert: map contains a formal parameter mapping for P.
    2. Return ! Get(map, P).

4.5.2.1.2 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of an arguments exotic object args takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If SameValue(args, Receiver) is false, then
    1. Let isMapped be false.
  2. Else,
    1. Let map be args.[[ParameterMap]].
    2. Let isMapped be ! HasOwnProperty(map, P).
  3. If isMapped is true, then
    1. Assert: The following Set will succeed, since formal parameters mapped by arguments objects are always writable.
    2. Perform ! Set(map, P, V, false).
  4. Return ? OrdinarySet(args, P, V, Receiver, unsafe).

4.5.2.2 TypedArray Exotic Objects

4.5.2.2.1 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of a TypedArray O takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. If P is a String, then
    1. Let numericIndex be CanonicalNumericIndexString(P).
    2. If numericIndex is not undefined, then
      1. Return TypedArrayGetElement(O, numericIndex).
  2. Return ? OrdinaryGet(O, P, Receiver, unsafe).

4.5.2.2.2 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of a TypedArray O takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If P is a String, then
    1. Let numericIndex be CanonicalNumericIndexString(P).
    2. If numericIndex is not undefined, then
      1. If SameValue(O, Receiver) is true, then
        1. Perform ? TypedArraySetElement(O, numericIndex, V).
        2. Return true.
      2. If IsValidIntegerIndex(O, numericIndex) is false, return true.
  2. Return ? OrdinarySet(O, P, V, Receiver, unsafe).

4.5.2.3 Module Namespace Exotic Objects

4.5.2.3.1 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of a module namespace exotic object O takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. If P is a Symbol, then
    1. Return ! OrdinaryGet(O, P, Receiver, unsafe).
  2. Let exports be O.[[Exports]].
  3. If exports does not contain P, return undefined.
  4. Let m be O.[[Module]].
  5. Let binding be m.ResolveExport(P).
  6. Assert: binding is a ResolvedBinding Record.
  7. Let targetModule be binding.[[Module]].
  8. Assert: targetModule is not undefined.
  9. If binding.[[BindingName]] is namespace, then
    1. Return GetModuleNamespace(targetModule).
  10. Let targetEnv be targetModule.[[Environment]].
  11. If targetEnv is empty, throw a ReferenceError exception.
  12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).
Note

ResolveExport is side-effect free. Each time this operation is called with a specific exportName, resolveSet pair as arguments it must return the same result. An implementation might choose to pre-compute or cache the ResolveExport results for the [[Exports]] of each module namespace exotic object.

4.5.2.3.2 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of a module namespace exotic object takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns a normal completion containing false. It performs the following steps when called:

  1. Return false.

4.5.3 Proxy Object Internal Methods and Internal Slots

4.5.3.1 [[Get]] ( P, Receiver, unsafe )

The [[Get]] internal method of a Proxy exotic object O takes arguments P (a property key), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. Perform ? ValidateNonRevokedProxy(O).
  2. Let target be O.[[ProxyTarget]].
  3. Let handler be O.[[ProxyHandler]].
  4. Assert: handler is an Object.
  5. Let trap be ? GetMethod(handler, "get").
  6. If trap is undefined, then
    1. Return ? target.[[Get]](P, Receiver, true).
  7. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
  8. Let targetDesc be ? target.[[GetOwnProperty]](P).
  9. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
    1. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
      1. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
    2. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
      1. If trapResult is not undefined, throw a TypeError exception.
  10. Return trapResult.
Note 1

[[Get]] for Proxy objects enforces the following invariants:

  • The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable own data property.
  • The value reported for a property must be undefined if the corresponding target object property is a non-configurable own accessor property that has undefined as its [[Get]] attribute.
Note 2

Proxies are always unsafe and permit reading from fields on Shared Struct instances even outside of an unsafe {} block.

4.5.3.2 [[Set]] ( P, V, Receiver, unsafe )

The [[Set]] internal method of a Proxy exotic object O takes arguments P (a property key), V (an ECMAScript language value), Receiver (an ECMAScript language value), and unsafe (a Boolean) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. Perform ? ValidateNonRevokedProxy(O).
  2. Let target be O.[[ProxyTarget]].
  3. Let handler be O.[[ProxyHandler]].
  4. Assert: handler is an Object.
  5. Let trap be ? GetMethod(handler, "set").
  6. If trap is undefined, then
    1. Return ? target.[[Set]](P, V, Receiver, true).
  7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
  8. If booleanTrapResult is false, return false.
  9. Let targetDesc be ? target.[[GetOwnProperty]](P).
  10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
    1. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
      1. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
    2. If IsAccessorDescriptor(targetDesc) is true, then
      1. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
  11. Return true.
Note 1

[[Set]] for Proxy objects enforces the following invariants:

  • The result of [[Set]] is a Boolean value.
  • Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable own data property.
  • Cannot set the value of a property if the corresponding target object property is a non-configurable own accessor property that has undefined as its [[Set]] attribute.
Note 2

Proxies are always unsafe and permit writing to fields on Shared Struct instances even outside of an unsafe {} block.

4.6 Changes to Expressions

4.6.1 Left-Hand-Side Expressions

4.6.1.1 Property Accessors

4.6.1.1.1 Runtime Semantics: Evaluation

MemberExpression : MemberExpression [ Expression ]
  1. Let baseReference be ? Evaluation of MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let strict be IsStrict(this MemberExpression).
  4. Let unsafe be IsUnsafe(this MemberExpression).
  5. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict, unsafe).
MemberExpression : MemberExpression . IdentifierName
  1. Let baseReference be ? Evaluation of MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let strict be IsStrict(this MemberExpression).
  4. Let unsafe be IsUnsafe(this MemberExpression).
  5. Return EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict, unsafe).
MemberExpression : MemberExpression . PrivateIdentifier
  1. Let baseReference be ? Evaluation of MemberExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let fieldNameString be the StringValue of PrivateIdentifier.
  4. Return MakePrivateReference(baseValue, fieldNameString).
CallExpression : CallExpression [ Expression ]
  1. Let baseReference be ? Evaluation of CallExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let strict be IsStrict(this CallExpression).
  4. Let unsafe be IsUnsafe(this MemberExpression).
  5. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict, unsafe).
CallExpression : CallExpression . IdentifierName
  1. Let baseReference be ? Evaluation of CallExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let strict be IsStrict(this CallExpression).
  4. Let unsafe be IsUnsafe(this MemberExpression).
  5. Return EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict, unsafe).
CallExpression : CallExpression . PrivateIdentifier
  1. Let baseReference be ? Evaluation of CallExpression.
  2. Let baseValue be ? GetValue(baseReference).
  3. Let fieldNameString be the StringValue of PrivateIdentifier.
  4. Return MakePrivateReference(baseValue, fieldNameString).

4.6.1.2 EvaluatePropertyAccessWithExpressionKey ( baseValue, expression, strict, unsafe )

The abstract operation EvaluatePropertyAccessWithExpressionKey takes arguments baseValue (an ECMAScript language value), expression (an Expression Parse Node), strict (a Boolean), and unsafe (a Boolean) and returns either a normal completion containing a Reference Record or an abrupt completion. It performs the following steps when called:

  1. Let propertyNameReference be ? Evaluation of expression.
  2. Let propertyNameValue be ? GetValue(propertyNameReference).
  3. Let propertyKey be ? ToPropertyKey(propertyNameValue).
  4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyKey, [[Strict]]: strict, [[Unsafe]]: unsafe, [[ThisValue]]: empty }.

4.6.1.3 EvaluatePropertyAccessWithIdentifierKey ( baseValue, identifierName, strict, unsafe )

The abstract operation EvaluatePropertyAccessWithIdentifierKey takes arguments baseValue (an ECMAScript language value), identifierName (an IdentifierName Parse Node), strict (a Boolean), and unsafe (a Boolean) and returns a Reference Record. It performs the following steps when called:

  1. Let propertyNameString be the StringValue of identifierName.
  2. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: propertyNameString, [[Strict]]: strict, [[Unsafe]]: unsafe, [[ThisValue]]: empty }.

4.6.1.4 Optional Chains

4.6.1.4.1 Runtime Semantics: Evaluation

4.6.1.4.1.1 Runtime Semantics: ChainEvaluation

The syntax-directed operation ChainEvaluation takes arguments baseValue (an ECMAScript language value) and baseReference (an ECMAScript language value or a Reference Record) and returns either a normal completion containing either an ECMAScript language value or a Reference Record, or an abrupt completion. It is defined piecewise over the following productions:

OptionalChain : ?. Arguments
  1. Let thisChain be this OptionalChain.
  2. Let tailCall be IsInTailPosition(thisChain).
  3. Return ? EvaluateCall(baseValue, baseReference, Arguments, tailCall).
OptionalChain : ?. [ Expression ]
  1. Let strict be IsStrict(this OptionalChain).
  2. Let unsafe be IsUnsafe(this MemberExpression).
  3. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict, unsafe).
OptionalChain : ?. IdentifierName
  1. Let strict be IsStrict(this OptionalChain).
  2. Let unsafe be IsUnsafe(this MemberExpression).
  3. Return EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict, unsafe).
OptionalChain : ?. PrivateIdentifier
  1. Let fieldNameString be the StringValue of PrivateIdentifier.
  2. Return MakePrivateReference(baseValue, fieldNameString).
OptionalChain : OptionalChain Arguments
  1. Let optionalChain be OptionalChain.
  2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
  3. Let newValue be ? GetValue(newReference).
  4. Let thisChain be this OptionalChain.
  5. Let tailCall be IsInTailPosition(thisChain).
  6. Return ? EvaluateCall(newValue, newReference, Arguments, tailCall).
OptionalChain : OptionalChain [ Expression ]
  1. Let optionalChain be OptionalChain.
  2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
  3. Let newValue be ? GetValue(newReference).
  4. Let strict be IsStrict(this OptionalChain).
  5. Let unsafe be IsUnsafe(this MemberExpression).
  6. Return ? EvaluatePropertyAccessWithExpressionKey(newValue, Expression, strict, unsafe).
OptionalChain : OptionalChain . IdentifierName
  1. Let optionalChain be OptionalChain.
  2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
  3. Let newValue be ? GetValue(newReference).
  4. Let strict be IsStrict(this OptionalChain).
  5. Let unsafe be IsUnsafe(this MemberExpression).
  6. Return EvaluatePropertyAccessWithIdentifierKey(newValue, IdentifierName, strict, unsafe).
OptionalChain : OptionalChain . PrivateIdentifier
  1. Let optionalChain be OptionalChain.
  2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
  3. Let newValue be ? GetValue(newReference).
  4. Let fieldNameString be the StringValue of PrivateIdentifier.
  5. Return MakePrivateReference(newValue, fieldNameString).

4.7 Changes to Reflection

4.7.1 The Reflect Object

4.7.1.1 Reflect.get ( target, propertyKey [ , receiver ] )

This function performs the following steps when called:

  1. If target is not an Object, throw a TypeError exception.
  2. Let key be ? ToPropertyKey(propertyKey).
  3. If receiver is not present, then
    1. Set receiver to target.
  4. Return ? target.[[Get]](key, receiver, true).

4.7.1.2 Reflect.set ( target, propertyKey, V [ , receiver ] )

This function performs the following steps when called:

  1. If target is not an Object, throw a TypeError exception.
  2. Let key be ? ToPropertyKey(propertyKey).
  3. If receiver is not present, then
    1. Set receiver to target.
  4. Return ? target.[[Set]](key, V, receiver, true).

5 Synchronization Primitives

Mutexes and condition variables are provided as higher level abstractions, as an easier to use alternative to user-built abstractions on top of Atomics.wait and Atomics.notify. They are Shared Structs with no fields.

5.1 Abstract Operations for Mutex Objects

5.1.1 UnlockTokenCreateIfNeeded ( token, mutex )

The abstract operation UnlockTokenCreateIfNeeded takes arguments token (an Object or undefined) and mutex (an Object) and returns an Object. It performs the following steps when called:

  1. Assert: mutex has a [[MutexWaiterList]] internal slot.
  2. If token is undefined, then
    1. Set token to OrdinaryObjectCreate(%Atomics.Mutex.UnlockToken.prototype%, « [[LockedMutex]] »).
  3. Else,
    1. Assert: token has a [[LockedMutex]] internal slot.
    2. Assert: token.[[LockedMutex]] is empty.
  4. Set token.[[LockedMutex]] to mutex.
  5. Return token.

5.1.2 LockMutex ( mutex, tMillis )

The abstract operation LockMutex takes arguments mutex (an Object) and tMillis (a mathematical value) and returns acquired, deadlock, or timed-out. It performs the following steps when called:

  1. Assert: mutex has a [[MutexWaiterList]] internal slot.
  2. Assert: If tMillis is not 0, AgentCanSuspend() is true.
  3. Let thisAgent be AgentSignifier().
  4. Let WL be mutex.[[MutexWaiterList]].
  5. Perform EnterCriticalSection(WL).
  6. If mutex.[[IsLockedBy]] is empty, then
    1. Set mutex.[[IsLockedBy]] to thisAgent.
    2. Let result be acquired.
  7. Else if mutex.[[IsLockedBy]] is thisAgent, then
    1. Let result be deadlock.
  8. Else,
    1. If tMillis is 0, return timed-out.
    2. Let now be the time value (UTC) identifying the current time.
    3. Let additionalTimeout be an implementation-defined non-negative mathematical value.
    4. Let timeoutTime be (now) + tMillis + additionalTimeout.
    5. NOTE: When tMillis is +∞, timeoutTime is also +∞.
    6. Let done be false.
    7. Repeat, while done is false,
      1. Let waiterRecord be a new Waiter Record { [[AgentSignifier]]: thisAgent, [[PromiseCapability]]: blocking, [[TimeoutTime]]: timeoutTime, [[Result]]: "ok" }.
      2. Perform AddWaiter(WL, waiterRecord).
      3. Perform SuspendThisAgent(WL, waiterRecord).
      4. If mutex.[[IsLockedBy]] is empty, then
        1. Set mutex.[[IsLockedBy]] to thisAgent.
        2. Set waiterRecord.[[Result]] to "ok".
        3. Set done to true.
      5. Else if waiterRecord.[[Result]] is "timed-out", then
        1. Set done to true.
    8. If waiterRecord.[[Result]] is "ok", then
      1. Let result be acquired.
    9. Else,
      1. Assert: waiterRecord.[[Result]] is "timed-out".
      2. Let result be timed-out.
  9. Perform LeaveCriticalSection(WL).
  10. Return result.

5.1.3 UnlockMutex ( mutex )

The abstract operation UnlockMutex takes argument mutex (an Object) and returns unused. It performs the following steps when called:

  1. Assert: mutex has a [[MutexWaiterList]] internal slot.
  2. Let WL be mutex.[[MutexWaiterList]].
  3. Perform EnterCriticalSection(WL).
  4. Assert: mutex.[[IsLockedBy]] is AgentSignifier().
  5. Set mutex.[[IsLockedBy]] to empty.
  6. Let S be RemoveWaiters(WL, 1).
  7. For each element W of S, do
    1. Perform NotifyWaiter(WL, W).
  8. Perform LeaveCriticalSection(WL).
  9. Return unused.

5.2 The Mutex Constructor

The Mutex constructor:

  • is %Atomics.Mutex%.
  • is the initial value of the "Mutex" property of the %Atomics% object.
  • creates and initializes a new Mutex when called as constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.

5.2.1 Atomics.Mutex ( )

This function performs the following steps when called:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let createInitializer be a new Abstract Closure with parameters (newMutex) that captures nothing and performs the following steps when called:
    1. Set newMutex.[[MutexWaiterList]] to a new WaiterList Record.
    2. Set newMutex.[[IsLockedBy]] to empty.
    3. Return unused.
  3. Return SharedStructCreate(createInitializer, « [[MutexWaiterList]], [[IsLockedBy]] »).

5.3 Properties of the Mutex Constructor

The Mutex constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:
Note

Per-Realm prototypes, which is currently an open design question and not included in this draft, will give Mutexes a per-Realm prototype with built-in methods instead of static methods.

5.3.1 Atomics.Mutex.UnlockToken ( )

See 5.4.1.1.

5.3.2 Atomics.Mutex.lock ( mutex [ , unlockToken ] )

This function puts the surrounding agent in a wait queue and suspends it until the mutex is unlocked.

It performs the following steps when called:

  1. Perform ? RequireInternalSlot(mutex, [[MutexWaiterList]]).
  2. If unlockToken not undefined, then
    1. Perform ? RequireInternalSlot(unlockToken, [[LockedMutex]]).
    2. If unlockToken.[[LockedMutex]] is not empty, throw a TypeError exception.
  3. If AgentCanSuspend() is false, throw a TypeError exception.
  4. Let result be LockMutex(mutex, +∞).
  5. If result is deadlock, throw a TypeError exception.
  6. Assert: result is acquired.
  7. Return UnlockTokenCreateIfNeeded(unlockToken, mutex).

5.3.3 Atomics.Mutex.lockIfAvailable ( mutex, timeout [ , unlockToken ] )

This function puts the surrounding agent in a wait queue and suspends it until the mutex is unlocked, or until the wait times out. If timeout is 0, this function can be called in agents that cannot suspend.

It performs the following steps when called:

  1. Perform ? RequireInternalSlot(mutex, [[MutexWaiterList]]).
  2. If unlockToken not undefined, then
    1. Perform ? RequireInternalSlot(unlockToken, [[LockedMutex]]).
    2. If unlockToken.[[LockedMutex]] is not empty, throw a TypeError exception.
  3. If timeout is not a Number, throw a TypeError exception.
  4. If timeout is either NaN or +∞𝔽, let tMillis be +∞; else if timeout is -∞𝔽, let tMillis be 0; else let tMillis be max((timeout), 0).
  5. If tMillis is not 0 and AgentCanSuspend() is false, throw a TypeError exception.
  6. Let result be LockMutex(mutex, tMillis).
  7. If result is deadlock, then
    1. Throw TypeError exception.
  8. Else if result is acquired, then
    1. Return UnlockTokenCreateIfNeeded(unlockToken, mutex).
  9. Else,
    1. Assert: result is timed-out.
    2. Return null.
    3. NOTE: The return value of the timed-out case is an open design question. Specifically, whether the return value ought to throw when attempted to be used with the using syntax.

5.4 UnlockToken Objects

An UnlockToken is the unlock capability returned when a Mutex's lock is acquired. It can be reused. An uninitialized UnlockToken can be created by using the Atomics.Mutex.UnlockToken constructor.

5.4.1 The UnlockToken Constructor

The UnlockToken constructor:

  • is %Atomics.Mutex.UnlockToken%.
  • is the initial value of the "UnlockToken" property of the %Atomics.Mutex% object.
  • creates and initializes a new UnlockToken when called as a constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.

5.4.1.1 Atomics.Mutex.UnlockToken ( )

This function performs the following steps when called:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let token be OrdinaryObjectCreate(%Atomics.Mutex.UnlockToken.prototype%, « [[LockedMutex]] »).
  3. Set token.[[LockedMutex]] to empty.
  4. Return token.

5.4.2 Properties of the UnlockToken Constructor

The UnlockToken constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

5.4.2.1 Atomics.Mutex.UnlockToken.prototype

The initial value of Atomics.Mutex.UnlockToken.prototype is the UnlockToken prototype object.

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

5.4.3 Properties of the UnlockToken Prototype Object

The UnlockToken prototype object:

  • is %Atomics.Mutex.UnlockToken.prototype%.
  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • is an ordinary object.
  • does not have a [[LockedMutex]] internal slot.
  • has the following properties:

5.4.3.1 get Atomics.Mutex.UnlockToken.prototype.locked

Atomics.Mutex.UnlockToken.locked is an accessor property whose set accessor is undefined. Its get accessor function performs the following steps when called:

  1. Let token be the this value.
  2. Perform ? RequireInternalSlot(token, [[LockedMutex]]).
  3. If token.[[LockedMutex]] is empty, return false; else return true.

5.4.3.2 Atomics.Mutex.UnlockToken.prototype.unlock ( )

This function performs the following steps when called:

  1. Let token be the this value.
  2. Perform ? RequireInternalSlot(token, [[LockedMutex]]).
  3. Let mutex be token.[[LockedMutex]].
  4. If mutex is not empty, then
    1. Set token.[[LockedMutex]] to empty.
    2. Perform UnlockMutex(mutex).
    3. Return true.
  5. Return false.

5.4.3.3 Atomics.Mutex.UnlockToken.prototype [ %Symbol.dispose% ] ( )

This function performs the following steps when called:

  1. Let token be the this value.
  2. Perform ? RequireInternalSlot(token, [[LockedMutex]]).
  3. Let mutex be token.[[LockedMutex]].
  4. If mutex is not empty, then
    1. Set token.[[LockedMutex]] to empty.
    2. Perform UnlockMutex(mutex).
  5. Return undefined.

5.5 The Condition Constructor

The Condition constructor:

  • is %Atomics.Condition%.
  • is the initial value of the "Condition" property of the %Atomics% object.
  • creates and initializes a new Condition when called as constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.

5.5.1 Atomics.Condition ( )

This function performs the following steps when called:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let createInitializer be a new Abstract Closure with parameters (newCV) that captures nothing and performs the following steps when called:
    1. Set newCV.[[ConditionWaiterList]] to a new WaiterList Record.
    2. Return unused.
  3. Return SharedStructCreate(createInitializer, « [[ConditionWaiterList]] »).

5.6 Properties of the Condition Constructor

The Condition constructor:

  • has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:
Note

Per-Realm prototypes, which is currently an open design question and not included in this draft, will give Conditions a per-Realm prototype with built-in methods instead of static methods.

5.6.1 Atomics.Condition.wait ( cv, mutexUnlockToken )

This function atomically unlocks mutexUnlockToken and puts the surrounding agent in a wait queue and suspends it until the condition variable is notified.

It performs the following steps when called:

  1. Perform ? RequireInternalSlot(cv, [[ConditionWaiterList]]).
  2. Perform ? RequireInternalSlot(mutexUnlockToken, [[LockedMutex]]).
  3. Let mutex be mutexUnlockToken.[[LockedMutex]].
  4. If mutex is empty, throw a TypeError exception.
  5. If AgentCanSuspend() is false, throw a TypeError exception.
  6. Let thisAgent be AgentSignifier().
  7. Let WL be cv.[[ConditionWaiterList]].
  8. Perform EnterCriticalSection(WL).
  9. Let waiterRecord be a new Waiter Record { [[AgentSignifier]]: thisAgent, [[PromiseCapability]]: blocking, [[TimeoutTime]]: +∞, [[Result]]: "ok" }.
  10. Perform AddWaiter(WL, waiterRecord).
  11. Perform UnlockMutex(mutex).
  12. Perform SuspendThisAgent(WL, waiterRecord).
  13. Perform LeaveCriticalSection(WL).
  14. Let lockResult be LockMutex(mutex, +∞).
  15. Assert: lockResult is acquired.
  16. Assert: waiterRecord.[[Result]] is "ok".
  17. Return undefined.

5.6.2 Atomics.Condition.waitFor ( cv, mutexUnlockToken, timeout [ , predicate ] )

If predicate is undefined, this function atomically unlocks mutexUnlockToken and puts the surrounding agent in a wait queue and suspends it until the condition variable is notified or until the wait times out, returning true for the former and false for the latter.

If a predicate is passed and calling it returns false, this function atomically unlocks mutexUnlockToken and puts the surrounding agent in a wait queue and suspends it until predicate returns true, or until the wait times out. Whenever predicate is executing, the lock on the underlying mutex of mutexUnlockToken is acquired. Returns return value of the final call to predicate.

It performs the following steps when called:

  1. Perform ? RequireInternalSlot(cv, [[ConditionWaiterList]]).
  2. Perform ? RequireInternalSlot(mutexUnlockToken, [[LockedMutex]]).
  3. Let mutex be mutexUnlockToken.[[LockedMutex]].
  4. If mutex is empty, throw a TypeError exception.
  5. If timeout is not a Number, throw a TypeError exception.
  6. If timeout is either NaN or +∞𝔽, let tMillis be +∞; else if timeout is -∞𝔽, let tMillis be 0; else let tMillis be max((timeout), 0).
  7. If predicate is not undefined and IsCallable(predicate) is false, throw a TypeError exception.
  8. If AgentCanSuspend() is false, throw a TypeError exception.
  9. Let timeBeforeWaitLoop be the time value (UTC) identifying the current time.
  10. Let additionalTimeout be an implementation-defined non-negative mathematical value.
  11. Let timeoutTime be (timeBeforeWaitLoop) + tMillis + additionalTimeout.
  12. NOTE: When tMillis is +∞, timeoutTime is also +∞.
  13. Let thisAgent be AgentSignifier().
  14. Let WL be cv.[[ConditionWaiterList]].
  15. Let satisfied be false.
  16. Repeat,
    1. If predicate is not undefined, then
      1. Set satisfied to ToBoolean(? Call(predicate, undefined)).
    2. If satisfied is true, return true.
    3. Let now be the time value (UTC) identifying the current time.
    4. If nowtimeoutTime, return false.
    5. Perform EnterCriticalSection(WL).
    6. Let waiterRecord be a new Waiter Record { [[AgentSignifier]]: thisAgent, [[PromiseCapability]]: blocking, [[TimeoutTime]]: timeoutTime, [[Result]]: "ok" }.
    7. Perform AddWaiter(WL, waiterRecord).
    8. Perform UnlockMutex(mutex).
    9. Perform SuspendThisAgent(WL, waiterRecord).
    10. Perform LeaveCriticalSection(WL).
    11. Let lockResult be LockMutex(mutex, +∞).
    12. Assert: lockResult is acquired.
    13. If waiterRecord.[[Result]] is "ok", then
      1. Set satisfied to true.
    14. Else,
      1. Assert: waiterRecord.[[Result]] is "timed-out".
      2. Set satisfied to false.

5.6.3 Atomics.Condition.notify ( cv [ , count ] )

It performs the following steps when called:

  1. Perform ? RequireInternalSlot(cv, [[ConditionWaiterList]]).
  2. If count is undefined, set count to +∞𝔽.
  3. If count is not an integral Number or is not +∞𝔽, throw a TypeError exception.
  4. Let WL be cv.[[ConditionWaiterList]].
  5. Perform EnterCriticalSection(WL).
  6. Let S be RemoveWaiters(WL, (count)).
  7. For each element W of S, do
    1. Perform NotifyWaiter(WL, W).
  8. Perform LeaveCriticalSection(WL).
  9. Let n be the number of elements in S.
  10. Return 𝔽(n).

A Copyright & Software License

Copyright Notice

© 2024 Shu-yu Guo

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.