| #include "TypedObjectConstants.h" |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Getters and setters for various slots. |
| |
| // Type object slots |
| |
| #define DESCR_KIND(obj) \ |
| UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_KIND) |
| #define DESCR_STRING_REPR(obj) \ |
| UnsafeGetStringFromReservedSlot(obj, JS_DESCR_SLOT_STRING_REPR) |
| #define DESCR_ALIGNMENT(obj) \ |
| UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_ALIGNMENT) |
| #define DESCR_SIZE(obj) \ |
| UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_SIZE) |
| #define DESCR_OPAQUE(obj) \ |
| UnsafeGetBooleanFromReservedSlot(obj, JS_DESCR_SLOT_OPAQUE) |
| #define DESCR_TYPE(obj) \ |
| UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_TYPE) |
| #define DESCR_ARRAY_ELEMENT_TYPE(obj) \ |
| UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE) |
| #define DESCR_ARRAY_LENGTH(obj) \ |
| TO_INT32(UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_ARRAY_LENGTH)) |
| #define DESCR_STRUCT_FIELD_NAMES(obj) \ |
| UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES) |
| #define DESCR_STRUCT_FIELD_TYPES(obj) \ |
| UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES) |
| #define DESCR_STRUCT_FIELD_OFFSETS(obj) \ |
| UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS) |
| |
| // Other |
| |
| #define HAS_PROPERTY(obj, prop) \ |
| callFunction(std_Object_hasOwnProperty, obj, prop) |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Getting values |
| // |
| // The methods in this section read from the memory pointed at |
| // by `this` and produce JS values. This process is called *reification* |
| // in the spec. |
| |
| // Reifies the value referenced by the pointer, meaning that it |
| // returns a new object pointing at the value. If the value is |
| // a scalar, it will return a JS number, but otherwise the reified |
| // result will be a typedObj of the same class as the ptr's typedObj. |
| function TypedObjectGet(descr, typedObj, offset) { |
| assert(IsObject(descr) && ObjectIsTypeDescr(descr), |
| "get() called with bad type descr"); |
| |
| if (!TypedObjectIsAttached(typedObj)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); |
| |
| switch (DESCR_KIND(descr)) { |
| case JS_TYPEREPR_SCALAR_KIND: |
| return TypedObjectGetScalar(descr, typedObj, offset); |
| |
| case JS_TYPEREPR_REFERENCE_KIND: |
| return TypedObjectGetReference(descr, typedObj, offset); |
| |
| case JS_TYPEREPR_SIMD_KIND: |
| return TypedObjectGetSimd(descr, typedObj, offset); |
| |
| case JS_TYPEREPR_ARRAY_KIND: |
| case JS_TYPEREPR_STRUCT_KIND: |
| return TypedObjectGetDerived(descr, typedObj, offset); |
| } |
| |
| assert(false, "Unhandled kind: " + DESCR_KIND(descr)); |
| return undefined; |
| } |
| |
| function TypedObjectGetDerived(descr, typedObj, offset) { |
| assert(!TypeDescrIsSimpleType(descr), |
| "getDerived() used with simple type"); |
| return NewDerivedTypedObject(descr, typedObj, offset); |
| } |
| |
| function TypedObjectGetDerivedIf(descr, typedObj, offset, cond) { |
| return (cond ? TypedObjectGetDerived(descr, typedObj, offset) : undefined); |
| } |
| |
| function TypedObjectGetOpaque(descr, typedObj, offset) { |
| assert(!TypeDescrIsSimpleType(descr), |
| "getDerived() used with simple type"); |
| var opaqueTypedObj = NewOpaqueTypedObject(descr); |
| AttachTypedObject(opaqueTypedObj, typedObj, offset); |
| return opaqueTypedObj; |
| } |
| |
| function TypedObjectGetOpaqueIf(descr, typedObj, offset, cond) { |
| return (cond ? TypedObjectGetOpaque(descr, typedObj, offset) : undefined); |
| } |
| |
| function TypedObjectGetScalar(descr, typedObj, offset) { |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_SCALARTYPEREPR_INT8: |
| return Load_int8(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_UINT8: |
| case JS_SCALARTYPEREPR_UINT8_CLAMPED: |
| return Load_uint8(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_INT16: |
| return Load_int16(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_UINT16: |
| return Load_uint16(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_INT32: |
| return Load_int32(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_UINT32: |
| return Load_uint32(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_FLOAT32: |
| return Load_float32(typedObj, offset); |
| |
| case JS_SCALARTYPEREPR_FLOAT64: |
| return Load_float64(typedObj, offset); |
| } |
| |
| assert(false, "Unhandled scalar type: " + type); |
| return undefined; |
| } |
| |
| function TypedObjectGetReference(descr, typedObj, offset) { |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_REFERENCETYPEREPR_ANY: |
| return Load_Any(typedObj, offset); |
| |
| case JS_REFERENCETYPEREPR_OBJECT: |
| return Load_Object(typedObj, offset); |
| |
| case JS_REFERENCETYPEREPR_STRING: |
| return Load_string(typedObj, offset); |
| } |
| |
| assert(false, "Unhandled scalar type: " + type); |
| return undefined; |
| } |
| |
| function TypedObjectGetSimd(descr, typedObj, offset) { |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_SIMDTYPEREPR_FLOAT32X4: |
| var x = Load_float32(typedObj, offset + 0); |
| var y = Load_float32(typedObj, offset + 4); |
| var z = Load_float32(typedObj, offset + 8); |
| var w = Load_float32(typedObj, offset + 12); |
| return GetFloat32x4TypeDescr()(x, y, z, w); |
| |
| case JS_SIMDTYPEREPR_FLOAT64X2: |
| var x = Load_float64(typedObj, offset + 0); |
| var y = Load_float64(typedObj, offset + 8); |
| return GetFloat64x2TypeDescr()(x, y); |
| |
| case JS_SIMDTYPEREPR_INT8X16: |
| var s0 = Load_int8(typedObj, offset + 0); |
| var s1 = Load_int8(typedObj, offset + 1); |
| var s2 = Load_int8(typedObj, offset + 2); |
| var s3 = Load_int8(typedObj, offset + 3); |
| var s4 = Load_int8(typedObj, offset + 4); |
| var s5 = Load_int8(typedObj, offset + 5); |
| var s6 = Load_int8(typedObj, offset + 6); |
| var s7 = Load_int8(typedObj, offset + 7); |
| var s8 = Load_int8(typedObj, offset + 8); |
| var s9 = Load_int8(typedObj, offset + 9); |
| var s10 = Load_int8(typedObj, offset + 10); |
| var s11 = Load_int8(typedObj, offset + 11); |
| var s12 = Load_int8(typedObj, offset + 12); |
| var s13 = Load_int8(typedObj, offset + 13); |
| var s14 = Load_int8(typedObj, offset + 14); |
| var s15 = Load_int8(typedObj, offset + 15); |
| return GetInt8x16TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7, |
| s8, s9, s10, s11, s12, s13, s14, s15); |
| |
| case JS_SIMDTYPEREPR_INT16X8: |
| var s0 = Load_int16(typedObj, offset + 0); |
| var s1 = Load_int16(typedObj, offset + 2); |
| var s2 = Load_int16(typedObj, offset + 4); |
| var s3 = Load_int16(typedObj, offset + 6); |
| var s4 = Load_int16(typedObj, offset + 8); |
| var s5 = Load_int16(typedObj, offset + 10); |
| var s6 = Load_int16(typedObj, offset + 12); |
| var s7 = Load_int16(typedObj, offset + 14); |
| return GetInt16x8TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7); |
| |
| case JS_SIMDTYPEREPR_INT32X4: |
| var x = Load_int32(typedObj, offset + 0); |
| var y = Load_int32(typedObj, offset + 4); |
| var z = Load_int32(typedObj, offset + 8); |
| var w = Load_int32(typedObj, offset + 12); |
| return GetInt32x4TypeDescr()(x, y, z, w); |
| } |
| |
| assert(false, "Unhandled SIMD type: " + type); |
| return undefined; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Setting values |
| // |
| // The methods in this section modify the data pointed at by `this`. |
| |
| // Writes `fromValue` into the `typedObj` at offset `offset`, adapting |
| // it to `descr` as needed. This is the most general entry point |
| // and works for any type. |
| function TypedObjectSet(descr, typedObj, offset, name, fromValue) { |
| if (!TypedObjectIsAttached(typedObj)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); |
| |
| switch (DESCR_KIND(descr)) { |
| case JS_TYPEREPR_SCALAR_KIND: |
| TypedObjectSetScalar(descr, typedObj, offset, fromValue); |
| return; |
| |
| case JS_TYPEREPR_REFERENCE_KIND: |
| TypedObjectSetReference(descr, typedObj, offset, name, fromValue); |
| return; |
| |
| case JS_TYPEREPR_SIMD_KIND: |
| TypedObjectSetSimd(descr, typedObj, offset, fromValue); |
| return; |
| |
| case JS_TYPEREPR_ARRAY_KIND: |
| var length = DESCR_ARRAY_LENGTH(descr); |
| if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue)) |
| return; |
| break; |
| |
| case JS_TYPEREPR_STRUCT_KIND: |
| if (!IsObject(fromValue)) |
| break; |
| |
| // Adapt each field. |
| var fieldNames = DESCR_STRUCT_FIELD_NAMES(descr); |
| var fieldDescrs = DESCR_STRUCT_FIELD_TYPES(descr); |
| var fieldOffsets = DESCR_STRUCT_FIELD_OFFSETS(descr); |
| for (var i = 0; i < fieldNames.length; i++) { |
| var fieldName = fieldNames[i]; |
| var fieldDescr = fieldDescrs[i]; |
| var fieldOffset = fieldOffsets[i]; |
| var fieldValue = fromValue[fieldName]; |
| TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldName, fieldValue); |
| } |
| return; |
| } |
| |
| ThrowTypeError(JSMSG_CANT_CONVERT_TO, |
| typeof(fromValue), |
| DESCR_STRING_REPR(descr)); |
| } |
| |
| function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) { |
| if (!IsObject(fromValue)) |
| return false; |
| |
| // Check that "array-like" fromValue has an appropriate length. |
| if (fromValue.length !== length) |
| return false; |
| |
| // Adapt each element. |
| if (length > 0) { |
| var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr); |
| var elemSize = DESCR_SIZE(elemDescr); |
| var elemOffset = offset; |
| for (var i = 0; i < length; i++) { |
| TypedObjectSet(elemDescr, typedObj, elemOffset, null, fromValue[i]); |
| elemOffset += elemSize; |
| } |
| } |
| return true; |
| } |
| |
| // Sets `fromValue` to `this` assuming that `this` is a scalar type. |
| function TypedObjectSetScalar(descr, typedObj, offset, fromValue) { |
| assert(DESCR_KIND(descr) === JS_TYPEREPR_SCALAR_KIND, |
| "Expected scalar type descriptor"); |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_SCALARTYPEREPR_INT8: |
| return Store_int8(typedObj, offset, |
| TO_INT32(fromValue) & 0xFF); |
| |
| case JS_SCALARTYPEREPR_UINT8: |
| return Store_uint8(typedObj, offset, |
| TO_UINT32(fromValue) & 0xFF); |
| |
| case JS_SCALARTYPEREPR_UINT8_CLAMPED: |
| var v = ClampToUint8(+fromValue); |
| return Store_int8(typedObj, offset, v); |
| |
| case JS_SCALARTYPEREPR_INT16: |
| return Store_int16(typedObj, offset, |
| TO_INT32(fromValue) & 0xFFFF); |
| |
| case JS_SCALARTYPEREPR_UINT16: |
| return Store_uint16(typedObj, offset, |
| TO_UINT32(fromValue) & 0xFFFF); |
| |
| case JS_SCALARTYPEREPR_INT32: |
| return Store_int32(typedObj, offset, |
| TO_INT32(fromValue)); |
| |
| case JS_SCALARTYPEREPR_UINT32: |
| return Store_uint32(typedObj, offset, |
| TO_UINT32(fromValue)); |
| |
| case JS_SCALARTYPEREPR_FLOAT32: |
| return Store_float32(typedObj, offset, +fromValue); |
| |
| case JS_SCALARTYPEREPR_FLOAT64: |
| return Store_float64(typedObj, offset, +fromValue); |
| } |
| |
| assert(false, "Unhandled scalar type: " + type); |
| return undefined; |
| } |
| |
| function TypedObjectSetReference(descr, typedObj, offset, name, fromValue) { |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_REFERENCETYPEREPR_ANY: |
| return Store_Any(typedObj, offset, name, fromValue); |
| |
| case JS_REFERENCETYPEREPR_OBJECT: |
| var value = (fromValue === null ? fromValue : ToObject(fromValue)); |
| return Store_Object(typedObj, offset, name, value); |
| |
| case JS_REFERENCETYPEREPR_STRING: |
| return Store_string(typedObj, offset, name, ToString(fromValue)); |
| } |
| |
| assert(false, "Unhandled scalar type: " + type); |
| return undefined; |
| } |
| |
| // Sets `fromValue` to `this` assuming that `this` is a scalar type. |
| function TypedObjectSetSimd(descr, typedObj, offset, fromValue) { |
| if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue)) |
| ThrowTypeError(JSMSG_CANT_CONVERT_TO, |
| typeof(fromValue), |
| DESCR_STRING_REPR(descr)); |
| |
| if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) |
| ThrowTypeError(JSMSG_CANT_CONVERT_TO, |
| typeof(fromValue), |
| DESCR_STRING_REPR(descr)); |
| |
| var type = DESCR_TYPE(descr); |
| switch (type) { |
| case JS_SIMDTYPEREPR_FLOAT32X4: |
| Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0)); |
| Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4)); |
| Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8)); |
| Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12)); |
| break; |
| case JS_SIMDTYPEREPR_FLOAT64X2: |
| Store_float64(typedObj, offset + 0, Load_float64(fromValue, 0)); |
| Store_float64(typedObj, offset + 8, Load_float64(fromValue, 8)); |
| break; |
| case JS_SIMDTYPEREPR_INT8X16: |
| Store_int8(typedObj, offset + 0, Load_int8(fromValue, 0)); |
| Store_int8(typedObj, offset + 1, Load_int8(fromValue, 1)); |
| Store_int8(typedObj, offset + 2, Load_int8(fromValue, 2)); |
| Store_int8(typedObj, offset + 3, Load_int8(fromValue, 3)); |
| Store_int8(typedObj, offset + 4, Load_int8(fromValue, 4)); |
| Store_int8(typedObj, offset + 5, Load_int8(fromValue, 5)); |
| Store_int8(typedObj, offset + 6, Load_int8(fromValue, 6)); |
| Store_int8(typedObj, offset + 7, Load_int8(fromValue, 7)); |
| Store_int8(typedObj, offset + 8, Load_int8(fromValue, 8)); |
| Store_int8(typedObj, offset + 9, Load_int8(fromValue, 9)); |
| Store_int8(typedObj, offset + 10, Load_int8(fromValue, 10)); |
| Store_int8(typedObj, offset + 11, Load_int8(fromValue, 11)); |
| Store_int8(typedObj, offset + 12, Load_int8(fromValue, 12)); |
| Store_int8(typedObj, offset + 13, Load_int8(fromValue, 13)); |
| Store_int8(typedObj, offset + 14, Load_int8(fromValue, 14)); |
| Store_int8(typedObj, offset + 15, Load_int8(fromValue, 15)); |
| break; |
| case JS_SIMDTYPEREPR_INT16X8: |
| Store_int16(typedObj, offset + 0, Load_int16(fromValue, 0)); |
| Store_int16(typedObj, offset + 2, Load_int16(fromValue, 2)); |
| Store_int16(typedObj, offset + 4, Load_int16(fromValue, 4)); |
| Store_int16(typedObj, offset + 6, Load_int16(fromValue, 6)); |
| Store_int16(typedObj, offset + 8, Load_int16(fromValue, 8)); |
| Store_int16(typedObj, offset + 10, Load_int16(fromValue, 10)); |
| Store_int16(typedObj, offset + 12, Load_int16(fromValue, 12)); |
| Store_int16(typedObj, offset + 14, Load_int16(fromValue, 14)); |
| break; |
| case JS_SIMDTYPEREPR_INT32X4: |
| Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0)); |
| Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4)); |
| Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8)); |
| Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12)); |
| break; |
| default: |
| assert(false, "Unhandled Simd type: " + type); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // C++ Wrappers |
| // |
| // These helpers are invoked by C++ code or used as method bodies. |
| |
| // Wrapper for use from C++ code. |
| function ConvertAndCopyTo(destDescr, |
| destTypedObj, |
| destOffset, |
| fieldName, |
| fromValue) |
| { |
| assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr), |
| "ConvertAndCopyTo: not type obj"); |
| assert(IsObject(destTypedObj) && ObjectIsTypedObject(destTypedObj), |
| "ConvertAndCopyTo: not type typedObj"); |
| |
| if (!TypedObjectIsAttached(destTypedObj)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); |
| |
| TypedObjectSet(destDescr, destTypedObj, destOffset, fieldName, fromValue); |
| } |
| |
| // Wrapper for use from C++ code. |
| function Reify(sourceDescr, |
| sourceTypedObj, |
| sourceOffset) { |
| assert(IsObject(sourceDescr) && ObjectIsTypeDescr(sourceDescr), |
| "Reify: not type obj"); |
| assert(IsObject(sourceTypedObj) && ObjectIsTypedObject(sourceTypedObj), |
| "Reify: not type typedObj"); |
| |
| if (!TypedObjectIsAttached(sourceTypedObj)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); |
| |
| return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset); |
| } |
| |
| // Warning: user exposed! |
| function TypeDescrEquivalent(otherDescr) { |
| if (!IsObject(this) || !ObjectIsTypeDescr(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| return DescrsEquiv(this, otherDescr); |
| } |
| |
| // TypedObjectArray.redimension(newArrayType) |
| // |
| // Method that "repackages" the data from this array into a new typed |
| // object whose type is `newArrayType`. Once you strip away all the |
| // outer array dimensions, the type of `this` array and `newArrayType` |
| // must share the same innermost element type. Moreover, those |
| // stripped away dimensions must amount to the same total number of |
| // elements. |
| // |
| // For example, given two equivalent types `T` and `U`, it is legal to |
| // interconvert between arrays types like: |
| // T[32] |
| // U[2][16] |
| // U[2][2][8] |
| // Because they all share the same total number (32) of equivalent elements. |
| // But it would be illegal to convert `T[32]` to `U[31]` or `U[2][17]`, since |
| // the number of elements differs. And it's just plain incompatible to convert |
| // if the base element types are not equivalent. |
| // |
| // Warning: user exposed! |
| function TypedObjectArrayRedimension(newArrayType) { |
| if (!IsObject(this) || !ObjectIsTypedObject(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| // Peel away the outermost array layers from the type of `this` to find |
| // the core element type. In the process, count the number of elements. |
| var oldArrayType = TypedObjectTypeDescr(this); |
| var oldElementType = oldArrayType; |
| var oldElementCount = 1; |
| |
| if (DESCR_KIND(oldArrayType) != JS_TYPEREPR_ARRAY_KIND) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| while (DESCR_KIND(oldElementType) === JS_TYPEREPR_ARRAY_KIND) { |
| oldElementCount *= oldElementType.length; |
| oldElementType = oldElementType.elementType; |
| } |
| |
| // Peel away the outermost array layers from `newArrayType`. In the |
| // process, count the number of elements. |
| var newElementType = newArrayType; |
| var newElementCount = 1; |
| while (DESCR_KIND(newElementType) == JS_TYPEREPR_ARRAY_KIND) { |
| newElementCount *= newElementType.length; |
| newElementType = newElementType.elementType; |
| } |
| |
| // Check that the total number of elements does not change. |
| if (oldElementCount !== newElementCount) { |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| |
| // Check that the element types are equivalent. |
| if (!DescrsEquiv(oldElementType, newElementType)) { |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| |
| // Together, this should imply that the sizes are unchanged. |
| assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType), |
| "Byte sizes should be equal"); |
| |
| // Rewrap the data from `this` in a new type. |
| return NewDerivedTypedObject(newArrayType, this, 0); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // SIMD |
| |
| function SimdProtoString(type) { |
| switch (type) { |
| case JS_SIMDTYPEREPR_INT8X16: |
| return "Int8x16"; |
| case JS_SIMDTYPEREPR_INT16X8: |
| return "Int16x8"; |
| case JS_SIMDTYPEREPR_INT32X4: |
| return "Int32x4"; |
| case JS_SIMDTYPEREPR_FLOAT32X4: |
| return "Float32x4"; |
| case JS_SIMDTYPEREPR_FLOAT64X2: |
| return "Float64x2"; |
| } |
| |
| assert(false, "Unhandled type constant"); |
| return undefined; |
| } |
| |
| function SimdTypeToLength(type) { |
| switch (type) { |
| case JS_SIMDTYPEREPR_INT8X16: |
| return 16; |
| case JS_SIMDTYPEREPR_INT16X8: |
| return 8; |
| case JS_SIMDTYPEREPR_INT32X4: |
| case JS_SIMDTYPEREPR_FLOAT32X4: |
| return 4; |
| case JS_SIMDTYPEREPR_FLOAT64X2: |
| return 2; |
| } |
| |
| assert(false, "Unhandled type constant"); |
| return undefined; |
| } |
| |
| function SimdToSource() { |
| if (!IsObject(this) || !ObjectIsTypedObject(this)) |
| ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); |
| |
| var descr = TypedObjectTypeDescr(this); |
| |
| if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND) |
| ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); |
| var typerepr = DESCR_TYPE(descr); |
| var protoString = SimdProtoString(typerepr); |
| switch (typerepr) { |
| case JS_SIMDTYPEREPR_INT8X16: { |
| var s1 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 0); |
| var s2 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 1); |
| var s3 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 2); |
| var s4 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 3); |
| var s5 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 4); |
| var s6 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 5); |
| var s7 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 6); |
| var s8 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 7); |
| var s9 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 0); |
| var s10 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 1); |
| var s11 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 2); |
| var s12 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 3); |
| var s13 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 4); |
| var s14 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 5); |
| var s15 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 6); |
| var s16 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 7); |
| return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`; |
| } |
| case JS_SIMDTYPEREPR_INT16X8: { |
| var s1 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 0); |
| var s2 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 1); |
| var s3 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 2); |
| var s4 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 3); |
| var s5 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 4); |
| var s6 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 5); |
| var s7 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 6); |
| var s8 = callFunction(std_SIMD_Int16x8_extractLane, null, this, 7); |
| return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8})`; |
| } |
| case JS_SIMDTYPEREPR_INT32X4: { |
| var x = callFunction(std_SIMD_Int32x4_extractLane, null, this, 0); |
| var y = callFunction(std_SIMD_Int32x4_extractLane, null, this, 1); |
| var z = callFunction(std_SIMD_Int32x4_extractLane, null, this, 2); |
| var w = callFunction(std_SIMD_Int32x4_extractLane, null, this, 3); |
| return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`; |
| } |
| case JS_SIMDTYPEREPR_FLOAT32X4: { |
| var x = callFunction(std_SIMD_Float32x4_extractLane, null, this, 0); |
| var y = callFunction(std_SIMD_Float32x4_extractLane, null, this, 1); |
| var z = callFunction(std_SIMD_Float32x4_extractLane, null, this, 2); |
| var w = callFunction(std_SIMD_Float32x4_extractLane, null, this, 3); |
| return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`; |
| } |
| case JS_SIMDTYPEREPR_FLOAT64X2: { |
| var x = callFunction(std_SIMD_Float64x2_extractLane, null, this, 0); |
| var y = callFunction(std_SIMD_Float64x2_extractLane, null, this, 1); |
| return `SIMD.${protoString}(${x}, ${y})`; |
| } |
| } |
| assert(false, "unexpected SIMD kind"); |
| return '?'; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Miscellaneous |
| |
| function DescrsEquiv(descr1, descr2) { |
| assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr"); |
| assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr"); |
| |
| // Potential optimization: these two strings are guaranteed to be |
| // atoms, and hence this string comparison can just be a pointer |
| // comparison. However, I don't think ion knows that. If this ever |
| // becomes a bottleneck, we can add a intrinsic at some point that |
| // is treated specially by Ion. (Bug 976688) |
| |
| return DESCR_STRING_REPR(descr1) === DESCR_STRING_REPR(descr2); |
| } |
| |
| // toSource() for type descriptors. |
| // |
| // Warning: user exposed! |
| function DescrToSource() { |
| if (!IsObject(this) || !ObjectIsTypeDescr(this)) |
| ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value"); |
| |
| return DESCR_STRING_REPR(this); |
| } |
| |
| // Warning: user exposed! |
| function ArrayShorthand(...dims) { |
| if (!IsObject(this) || !ObjectIsTypeDescr(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| var T = GetTypedObjectModule(); |
| |
| if (dims.length == 0) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| var accum = this; |
| for (var i = dims.length - 1; i >= 0; i--) |
| accum = new T.ArrayType(accum, dims[i]); |
| return accum; |
| } |
| |
| // This is the `storage()` function defined in the spec. When |
| // provided with a *transparent* typed object, it returns an object |
| // containing buffer, byteOffset, byteLength. When given an opaque |
| // typed object, it returns null. Otherwise it throws. |
| // |
| // Warning: user exposed! |
| function StorageOfTypedObject(obj) { |
| if (IsObject(obj)) { |
| if (ObjectIsOpaqueTypedObject(obj)) |
| return null; |
| |
| if (ObjectIsTransparentTypedObject(obj)) { |
| if (!TypedObjectIsAttached(obj)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); |
| |
| var descr = TypedObjectTypeDescr(obj); |
| var byteLength = DESCR_SIZE(descr); |
| |
| return { buffer: TypedObjectBuffer(obj), |
| byteLength: byteLength, |
| byteOffset: TypedObjectByteOffset(obj) }; |
| } |
| } |
| |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| |
| // This is the `objectType()` function defined in the spec. |
| // It returns the type of its argument. |
| // |
| // Warning: user exposed! |
| function TypeOfTypedObject(obj) { |
| if (IsObject(obj) && ObjectIsTypedObject(obj)) |
| return TypedObjectTypeDescr(obj); |
| |
| // Note: Do not create bindings for `Any`, `String`, etc in |
| // Utilities.js, but rather access them through |
| // `GetTypedObjectModule()`. The reason is that bindings |
| // you create in Utilities.js are part of the self-hosted global, |
| // vs the user-accessible global, and hence should not escape to |
| // user script. |
| var T = GetTypedObjectModule(); |
| switch (typeof obj) { |
| case "object": return T.Object; |
| case "function": return T.Object; |
| case "string": return T.String; |
| case "number": return T.float64; |
| case "undefined": return T.Any; |
| default: return T.Any; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // TypedObject surface API methods (sequential implementations). |
| |
| // Warning: user exposed! |
| function TypedObjectArrayTypeBuild(a,b,c) { |
| // Arguments : [depth], func |
| |
| if (!IsObject(this) || !ObjectIsTypeDescr(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| var kind = DESCR_KIND(this); |
| switch (kind) { |
| case JS_TYPEREPR_ARRAY_KIND: |
| if (typeof a === "function") // XXX here and elsewhere: these type dispatches are fragile at best. |
| return BuildTypedSeqImpl(this, this.length, 1, a); |
| else if (typeof a === "number" && typeof b === "function") |
| return BuildTypedSeqImpl(this, this.length, a, b); |
| else if (typeof a === "number") |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| else |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| default: |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| } |
| |
| // Warning: user exposed! |
| function TypedObjectArrayTypeFrom(a, b, c) { |
| // Arguments: arrayLike, [depth], func |
| |
| if (!IsObject(this) || !ObjectIsTypeDescr(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a); |
| |
| // for untyped input array, the expectation (in terms of error |
| // reporting for invalid parameters) is no-depth, despite |
| // supporting an explicit depth of 1; while for typed input array, |
| // the expectation is explicit depth. |
| |
| if (untypedInput) { |
| var explicitDepth = (b === 1); |
| if (explicitDepth && IsCallable(c)) |
| return MapUntypedSeqImpl(a, this, c); |
| else if (IsCallable(b)) |
| return MapUntypedSeqImpl(a, this, b); |
| else |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } else { |
| var explicitDepth = (typeof b === "number"); |
| if (explicitDepth && IsCallable(c)) |
| return MapTypedSeqImpl(a, b, this, c); |
| else if (IsCallable(b)) |
| return MapTypedSeqImpl(a, 1, this, b); |
| else if (explicitDepth) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| else |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| } |
| |
| // Warning: user exposed! |
| function TypedObjectArrayMap(a, b) { |
| if (!IsObject(this) || !ObjectIsTypedObject(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| var thisType = TypedObjectTypeDescr(this); |
| if (!TypeDescrIsArrayType(thisType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| // Arguments: [depth], func |
| if (typeof a === "number" && typeof b === "function") |
| return MapTypedSeqImpl(this, a, thisType, b); |
| else if (typeof a === "function") |
| return MapTypedSeqImpl(this, 1, thisType, a); |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| |
| // Warning: user exposed! |
| function TypedObjectArrayReduce(a, b) { |
| // Arguments: func, [initial] |
| if (!IsObject(this) || !ObjectIsTypedObject(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| var thisType = TypedObjectTypeDescr(this); |
| if (!TypeDescrIsArrayType(thisType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| if (a !== undefined && typeof a !== "function") |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| var outputType = thisType.elementType; |
| return ReduceTypedSeqImpl(this, outputType, a, b); |
| } |
| |
| // Warning: user exposed! |
| function TypedObjectArrayFilter(func) { |
| // Arguments: predicate |
| if (!IsObject(this) || !ObjectIsTypedObject(this)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| var thisType = TypedObjectTypeDescr(this); |
| if (!TypeDescrIsArrayType(thisType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| if (typeof func !== "function") |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| return FilterTypedSeqImpl(this, func); |
| } |
| |
| // should eventually become macros |
| function NUM_BYTES(bits) { |
| return (bits + 7) >> 3; |
| } |
| function SET_BIT(data, index) { |
| var word = index >> 3; |
| var mask = 1 << (index & 0x7); |
| data[word] |= mask; |
| } |
| function GET_BIT(data, index) { |
| var word = index >> 3; |
| var mask = 1 << (index & 0x7); |
| return (data[word] & mask) != 0; |
| } |
| |
| // Bug 956914: make performance-tuned variants tailored to 1, 2, and 3 dimensions. |
| function BuildTypedSeqImpl(arrayType, len, depth, func) { |
| assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object"); |
| |
| if (depth <= 0 || TO_INT32(depth) !== depth) { |
| // RangeError("bad depth") |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| |
| // For example, if we have as input |
| // ArrayType(ArrayType(T, 4), 5) |
| // and a depth of 2, we get |
| // grainType = T |
| // iterationSpace = [5, 4] |
| var {iterationSpace, grainType, totalLength} = |
| ComputeIterationSpace(arrayType, depth, len); |
| |
| // Create a zeroed instance with no data |
| var result = new arrayType(); |
| |
| var indices = new List(); |
| indices.length = depth; |
| for (var i = 0; i < depth; i++) { |
| indices[i] = 0; |
| } |
| |
| var grainTypeIsComplex = !TypeDescrIsSimpleType(grainType); |
| var size = DESCR_SIZE(grainType); |
| var outOffset = 0; |
| for (i = 0; i < totalLength; i++) { |
| // Position out-pointer to point at &result[...indices], if appropriate. |
| var userOutPointer = TypedObjectGetOpaqueIf(grainType, result, outOffset, |
| grainTypeIsComplex); |
| |
| // Invoke func(...indices, userOutPointer) and store the result |
| callFunction(std_Array_push, indices, userOutPointer); |
| var r = callFunction(std_Function_apply, func, undefined, indices); |
| callFunction(std_Array_pop, indices); |
| if (r !== undefined) |
| TypedObjectSet(grainType, result, outOffset, null, r); // result[...indices] = r; |
| |
| // Increment indices. |
| IncrementIterationSpace(indices, iterationSpace); |
| outOffset += size; |
| } |
| |
| return result; |
| } |
| |
| function ComputeIterationSpace(arrayType, depth, len) { |
| assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "ComputeIterationSpace called on non-type-object"); |
| assert(TypeDescrIsArrayType(arrayType), "ComputeIterationSpace called on non-array-type"); |
| assert(depth > 0, "ComputeIterationSpace called on non-positive depth"); |
| var iterationSpace = new List(); |
| iterationSpace.length = depth; |
| iterationSpace[0] = len; |
| var totalLength = len; |
| var grainType = arrayType.elementType; |
| |
| for (var i = 1; i < depth; i++) { |
| if (TypeDescrIsArrayType(grainType)) { |
| var grainLen = grainType.length; |
| iterationSpace[i] = grainLen; |
| totalLength *= grainLen; |
| grainType = grainType.elementType; |
| } else { |
| // RangeError("Depth "+depth+" too high"); |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| } |
| } |
| return { iterationSpace: iterationSpace, |
| grainType: grainType, |
| totalLength: totalLength }; |
| } |
| |
| function IncrementIterationSpace(indices, iterationSpace) { |
| // Increment something like |
| // [5, 5, 7, 8] |
| // in an iteration space of |
| // [9, 9, 9, 9] |
| // to |
| // [5, 5, 8, 0] |
| |
| assert(indices.length === iterationSpace.length, |
| "indices dimension must equal iterationSpace dimension."); |
| var n = indices.length - 1; |
| while (true) { |
| indices[n] += 1; |
| if (indices[n] < iterationSpace[n]) |
| return; |
| |
| assert(indices[n] === iterationSpace[n], |
| "Components of indices must match those of iterationSpace."); |
| indices[n] = 0; |
| if (n == 0) |
| return; |
| |
| n -= 1; |
| } |
| } |
| |
| // Implements |from| method for untyped |inArray|. (Depth is implicitly 1 for untyped input.) |
| function MapUntypedSeqImpl(inArray, outputType, maybeFunc) { |
| assert(IsObject(outputType), "1. Map/From called on non-object outputType"); |
| assert(ObjectIsTypeDescr(outputType), "1. Map/From called on non-type-object outputType"); |
| inArray = ToObject(inArray); |
| assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); |
| |
| if (!IsCallable(maybeFunc)) |
| ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc)); |
| var func = maybeFunc; |
| |
| // Skip check for compatible iteration spaces; any normal JS array |
| // is trivially compatible with any iteration space of depth 1. |
| |
| var outLength = outputType.length; |
| var outGrainType = outputType.elementType; |
| |
| // Create a zeroed instance with no data |
| var result = new outputType(); |
| |
| var outUnitSize = DESCR_SIZE(outGrainType); |
| var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); |
| var outOffset = 0; |
| |
| // Core of map computation starts here (comparable to |
| // DoMapTypedSeqDepth1 and DoMapTypedSeqDepthN below). |
| |
| for (var i = 0; i < outLength; i++) { |
| // In this loop, since depth is 1, "indices" denotes singleton array [i]. |
| |
| if (i in inArray) { // Check for holes (only needed for untyped case). |
| // Extract element value. |
| var element = inArray[i]; |
| |
| // Create out pointer to point at &array[...indices] for result array. |
| var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, |
| outGrainTypeIsComplex); |
| |
| // Invoke: var r = func(element, ...indices, collection, out); |
| var r = func(element, i, inArray, out); |
| |
| if (r !== undefined) |
| TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r |
| } |
| |
| // Update offset and (implicitly) increment indices. |
| outOffset += outUnitSize; |
| } |
| |
| return result; |
| } |
| |
| // Implements |map| and |from| methods for typed |inArray|. |
| function MapTypedSeqImpl(inArray, depth, outputType, func) { |
| assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "2. Map/From called on non-type-object outputType"); |
| assert(IsObject(inArray) && ObjectIsTypedObject(inArray), "Map/From called on non-object or untyped input array."); |
| assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); |
| |
| if (depth <= 0 || TO_INT32(depth) !== depth) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| // Compute iteration space for input and output and check for compatibility. |
| var inputType = TypeOfTypedObject(inArray); |
| var {iterationSpace:inIterationSpace, grainType:inGrainType} = |
| ComputeIterationSpace(inputType, depth, inArray.length); |
| if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| var {iterationSpace, grainType:outGrainType, totalLength} = |
| ComputeIterationSpace(outputType, depth, outputType.length); |
| for (var i = 0; i < depth; i++) |
| if (inIterationSpace[i] !== iterationSpace[i]) |
| // TypeError("Incompatible iteration space in input and output type"); |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| // Create a zeroed instance with no data |
| var result = new outputType(); |
| |
| var inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType); |
| var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); |
| |
| var inOffset = 0; |
| var outOffset = 0; |
| |
| var isDepth1Simple = depth == 1 && !(inGrainTypeIsComplex || outGrainTypeIsComplex); |
| |
| var inUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(inGrainType); |
| var outUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(outGrainType); |
| |
| // Bug 956914: add additional variants for depth = 2, 3, etc. |
| |
| function DoMapTypedSeqDepth1() { |
| for (var i = 0; i < totalLength; i++) { |
| // In this loop, since depth is 1, "indices" denotes singleton array [i]. |
| |
| // Prepare input element/handle and out pointer |
| var element = TypedObjectGet(inGrainType, inArray, inOffset); |
| var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, |
| outGrainTypeIsComplex); |
| |
| // Invoke: var r = func(element, ...indices, collection, out); |
| var r = func(element, i, inArray, out); |
| if (r !== undefined) |
| TypedObjectSet(outGrainType, result, outOffset, null, r); // result[i] = r |
| |
| // Update offsets and (implicitly) increment indices. |
| inOffset += inUnitSize; |
| outOffset += outUnitSize; |
| } |
| |
| return result; |
| } |
| |
| function DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result) { |
| for (var i = 0; i < totalLength; i++) { |
| var r = func(inArray[i], i, inArray, undefined); |
| if (r !== undefined) |
| result[i] = r; |
| } |
| |
| return result; |
| } |
| |
| function DoMapTypedSeqDepthN() { |
| var indices = new Uint32Array(depth); |
| |
| for (var i = 0; i < totalLength; i++) { |
| // Prepare input element and out pointer |
| var element = TypedObjectGet(inGrainType, inArray, inOffset); |
| var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, |
| outGrainTypeIsComplex); |
| |
| // Invoke: var r = func(element, ...indices, collection, out); |
| var args = [element]; |
| callFunction(std_Function_apply, std_Array_push, args, indices); |
| callFunction(std_Array_push, args, inArray, out); |
| var r = callFunction(std_Function_apply, func, void 0, args); |
| if (r !== undefined) |
| TypedObjectSet(outGrainType, result, outOffset, null, r); // result[...indices] = r |
| |
| // Update offsets and explicitly increment indices. |
| inOffset += inUnitSize; |
| outOffset += outUnitSize; |
| IncrementIterationSpace(indices, iterationSpace); |
| } |
| |
| return result; |
| } |
| |
| if (isDepth1Simple) |
| return DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result); |
| |
| if (depth == 1) |
| return DoMapTypedSeqDepth1(); |
| |
| return DoMapTypedSeqDepthN(); |
| } |
| |
| function ReduceTypedSeqImpl(array, outputType, func, initial) { |
| assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array."); |
| assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType"); |
| |
| var start, value; |
| |
| if (initial === undefined && array.length < 1) |
| // RangeError("reduce requires array of length > 0") |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| // FIXME bug 950106 Should reduce method supply an outptr handle? |
| // For now, reduce never supplies an outptr, regardless of outputType. |
| |
| if (TypeDescrIsSimpleType(outputType)) { |
| if (initial === undefined) { |
| start = 1; |
| value = array[0]; |
| } else { |
| start = 0; |
| value = outputType(initial); |
| } |
| |
| for (var i = start; i < array.length; i++) |
| value = outputType(func(value, array[i])); |
| |
| } else { |
| if (initial === undefined) { |
| start = 1; |
| value = new outputType(array[0]); |
| } else { |
| start = 0; |
| value = initial; |
| } |
| |
| for (var i = start; i < array.length; i++) |
| value = func(value, array[i]); |
| } |
| |
| return value; |
| } |
| |
| function FilterTypedSeqImpl(array, func) { |
| assert(IsObject(array) && ObjectIsTypedObject(array), "Filter called on non-object or untyped input array."); |
| assert(typeof func === "function", "Filter called with non-function predicate"); |
| |
| var arrayType = TypeOfTypedObject(array); |
| if (!TypeDescrIsArrayType(arrayType)) |
| ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); |
| |
| var elementType = arrayType.elementType; |
| var flags = new Uint8Array(NUM_BYTES(array.length)); |
| var count = 0; |
| var size = DESCR_SIZE(elementType); |
| var inOffset = 0; |
| for (var i = 0; i < array.length; i++) { |
| var v = TypedObjectGet(elementType, array, inOffset); |
| if (func(v, i, array)) { |
| SET_BIT(flags, i); |
| count++; |
| } |
| inOffset += size; |
| } |
| |
| var T = GetTypedObjectModule(); |
| |
| var resultType = new T.ArrayType(elementType, count); |
| var result = new resultType(); |
| for (var i = 0, j = 0; i < array.length; i++) { |
| if (GET_BIT(flags, i)) |
| result[j++] = array[i]; |
| } |
| return result; |
| } |