Home

Awesome

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

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:

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:

Type Objects

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

Every type object carries the following internal slots:

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:

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:

Typed Object

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

[[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:
  7. Let s be value of the [[Structure]] internal slot from typeDescriptor.
  8. Let field record r be a field record with name P from s
  9. Return undefined if r does not exist
  10. Let o be OffsetOf(s, P) + offset
  11. Let value be Reify(_r.type.[[TypeDescriptor]], r.type.[[Dimensions]], buffer, o, opacity)
  12. Return a PropertyDescriptor{ [[Value]] : value, [[Enumerable]]: false, [[Writable]]: true, [[Configurable]]: false }
  13. Otherwise, assert dimensions` is Cons(length, remainingDimensions):
  14. Set isInteger to be true if ToInteger(P) is not an abrupt completion, false otherwise
  15. If isInteger is false, return undefined
  16. Let i be the result of ToInteger(P)
  17. Let o be s * i + offset
  18. Let value be Reify(typeDescriptor, remainingDimensions, buffer, o, opacity)
  19. 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:

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.
  2. Let alignment be 1.
  3. Let i be 0.
  4. While i less than l:
  5. Let fr be the _i_th field record in fieldRecords.
  6. Let a be Alignment(fr.type).
  7. Let alignment be the maximum of a and alignment.
  8. Set size to AlignTo(size, a).
  9. If name is provided, and fr.name equals name, return size.
  10. Let s be Size(fr).
  11. Set size to size plus s.
  12. Set size to AlignTo(size, alignment).
  13. 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:

Default(typeDescriptor)

Where:

  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:

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

Initialize(typeDescriptor, dimensions, buffer, offset)

// TODO Formalify

Where:

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

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

Where:

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

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

Where:

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