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
- 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]]
, eitherundefined
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):
- If typeObject is a ground type object,
- Let typeDescriptor be a value of
[[TypeDescriptor]]
internal slot of typeObject. - return Default(typeDescriptor)`
- Let typeDescriptor be a value of
- Otherwise, return CreateTypedObject(typeObject)
- If typeObject is a ground type object,
-
typeObject(buffer[, length]) (first argument is an array buffer):
- If typeObject is a ground type object, throw
TypeError
- Otherwise, return CreateTypedObjectFromBuffer(typeObject, buffer, length)
- If typeObject is a ground type object, throw
-
typeObject(value) (first argument is not undefined nor an array buffer):
- If typeObject is a ground type object, return Coerce(typeObject, value)
- Otherwise:
- Let o be CreateTypedObject(typeObject)
- 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:
- Let typeDescriptor be the value of the
[[TypeDescriptor]]
internal slot of O. - Let dimensions be the value of the
[[Dimensions]]
internal slot of O. - Let buffer be the value of the
[[ViewedArrayBuffer]]
internal slot of O. - Let offset be the value of the
[[ByteOffset]]
internal slot of O. - Let opacity be the value of the
[[OPacity]]
internal slot of O. - If dimensions is
Nil
: - Let s be value of the
[[Structure]]
internal slot from typeDescriptor. - Let field record r be a field record with name P from s
- Return undefined if r does not exist
- Let o be OffsetOf(s, P) + offset
- Let value be Reify(_r.type.
[[TypeDescriptor]]
, r.type.[[Dimensions]]
, buffer, o, opacity) - Return a PropertyDescriptor{
[[Value]]
: value,[[Enumerable]]
: false,[[Writable]]
: true,[[Configurable]]
: false } - Otherwise, assert dimensions` is Cons(length, remainingDimensions):
- Set isInteger to be true if ToInteger(P) is not an abrupt completion, false otherwise
- If isInteger is false, return undefined
- Let i be the result of ToInteger(P)
- Let o be s * i + offset
- Let value be Reify(typeDescriptor, remainingDimensions, buffer, o, opacity)
- 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:
- Assert: IsPropertyKey(P) is true.
- If Type(P) is String and if SameValue(O, Receiver) is true, then
- Return the result of SetFieldInTypedObject(O, P, V).
- 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:
- Let typeDescriptor be a value of O's
[[TypeDescriptor]]
internal slot. - Return typeDescriptor
[[IsExtensible]]
()
[[IsExtensible]]
for typed object O returns false.
[[Structure]]
(O)
For exotic typed object O:
- Let typeDescriptor be a value of
[[TypeDescriptor]]
internal slot of O. - 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:
- values of internal slots
[[TypeDescriptor]]
,[[ViewedArrayBuffer]]
,[[ByteOffset]]
and[[Opacity]]
are SameValue respectively. - Values of internal slot
[[Dimensions]]
of x and y are SameDimensions
- values of internal slots
- Return false otherwise.
- Return true if the following holds:
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:
- Let O be the this value.
- If O does not have a
[[TypeDescriptor]]
internal slot, throw TypeError. - Let typeDescriptor be a value of O's
[[TypeDescriptor]]
internal slot. - Return typeDescriptor.
Type.prototype.arrayType(length) [function]
- Let O be the this value.
- If IsTypeObject(O) is false, throw TypeError.
- Let typeDescriptor be a value of O's [[TypeDescriptor]] internal slot.
- Let numberLength be ToNumber(length)
- Let elementLength be ToLength(numberLength)
- ReturnIfAbrupt(elementLength)
- If SameValueZero(numberLength, elementLength) is false, then throw RangeError.
- Let arrayDescriptor be GetOrCreateArrayTypeDescriptor(typeDescriptor).
- ReturnIfAbrupt(arrayDescriptor)
- Let R be a newly created TypeObject.
- Set the [[TypeDescriptor]] internal slot of R to arrayDescriptor.
- Let newDimesions be a result of
Cons(N, dimensions)
- Set the [[Dimensions]] internal slot of R to newDimensions.
- 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:
- Assert: Type(object) is Object.
- Let O be the this value.
- If IsTypeObject(O) is false, throw TypeError.
- Let currentOffset be 0.
- Let maxAlignment be 1.
- Let structure be an empty list.
- For each own property key P of object, iterated in the standard
own property iteration order:
- Let fieldType be the result of Get(object, P).
- ReturnIfAbrupt(fieldType).
- If IsTypeObject(fieldType) is false, throw TypeError.
- Let alignment be a result of Alignment(fieldType).
- Let maxAlignment be the maximum of alignment and maxAlignment.
- Set currentOffset to AlignTo(currentOffset, alignment).
- Let r be a field record with name equal to fieldName, byteOffset equal to currentOffset, and type equal to fieldType.
- Add r to the end of structure list.
- Let s be Size(fieldType).
- ReturnIfAbrupt(s).
- Set currentOffset to currentOffset + s.
- Let size be AlignTo(currentOffset, maxAlignment).
- Let typeDescriptor be CreateStructTypeDescriptor(structure).
- Set O's
[[TypeDescriptor]]
to typeDescriptor. - Set O's
prototype
property to typeDescriptor. - Make O's
prototype
property read-only and non-configurable. - 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.
- If Type(O) is not Object, return false.
- If O does not a [[TypeDescriptor]] internal slot, return false.
- If O does have a [[ViewedArrayBuffer]] internal slot, return false.
- Return true.
Alignment(typeDescriptor)
- Let S be a value of typeDescriptor's `[[Structure]]`` internal slot.
- If S is a ground structure, return Size(S).
- 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)
- If dimensions is
Nil
, return Size(structure) - If dimensions is
Cons(length, remainingDimensions)
, return Size(structure, remainingDimensions) * length.
Size(structure)
- If structure is one of the ground type objects return:
- 1 for uint8, int8.
- 2 for uint16, int16.
- 4 for uint32, int32, float32.
- 8 for float64.
- An implementation-defined value for object, string, or any
- Otherwise, structure is a list of field records:
- 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.
- Let size be 0.
- Let alignment be 1.
- Let i be 0.
- While i less than l:
- Let fr be the _i_th field record in fieldRecords.
- Let a be Alignment(fr.type).
- Let alignment be the maximum of a and alignment.
- Set size to AlignTo(size, a).
- If name is provided, and fr.name equals name, return size.
- Let s be Size(fr).
- Set size to size plus s.
- Set size to AlignTo(size, alignment).
- Return size.
Opaque(structure)
Returns true if typed objects with structure structure must be opaque, false otherwise.
- If structure is a ground structure, return:
- true for
any
,string
orobject
. - false for all other gorund structures
- true for
- Otherwise structure is a list of field records.
- For every field record {
name
: name,type
: typeObject }:- Let d be typeObject's
[[TypeDescriptor]]
. - Let s be d's
[[Structure]]
. - If Opaque(s), return true
- Let d be typeObject's
- Return false.
- For every field record {
CreateStructTypeDescriptor(structure)
Creates a new type descriptor that describes a struct.
- Let result be a new TypeDescriptor TODO: proper spec
- Set result's
[[Structure]]
to structure. - Set result's
[[Rank]]
to 0. - Set result's
[[ArrayDescriptor]]
toundefined
. - Return result.
CreateArrayTypeDescriptor(typeDescriptor)
Creates a new type descriptor that describes an array with elements of type described by typeDescriptor.
- Let result be a new TypeDescriptor TODO: proper spec
- Set result's
[[Structure]]
to typeDescriptor's[[Structure]]
. - Set result's
[[Rank]]
to typeDescriptor's[[Rank]]
+ 1 - Set result's
[[ArrayDescriptor]]
toundefined
. - Return result.
GetOrCreateArrayTypeDescriptor(typeDescriptor)
- Let cached be typeDescriptor's
[[ArrayDescriptor]]
. - If cached is not
undefined
, return cached. - Let result be CreateArrayTypeDescriptor(typeDescriptor).
- Set
[[ArrayDescriptor]]
of typeDescriptor to result. - Return result.
CreateTypedObjectFromBuffer(arrayBuffer, byteOffset, typeObject)
- If byteOffset + Size(typeObject) is bigger than arrayBuffer's
[[ByteLength]]
, throw RangeError. - Let s be typeObject's [[Structure]].
- Let O be a result of ObjectCreate(typeObject.prototype, ([[Structure]], [[ViewedArrayBuffer]], [[ByteOffset]], [[TypeObject]])).
- ReturnIfAbrupt(O).
- InitializeTypeObjectInternals(O, arrayBuffer, typeObject).
- Return O.
CreateTypedObject(typeObject)
// TODO Formalify
- Let
buffer
be a new buffer of sizeSize(typeObject)
- Call
Initialize(typeObject.\[\[TypeDescriptor]], typeObject.\[\[Dimensions]], buffer, 0)
- 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]]
)- Return
typeObject
Default(typeDescriptor)
Where:
- typeDescriptor is a ground type descriptor
- Let structure be typeDescriptor.
[[Structure]]
- If structure is
object
, returnnull
- Otherwise, if structure is
any
, returnundefined
- Otherwise, if structure is
string
, return""
- Otherwise, return 0
Coerce(typeDescriptor, value)
Where:
- typeDescriptor is a ground type descriptor
- Let _structure+ be typeDescriptor.
[[Structure]]
- If structure is
object
: - If value is an object, return
value
- Throw TypeError
- Otherwise, if structure is
any
, return value - Otherwise, if structure is
string
, return ToString(value) - Otherwise, if structure is
float32
orfloat64
, return ToNumber(value) - 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
- If dimensions is
Cons(length, remainingDimensions)
: - Let size be Size(typeDescriptor, remainingDimensions)
- For each i from 0 to length - 1: 1. Call Initialize(typeDescriptor, remainingDimensions, buffer, offset + i * size)
- Return
- Otherwise, if typeDescriptor is a ground type descriptor:
- Call ConvertAndCopyTo(typeDescriptor, dimensions, buffer, offset, Default(typeDescriptor)))
- Otherwise, structure must be a list of field records:
- 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:
typeDescriptor
is a type descriptordimensions
is an array of integersbuffer
is an array bufferoffset
is an integervalue
is an arbitrary JS value
- If
dimensions
isCons(length, remainingDimensions)
: - Let
valueLength
bevalue.length
// TODO formalify - If
length !== valueLength
, throw TypeError - Let
size
beSize(typeDescriptor, remainingDimensions)
- Let
o
equaloffset
- For each
i
from0
tolength - 1
: 1. Letv
bevalue[i]
// TODO formalify 1. CallConvertAndCopyTo(typeDescriptor, remainingDimensions, buffer, o, v)
1. Leto
equalo + size
- Return
- Otherwise, let
structure
betypeDescriptor.\[\[Structure]]
- If
structure
isobject
: - If
value
is not an object, throw - Store the object in buffer at offset // TODO formalify
- Otherwise, if
structure
isany
: - Otherwise, if
structure
isstring
: - Otherwise, if
structure
is a ground structure: - Call
SetValueInBuffer(buffer, offset, value, typeDescriptor)
- Otherwise,
structure
must be a list of field records: - For each field record
{name, byteOffset, type}
instructure
: 1. LetfieldValue
bevalue[name]
// TODO formalify 1. LetfieldOffset
beoffset + byteOffset
1. LetfieldTypeDescriptor
betype.\[\[TypeDescriptor]]
1. LetfieldDimensions
betype.\[\[Dimensions]]
1. CallConvertAndCopyTo(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
- If dimensions is
Cons(length, remainingDimensions)
OR typeDescriptor is not a ground type descriptor: - Return a new typed object with the following properties:
-
[[TypeDescriptor]]``: _typeDescriptor_ -
[[Dimensions]]: _dimensions_ -
[[ViewedArrayBuffer]]: _buffer_ -
[[ByteOffset]]: _offset_ -
[[Opacity]]`: opacity - Otherwise, let structure be typeDescriptor's
[[Structure]]
- If structure is
object
, load and return object from buffer at offset. - Otherwise, if structure is
any
, load and return value from buffer at offset. - Otherwise, if structure is
string
, load and return string from buffer at offset. - Otherwise, return GetValueInBuffer(buffer, offset, value, typeDescriptor).