ES7 typed objects spec draft
Latest commit 419d79a Apr 7, 2014 @dslomov Formatting
Permalink
Failed to load latest commit information.
README.md Formatting Apr 7, 2014

README.md

Typed Objects

Module

All top-level names are defined in a typed objects module, the precise path of which is not yet defined. For now, we shall refer to it as The Module (capitalized for easier search-and-replace).

Overview

Structure: either

  • one of uint8, int8, uint16, int16, uint32, int32, float32, float64, any, string, object (ground structures)
  • an ordered list of FieldRecord.

FieldRecord: a pair of {name : property name, type : TypeObject }.

Dimensions: either Nil or Cons(int, Dimensions)

Type Descriptors

TypeDescriptor is an exotic object that represents a shape of a typed object.

Type descriptors carry the following internal slots:

  • [[Structure]], should have a Structure value.
  • [[Rank]], an integer.
  • [[ArrayDescriptor]], either undefined or a TypeDescriptor.

Type descriptors represent a shape of typed object. Identical typed objects have identical type descriptor. Type descriptors act as prototypes of typed objects.

All array type objects with the same element type share their type descriptor.

Ground Type Descriptors

There is a fixed set of ground type descriptors. Their [[Structure]]s are ground structures.

All ground type descriptors are have [[Rank]] equal to 0. Their [[ArrayDescriptor]] internal slot is initially undefined.

Their other properties are as follows:

  • [[uint8]]: [[Structure]]: uint8.
  • [[int8]]: [[Structure]]: int8.
  • [[uint16]]: [[Structure]]: uint16.
  • [[int16]]: [[Structure]]: int16.
  • [[uint32]]: [[Structure]]: uint32.
  • [[int32]]: [[Structure]]: int32.
  • [[float32]]: [[Structure]]: float32.
  • [[float64]]: [[Structure]]: float64.
  • [[any]]: [[Structure]]: any.
  • [[string]]: [[Structure]]: string.
  • [[object]]: [[Structure]]: object.

Type Objects

TypeObject is an exotic object, constructable with a type object constructor (StructType or ArrayType).

Every type object carries the following internal slots:

  • [[TypeDescriptor]]
  • [[Dimensions]]

For every type object, length([[Dimensions]]) == [[Rank]] of [[TypeDescriptor]].

Ground type objects

The following type objects with [[TypeDescriptor]]s being ground type descriptor ara available to ECMAScript programs under the following names defined within The Module:

  • ${NAME} : [[TypeDescriptor]] : [[${NAME}]]. TODO nice list

Their [[Dimensions]] are Nil as required by the above invariant.

[[Call]] for Type Objects

Type objects have a [[Call]] internal method defined. Its behaviour is specified below:

  • typeObject() (first argument is undefined or not supplied):

    1. If typeObject is a ground type object,
      1. Let typeDescriptor be a value of [[TypeDescriptor]] internal slot of typeObject.
      2. return Default(typeDescriptor)`
    2. Otherwise, return CreateTypedObject(typeObject)
  • typeObject(buffer[, length]) (first argument is an array buffer):

    1. If typeObject is a ground type object, throw TypeError
    2. Otherwise, return CreateTypedObjectFromBuffer(typeObject, buffer, length)
  • typeObject(value) (first argument is not undefined nor an array buffer):

    1. If typeObject is a ground type object, return Coerce(typeObject, value)
    2. Otherwise:
      1. Let o be CreateTypedObject(typeObject)
      2. Call ConvertAndCopyTo(typeObject.[[TypeDescriptor]], typeObject.[[Dimensions]], o.[[ViewedArrayBuffer]], o.[[ByteOffset]], value)

Typed Object

Typed objects are exotic objects that are created from Type Objects. They carry the following internal slots:

  • [[TypeDescriptor]]: the values should be a type descriptor
  • [[Dimensions]]: number of elements in dimensions should be equal to the rank of type descriptor.
  • [[ViewedArrayBuffer]]
  • [[ByteOffset]]
  • [[Opacity]]

[[GetOwnProperty]] ( P )

When the [[GetOwnProperty]] internal method of a Typed Object exotic object O is called with property key P the following steps are taken:

  1. Let typeDescriptor be the value of the [[TypeDescriptor]] internal slot of O.
  2. Let dimensions be the value of the [[Dimensions]] internal slot of O.
  3. Let buffer be the value of the [[ViewedArrayBuffer]] internal slot of O.
  4. Let offset be the value of the [[ByteOffset]] internal slot of O.
  5. Let opacity be the value of the [[OPacity]] internal slot of O.
  6. If dimensions is Nil:
    1. Let s be value of the [[Structure]] internal slot from typeDescriptor.
    2. Let field record r be a field record with name P from s
    3. Return undefined if r does not exist
    4. Let o be OffsetOf(s, P) + offset
    5. Let value be Reify(r.type.[[TypeDescriptor]], _r.type.[[Dimensions]], buffer, o, opacity)
    6. Return a PropertyDescriptor{ [[Value]] : value, [[Enumerable]]: false, [[Writable]]: true, [[Configurable]]: false }
  7. Otherwise, assert dimensions` is Cons(length, remainingDimensions):
    1. Set isInteger to be true if ToInteger(P) is not an abrupt completion, false otherwise
    2. If isInteger is false, return undefined
    3. Let i be the result of ToInteger(P)
    4. Let o be s * i + offset
    5. Let value be Reify(typeDescriptor, remainingDimensions, buffer, o, opacity)
    6. Return a PropertyDescriptor{ [[Value]] : value, [[Enumerable]]: true, [[Writable]]: true, [[Configurable]]: false }

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

When the [[Set]] internal method of an exotic typed object O is called with property key P, value V, and ECMAScript language value Receiver, the following steps are taken:

  1. Assert: IsPropertyKey(P) is true.
  2. If Type(P) is String and if SameValue(O, Receiver) is true, then
    1. Return the result of SetFieldInTypedObject(O, P, V).
  3. Otherwise Return the result of calling the default ordinary object [[Set]] internal method (9.1.8) on O passing P, V, and Receiver as arguments

[[GetPrototypeOf]]()

When [[GetPrototypeOf]] is called on typed object O, the following steps are taken:

  1. Let typeDescriptor be a value of O's [[TypeDescriptor]] internal slot.
  2. Return typeDescriptor

[[IsExtensible]]()

[[IsExtensible]] for typed object O returns false.

[[Structure]](O)

For exotic typed object O:

  1. Let typeDescriptor be a value of [[TypeDescriptor]] internal slot of O.
  2. Return a value of [[Structure]] internal slot of typeDescriptor.

SameValue, SameValueZero and === algorithm on typed objects

All three algorithms are modified in the same way:

  • If x and y are typed objects
    • Return true if the following holds:
      1. values of internal slots [[TypeDescriptor]], [[ViewedArrayBuffer]], [[ByteOffset]] and [[Opacity]] are SameValue respectively.
      2. Values of internal slot [[Dimensions]] of x and y are SameDimensions
    • Return false otherwise.

SameDimensions(d1, d2)

SameDimensions holds if d1 and d2 are both Nil, or if d1 = Cons(l, remainingDimensions1) and d2 = Cons(l, remainingDimensions2) and SameDimensions(remainingDimensions1, remainingDimensions2).

Type Object Constructors

Each of these names is defined within The Module.

Type

Type is a function that exists solely as an abstract superclass for other type objects. It's constructor is a no-op.

Type.prototype.prototype [getter]

This is a getter which performs the following steps:

  1. Let O be the this value.
  2. If O does not have a [[TypeDescriptor]] internal slot, throw TypeError.
  3. Let typeDescriptor be a value of O's [[TypeDescriptor]] internal slot.
  4. Return typeDescriptor.

Type.prototype.arrayType(length) [function]

  1. Let O be the this value.
  2. If IsTypeObject(O) is false, throw TypeError.
  3. Let typeDescriptor be a value of O's [[TypeDescriptor]] internal slot.
  4. Let numberLength be ToNumber(length)
  5. Let elementLength be ToLength(numberLength)
  6. ReturnIfAbrupt(elementLength)
  7. If SameValueZero(numberLength, elementLength) is false, then throw RangeError.
  8. Let arrayDescriptor be GetOrCreateArrayTypeDescriptor(typeDescriptor).
  9. ReturnIfAbrupt(arrayDescriptor)
  10. Let R be a newly created TypeObject.
  11. Set the [[TypeDescriptor]] internal slot of R to arrayDescriptor.
  12. Let newDimesions be a result of Cons(N, dimensions)
  13. Set the [[Dimensions]] internal slot of R to newDimensions.
  14. Return R.

StructType

The StructType object is a constructor-like function that creates type objects.

StructType(object)

StructType called with an object argument object performs the following steps:

  1. Assert: Type(object) is Object.
  2. Let O be the this value.
  3. If IsTypeObject(O) is false, throw TypeError.
  4. Let currentOffset be 0.
  5. Let maxAlignment be 1.
  6. Let structure be an empty list.
  7. For each own property key P of object, iterated in the standard own property iteration order:
    1. Let fieldType be the result of Get(object, P).
    2. ReturnIfAbrupt(fieldType).
    3. If IsTypeObject(fieldType) is false, throw TypeError.
    4. Let alignment be a result of Alignment(fieldType).
    5. Let maxAlignment be the maximum of alignment and maxAlignment.
    6. Set currentOffset to AlignTo(currentOffset, alignment).
    7. Let r be a field record with name equal to fieldName, byteOffset equal to currentOffset, and type equal to fieldType.
    8. Add r to the end of structure list.
    9. Let s be Size(fieldType).
    10. ReturnIfAbrupt(s).
    11. Set currentOffset to currentOffset + s.
  8. Let size be AlignTo(currentOffset, maxAlignment).
  9. Let typeDescriptor be CreateStructTypeDescriptor(structure).
  10. Set O's [[TypeDescriptor]] to typeDescriptor.
  11. Set O's prototype property to typeDescriptor.
  12. Make O's prototype property read-only and non-configurable.
  13. Return O.

Abstract Operations

AlignTo(value, alignment)

Returns the minimal integer equal to or greater than value that is evenly divisible by alignment.

IsTypeObject(O)

The abstract operation IsTypeObject checks for the type object branch on an object.

  1. If Type(O) is not Object, return false.
  2. If O does not a [[TypeDescriptor]] internal slot, return false.
  3. If O does have a [[ViewedArrayBuffer]] internal slot, return false.
  4. Return true.

Alignment(typeDescriptor)

  1. Let S be a value of typeDescriptor's [[Structure]]` internal slot.
  2. If S is a ground structure, return Size(S).
  3. Otherwise, return a maximum of Alignment(TypeDescriptor(t)) where t goes over values of fieldType properties of field records in S.

Size(typeObject)

// TODO

Size(structure, dimensions)

  1. If dimensions is Nil, return Size(structure)
  2. If dimensions is Cons(length, remainingDimensions), return Size(structure, remainingDimensions) * length.

Size(structure)

  1. If structure is one of the ground type objects return:
    1. 1 for uint8, int8.
    2. 2 for uint16, int16.
    3. 4 for uint32, int32, float32.
    4. 8 for float64.
    5. An implementation-defined value for object, string, or any
  2. Otherwise, structure is a list of field records:
    1. Return OffsetOf(structure).

OffsetOf(fieldRecords, name?)

Returns the offset of the field named name in a list of field records. The name argument is optional.

1 Let l be the length of fieldRecords. 1. Let size be 0. 1. Let alignment be 1. 1. Let i be 0. 1. While i less than l:

  1. Let fr be the i_th field record in _fieldRecords.
  2. Let a be Alignment(fr.type).
  3. Let alignment be the maximum of a and alignment.
  4. Set size to AlignTo(size, a).
  5. If name is provided, and fr.name equals name, return size.
  6. Let s be Size(fr).
  7. Set size to size plus s.
    1. Set size to AlignTo(size, alignment).
    2. Return size.

Opaque(structure)

Returns true if typed objects with structure structure must be opaque, false otherwise.

  1. If structure is a ground structure, return:
    1. true for any, string or object.
    2. false for all other gorund structures
  2. Otherwise structure is a list of field records.
    1. For every field record { name : name, type: typeObject }:
      1. Let d be typeObject's [[TypeDescriptor]].
      2. Let s be d's [[Structure]].
      3. If Opaque(s), return true
    2. Return false.

CreateStructTypeDescriptor(structure)

Creates a new type descriptor that describes a struct.

  1. Let result be a new TypeDescriptor TODO: proper spec
  2. Set result's [[Structure]] to structure.
  3. Set result's [[Rank]] to 0.
  4. Set result's [[ArrayDescriptor]] to undefined.
  5. Return result.

CreateArrayTypeDescriptor(typeDescriptor)

Creates a new type descriptor that describes an array with elements of type described by typeDescriptor.

  1. Let result be a new TypeDescriptor TODO: proper spec
  2. Set result's [[Structure]] to typeDescriptor's [[Structure]].
  3. Set result's [[Rank]] to typeDescriptor's [[Rank]] + 1
  4. Set result's [[ArrayDescriptor]] to undefined.
  5. Return result.

GetOrCreateArrayTypeDescriptor(typeDescriptor)

  1. Let cached be typeDescriptor's [[ArrayDescriptor]].
  2. If cached is not undefined, return cached.
  3. Let result be CreateArrayTypeDescriptor(typeDescriptor).
  4. Set [[ArrayDescriptor]] of typeDescriptor to result.
  5. Return result.

CreateTypedObjectFromBuffer(arrayBuffer, byteOffset, typeObject)

  1. If byteOffset + Size(typeObject) is bigger than arrayBuffer's [[ByteLength]], throw RangeError.
  2. Let s be typeObject's [[Structure]].
  3. Let O be a result of ObjectCreate(typeObject.prototype, ([[Structure]], [[ViewedArrayBuffer]], [[ByteOffset]], [[TypeObject]])).
  4. ReturnIfAbrupt(O).
  5. InitializeTypeObjectInternals(O, arrayBuffer, typeObject).
  6. Return O.

CreateTypedObject(typeObject)

// TODO Formalify

  1. Let buffer be a new buffer of size Size(typeObject)
  2. Call Initialize(typeObject.\[\[TypeDescriptor]], typeObject.\[\[Dimensions]], buffer, 0)
  3. Let typeObject be a new typed object with the following properties:
    • [[TypeDescriptor]]: typeObject.[[TypeDescriptor]]`
    • [[Dimensions]]: typeObject.[[Dimensions]]
    • [[ViewedArrayBuffer]]: buffer
    • [[ByteOffset]]: 0
    • [[Opacity]]: Opaque(typeObject.[[TypeDescriptor]].[[Structure]])
  4. Return typeObject

Default(typeDescriptor)

Where:

  • typeDescriptor is a ground type descriptor

  1. Let structure be typeDescriptor.[[Structure]]
  2. If structure is object, return null
  3. Otherwise, if structure is any, return undefined
  4. Otherwise, if structure is string, return ""
  5. Otherwise, return 0

Coerce(typeDescriptor, value)

Where:

  • typeDescriptor is a ground type descriptor

  1. Let structure+ be _typeDescriptor.[[Structure]]
  2. If structure is object:
    1. If value is an object, return value
    2. Throw TypeError
  3. Otherwise, if structure is any, return value
  4. Otherwise, if structure is string, return ToString(value)
  5. Otherwise, if structure is float32 or float64, return ToNumber(value)
  6. Otherwise, return ToInteger(value)

Initialize(typeDescriptor, dimensions, buffer, offset)

// TODO Formalify

Where:

  • typeDescriptor is a type descriptor
  • dimensions is an array of integers
  • buffer is an array buffer
  • offset is an integer
  • value is an arbitrary JS value

  1. If dimensions is Cons(length, remainingDimensions):
    1. Let size be Size(typeDescriptor, remainingDimensions)
    2. For each i from 0 to length - 1:
      1. Call Initialize(typeDescriptor, remainingDimensions, buffer, offset + i * size)
    3. Return
  2. Otherwise, if typeDescriptor is a ground type descriptor:
    1. Call ConvertAndCopyTo(typeDescriptor, dimensions, buffer, offset, Default(typeDescriptor)))
  3. Otherwise, structure must be a list of field records:
    1. For each field record {name, byteOffset, type} in structure:
      1. Let fieldOffset be offset + byteOffset
      2. Let fieldTypeDescriptor be type.[[TypeDescriptor]]
      3. Let fieldDimensions be type.[[Dimensions]]
      4. Call Initialize(fieldTypeDescriptor, fieldDimensions, buffer, fieldOffset)

ConvertAndCopyTo(typeDescriptor, dimensions, buffer, offset, value)

Where:

  • typeDescriptor is a type descriptor
  • dimensions is an array of integers
  • buffer is an array buffer
  • offset is an integer
  • value is an arbitrary JS value

  1. If dimensions is Cons(length, remainingDimensions):
    1. Let valueLength be value.length // TODO formalify
    2. If length !== valueLength, throw TypeError
    3. Let size be Size(typeDescriptor, remainingDimensions)
    4. Let o equal offset
    5. For each i from 0 to length - 1:
      1. Let v be value[i] // TODO formalify
      2. Call ConvertAndCopyTo(typeDescriptor, remainingDimensions, buffer, o, v)
      3. Let o equal o + size
    6. Return
  2. Otherwise, let structure be typeDescriptor.\[\[Structure]]
  3. If structure is object:
    1. If value is not an object, throw
    2. Store the object in buffer at offset // TODO formalify
  4. Otherwise, if structure is any:
  5. Otherwise, if structure is string:
  6. Otherwise, if structure is a ground structure:
    1. Call SetValueInBuffer(buffer, offset, value, typeDescriptor)
  7. Otherwise, structure must be a list of field records:
    1. For each field record {name, byteOffset, type} in structure:
      1. Let fieldValue be value[name] // TODO formalify
      2. Let fieldOffset be offset + byteOffset
      3. Let fieldTypeDescriptor be type.\[\[TypeDescriptor]]
      4. Let fieldDimensions be type.\[\[Dimensions]]
      5. Call ConvertAndCopyTo(fieldTypeDescriptor, fieldDimensions, buffer, fieldOffset, fieldValue)

Reify(typeDescriptor, dimensions, buffer, offset, opacity)

Where:

  • typeDescriptor is a type descriptor
  • dimensions is an array of integers
  • buffer is an array buffer
  • offset is an integer
  • value is an arbitrary JS value

  1. If dimensions is Cons(length, remainingDimensions) OR typeDescriptor is not a ground type descriptor:
    1. Return a new typed object with the following properties:
      • [[TypeDescriptor]]`: typeDescriptor
      • [[Dimensions]]: dimensions
      • [[ViewedArrayBuffer]]: buffer
      • [[ByteOffset]]: offset
      • [[Opacity]]: opacity
  2. Otherwise, let structure be typeDescriptor's [[Structure]]
  3. If structure is object, load and return object from buffer at offset.
  4. Otherwise, if structure is any, load and return value from buffer at offset.
  5. Otherwise, if structure is string, load and return string from buffer at offset.
  6. Otherwise, return GetValueInBuffer(buffer, offset, value, typeDescriptor).