michael@0: #include "TypedObjectConstants.h" michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Getters and setters for various slots. michael@0: michael@0: // Type object slots michael@0: michael@0: #define DESCR_KIND(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_KIND) michael@0: #define DESCR_STRING_REPR(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRING_REPR) michael@0: #define DESCR_ALIGNMENT(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ALIGNMENT) michael@0: #define DESCR_SIZE(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZE) michael@0: #define DESCR_OPAQUE(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_OPAQUE) michael@0: #define DESCR_TYPE(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_TYPE) michael@0: #define DESCR_ARRAY_ELEMENT_TYPE(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE) michael@0: #define DESCR_SIZED_ARRAY_LENGTH(obj) \ michael@0: TO_INT32(UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZED_ARRAY_LENGTH)) michael@0: #define DESCR_STRUCT_FIELD_NAMES(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES) michael@0: #define DESCR_STRUCT_FIELD_TYPES(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES) michael@0: #define DESCR_STRUCT_FIELD_OFFSETS(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS) michael@0: michael@0: // Typed object slots michael@0: michael@0: #define TYPEDOBJ_BYTEOFFSET(obj) \ michael@0: TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTEOFFSET)) michael@0: #define TYPEDOBJ_BYTELENGTH(obj) \ michael@0: TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTELENGTH)) michael@0: #define TYPEDOBJ_TYPE_DESCR(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_DESCR) michael@0: #define TYPEDOBJ_OWNER(obj) \ michael@0: UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_OWNER) michael@0: #define TYPEDOBJ_LENGTH(obj) \ michael@0: TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_LENGTH)) michael@0: michael@0: #define HAS_PROPERTY(obj, prop) \ michael@0: callFunction(std_Object_hasOwnProperty, obj, prop) michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Getting values michael@0: // michael@0: // The methods in this section read from the memory pointed at michael@0: // by `this` and produce JS values. This process is called *reification* michael@0: // in the spec. michael@0: michael@0: // Reifies the value referenced by the pointer, meaning that it michael@0: // returns a new object pointing at the value. If the value is michael@0: // a scalar, it will return a JS number, but otherwise the reified michael@0: // result will be a typedObj of the same class as the ptr's typedObj. michael@0: function TypedObjectGet(descr, typedObj, offset) { michael@0: assert(IsObject(descr) && ObjectIsTypeDescr(descr), michael@0: "get() called with bad type descr"); michael@0: assert(TypedObjectIsAttached(typedObj), michael@0: "get() called with unattached typedObj"); michael@0: michael@0: switch (DESCR_KIND(descr)) { michael@0: case JS_TYPEREPR_SCALAR_KIND: michael@0: return TypedObjectGetScalar(descr, typedObj, offset); michael@0: michael@0: case JS_TYPEREPR_REFERENCE_KIND: michael@0: return TypedObjectGetReference(descr, typedObj, offset); michael@0: michael@0: case JS_TYPEREPR_X4_KIND: michael@0: return TypedObjectGetX4(descr, typedObj, offset); michael@0: michael@0: case JS_TYPEREPR_SIZED_ARRAY_KIND: michael@0: case JS_TYPEREPR_STRUCT_KIND: michael@0: return TypedObjectGetDerived(descr, typedObj, offset); michael@0: michael@0: case JS_TYPEREPR_UNSIZED_ARRAY_KIND: michael@0: assert(false, "Unhandled repr kind: " + DESCR_KIND(descr)); michael@0: } michael@0: michael@0: assert(false, "Unhandled kind: " + DESCR_KIND(descr)); michael@0: return undefined; michael@0: } michael@0: michael@0: function TypedObjectGetDerived(descr, typedObj, offset) { michael@0: assert(!TypeDescrIsSimpleType(descr), michael@0: "getDerived() used with simple type"); michael@0: return NewDerivedTypedObject(descr, typedObj, offset); michael@0: } michael@0: michael@0: function TypedObjectGetDerivedIf(descr, typedObj, offset, cond) { michael@0: return (cond ? TypedObjectGetDerived(descr, typedObj, offset) : undefined); michael@0: } michael@0: michael@0: function TypedObjectGetOpaque(descr, typedObj, offset) { michael@0: assert(!TypeDescrIsSimpleType(descr), michael@0: "getDerived() used with simple type"); michael@0: var opaqueTypedObj = NewOpaqueTypedObject(descr); michael@0: AttachTypedObject(opaqueTypedObj, typedObj, offset); michael@0: return opaqueTypedObj; michael@0: } michael@0: michael@0: function TypedObjectGetOpaqueIf(descr, typedObj, offset, cond) { michael@0: return (cond ? TypedObjectGetOpaque(descr, typedObj, offset) : undefined); michael@0: } michael@0: michael@0: function TypedObjectGetScalar(descr, typedObj, offset) { michael@0: var type = DESCR_TYPE(descr); michael@0: switch (type) { michael@0: case JS_SCALARTYPEREPR_INT8: michael@0: return Load_int8(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT8: michael@0: case JS_SCALARTYPEREPR_UINT8_CLAMPED: michael@0: return Load_uint8(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_INT16: michael@0: return Load_int16(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT16: michael@0: return Load_uint16(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_INT32: michael@0: return Load_int32(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT32: michael@0: return Load_uint32(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_FLOAT32: michael@0: return Load_float32(typedObj, offset); michael@0: michael@0: case JS_SCALARTYPEREPR_FLOAT64: michael@0: return Load_float64(typedObj, offset); michael@0: } michael@0: michael@0: assert(false, "Unhandled scalar type: " + type); michael@0: return undefined; michael@0: } michael@0: michael@0: function TypedObjectGetReference(descr, typedObj, offset) { michael@0: var type = DESCR_TYPE(descr); michael@0: switch (type) { michael@0: case JS_REFERENCETYPEREPR_ANY: michael@0: return Load_Any(typedObj, offset); michael@0: michael@0: case JS_REFERENCETYPEREPR_OBJECT: michael@0: return Load_Object(typedObj, offset); michael@0: michael@0: case JS_REFERENCETYPEREPR_STRING: michael@0: return Load_string(typedObj, offset); michael@0: } michael@0: michael@0: assert(false, "Unhandled scalar type: " + type); michael@0: return undefined; michael@0: } michael@0: michael@0: function TypedObjectGetX4(descr, typedObj, offset) { michael@0: var type = DESCR_TYPE(descr); michael@0: switch (type) { michael@0: case JS_X4TYPEREPR_FLOAT32: michael@0: var x = Load_float32(typedObj, offset + 0); michael@0: var y = Load_float32(typedObj, offset + 4); michael@0: var z = Load_float32(typedObj, offset + 8); michael@0: var w = Load_float32(typedObj, offset + 12); michael@0: return GetFloat32x4TypeDescr()(x, y, z, w); michael@0: michael@0: case JS_X4TYPEREPR_INT32: michael@0: var x = Load_int32(typedObj, offset + 0); michael@0: var y = Load_int32(typedObj, offset + 4); michael@0: var z = Load_int32(typedObj, offset + 8); michael@0: var w = Load_int32(typedObj, offset + 12); michael@0: return GetInt32x4TypeDescr()(x, y, z, w); michael@0: } michael@0: michael@0: assert(false, "Unhandled x4 type: " + type); michael@0: return undefined; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Setting values michael@0: // michael@0: // The methods in this section modify the data pointed at by `this`. michael@0: michael@0: // Writes `fromValue` into the `typedObj` at offset `offset`, adapting michael@0: // it to `descr` as needed. This is the most general entry point michael@0: // and works for any type. michael@0: function TypedObjectSet(descr, typedObj, offset, fromValue) { michael@0: assert(TypedObjectIsAttached(typedObj), "set() called with unattached typedObj"); michael@0: michael@0: // Fast path: `fromValue` is a typed object with same type michael@0: // representation as the destination. In that case, we can just do a michael@0: // memcpy. michael@0: if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) { michael@0: if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) { michael@0: if (!TypedObjectIsAttached(fromValue)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); michael@0: michael@0: var size = DESCR_SIZE(descr); michael@0: Memcpy(typedObj, offset, fromValue, 0, size); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: switch (DESCR_KIND(descr)) { michael@0: case JS_TYPEREPR_SCALAR_KIND: michael@0: TypedObjectSetScalar(descr, typedObj, offset, fromValue); michael@0: return; michael@0: michael@0: case JS_TYPEREPR_REFERENCE_KIND: michael@0: TypedObjectSetReference(descr, typedObj, offset, fromValue); michael@0: return; michael@0: michael@0: case JS_TYPEREPR_X4_KIND: michael@0: TypedObjectSetX4(descr, typedObj, offset, fromValue); michael@0: return; michael@0: michael@0: case JS_TYPEREPR_SIZED_ARRAY_KIND: michael@0: var length = DESCR_SIZED_ARRAY_LENGTH(descr); michael@0: if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue)) michael@0: return; michael@0: break; michael@0: michael@0: case JS_TYPEREPR_UNSIZED_ARRAY_KIND: michael@0: var length = typedObj.length; michael@0: if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue)) michael@0: return; michael@0: break; michael@0: michael@0: case JS_TYPEREPR_STRUCT_KIND: michael@0: if (!IsObject(fromValue)) michael@0: break; michael@0: michael@0: // Adapt each field. michael@0: var fieldNames = DESCR_STRUCT_FIELD_NAMES(descr); michael@0: var fieldDescrs = DESCR_STRUCT_FIELD_TYPES(descr); michael@0: var fieldOffsets = DESCR_STRUCT_FIELD_OFFSETS(descr); michael@0: for (var i = 0; i < fieldNames.length; i++) { michael@0: var fieldName = fieldNames[i]; michael@0: var fieldDescr = fieldDescrs[i]; michael@0: var fieldOffset = fieldOffsets[i]; michael@0: var fieldValue = fromValue[fieldName]; michael@0: TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldValue); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: ThrowError(JSMSG_CANT_CONVERT_TO, michael@0: typeof(fromValue), michael@0: DESCR_STRING_REPR(descr)); michael@0: } michael@0: michael@0: function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) { michael@0: if (!IsObject(fromValue)) michael@0: return false; michael@0: michael@0: // Check that "array-like" fromValue has an appropriate length. michael@0: if (fromValue.length !== length) michael@0: return false; michael@0: michael@0: // Adapt each element. michael@0: if (length > 0) { michael@0: var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr); michael@0: var elemSize = DESCR_SIZE(elemDescr); michael@0: var elemOffset = offset; michael@0: for (var i = 0; i < length; i++) { michael@0: TypedObjectSet(elemDescr, typedObj, elemOffset, fromValue[i]); michael@0: elemOffset += elemSize; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Sets `fromValue` to `this` assuming that `this` is a scalar type. michael@0: function TypedObjectSetScalar(descr, typedObj, offset, fromValue) { michael@0: assert(DESCR_KIND(descr) === JS_TYPEREPR_SCALAR_KIND, michael@0: "Expected scalar type descriptor"); michael@0: var type = DESCR_TYPE(descr); michael@0: switch (type) { michael@0: case JS_SCALARTYPEREPR_INT8: michael@0: return Store_int8(typedObj, offset, michael@0: TO_INT32(fromValue) & 0xFF); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT8: michael@0: return Store_uint8(typedObj, offset, michael@0: TO_UINT32(fromValue) & 0xFF); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT8_CLAMPED: michael@0: var v = ClampToUint8(+fromValue); michael@0: return Store_int8(typedObj, offset, v); michael@0: michael@0: case JS_SCALARTYPEREPR_INT16: michael@0: return Store_int16(typedObj, offset, michael@0: TO_INT32(fromValue) & 0xFFFF); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT16: michael@0: return Store_uint16(typedObj, offset, michael@0: TO_UINT32(fromValue) & 0xFFFF); michael@0: michael@0: case JS_SCALARTYPEREPR_INT32: michael@0: return Store_int32(typedObj, offset, michael@0: TO_INT32(fromValue)); michael@0: michael@0: case JS_SCALARTYPEREPR_UINT32: michael@0: return Store_uint32(typedObj, offset, michael@0: TO_UINT32(fromValue)); michael@0: michael@0: case JS_SCALARTYPEREPR_FLOAT32: michael@0: return Store_float32(typedObj, offset, +fromValue); michael@0: michael@0: case JS_SCALARTYPEREPR_FLOAT64: michael@0: return Store_float64(typedObj, offset, +fromValue); michael@0: } michael@0: michael@0: assert(false, "Unhandled scalar type: " + type); michael@0: return undefined; michael@0: } michael@0: michael@0: function TypedObjectSetReference(descr, typedObj, offset, fromValue) { michael@0: var type = DESCR_TYPE(descr); michael@0: switch (type) { michael@0: case JS_REFERENCETYPEREPR_ANY: michael@0: return Store_Any(typedObj, offset, fromValue); michael@0: michael@0: case JS_REFERENCETYPEREPR_OBJECT: michael@0: var value = (fromValue === null ? fromValue : ToObject(fromValue)); michael@0: return Store_Object(typedObj, offset, value); michael@0: michael@0: case JS_REFERENCETYPEREPR_STRING: michael@0: return Store_string(typedObj, offset, ToString(fromValue)); michael@0: } michael@0: michael@0: assert(false, "Unhandled scalar type: " + type); michael@0: return undefined; michael@0: } michael@0: michael@0: // Sets `fromValue` to `this` assuming that `this` is a scalar type. michael@0: function TypedObjectSetX4(descr, typedObj, offset, fromValue) { michael@0: // It is only permitted to set a float32x4/int32x4 value from another michael@0: // float32x4/int32x4; in that case, the "fast path" that uses memcopy will michael@0: // have already matched. So if we get to this point, we're supposed michael@0: // to "adapt" fromValue, but there are no legal adaptions. michael@0: ThrowError(JSMSG_CANT_CONVERT_TO, michael@0: typeof(fromValue), michael@0: DESCR_STRING_REPR(descr)); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // C++ Wrappers michael@0: // michael@0: // These helpers are invoked by C++ code or used as method bodies. michael@0: michael@0: // Wrapper for use from C++ code. michael@0: function ConvertAndCopyTo(destDescr, michael@0: destTypedObj, michael@0: destOffset, michael@0: fromValue) michael@0: { michael@0: assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr), michael@0: "ConvertAndCopyTo: not type obj"); michael@0: assert(IsObject(destTypedObj) && ObjectIsTypedObject(destTypedObj), michael@0: "ConvertAndCopyTo: not type typedObj"); michael@0: michael@0: if (!TypedObjectIsAttached(destTypedObj)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); michael@0: michael@0: TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue); michael@0: } michael@0: michael@0: // Wrapper for use from C++ code. michael@0: function Reify(sourceDescr, michael@0: sourceTypedObj, michael@0: sourceOffset) { michael@0: assert(IsObject(sourceDescr) && ObjectIsTypeDescr(sourceDescr), michael@0: "Reify: not type obj"); michael@0: assert(IsObject(sourceTypedObj) && ObjectIsTypedObject(sourceTypedObj), michael@0: "Reify: not type typedObj"); michael@0: michael@0: if (!TypedObjectIsAttached(sourceTypedObj)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); michael@0: michael@0: return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset); michael@0: } michael@0: michael@0: function FillTypedArrayWithValue(destArray, fromValue) { michael@0: assert(IsObject(handle) && ObjectIsTypedObject(destArray), michael@0: "FillTypedArrayWithValue: not typed handle"); michael@0: michael@0: var descr = TYPEDOBJ_TYPE_DESCR(destArray); michael@0: var length = DESCR_SIZED_ARRAY_LENGTH(descr); michael@0: if (length === 0) michael@0: return; michael@0: michael@0: // Use convert and copy to to produce the first element: michael@0: var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr); michael@0: TypedObjectSet(elemDescr, destArray, 0, fromValue); michael@0: michael@0: // Stamp out the remaining copies: michael@0: var elemSize = DESCR_SIZE(elemDescr); michael@0: var totalSize = length * elemSize; michael@0: for (var offset = elemSize; offset < totalSize; offset += elemSize) michael@0: Memcpy(destArray, offset, destArray, 0, elemSize); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypeDescrEquivalent(otherDescr) { michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return DescrsEquiv(this, otherDescr); michael@0: } michael@0: michael@0: // TypedArray.redimension(newArrayType) michael@0: // michael@0: // Method that "repackages" the data from this array into a new typed michael@0: // object whose type is `newArrayType`. Once you strip away all the michael@0: // outer array dimensions, the type of `this` array and `newArrayType` michael@0: // must share the same innermost element type. Moreover, those michael@0: // stripped away dimensions must amount to the same total number of michael@0: // elements. michael@0: // michael@0: // For example, given two equivalent types `T` and `U`, it is legal to michael@0: // interconvert between arrays types like: michael@0: // T[32] michael@0: // U[2][16] michael@0: // U[2][2][8] michael@0: // Because they all share the same total number (32) of equivalent elements. michael@0: // But it would be illegal to convert `T[32]` to `U[31]` or `U[2][17]`, since michael@0: // the number of elements differs. And it's just plain incompatible to convert michael@0: // if the base element types are not equivalent. michael@0: // michael@0: // Warning: user exposed! michael@0: function TypedArrayRedimension(newArrayType) { michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: // Peel away the outermost array layers from the type of `this` to find michael@0: // the core element type. In the process, count the number of elements. michael@0: var oldArrayType = TYPEDOBJ_TYPE_DESCR(this); michael@0: var oldArrayReprKind = DESCR_KIND(oldArrayType); michael@0: var oldElementType = oldArrayType; michael@0: var oldElementCount = 1; michael@0: switch (oldArrayReprKind) { michael@0: case JS_TYPEREPR_UNSIZED_ARRAY_KIND: michael@0: oldElementCount *= this.length; michael@0: oldElementType = oldElementType.elementType; michael@0: break; michael@0: michael@0: case JS_TYPEREPR_SIZED_ARRAY_KIND: michael@0: break; michael@0: michael@0: default: michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: while (DESCR_KIND(oldElementType) === JS_TYPEREPR_SIZED_ARRAY_KIND) { michael@0: oldElementCount *= oldElementType.length; michael@0: oldElementType = oldElementType.elementType; michael@0: } michael@0: michael@0: // Peel away the outermost array layers from `newArrayType`. In the michael@0: // process, count the number of elements. michael@0: var newElementType = newArrayType; michael@0: var newElementCount = 1; michael@0: while (DESCR_KIND(newElementType) == JS_TYPEREPR_SIZED_ARRAY_KIND) { michael@0: newElementCount *= newElementType.length; michael@0: newElementType = newElementType.elementType; michael@0: } michael@0: michael@0: // Check that the total number of elements does not change. michael@0: if (oldElementCount !== newElementCount) { michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: michael@0: // Check that the element types are equivalent. michael@0: if (!DescrsEquiv(oldElementType, newElementType)) { michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: michael@0: // Together, this should imply that the sizes are unchanged. michael@0: assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType), michael@0: "Byte sizes should be equal"); michael@0: michael@0: // Rewrap the data from `this` in a new type. michael@0: return NewDerivedTypedObject(newArrayType, this, 0); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // X4 michael@0: michael@0: function X4ProtoString(type) { michael@0: switch (type) { michael@0: case JS_X4TYPEREPR_INT32: michael@0: return "int32x4"; michael@0: case JS_X4TYPEREPR_FLOAT32: michael@0: return "float32x4"; michael@0: } michael@0: michael@0: assert(false, "Unhandled type constant"); michael@0: return undefined; michael@0: } michael@0: michael@0: function X4ToSource() { michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this); michael@0: michael@0: var descr = TYPEDOBJ_TYPE_DESCR(this); michael@0: michael@0: if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND) michael@0: ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this); michael@0: michael@0: var type = DESCR_TYPE(descr); michael@0: return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")"; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // Miscellaneous michael@0: michael@0: function DescrsEquiv(descr1, descr2) { michael@0: assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr"); michael@0: assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr"); michael@0: michael@0: // Potential optimization: these two strings are guaranteed to be michael@0: // atoms, and hence this string comparison can just be a pointer michael@0: // comparison. However, I don't think ion knows that. If this ever michael@0: // becomes a bottleneck, we can add a intrinsic at some point that michael@0: // is treated specially by Ion. (Bug 976688) michael@0: michael@0: return DESCR_STRING_REPR(descr1) === DESCR_STRING_REPR(descr2); michael@0: } michael@0: michael@0: // toSource() for type descriptors. michael@0: // michael@0: // Warning: user exposed! michael@0: function DescrToSource() { michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this)) michael@0: ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value"); michael@0: michael@0: return DESCR_STRING_REPR(this); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function ArrayShorthand(...dims) { michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: var T = GetTypedObjectModule(); michael@0: michael@0: if (dims.length == 0) michael@0: return new T.ArrayType(this); michael@0: michael@0: var accum = this; michael@0: for (var i = dims.length - 1; i >= 0; i--) michael@0: accum = new T.ArrayType(accum).dimension(dims[i]); michael@0: return accum; michael@0: } michael@0: michael@0: // This is the `storage()` function defined in the spec. When michael@0: // provided with a *transparent* typed object, it returns an object michael@0: // containing buffer, byteOffset, byteLength. When given an opaque michael@0: // typed object, it returns null. Otherwise it throws. michael@0: // michael@0: // Warning: user exposed! michael@0: function StorageOfTypedObject(obj) { michael@0: if (IsObject(obj)) { michael@0: if (ObjectIsOpaqueTypedObject(obj)) michael@0: return null; michael@0: michael@0: if (ObjectIsTransparentTypedObject(obj)) michael@0: return { buffer: TYPEDOBJ_OWNER(obj), michael@0: byteLength: TYPEDOBJ_BYTELENGTH(obj), michael@0: byteOffset: TYPEDOBJ_BYTEOFFSET(obj) }; michael@0: } michael@0: michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: return null; // pacify silly "always returns a value" lint michael@0: } michael@0: michael@0: // This is the `objectType()` function defined in the spec. michael@0: // It returns the type of its argument. michael@0: // michael@0: // Warning: user exposed! michael@0: function TypeOfTypedObject(obj) { michael@0: if (IsObject(obj) && ObjectIsTypedObject(obj)) michael@0: return TYPEDOBJ_TYPE_DESCR(obj); michael@0: michael@0: // Note: Do not create bindings for `Any`, `String`, etc in michael@0: // Utilities.js, but rather access them through michael@0: // `GetTypedObjectModule()`. The reason is that bindings michael@0: // you create in Utilities.js are part of the self-hosted global, michael@0: // vs the user-accessible global, and hence should not escape to michael@0: // user script. michael@0: var T = GetTypedObjectModule(); michael@0: switch (typeof obj) { michael@0: case "object": return T.Object; michael@0: case "function": return T.Object; michael@0: case "string": return T.String; michael@0: case "number": return T.float64; michael@0: case "undefined": return T.Any; michael@0: default: return T.Any; michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // TypedObject surface API methods (sequential implementations). michael@0: michael@0: // Warning: user exposed! michael@0: function TypedObjectArrayTypeBuild(a,b,c) { michael@0: // Arguments (this sized) : [depth], func michael@0: // Arguments (this unsized) : length, [depth], func michael@0: michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var kind = DESCR_KIND(this); michael@0: switch (kind) { michael@0: case JS_TYPEREPR_SIZED_ARRAY_KIND: michael@0: if (typeof a === "function") // XXX here and elsewhere: these type dispatches are fragile at best. michael@0: return BuildTypedSeqImpl(this, this.length, 1, a); michael@0: else if (typeof a === "number" && typeof b === "function") michael@0: return BuildTypedSeqImpl(this, this.length, a, b); michael@0: else if (typeof a === "number") michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: else michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: case JS_TYPEREPR_UNSIZED_ARRAY_KIND: michael@0: var len = a; michael@0: if (typeof b === "function") michael@0: return BuildTypedSeqImpl(this, len, 1, b); michael@0: else if (typeof b === "number" && typeof c === "function") michael@0: return BuildTypedSeqImpl(this, len, b, c); michael@0: else if (typeof b === "number") michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: else michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: default: michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedObjectArrayTypeFrom(a, b, c) { michael@0: // Arguments: arrayLike, [depth], func michael@0: michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a); michael@0: michael@0: // for untyped input array, the expectation (in terms of error michael@0: // reporting for invalid parameters) is no-depth, despite michael@0: // supporting an explicit depth of 1; while for typed input array, michael@0: // the expectation is explicit depth. michael@0: michael@0: if (untypedInput) { michael@0: var explicitDepth = (b === 1); michael@0: if (explicitDepth && IsCallable(c)) michael@0: return MapUntypedSeqImpl(a, this, c); michael@0: else if (IsCallable(b)) michael@0: return MapUntypedSeqImpl(a, this, b); michael@0: else michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } else { michael@0: var explicitDepth = (typeof b === "number"); michael@0: if (explicitDepth && IsCallable(c)) michael@0: return MapTypedSeqImpl(a, b, this, c); michael@0: else if (IsCallable(b)) michael@0: return MapTypedSeqImpl(a, 1, this, b); michael@0: else if (explicitDepth) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: else michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayMap(a, b) { michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var thisType = TYPEDOBJ_TYPE_DESCR(this); michael@0: if (!TypeDescrIsArrayType(thisType)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: // Arguments: [depth], func michael@0: if (typeof a === "number" && typeof b === "function") michael@0: return MapTypedSeqImpl(this, a, thisType, b); michael@0: else if (typeof a === "function") michael@0: return MapTypedSeqImpl(this, 1, thisType, a); michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayMapPar(a, b) { michael@0: // Arguments: [depth], func michael@0: michael@0: // Defer to the sequential variant for error cases or michael@0: // when not working with typed objects. michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: return callFunction(TypedArrayMap, this, a, b); michael@0: var thisType = TYPEDOBJ_TYPE_DESCR(this); michael@0: if (!TypeDescrIsArrayType(thisType)) michael@0: return callFunction(TypedArrayMap, this, a, b); michael@0: michael@0: if (typeof a === "number" && IsCallable(b)) michael@0: return MapTypedParImpl(this, a, thisType, b); michael@0: else if (IsCallable(a)) michael@0: return MapTypedParImpl(this, 1, thisType, a); michael@0: return callFunction(TypedArrayMap, this, a, b); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayReduce(a, b) { michael@0: // Arguments: func, [initial] michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var thisType = TYPEDOBJ_TYPE_DESCR(this); michael@0: if (!TypeDescrIsArrayType(thisType)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: if (a !== undefined && typeof a !== "function") michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: var outputType = thisType.elementType; michael@0: return ReduceTypedSeqImpl(this, outputType, a, b); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayScatter(a, b, c, d) { michael@0: // Arguments: outputArrayType, indices, defaultValue, conflictFunction michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var thisType = TYPEDOBJ_TYPE_DESCR(this); michael@0: if (!TypeDescrIsArrayType(thisType)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: if (d !== undefined && typeof d !== "function") michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: return ScatterTypedSeqImpl(this, a, b, c, d); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayFilter(func) { michael@0: // Arguments: predicate michael@0: if (!IsObject(this) || !ObjectIsTypedObject(this)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var thisType = TYPEDOBJ_TYPE_DESCR(this); michael@0: if (!TypeDescrIsArrayType(thisType)) michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: if (typeof func !== "function") michael@0: return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: return FilterTypedSeqImpl(this, func); michael@0: } michael@0: michael@0: // placeholders michael@0: michael@0: // Warning: user exposed! michael@0: function TypedObjectArrayTypeBuildPar(a,b,c) { michael@0: return callFunction(TypedObjectArrayTypeBuild, this, a, b, c); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedObjectArrayTypeFromPar(a,b,c) { michael@0: // Arguments: arrayLike, [depth], func michael@0: michael@0: // Use the sequential version for error cases or when arrayLike is michael@0: // not a typed object. michael@0: if (!IsObject(this) || !ObjectIsTypeDescr(this) || !TypeDescrIsArrayType(this)) michael@0: return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); michael@0: if (!IsObject(a) || !ObjectIsTypedObject(a)) michael@0: return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); michael@0: michael@0: // Detect whether an explicit depth is supplied. michael@0: if (typeof b === "number" && IsCallable(c)) michael@0: return MapTypedParImpl(a, b, this, c); michael@0: if (IsCallable(b)) michael@0: return MapTypedParImpl(a, 1, this, b); michael@0: return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayReducePar(a, b) { michael@0: return callFunction(TypedArrayReduce, this, a, b); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayScatterPar(a, b, c, d) { michael@0: return callFunction(TypedArrayScatter, this, a, b, c, d); michael@0: } michael@0: michael@0: // Warning: user exposed! michael@0: function TypedArrayFilterPar(func) { michael@0: return callFunction(TypedArrayFilter, this, func); michael@0: } michael@0: michael@0: // should eventually become macros michael@0: function NUM_BYTES(bits) { michael@0: return (bits + 7) >> 3; michael@0: } michael@0: function SET_BIT(data, index) { michael@0: var word = index >> 3; michael@0: var mask = 1 << (index & 0x7); michael@0: data[word] |= mask; michael@0: } michael@0: function GET_BIT(data, index) { michael@0: var word = index >> 3; michael@0: var mask = 1 << (index & 0x7); michael@0: return (data[word] & mask) != 0; michael@0: } michael@0: michael@0: // Bug 956914: make performance-tuned variants tailored to 1, 2, and 3 dimensions. michael@0: function BuildTypedSeqImpl(arrayType, len, depth, func) { michael@0: assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object"); michael@0: michael@0: if (depth <= 0 || TO_INT32(depth) !== depth) michael@0: // RangeError("bad depth") michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: // For example, if we have as input michael@0: // ArrayType(ArrayType(T, 4), 5) michael@0: // and a depth of 2, we get michael@0: // grainType = T michael@0: // iterationSpace = [5, 4] michael@0: var [iterationSpace, grainType, totalLength] = michael@0: ComputeIterationSpace(arrayType, depth, len); michael@0: michael@0: // Create a zeroed instance with no data michael@0: var result = arrayType.variable ? new arrayType(len) : new arrayType(); michael@0: michael@0: var indices = NewDenseArray(depth); michael@0: for (var i = 0; i < depth; i++) { michael@0: indices[i] = 0; michael@0: } michael@0: michael@0: var grainTypeIsComplex = !TypeDescrIsSimpleType(grainType); michael@0: var size = DESCR_SIZE(grainType); michael@0: var outOffset = 0; michael@0: for (i = 0; i < totalLength; i++) { michael@0: // Position out-pointer to point at &result[...indices], if appropriate. michael@0: var userOutPointer = TypedObjectGetOpaqueIf(grainType, result, outOffset, michael@0: grainTypeIsComplex); michael@0: michael@0: // Invoke func(...indices, userOutPointer) and store the result michael@0: callFunction(std_Array_push, indices, userOutPointer); michael@0: var r = callFunction(std_Function_apply, func, undefined, indices); michael@0: callFunction(std_Array_pop, indices); michael@0: if (r !== undefined) michael@0: TypedObjectSet(grainType, result, outOffset, r); // result[...indices] = r; michael@0: michael@0: // Increment indices. michael@0: IncrementIterationSpace(indices, iterationSpace); michael@0: outOffset += size; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: function ComputeIterationSpace(arrayType, depth, len) { michael@0: assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "ComputeIterationSpace called on non-type-object"); michael@0: assert(TypeDescrIsArrayType(arrayType), "ComputeIterationSpace called on non-array-type"); michael@0: assert(depth > 0, "ComputeIterationSpace called on non-positive depth"); michael@0: var iterationSpace = NewDenseArray(depth); michael@0: iterationSpace[0] = len; michael@0: var totalLength = len; michael@0: var grainType = arrayType.elementType; michael@0: michael@0: for (var i = 1; i < depth; i++) { michael@0: if (TypeDescrIsArrayType(grainType)) { michael@0: var grainLen = grainType.length; michael@0: iterationSpace[i] = grainLen; michael@0: totalLength *= grainLen; michael@0: grainType = grainType.elementType; michael@0: } else { michael@0: // RangeError("Depth "+depth+" too high"); michael@0: ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); michael@0: } michael@0: } michael@0: return [iterationSpace, grainType, totalLength]; michael@0: } michael@0: michael@0: function IncrementIterationSpace(indices, iterationSpace) { michael@0: // Increment something like michael@0: // [5, 5, 7, 8] michael@0: // in an iteration space of michael@0: // [9, 9, 9, 9] michael@0: // to michael@0: // [5, 5, 8, 0] michael@0: michael@0: assert(indices.length === iterationSpace.length, michael@0: "indices dimension must equal iterationSpace dimension."); michael@0: var n = indices.length - 1; michael@0: while (true) { michael@0: indices[n] += 1; michael@0: if (indices[n] < iterationSpace[n]) michael@0: return; michael@0: michael@0: assert(indices[n] === iterationSpace[n], michael@0: "Components of indices must match those of iterationSpace."); michael@0: indices[n] = 0; michael@0: if (n == 0) michael@0: return; michael@0: michael@0: n -= 1; michael@0: } michael@0: } michael@0: michael@0: // Implements |from| method for untyped |inArray|. (Depth is implicitly 1 for untyped input.) michael@0: function MapUntypedSeqImpl(inArray, outputType, maybeFunc) { michael@0: assert(IsObject(outputType), "1. Map/From called on non-object outputType"); michael@0: assert(ObjectIsTypeDescr(outputType), "1. Map/From called on non-type-object outputType"); michael@0: inArray = ToObject(inArray); michael@0: assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); michael@0: michael@0: if (!IsCallable(maybeFunc)) michael@0: ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc)); michael@0: var func = maybeFunc; michael@0: michael@0: // Skip check for compatible iteration spaces; any normal JS array michael@0: // is trivially compatible with any iteration space of depth 1. michael@0: michael@0: var outLength = outputType.variable ? inArray.length : outputType.length; michael@0: var outGrainType = outputType.elementType; michael@0: michael@0: // Create a zeroed instance with no data michael@0: var result = outputType.variable ? new outputType(inArray.length) : new outputType(); michael@0: michael@0: var outUnitSize = DESCR_SIZE(outGrainType); michael@0: var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); michael@0: var outOffset = 0; michael@0: michael@0: // Core of map computation starts here (comparable to michael@0: // DoMapTypedSeqDepth1 and DoMapTypedSeqDepthN below). michael@0: michael@0: for (var i = 0; i < outLength; i++) { michael@0: // In this loop, since depth is 1, "indices" denotes singleton array [i]. michael@0: michael@0: if (i in inArray) { // Check for holes (only needed for untyped case). michael@0: // Extract element value. michael@0: var element = inArray[i]; michael@0: michael@0: // Create out pointer to point at &array[...indices] for result array. michael@0: var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, michael@0: outGrainTypeIsComplex); michael@0: michael@0: // Invoke: var r = func(element, ...indices, collection, out); michael@0: var r = func(element, i, inArray, out); michael@0: michael@0: if (r !== undefined) michael@0: TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r michael@0: } michael@0: michael@0: // Update offset and (implicitly) increment indices. michael@0: outOffset += outUnitSize; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: // Implements |map| and |from| methods for typed |inArray|. michael@0: function MapTypedSeqImpl(inArray, depth, outputType, func) { michael@0: assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "2. Map/From called on non-type-object outputType"); michael@0: assert(IsObject(inArray) && ObjectIsTypedObject(inArray), "Map/From called on non-object or untyped input array."); michael@0: assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); michael@0: michael@0: if (depth <= 0 || TO_INT32(depth) !== depth) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: // Compute iteration space for input and output and check for compatibility. michael@0: var inputType = TypeOfTypedObject(inArray); michael@0: var [inIterationSpace, inGrainType, _] = michael@0: ComputeIterationSpace(inputType, depth, inArray.length); michael@0: if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: var [iterationSpace, outGrainType, totalLength] = michael@0: ComputeIterationSpace(outputType, depth, outputType.variable ? inArray.length : outputType.length); michael@0: for (var i = 0; i < depth; i++) michael@0: if (inIterationSpace[i] !== iterationSpace[i]) michael@0: // TypeError("Incompatible iteration space in input and output type"); michael@0: ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); michael@0: michael@0: // Create a zeroed instance with no data michael@0: var result = outputType.variable ? new outputType(inArray.length) : new outputType(); michael@0: michael@0: var inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType); michael@0: var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); michael@0: michael@0: var inOffset = 0; michael@0: var outOffset = 0; michael@0: michael@0: var isDepth1Simple = depth == 1 && !(inGrainTypeIsComplex || outGrainTypeIsComplex); michael@0: michael@0: var inUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(inGrainType); michael@0: var outUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(outGrainType); michael@0: michael@0: // Bug 956914: add additional variants for depth = 2, 3, etc. michael@0: michael@0: function DoMapTypedSeqDepth1() { michael@0: for (var i = 0; i < totalLength; i++) { michael@0: // In this loop, since depth is 1, "indices" denotes singleton array [i]. michael@0: michael@0: // Prepare input element/handle and out pointer michael@0: var element = TypedObjectGet(inGrainType, inArray, inOffset); michael@0: var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, michael@0: outGrainTypeIsComplex); michael@0: michael@0: // Invoke: var r = func(element, ...indices, collection, out); michael@0: var r = func(element, i, inArray, out); michael@0: if (r !== undefined) michael@0: TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r michael@0: michael@0: // Update offsets and (implicitly) increment indices. michael@0: inOffset += inUnitSize; michael@0: outOffset += outUnitSize; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: function DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result) { michael@0: for (var i = 0; i < totalLength; i++) { michael@0: var r = func(inArray[i], i, inArray, undefined); michael@0: if (r !== undefined) michael@0: result[i] = r; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: function DoMapTypedSeqDepthN() { michael@0: var indices = new Uint32Array(depth); michael@0: michael@0: for (var i = 0; i < totalLength; i++) { michael@0: // Prepare input element and out pointer michael@0: var element = TypedObjectGet(inGrainType, inArray, inOffset); michael@0: var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, michael@0: outGrainTypeIsComplex); michael@0: michael@0: // Invoke: var r = func(element, ...indices, collection, out); michael@0: var args = [element]; michael@0: callFunction(std_Function_apply, std_Array_push, args, indices); michael@0: callFunction(std_Array_push, args, inArray, out); michael@0: var r = callFunction(std_Function_apply, func, void 0, args); michael@0: if (r !== undefined) michael@0: TypedObjectSet(outGrainType, result, outOffset, r); // result[...indices] = r michael@0: michael@0: // Update offsets and explicitly increment indices. michael@0: inOffset += inUnitSize; michael@0: outOffset += outUnitSize; michael@0: IncrementIterationSpace(indices, iterationSpace); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: if (isDepth1Simple) michael@0: return DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result); michael@0: michael@0: if (depth == 1) michael@0: return DoMapTypedSeqDepth1(); michael@0: michael@0: return DoMapTypedSeqDepthN(); michael@0: } michael@0: michael@0: // Implements |map| and |from| methods for typed |inArray|. michael@0: function MapTypedParImpl(inArray, depth, outputType, func) { michael@0: assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), michael@0: "Map/From called on non-type-object outputType"); michael@0: assert(IsObject(inArray) && ObjectIsTypedObject(inArray), michael@0: "Map/From called on non-object or untyped input array."); michael@0: assert(TypeDescrIsArrayType(outputType), michael@0: "Map/From called on non array-type outputType"); michael@0: assert(typeof depth === "number", michael@0: "Map/From called with non-numeric depth"); michael@0: assert(IsCallable(func), michael@0: "Map/From called on something not callable"); michael@0: michael@0: var inArrayType = TypeOfTypedObject(inArray); michael@0: michael@0: if (ShouldForceSequential() || michael@0: depth <= 0 || michael@0: TO_INT32(depth) !== depth || michael@0: !TypeDescrIsArrayType(inArrayType) || michael@0: !TypeDescrIsUnsizedArrayType(outputType)) michael@0: { michael@0: // defer error cases to seq implementation: michael@0: return MapTypedSeqImpl(inArray, depth, outputType, func); michael@0: } michael@0: michael@0: switch (depth) { michael@0: case 1: michael@0: return MapTypedParImplDepth1(inArray, inArrayType, outputType, func); michael@0: default: michael@0: return MapTypedSeqImpl(inArray, depth, outputType, func); michael@0: } michael@0: } michael@0: michael@0: function RedirectPointer(typedObj, offset, outputIsScalar) { michael@0: if (!outputIsScalar || !InParallelSection()) { michael@0: // ^ Subtle note: always check InParallelSection() last, because michael@0: // otherwise the other if conditions will not execute during michael@0: // sequential mode and we will not gather enough type michael@0: // information. michael@0: michael@0: // Here `typedObj` represents the input or output pointer we will michael@0: // pass to the user function. Ideally, we will just update the michael@0: // offset of `typedObj` in place so that it moves along the michael@0: // input/output buffer without incurring any allocation costs. But michael@0: // we can only do this if these changes are invisible to the user. michael@0: // michael@0: // Under normal uses, such changes *should* be invisible -- the michael@0: // in/out pointers are only intended to be used during the michael@0: // callback and then discarded, but of course in the general case michael@0: // nothing prevents them from escaping. michael@0: // michael@0: // However, if we are in parallel mode, we know that the pointers michael@0: // will not escape into global state. They could still escape by michael@0: // being returned into the resulting array, but even that avenue michael@0: // is impossible if the result array cannot contain objects. michael@0: // michael@0: // Therefore, we reuse a pointer if we are both in parallel mode michael@0: // and we have a transparent output type. It'd be nice to loosen michael@0: // this condition later by using fancy ion optimizations that michael@0: // assume the value won't escape and copy it if it does. But those michael@0: // don't exist yet. Moreover, checking if the type is transparent michael@0: // is an overapproximation: users can manually declare opaque michael@0: // types that nonetheless only contain scalar data. michael@0: michael@0: typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj), michael@0: typedObj, 0); michael@0: } michael@0: michael@0: SetTypedObjectOffset(typedObj, offset); michael@0: return typedObj; michael@0: } michael@0: SetScriptHints(RedirectPointer, { inline: true }); michael@0: michael@0: function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) { michael@0: assert(IsObject(inArrayType) && ObjectIsTypeDescr(inArrayType) && michael@0: TypeDescrIsArrayType(inArrayType), michael@0: "DoMapTypedParDepth1: invalid inArrayType"); michael@0: assert(IsObject(outArrayType) && ObjectIsTypeDescr(outArrayType) && michael@0: TypeDescrIsArrayType(outArrayType), michael@0: "DoMapTypedParDepth1: invalid outArrayType"); michael@0: assert(IsObject(inArray) && ObjectIsTypedObject(inArray), michael@0: "DoMapTypedParDepth1: invalid inArray"); michael@0: michael@0: // Determine the grain types of the input and output. michael@0: const inGrainType = inArrayType.elementType; michael@0: const outGrainType = outArrayType.elementType; michael@0: const inGrainTypeSize = DESCR_SIZE(inGrainType); michael@0: const outGrainTypeSize = DESCR_SIZE(outGrainType); michael@0: const inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType); michael@0: const outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); michael@0: michael@0: const length = inArray.length; michael@0: const mode = undefined; michael@0: michael@0: const outArray = new outArrayType(length); michael@0: if (length === 0) michael@0: return outArray; michael@0: michael@0: const outGrainTypeIsTransparent = ObjectIsTransparentTypedObject(outArray); michael@0: michael@0: // Construct the slices and initial pointers for each worker: michael@0: const slicesInfo = ComputeSlicesInfo(length); michael@0: const numWorkers = ForkJoinNumWorkers(); michael@0: assert(numWorkers > 0, "Should have at least the main thread"); michael@0: const pointers = []; michael@0: for (var i = 0; i < numWorkers; i++) { michael@0: const inTypedObject = TypedObjectGetDerivedIf(inGrainType, inArray, 0, michael@0: inGrainTypeIsComplex); michael@0: const outTypedObject = TypedObjectGetOpaqueIf(outGrainType, outArray, 0, michael@0: outGrainTypeIsComplex); michael@0: ARRAY_PUSH(pointers, ({ inTypedObject: inTypedObject, michael@0: outTypedObject: outTypedObject })); michael@0: } michael@0: michael@0: // Below we will be adjusting offsets within the input to point at michael@0: // successive entries; we'll need to know the offset of inArray michael@0: // relative to its owner (which is often but not always 0). michael@0: const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray); michael@0: michael@0: ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode)); michael@0: return outArray; michael@0: michael@0: function mapThread(workerId, sliceStart, sliceEnd) { michael@0: assert(TO_INT32(workerId) === workerId, michael@0: "workerId not int: " + workerId); michael@0: assert(workerId < pointers.length, michael@0: "workerId too large: " + workerId + " >= " + pointers.length); michael@0: michael@0: var pointerIndex = InParallelSection() ? workerId : 0; michael@0: assert(!!pointers[pointerIndex], michael@0: "no pointer data for workerId: " + workerId); michael@0: michael@0: const { inTypedObject, outTypedObject } = pointers[pointerIndex]; michael@0: const sliceShift = slicesInfo.shift; michael@0: var sliceId; michael@0: michael@0: while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { michael@0: const indexStart = SLICE_START_INDEX(sliceShift, sliceId); michael@0: const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); michael@0: michael@0: var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart); michael@0: var outOffset = std_Math_imul(outGrainTypeSize, indexStart); michael@0: michael@0: // Set the target region so that user is only permitted to write michael@0: // within the range set aside for this slice. This prevents user michael@0: // from writing to typed objects that escaped from prior slices michael@0: // during sequential iteration. Note that, for any particular michael@0: // iteration of the loop below, it's only valid to write to the michael@0: // memory range corresponding to the index `i` -- however, since michael@0: // the different iterations cannot communicate typed object michael@0: // pointers to one another during parallel exec, we need only michael@0: // fear escaped typed objects from *other* slices, so we can michael@0: // just set the target region once. michael@0: const endOffset = std_Math_imul(outGrainTypeSize, indexEnd); michael@0: SetForkJoinTargetRegion(outArray, outOffset, endOffset); michael@0: michael@0: for (var i = indexStart; i < indexEnd; i++) { michael@0: var inVal = (inGrainTypeIsComplex michael@0: ? RedirectPointer(inTypedObject, inOffset, michael@0: outGrainTypeIsTransparent) michael@0: : inArray[i]); michael@0: var outVal = (outGrainTypeIsComplex michael@0: ? RedirectPointer(outTypedObject, outOffset, michael@0: outGrainTypeIsTransparent) michael@0: : undefined); michael@0: const r = func(inVal, i, inArray, outVal); michael@0: if (r !== undefined) { michael@0: if (outGrainTypeIsComplex) michael@0: SetTypedObjectValue(outGrainType, outArray, outOffset, r); michael@0: else michael@0: UnsafePutElements(outArray, i, r); michael@0: } michael@0: inOffset += inGrainTypeSize; michael@0: outOffset += outGrainTypeSize; michael@0: michael@0: // A transparent result type cannot contain references, and michael@0: // hence there is no way for a pointer to a thread-local object michael@0: // to escape. michael@0: if (outGrainTypeIsTransparent) michael@0: ClearThreadLocalArenas(); michael@0: } michael@0: } michael@0: michael@0: return sliceId; michael@0: } michael@0: michael@0: return undefined; michael@0: } michael@0: SetScriptHints(MapTypedParImplDepth1, { cloneAtCallsite: true }); michael@0: michael@0: function ReduceTypedSeqImpl(array, outputType, func, initial) { michael@0: assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array."); michael@0: assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType"); michael@0: michael@0: var start, value; michael@0: michael@0: if (initial === undefined && array.length < 1) michael@0: // RangeError("reduce requires array of length > 0") michael@0: ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); michael@0: michael@0: // FIXME bug 950106 Should reduce method supply an outptr handle? michael@0: // For now, reduce never supplies an outptr, regardless of outputType. michael@0: michael@0: if (TypeDescrIsSimpleType(outputType)) { michael@0: if (initial === undefined) { michael@0: start = 1; michael@0: value = array[0]; michael@0: } else { michael@0: start = 0; michael@0: value = outputType(initial); michael@0: } michael@0: michael@0: for (var i = start; i < array.length; i++) michael@0: value = outputType(func(value, array[i])); michael@0: michael@0: } else { michael@0: if (initial === undefined) { michael@0: start = 1; michael@0: value = new outputType(array[0]); michael@0: } else { michael@0: start = 0; michael@0: value = initial; michael@0: } michael@0: michael@0: for (var i = start; i < array.length; i++) michael@0: value = func(value, array[i]); michael@0: } michael@0: michael@0: return value; michael@0: } michael@0: michael@0: function ScatterTypedSeqImpl(array, outputType, indices, defaultValue, conflictFunc) { michael@0: assert(IsObject(array) && ObjectIsTypedObject(array), "Scatter called on non-object or untyped input array."); michael@0: assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Scatter called on non-type-object outputType"); michael@0: assert(TypeDescrIsSizedArrayType(outputType), "Scatter called on non-sized array type"); michael@0: assert(conflictFunc === undefined || typeof conflictFunc === "function", "Scatter called with invalid conflictFunc"); michael@0: michael@0: var result = new outputType(); michael@0: var bitvec = new Uint8Array(result.length); michael@0: var elemType = outputType.elementType; michael@0: var i, j; michael@0: if (defaultValue !== elemType(undefined)) { michael@0: for (i = 0; i < result.length; i++) { michael@0: result[i] = defaultValue; michael@0: } michael@0: } michael@0: michael@0: for (i = 0; i < indices.length; i++) { michael@0: j = indices[i]; michael@0: if (!GET_BIT(bitvec, j)) { michael@0: result[j] = array[i]; michael@0: SET_BIT(bitvec, j); michael@0: } else if (conflictFunc === undefined) { michael@0: ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); michael@0: } else { michael@0: result[j] = conflictFunc(result[j], elemType(array[i])); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: function FilterTypedSeqImpl(array, func) { michael@0: assert(IsObject(array) && ObjectIsTypedObject(array), "Filter called on non-object or untyped input array."); michael@0: assert(typeof func === "function", "Filter called with non-function predicate"); michael@0: michael@0: var arrayType = TypeOfTypedObject(array); michael@0: if (!TypeDescrIsArrayType(arrayType)) michael@0: ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); michael@0: michael@0: var elementType = arrayType.elementType; michael@0: var flags = new Uint8Array(NUM_BYTES(array.length)); michael@0: var count = 0; michael@0: var size = DESCR_SIZE(elementType); michael@0: var inOffset = 0; michael@0: for (var i = 0; i < array.length; i++) { michael@0: var v = TypedObjectGet(elementType, array, inOffset); michael@0: if (func(v, i, array)) { michael@0: SET_BIT(flags, i); michael@0: count++; michael@0: } michael@0: inOffset += size; michael@0: } michael@0: michael@0: var resultType = (arrayType.variable ? arrayType : arrayType.unsized); michael@0: var result = new resultType(count); michael@0: for (var i = 0, j = 0; i < array.length; i++) { michael@0: if (GET_BIT(flags, i)) michael@0: result[j++] = array[i]; michael@0: } michael@0: return result; michael@0: }