1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/builtin/TypedObject.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1361 @@ 1.4 +#include "TypedObjectConstants.h" 1.5 + 1.6 +/////////////////////////////////////////////////////////////////////////// 1.7 +// Getters and setters for various slots. 1.8 + 1.9 +// Type object slots 1.10 + 1.11 +#define DESCR_KIND(obj) \ 1.12 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_KIND) 1.13 +#define DESCR_STRING_REPR(obj) \ 1.14 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRING_REPR) 1.15 +#define DESCR_ALIGNMENT(obj) \ 1.16 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ALIGNMENT) 1.17 +#define DESCR_SIZE(obj) \ 1.18 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZE) 1.19 +#define DESCR_OPAQUE(obj) \ 1.20 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_OPAQUE) 1.21 +#define DESCR_TYPE(obj) \ 1.22 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_TYPE) 1.23 +#define DESCR_ARRAY_ELEMENT_TYPE(obj) \ 1.24 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE) 1.25 +#define DESCR_SIZED_ARRAY_LENGTH(obj) \ 1.26 + TO_INT32(UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZED_ARRAY_LENGTH)) 1.27 +#define DESCR_STRUCT_FIELD_NAMES(obj) \ 1.28 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES) 1.29 +#define DESCR_STRUCT_FIELD_TYPES(obj) \ 1.30 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES) 1.31 +#define DESCR_STRUCT_FIELD_OFFSETS(obj) \ 1.32 + UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS) 1.33 + 1.34 +// Typed object slots 1.35 + 1.36 +#define TYPEDOBJ_BYTEOFFSET(obj) \ 1.37 + TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTEOFFSET)) 1.38 +#define TYPEDOBJ_BYTELENGTH(obj) \ 1.39 + TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTELENGTH)) 1.40 +#define TYPEDOBJ_TYPE_DESCR(obj) \ 1.41 + UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_DESCR) 1.42 +#define TYPEDOBJ_OWNER(obj) \ 1.43 + UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_OWNER) 1.44 +#define TYPEDOBJ_LENGTH(obj) \ 1.45 + TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_LENGTH)) 1.46 + 1.47 +#define HAS_PROPERTY(obj, prop) \ 1.48 + callFunction(std_Object_hasOwnProperty, obj, prop) 1.49 + 1.50 +/////////////////////////////////////////////////////////////////////////// 1.51 +// Getting values 1.52 +// 1.53 +// The methods in this section read from the memory pointed at 1.54 +// by `this` and produce JS values. This process is called *reification* 1.55 +// in the spec. 1.56 + 1.57 +// Reifies the value referenced by the pointer, meaning that it 1.58 +// returns a new object pointing at the value. If the value is 1.59 +// a scalar, it will return a JS number, but otherwise the reified 1.60 +// result will be a typedObj of the same class as the ptr's typedObj. 1.61 +function TypedObjectGet(descr, typedObj, offset) { 1.62 + assert(IsObject(descr) && ObjectIsTypeDescr(descr), 1.63 + "get() called with bad type descr"); 1.64 + assert(TypedObjectIsAttached(typedObj), 1.65 + "get() called with unattached typedObj"); 1.66 + 1.67 + switch (DESCR_KIND(descr)) { 1.68 + case JS_TYPEREPR_SCALAR_KIND: 1.69 + return TypedObjectGetScalar(descr, typedObj, offset); 1.70 + 1.71 + case JS_TYPEREPR_REFERENCE_KIND: 1.72 + return TypedObjectGetReference(descr, typedObj, offset); 1.73 + 1.74 + case JS_TYPEREPR_X4_KIND: 1.75 + return TypedObjectGetX4(descr, typedObj, offset); 1.76 + 1.77 + case JS_TYPEREPR_SIZED_ARRAY_KIND: 1.78 + case JS_TYPEREPR_STRUCT_KIND: 1.79 + return TypedObjectGetDerived(descr, typedObj, offset); 1.80 + 1.81 + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: 1.82 + assert(false, "Unhandled repr kind: " + DESCR_KIND(descr)); 1.83 + } 1.84 + 1.85 + assert(false, "Unhandled kind: " + DESCR_KIND(descr)); 1.86 + return undefined; 1.87 +} 1.88 + 1.89 +function TypedObjectGetDerived(descr, typedObj, offset) { 1.90 + assert(!TypeDescrIsSimpleType(descr), 1.91 + "getDerived() used with simple type"); 1.92 + return NewDerivedTypedObject(descr, typedObj, offset); 1.93 +} 1.94 + 1.95 +function TypedObjectGetDerivedIf(descr, typedObj, offset, cond) { 1.96 + return (cond ? TypedObjectGetDerived(descr, typedObj, offset) : undefined); 1.97 +} 1.98 + 1.99 +function TypedObjectGetOpaque(descr, typedObj, offset) { 1.100 + assert(!TypeDescrIsSimpleType(descr), 1.101 + "getDerived() used with simple type"); 1.102 + var opaqueTypedObj = NewOpaqueTypedObject(descr); 1.103 + AttachTypedObject(opaqueTypedObj, typedObj, offset); 1.104 + return opaqueTypedObj; 1.105 +} 1.106 + 1.107 +function TypedObjectGetOpaqueIf(descr, typedObj, offset, cond) { 1.108 + return (cond ? TypedObjectGetOpaque(descr, typedObj, offset) : undefined); 1.109 +} 1.110 + 1.111 +function TypedObjectGetScalar(descr, typedObj, offset) { 1.112 + var type = DESCR_TYPE(descr); 1.113 + switch (type) { 1.114 + case JS_SCALARTYPEREPR_INT8: 1.115 + return Load_int8(typedObj, offset); 1.116 + 1.117 + case JS_SCALARTYPEREPR_UINT8: 1.118 + case JS_SCALARTYPEREPR_UINT8_CLAMPED: 1.119 + return Load_uint8(typedObj, offset); 1.120 + 1.121 + case JS_SCALARTYPEREPR_INT16: 1.122 + return Load_int16(typedObj, offset); 1.123 + 1.124 + case JS_SCALARTYPEREPR_UINT16: 1.125 + return Load_uint16(typedObj, offset); 1.126 + 1.127 + case JS_SCALARTYPEREPR_INT32: 1.128 + return Load_int32(typedObj, offset); 1.129 + 1.130 + case JS_SCALARTYPEREPR_UINT32: 1.131 + return Load_uint32(typedObj, offset); 1.132 + 1.133 + case JS_SCALARTYPEREPR_FLOAT32: 1.134 + return Load_float32(typedObj, offset); 1.135 + 1.136 + case JS_SCALARTYPEREPR_FLOAT64: 1.137 + return Load_float64(typedObj, offset); 1.138 + } 1.139 + 1.140 + assert(false, "Unhandled scalar type: " + type); 1.141 + return undefined; 1.142 +} 1.143 + 1.144 +function TypedObjectGetReference(descr, typedObj, offset) { 1.145 + var type = DESCR_TYPE(descr); 1.146 + switch (type) { 1.147 + case JS_REFERENCETYPEREPR_ANY: 1.148 + return Load_Any(typedObj, offset); 1.149 + 1.150 + case JS_REFERENCETYPEREPR_OBJECT: 1.151 + return Load_Object(typedObj, offset); 1.152 + 1.153 + case JS_REFERENCETYPEREPR_STRING: 1.154 + return Load_string(typedObj, offset); 1.155 + } 1.156 + 1.157 + assert(false, "Unhandled scalar type: " + type); 1.158 + return undefined; 1.159 +} 1.160 + 1.161 +function TypedObjectGetX4(descr, typedObj, offset) { 1.162 + var type = DESCR_TYPE(descr); 1.163 + switch (type) { 1.164 + case JS_X4TYPEREPR_FLOAT32: 1.165 + var x = Load_float32(typedObj, offset + 0); 1.166 + var y = Load_float32(typedObj, offset + 4); 1.167 + var z = Load_float32(typedObj, offset + 8); 1.168 + var w = Load_float32(typedObj, offset + 12); 1.169 + return GetFloat32x4TypeDescr()(x, y, z, w); 1.170 + 1.171 + case JS_X4TYPEREPR_INT32: 1.172 + var x = Load_int32(typedObj, offset + 0); 1.173 + var y = Load_int32(typedObj, offset + 4); 1.174 + var z = Load_int32(typedObj, offset + 8); 1.175 + var w = Load_int32(typedObj, offset + 12); 1.176 + return GetInt32x4TypeDescr()(x, y, z, w); 1.177 + } 1.178 + 1.179 + assert(false, "Unhandled x4 type: " + type); 1.180 + return undefined; 1.181 +} 1.182 + 1.183 +/////////////////////////////////////////////////////////////////////////// 1.184 +// Setting values 1.185 +// 1.186 +// The methods in this section modify the data pointed at by `this`. 1.187 + 1.188 +// Writes `fromValue` into the `typedObj` at offset `offset`, adapting 1.189 +// it to `descr` as needed. This is the most general entry point 1.190 +// and works for any type. 1.191 +function TypedObjectSet(descr, typedObj, offset, fromValue) { 1.192 + assert(TypedObjectIsAttached(typedObj), "set() called with unattached typedObj"); 1.193 + 1.194 + // Fast path: `fromValue` is a typed object with same type 1.195 + // representation as the destination. In that case, we can just do a 1.196 + // memcpy. 1.197 + if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) { 1.198 + if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) { 1.199 + if (!TypedObjectIsAttached(fromValue)) 1.200 + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); 1.201 + 1.202 + var size = DESCR_SIZE(descr); 1.203 + Memcpy(typedObj, offset, fromValue, 0, size); 1.204 + return; 1.205 + } 1.206 + } 1.207 + 1.208 + switch (DESCR_KIND(descr)) { 1.209 + case JS_TYPEREPR_SCALAR_KIND: 1.210 + TypedObjectSetScalar(descr, typedObj, offset, fromValue); 1.211 + return; 1.212 + 1.213 + case JS_TYPEREPR_REFERENCE_KIND: 1.214 + TypedObjectSetReference(descr, typedObj, offset, fromValue); 1.215 + return; 1.216 + 1.217 + case JS_TYPEREPR_X4_KIND: 1.218 + TypedObjectSetX4(descr, typedObj, offset, fromValue); 1.219 + return; 1.220 + 1.221 + case JS_TYPEREPR_SIZED_ARRAY_KIND: 1.222 + var length = DESCR_SIZED_ARRAY_LENGTH(descr); 1.223 + if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue)) 1.224 + return; 1.225 + break; 1.226 + 1.227 + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: 1.228 + var length = typedObj.length; 1.229 + if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue)) 1.230 + return; 1.231 + break; 1.232 + 1.233 + case JS_TYPEREPR_STRUCT_KIND: 1.234 + if (!IsObject(fromValue)) 1.235 + break; 1.236 + 1.237 + // Adapt each field. 1.238 + var fieldNames = DESCR_STRUCT_FIELD_NAMES(descr); 1.239 + var fieldDescrs = DESCR_STRUCT_FIELD_TYPES(descr); 1.240 + var fieldOffsets = DESCR_STRUCT_FIELD_OFFSETS(descr); 1.241 + for (var i = 0; i < fieldNames.length; i++) { 1.242 + var fieldName = fieldNames[i]; 1.243 + var fieldDescr = fieldDescrs[i]; 1.244 + var fieldOffset = fieldOffsets[i]; 1.245 + var fieldValue = fromValue[fieldName]; 1.246 + TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldValue); 1.247 + } 1.248 + return; 1.249 + } 1.250 + 1.251 + ThrowError(JSMSG_CANT_CONVERT_TO, 1.252 + typeof(fromValue), 1.253 + DESCR_STRING_REPR(descr)); 1.254 +} 1.255 + 1.256 +function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) { 1.257 + if (!IsObject(fromValue)) 1.258 + return false; 1.259 + 1.260 + // Check that "array-like" fromValue has an appropriate length. 1.261 + if (fromValue.length !== length) 1.262 + return false; 1.263 + 1.264 + // Adapt each element. 1.265 + if (length > 0) { 1.266 + var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr); 1.267 + var elemSize = DESCR_SIZE(elemDescr); 1.268 + var elemOffset = offset; 1.269 + for (var i = 0; i < length; i++) { 1.270 + TypedObjectSet(elemDescr, typedObj, elemOffset, fromValue[i]); 1.271 + elemOffset += elemSize; 1.272 + } 1.273 + } 1.274 + return true; 1.275 +} 1.276 + 1.277 +// Sets `fromValue` to `this` assuming that `this` is a scalar type. 1.278 +function TypedObjectSetScalar(descr, typedObj, offset, fromValue) { 1.279 + assert(DESCR_KIND(descr) === JS_TYPEREPR_SCALAR_KIND, 1.280 + "Expected scalar type descriptor"); 1.281 + var type = DESCR_TYPE(descr); 1.282 + switch (type) { 1.283 + case JS_SCALARTYPEREPR_INT8: 1.284 + return Store_int8(typedObj, offset, 1.285 + TO_INT32(fromValue) & 0xFF); 1.286 + 1.287 + case JS_SCALARTYPEREPR_UINT8: 1.288 + return Store_uint8(typedObj, offset, 1.289 + TO_UINT32(fromValue) & 0xFF); 1.290 + 1.291 + case JS_SCALARTYPEREPR_UINT8_CLAMPED: 1.292 + var v = ClampToUint8(+fromValue); 1.293 + return Store_int8(typedObj, offset, v); 1.294 + 1.295 + case JS_SCALARTYPEREPR_INT16: 1.296 + return Store_int16(typedObj, offset, 1.297 + TO_INT32(fromValue) & 0xFFFF); 1.298 + 1.299 + case JS_SCALARTYPEREPR_UINT16: 1.300 + return Store_uint16(typedObj, offset, 1.301 + TO_UINT32(fromValue) & 0xFFFF); 1.302 + 1.303 + case JS_SCALARTYPEREPR_INT32: 1.304 + return Store_int32(typedObj, offset, 1.305 + TO_INT32(fromValue)); 1.306 + 1.307 + case JS_SCALARTYPEREPR_UINT32: 1.308 + return Store_uint32(typedObj, offset, 1.309 + TO_UINT32(fromValue)); 1.310 + 1.311 + case JS_SCALARTYPEREPR_FLOAT32: 1.312 + return Store_float32(typedObj, offset, +fromValue); 1.313 + 1.314 + case JS_SCALARTYPEREPR_FLOAT64: 1.315 + return Store_float64(typedObj, offset, +fromValue); 1.316 + } 1.317 + 1.318 + assert(false, "Unhandled scalar type: " + type); 1.319 + return undefined; 1.320 +} 1.321 + 1.322 +function TypedObjectSetReference(descr, typedObj, offset, fromValue) { 1.323 + var type = DESCR_TYPE(descr); 1.324 + switch (type) { 1.325 + case JS_REFERENCETYPEREPR_ANY: 1.326 + return Store_Any(typedObj, offset, fromValue); 1.327 + 1.328 + case JS_REFERENCETYPEREPR_OBJECT: 1.329 + var value = (fromValue === null ? fromValue : ToObject(fromValue)); 1.330 + return Store_Object(typedObj, offset, value); 1.331 + 1.332 + case JS_REFERENCETYPEREPR_STRING: 1.333 + return Store_string(typedObj, offset, ToString(fromValue)); 1.334 + } 1.335 + 1.336 + assert(false, "Unhandled scalar type: " + type); 1.337 + return undefined; 1.338 +} 1.339 + 1.340 +// Sets `fromValue` to `this` assuming that `this` is a scalar type. 1.341 +function TypedObjectSetX4(descr, typedObj, offset, fromValue) { 1.342 + // It is only permitted to set a float32x4/int32x4 value from another 1.343 + // float32x4/int32x4; in that case, the "fast path" that uses memcopy will 1.344 + // have already matched. So if we get to this point, we're supposed 1.345 + // to "adapt" fromValue, but there are no legal adaptions. 1.346 + ThrowError(JSMSG_CANT_CONVERT_TO, 1.347 + typeof(fromValue), 1.348 + DESCR_STRING_REPR(descr)); 1.349 +} 1.350 + 1.351 +/////////////////////////////////////////////////////////////////////////// 1.352 +// C++ Wrappers 1.353 +// 1.354 +// These helpers are invoked by C++ code or used as method bodies. 1.355 + 1.356 +// Wrapper for use from C++ code. 1.357 +function ConvertAndCopyTo(destDescr, 1.358 + destTypedObj, 1.359 + destOffset, 1.360 + fromValue) 1.361 +{ 1.362 + assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr), 1.363 + "ConvertAndCopyTo: not type obj"); 1.364 + assert(IsObject(destTypedObj) && ObjectIsTypedObject(destTypedObj), 1.365 + "ConvertAndCopyTo: not type typedObj"); 1.366 + 1.367 + if (!TypedObjectIsAttached(destTypedObj)) 1.368 + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); 1.369 + 1.370 + TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue); 1.371 +} 1.372 + 1.373 +// Wrapper for use from C++ code. 1.374 +function Reify(sourceDescr, 1.375 + sourceTypedObj, 1.376 + sourceOffset) { 1.377 + assert(IsObject(sourceDescr) && ObjectIsTypeDescr(sourceDescr), 1.378 + "Reify: not type obj"); 1.379 + assert(IsObject(sourceTypedObj) && ObjectIsTypedObject(sourceTypedObj), 1.380 + "Reify: not type typedObj"); 1.381 + 1.382 + if (!TypedObjectIsAttached(sourceTypedObj)) 1.383 + ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); 1.384 + 1.385 + return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset); 1.386 +} 1.387 + 1.388 +function FillTypedArrayWithValue(destArray, fromValue) { 1.389 + assert(IsObject(handle) && ObjectIsTypedObject(destArray), 1.390 + "FillTypedArrayWithValue: not typed handle"); 1.391 + 1.392 + var descr = TYPEDOBJ_TYPE_DESCR(destArray); 1.393 + var length = DESCR_SIZED_ARRAY_LENGTH(descr); 1.394 + if (length === 0) 1.395 + return; 1.396 + 1.397 + // Use convert and copy to to produce the first element: 1.398 + var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr); 1.399 + TypedObjectSet(elemDescr, destArray, 0, fromValue); 1.400 + 1.401 + // Stamp out the remaining copies: 1.402 + var elemSize = DESCR_SIZE(elemDescr); 1.403 + var totalSize = length * elemSize; 1.404 + for (var offset = elemSize; offset < totalSize; offset += elemSize) 1.405 + Memcpy(destArray, offset, destArray, 0, elemSize); 1.406 +} 1.407 + 1.408 +// Warning: user exposed! 1.409 +function TypeDescrEquivalent(otherDescr) { 1.410 + if (!IsObject(this) || !ObjectIsTypeDescr(this)) 1.411 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.412 + if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr)) 1.413 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.414 + return DescrsEquiv(this, otherDescr); 1.415 +} 1.416 + 1.417 +// TypedArray.redimension(newArrayType) 1.418 +// 1.419 +// Method that "repackages" the data from this array into a new typed 1.420 +// object whose type is `newArrayType`. Once you strip away all the 1.421 +// outer array dimensions, the type of `this` array and `newArrayType` 1.422 +// must share the same innermost element type. Moreover, those 1.423 +// stripped away dimensions must amount to the same total number of 1.424 +// elements. 1.425 +// 1.426 +// For example, given two equivalent types `T` and `U`, it is legal to 1.427 +// interconvert between arrays types like: 1.428 +// T[32] 1.429 +// U[2][16] 1.430 +// U[2][2][8] 1.431 +// Because they all share the same total number (32) of equivalent elements. 1.432 +// But it would be illegal to convert `T[32]` to `U[31]` or `U[2][17]`, since 1.433 +// the number of elements differs. And it's just plain incompatible to convert 1.434 +// if the base element types are not equivalent. 1.435 +// 1.436 +// Warning: user exposed! 1.437 +function TypedArrayRedimension(newArrayType) { 1.438 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.439 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.440 + 1.441 + if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType)) 1.442 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.443 + 1.444 + // Peel away the outermost array layers from the type of `this` to find 1.445 + // the core element type. In the process, count the number of elements. 1.446 + var oldArrayType = TYPEDOBJ_TYPE_DESCR(this); 1.447 + var oldArrayReprKind = DESCR_KIND(oldArrayType); 1.448 + var oldElementType = oldArrayType; 1.449 + var oldElementCount = 1; 1.450 + switch (oldArrayReprKind) { 1.451 + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: 1.452 + oldElementCount *= this.length; 1.453 + oldElementType = oldElementType.elementType; 1.454 + break; 1.455 + 1.456 + case JS_TYPEREPR_SIZED_ARRAY_KIND: 1.457 + break; 1.458 + 1.459 + default: 1.460 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.461 + } 1.462 + while (DESCR_KIND(oldElementType) === JS_TYPEREPR_SIZED_ARRAY_KIND) { 1.463 + oldElementCount *= oldElementType.length; 1.464 + oldElementType = oldElementType.elementType; 1.465 + } 1.466 + 1.467 + // Peel away the outermost array layers from `newArrayType`. In the 1.468 + // process, count the number of elements. 1.469 + var newElementType = newArrayType; 1.470 + var newElementCount = 1; 1.471 + while (DESCR_KIND(newElementType) == JS_TYPEREPR_SIZED_ARRAY_KIND) { 1.472 + newElementCount *= newElementType.length; 1.473 + newElementType = newElementType.elementType; 1.474 + } 1.475 + 1.476 + // Check that the total number of elements does not change. 1.477 + if (oldElementCount !== newElementCount) { 1.478 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.479 + } 1.480 + 1.481 + // Check that the element types are equivalent. 1.482 + if (!DescrsEquiv(oldElementType, newElementType)) { 1.483 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.484 + } 1.485 + 1.486 + // Together, this should imply that the sizes are unchanged. 1.487 + assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType), 1.488 + "Byte sizes should be equal"); 1.489 + 1.490 + // Rewrap the data from `this` in a new type. 1.491 + return NewDerivedTypedObject(newArrayType, this, 0); 1.492 +} 1.493 + 1.494 +/////////////////////////////////////////////////////////////////////////// 1.495 +// X4 1.496 + 1.497 +function X4ProtoString(type) { 1.498 + switch (type) { 1.499 + case JS_X4TYPEREPR_INT32: 1.500 + return "int32x4"; 1.501 + case JS_X4TYPEREPR_FLOAT32: 1.502 + return "float32x4"; 1.503 + } 1.504 + 1.505 + assert(false, "Unhandled type constant"); 1.506 + return undefined; 1.507 +} 1.508 + 1.509 +function X4ToSource() { 1.510 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.511 + ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this); 1.512 + 1.513 + var descr = TYPEDOBJ_TYPE_DESCR(this); 1.514 + 1.515 + if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND) 1.516 + ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this); 1.517 + 1.518 + var type = DESCR_TYPE(descr); 1.519 + return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")"; 1.520 +} 1.521 + 1.522 +/////////////////////////////////////////////////////////////////////////// 1.523 +// Miscellaneous 1.524 + 1.525 +function DescrsEquiv(descr1, descr2) { 1.526 + assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr"); 1.527 + assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr"); 1.528 + 1.529 + // Potential optimization: these two strings are guaranteed to be 1.530 + // atoms, and hence this string comparison can just be a pointer 1.531 + // comparison. However, I don't think ion knows that. If this ever 1.532 + // becomes a bottleneck, we can add a intrinsic at some point that 1.533 + // is treated specially by Ion. (Bug 976688) 1.534 + 1.535 + return DESCR_STRING_REPR(descr1) === DESCR_STRING_REPR(descr2); 1.536 +} 1.537 + 1.538 +// toSource() for type descriptors. 1.539 +// 1.540 +// Warning: user exposed! 1.541 +function DescrToSource() { 1.542 + if (!IsObject(this) || !ObjectIsTypeDescr(this)) 1.543 + ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value"); 1.544 + 1.545 + return DESCR_STRING_REPR(this); 1.546 +} 1.547 + 1.548 +// Warning: user exposed! 1.549 +function ArrayShorthand(...dims) { 1.550 + if (!IsObject(this) || !ObjectIsTypeDescr(this)) 1.551 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.552 + 1.553 + var T = GetTypedObjectModule(); 1.554 + 1.555 + if (dims.length == 0) 1.556 + return new T.ArrayType(this); 1.557 + 1.558 + var accum = this; 1.559 + for (var i = dims.length - 1; i >= 0; i--) 1.560 + accum = new T.ArrayType(accum).dimension(dims[i]); 1.561 + return accum; 1.562 +} 1.563 + 1.564 +// This is the `storage()` function defined in the spec. When 1.565 +// provided with a *transparent* typed object, it returns an object 1.566 +// containing buffer, byteOffset, byteLength. When given an opaque 1.567 +// typed object, it returns null. Otherwise it throws. 1.568 +// 1.569 +// Warning: user exposed! 1.570 +function StorageOfTypedObject(obj) { 1.571 + if (IsObject(obj)) { 1.572 + if (ObjectIsOpaqueTypedObject(obj)) 1.573 + return null; 1.574 + 1.575 + if (ObjectIsTransparentTypedObject(obj)) 1.576 + return { buffer: TYPEDOBJ_OWNER(obj), 1.577 + byteLength: TYPEDOBJ_BYTELENGTH(obj), 1.578 + byteOffset: TYPEDOBJ_BYTEOFFSET(obj) }; 1.579 + } 1.580 + 1.581 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.582 + return null; // pacify silly "always returns a value" lint 1.583 +} 1.584 + 1.585 +// This is the `objectType()` function defined in the spec. 1.586 +// It returns the type of its argument. 1.587 +// 1.588 +// Warning: user exposed! 1.589 +function TypeOfTypedObject(obj) { 1.590 + if (IsObject(obj) && ObjectIsTypedObject(obj)) 1.591 + return TYPEDOBJ_TYPE_DESCR(obj); 1.592 + 1.593 + // Note: Do not create bindings for `Any`, `String`, etc in 1.594 + // Utilities.js, but rather access them through 1.595 + // `GetTypedObjectModule()`. The reason is that bindings 1.596 + // you create in Utilities.js are part of the self-hosted global, 1.597 + // vs the user-accessible global, and hence should not escape to 1.598 + // user script. 1.599 + var T = GetTypedObjectModule(); 1.600 + switch (typeof obj) { 1.601 + case "object": return T.Object; 1.602 + case "function": return T.Object; 1.603 + case "string": return T.String; 1.604 + case "number": return T.float64; 1.605 + case "undefined": return T.Any; 1.606 + default: return T.Any; 1.607 + } 1.608 +} 1.609 + 1.610 +/////////////////////////////////////////////////////////////////////////// 1.611 +// TypedObject surface API methods (sequential implementations). 1.612 + 1.613 +// Warning: user exposed! 1.614 +function TypedObjectArrayTypeBuild(a,b,c) { 1.615 + // Arguments (this sized) : [depth], func 1.616 + // Arguments (this unsized) : length, [depth], func 1.617 + 1.618 + if (!IsObject(this) || !ObjectIsTypeDescr(this)) 1.619 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.620 + var kind = DESCR_KIND(this); 1.621 + switch (kind) { 1.622 + case JS_TYPEREPR_SIZED_ARRAY_KIND: 1.623 + if (typeof a === "function") // XXX here and elsewhere: these type dispatches are fragile at best. 1.624 + return BuildTypedSeqImpl(this, this.length, 1, a); 1.625 + else if (typeof a === "number" && typeof b === "function") 1.626 + return BuildTypedSeqImpl(this, this.length, a, b); 1.627 + else if (typeof a === "number") 1.628 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.629 + else 1.630 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.631 + case JS_TYPEREPR_UNSIZED_ARRAY_KIND: 1.632 + var len = a; 1.633 + if (typeof b === "function") 1.634 + return BuildTypedSeqImpl(this, len, 1, b); 1.635 + else if (typeof b === "number" && typeof c === "function") 1.636 + return BuildTypedSeqImpl(this, len, b, c); 1.637 + else if (typeof b === "number") 1.638 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.639 + else 1.640 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.641 + default: 1.642 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.643 + } 1.644 +} 1.645 + 1.646 +// Warning: user exposed! 1.647 +function TypedObjectArrayTypeFrom(a, b, c) { 1.648 + // Arguments: arrayLike, [depth], func 1.649 + 1.650 + if (!IsObject(this) || !ObjectIsTypeDescr(this)) 1.651 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.652 + 1.653 + var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a); 1.654 + 1.655 + // for untyped input array, the expectation (in terms of error 1.656 + // reporting for invalid parameters) is no-depth, despite 1.657 + // supporting an explicit depth of 1; while for typed input array, 1.658 + // the expectation is explicit depth. 1.659 + 1.660 + if (untypedInput) { 1.661 + var explicitDepth = (b === 1); 1.662 + if (explicitDepth && IsCallable(c)) 1.663 + return MapUntypedSeqImpl(a, this, c); 1.664 + else if (IsCallable(b)) 1.665 + return MapUntypedSeqImpl(a, this, b); 1.666 + else 1.667 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.668 + } else { 1.669 + var explicitDepth = (typeof b === "number"); 1.670 + if (explicitDepth && IsCallable(c)) 1.671 + return MapTypedSeqImpl(a, b, this, c); 1.672 + else if (IsCallable(b)) 1.673 + return MapTypedSeqImpl(a, 1, this, b); 1.674 + else if (explicitDepth) 1.675 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.676 + else 1.677 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.678 + } 1.679 +} 1.680 + 1.681 +// Warning: user exposed! 1.682 +function TypedArrayMap(a, b) { 1.683 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.684 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.685 + var thisType = TYPEDOBJ_TYPE_DESCR(this); 1.686 + if (!TypeDescrIsArrayType(thisType)) 1.687 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.688 + 1.689 + // Arguments: [depth], func 1.690 + if (typeof a === "number" && typeof b === "function") 1.691 + return MapTypedSeqImpl(this, a, thisType, b); 1.692 + else if (typeof a === "function") 1.693 + return MapTypedSeqImpl(this, 1, thisType, a); 1.694 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.695 +} 1.696 + 1.697 +// Warning: user exposed! 1.698 +function TypedArrayMapPar(a, b) { 1.699 + // Arguments: [depth], func 1.700 + 1.701 + // Defer to the sequential variant for error cases or 1.702 + // when not working with typed objects. 1.703 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.704 + return callFunction(TypedArrayMap, this, a, b); 1.705 + var thisType = TYPEDOBJ_TYPE_DESCR(this); 1.706 + if (!TypeDescrIsArrayType(thisType)) 1.707 + return callFunction(TypedArrayMap, this, a, b); 1.708 + 1.709 + if (typeof a === "number" && IsCallable(b)) 1.710 + return MapTypedParImpl(this, a, thisType, b); 1.711 + else if (IsCallable(a)) 1.712 + return MapTypedParImpl(this, 1, thisType, a); 1.713 + return callFunction(TypedArrayMap, this, a, b); 1.714 +} 1.715 + 1.716 +// Warning: user exposed! 1.717 +function TypedArrayReduce(a, b) { 1.718 + // Arguments: func, [initial] 1.719 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.720 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.721 + var thisType = TYPEDOBJ_TYPE_DESCR(this); 1.722 + if (!TypeDescrIsArrayType(thisType)) 1.723 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.724 + 1.725 + if (a !== undefined && typeof a !== "function") 1.726 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.727 + 1.728 + var outputType = thisType.elementType; 1.729 + return ReduceTypedSeqImpl(this, outputType, a, b); 1.730 +} 1.731 + 1.732 +// Warning: user exposed! 1.733 +function TypedArrayScatter(a, b, c, d) { 1.734 + // Arguments: outputArrayType, indices, defaultValue, conflictFunction 1.735 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.736 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.737 + var thisType = TYPEDOBJ_TYPE_DESCR(this); 1.738 + if (!TypeDescrIsArrayType(thisType)) 1.739 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.740 + 1.741 + if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a)) 1.742 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.743 + 1.744 + if (d !== undefined && typeof d !== "function") 1.745 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.746 + 1.747 + return ScatterTypedSeqImpl(this, a, b, c, d); 1.748 +} 1.749 + 1.750 +// Warning: user exposed! 1.751 +function TypedArrayFilter(func) { 1.752 + // Arguments: predicate 1.753 + if (!IsObject(this) || !ObjectIsTypedObject(this)) 1.754 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.755 + var thisType = TYPEDOBJ_TYPE_DESCR(this); 1.756 + if (!TypeDescrIsArrayType(thisType)) 1.757 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.758 + 1.759 + if (typeof func !== "function") 1.760 + return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.761 + 1.762 + return FilterTypedSeqImpl(this, func); 1.763 +} 1.764 + 1.765 +// placeholders 1.766 + 1.767 +// Warning: user exposed! 1.768 +function TypedObjectArrayTypeBuildPar(a,b,c) { 1.769 + return callFunction(TypedObjectArrayTypeBuild, this, a, b, c); 1.770 +} 1.771 + 1.772 +// Warning: user exposed! 1.773 +function TypedObjectArrayTypeFromPar(a,b,c) { 1.774 + // Arguments: arrayLike, [depth], func 1.775 + 1.776 + // Use the sequential version for error cases or when arrayLike is 1.777 + // not a typed object. 1.778 + if (!IsObject(this) || !ObjectIsTypeDescr(this) || !TypeDescrIsArrayType(this)) 1.779 + return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); 1.780 + if (!IsObject(a) || !ObjectIsTypedObject(a)) 1.781 + return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); 1.782 + 1.783 + // Detect whether an explicit depth is supplied. 1.784 + if (typeof b === "number" && IsCallable(c)) 1.785 + return MapTypedParImpl(a, b, this, c); 1.786 + if (IsCallable(b)) 1.787 + return MapTypedParImpl(a, 1, this, b); 1.788 + return callFunction(TypedObjectArrayTypeFrom, this, a, b, c); 1.789 +} 1.790 + 1.791 +// Warning: user exposed! 1.792 +function TypedArrayReducePar(a, b) { 1.793 + return callFunction(TypedArrayReduce, this, a, b); 1.794 +} 1.795 + 1.796 +// Warning: user exposed! 1.797 +function TypedArrayScatterPar(a, b, c, d) { 1.798 + return callFunction(TypedArrayScatter, this, a, b, c, d); 1.799 +} 1.800 + 1.801 +// Warning: user exposed! 1.802 +function TypedArrayFilterPar(func) { 1.803 + return callFunction(TypedArrayFilter, this, func); 1.804 +} 1.805 + 1.806 +// should eventually become macros 1.807 +function NUM_BYTES(bits) { 1.808 + return (bits + 7) >> 3; 1.809 +} 1.810 +function SET_BIT(data, index) { 1.811 + var word = index >> 3; 1.812 + var mask = 1 << (index & 0x7); 1.813 + data[word] |= mask; 1.814 +} 1.815 +function GET_BIT(data, index) { 1.816 + var word = index >> 3; 1.817 + var mask = 1 << (index & 0x7); 1.818 + return (data[word] & mask) != 0; 1.819 +} 1.820 + 1.821 +// Bug 956914: make performance-tuned variants tailored to 1, 2, and 3 dimensions. 1.822 +function BuildTypedSeqImpl(arrayType, len, depth, func) { 1.823 + assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object"); 1.824 + 1.825 + if (depth <= 0 || TO_INT32(depth) !== depth) 1.826 + // RangeError("bad depth") 1.827 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.828 + 1.829 + // For example, if we have as input 1.830 + // ArrayType(ArrayType(T, 4), 5) 1.831 + // and a depth of 2, we get 1.832 + // grainType = T 1.833 + // iterationSpace = [5, 4] 1.834 + var [iterationSpace, grainType, totalLength] = 1.835 + ComputeIterationSpace(arrayType, depth, len); 1.836 + 1.837 + // Create a zeroed instance with no data 1.838 + var result = arrayType.variable ? new arrayType(len) : new arrayType(); 1.839 + 1.840 + var indices = NewDenseArray(depth); 1.841 + for (var i = 0; i < depth; i++) { 1.842 + indices[i] = 0; 1.843 + } 1.844 + 1.845 + var grainTypeIsComplex = !TypeDescrIsSimpleType(grainType); 1.846 + var size = DESCR_SIZE(grainType); 1.847 + var outOffset = 0; 1.848 + for (i = 0; i < totalLength; i++) { 1.849 + // Position out-pointer to point at &result[...indices], if appropriate. 1.850 + var userOutPointer = TypedObjectGetOpaqueIf(grainType, result, outOffset, 1.851 + grainTypeIsComplex); 1.852 + 1.853 + // Invoke func(...indices, userOutPointer) and store the result 1.854 + callFunction(std_Array_push, indices, userOutPointer); 1.855 + var r = callFunction(std_Function_apply, func, undefined, indices); 1.856 + callFunction(std_Array_pop, indices); 1.857 + if (r !== undefined) 1.858 + TypedObjectSet(grainType, result, outOffset, r); // result[...indices] = r; 1.859 + 1.860 + // Increment indices. 1.861 + IncrementIterationSpace(indices, iterationSpace); 1.862 + outOffset += size; 1.863 + } 1.864 + 1.865 + return result; 1.866 +} 1.867 + 1.868 +function ComputeIterationSpace(arrayType, depth, len) { 1.869 + assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "ComputeIterationSpace called on non-type-object"); 1.870 + assert(TypeDescrIsArrayType(arrayType), "ComputeIterationSpace called on non-array-type"); 1.871 + assert(depth > 0, "ComputeIterationSpace called on non-positive depth"); 1.872 + var iterationSpace = NewDenseArray(depth); 1.873 + iterationSpace[0] = len; 1.874 + var totalLength = len; 1.875 + var grainType = arrayType.elementType; 1.876 + 1.877 + for (var i = 1; i < depth; i++) { 1.878 + if (TypeDescrIsArrayType(grainType)) { 1.879 + var grainLen = grainType.length; 1.880 + iterationSpace[i] = grainLen; 1.881 + totalLength *= grainLen; 1.882 + grainType = grainType.elementType; 1.883 + } else { 1.884 + // RangeError("Depth "+depth+" too high"); 1.885 + ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); 1.886 + } 1.887 + } 1.888 + return [iterationSpace, grainType, totalLength]; 1.889 +} 1.890 + 1.891 +function IncrementIterationSpace(indices, iterationSpace) { 1.892 + // Increment something like 1.893 + // [5, 5, 7, 8] 1.894 + // in an iteration space of 1.895 + // [9, 9, 9, 9] 1.896 + // to 1.897 + // [5, 5, 8, 0] 1.898 + 1.899 + assert(indices.length === iterationSpace.length, 1.900 + "indices dimension must equal iterationSpace dimension."); 1.901 + var n = indices.length - 1; 1.902 + while (true) { 1.903 + indices[n] += 1; 1.904 + if (indices[n] < iterationSpace[n]) 1.905 + return; 1.906 + 1.907 + assert(indices[n] === iterationSpace[n], 1.908 + "Components of indices must match those of iterationSpace."); 1.909 + indices[n] = 0; 1.910 + if (n == 0) 1.911 + return; 1.912 + 1.913 + n -= 1; 1.914 + } 1.915 +} 1.916 + 1.917 +// Implements |from| method for untyped |inArray|. (Depth is implicitly 1 for untyped input.) 1.918 +function MapUntypedSeqImpl(inArray, outputType, maybeFunc) { 1.919 + assert(IsObject(outputType), "1. Map/From called on non-object outputType"); 1.920 + assert(ObjectIsTypeDescr(outputType), "1. Map/From called on non-type-object outputType"); 1.921 + inArray = ToObject(inArray); 1.922 + assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); 1.923 + 1.924 + if (!IsCallable(maybeFunc)) 1.925 + ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc)); 1.926 + var func = maybeFunc; 1.927 + 1.928 + // Skip check for compatible iteration spaces; any normal JS array 1.929 + // is trivially compatible with any iteration space of depth 1. 1.930 + 1.931 + var outLength = outputType.variable ? inArray.length : outputType.length; 1.932 + var outGrainType = outputType.elementType; 1.933 + 1.934 + // Create a zeroed instance with no data 1.935 + var result = outputType.variable ? new outputType(inArray.length) : new outputType(); 1.936 + 1.937 + var outUnitSize = DESCR_SIZE(outGrainType); 1.938 + var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); 1.939 + var outOffset = 0; 1.940 + 1.941 + // Core of map computation starts here (comparable to 1.942 + // DoMapTypedSeqDepth1 and DoMapTypedSeqDepthN below). 1.943 + 1.944 + for (var i = 0; i < outLength; i++) { 1.945 + // In this loop, since depth is 1, "indices" denotes singleton array [i]. 1.946 + 1.947 + if (i in inArray) { // Check for holes (only needed for untyped case). 1.948 + // Extract element value. 1.949 + var element = inArray[i]; 1.950 + 1.951 + // Create out pointer to point at &array[...indices] for result array. 1.952 + var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, 1.953 + outGrainTypeIsComplex); 1.954 + 1.955 + // Invoke: var r = func(element, ...indices, collection, out); 1.956 + var r = func(element, i, inArray, out); 1.957 + 1.958 + if (r !== undefined) 1.959 + TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r 1.960 + } 1.961 + 1.962 + // Update offset and (implicitly) increment indices. 1.963 + outOffset += outUnitSize; 1.964 + } 1.965 + 1.966 + return result; 1.967 +} 1.968 + 1.969 +// Implements |map| and |from| methods for typed |inArray|. 1.970 +function MapTypedSeqImpl(inArray, depth, outputType, func) { 1.971 + assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "2. Map/From called on non-type-object outputType"); 1.972 + assert(IsObject(inArray) && ObjectIsTypedObject(inArray), "Map/From called on non-object or untyped input array."); 1.973 + assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); 1.974 + 1.975 + if (depth <= 0 || TO_INT32(depth) !== depth) 1.976 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.977 + 1.978 + // Compute iteration space for input and output and check for compatibility. 1.979 + var inputType = TypeOfTypedObject(inArray); 1.980 + var [inIterationSpace, inGrainType, _] = 1.981 + ComputeIterationSpace(inputType, depth, inArray.length); 1.982 + if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType)) 1.983 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.984 + var [iterationSpace, outGrainType, totalLength] = 1.985 + ComputeIterationSpace(outputType, depth, outputType.variable ? inArray.length : outputType.length); 1.986 + for (var i = 0; i < depth; i++) 1.987 + if (inIterationSpace[i] !== iterationSpace[i]) 1.988 + // TypeError("Incompatible iteration space in input and output type"); 1.989 + ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); 1.990 + 1.991 + // Create a zeroed instance with no data 1.992 + var result = outputType.variable ? new outputType(inArray.length) : new outputType(); 1.993 + 1.994 + var inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType); 1.995 + var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); 1.996 + 1.997 + var inOffset = 0; 1.998 + var outOffset = 0; 1.999 + 1.1000 + var isDepth1Simple = depth == 1 && !(inGrainTypeIsComplex || outGrainTypeIsComplex); 1.1001 + 1.1002 + var inUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(inGrainType); 1.1003 + var outUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(outGrainType); 1.1004 + 1.1005 + // Bug 956914: add additional variants for depth = 2, 3, etc. 1.1006 + 1.1007 + function DoMapTypedSeqDepth1() { 1.1008 + for (var i = 0; i < totalLength; i++) { 1.1009 + // In this loop, since depth is 1, "indices" denotes singleton array [i]. 1.1010 + 1.1011 + // Prepare input element/handle and out pointer 1.1012 + var element = TypedObjectGet(inGrainType, inArray, inOffset); 1.1013 + var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, 1.1014 + outGrainTypeIsComplex); 1.1015 + 1.1016 + // Invoke: var r = func(element, ...indices, collection, out); 1.1017 + var r = func(element, i, inArray, out); 1.1018 + if (r !== undefined) 1.1019 + TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r 1.1020 + 1.1021 + // Update offsets and (implicitly) increment indices. 1.1022 + inOffset += inUnitSize; 1.1023 + outOffset += outUnitSize; 1.1024 + } 1.1025 + 1.1026 + return result; 1.1027 + } 1.1028 + 1.1029 + function DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result) { 1.1030 + for (var i = 0; i < totalLength; i++) { 1.1031 + var r = func(inArray[i], i, inArray, undefined); 1.1032 + if (r !== undefined) 1.1033 + result[i] = r; 1.1034 + } 1.1035 + 1.1036 + return result; 1.1037 + } 1.1038 + 1.1039 + function DoMapTypedSeqDepthN() { 1.1040 + var indices = new Uint32Array(depth); 1.1041 + 1.1042 + for (var i = 0; i < totalLength; i++) { 1.1043 + // Prepare input element and out pointer 1.1044 + var element = TypedObjectGet(inGrainType, inArray, inOffset); 1.1045 + var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset, 1.1046 + outGrainTypeIsComplex); 1.1047 + 1.1048 + // Invoke: var r = func(element, ...indices, collection, out); 1.1049 + var args = [element]; 1.1050 + callFunction(std_Function_apply, std_Array_push, args, indices); 1.1051 + callFunction(std_Array_push, args, inArray, out); 1.1052 + var r = callFunction(std_Function_apply, func, void 0, args); 1.1053 + if (r !== undefined) 1.1054 + TypedObjectSet(outGrainType, result, outOffset, r); // result[...indices] = r 1.1055 + 1.1056 + // Update offsets and explicitly increment indices. 1.1057 + inOffset += inUnitSize; 1.1058 + outOffset += outUnitSize; 1.1059 + IncrementIterationSpace(indices, iterationSpace); 1.1060 + } 1.1061 + 1.1062 + return result; 1.1063 + } 1.1064 + 1.1065 + if (isDepth1Simple) 1.1066 + return DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result); 1.1067 + 1.1068 + if (depth == 1) 1.1069 + return DoMapTypedSeqDepth1(); 1.1070 + 1.1071 + return DoMapTypedSeqDepthN(); 1.1072 +} 1.1073 + 1.1074 +// Implements |map| and |from| methods for typed |inArray|. 1.1075 +function MapTypedParImpl(inArray, depth, outputType, func) { 1.1076 + assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), 1.1077 + "Map/From called on non-type-object outputType"); 1.1078 + assert(IsObject(inArray) && ObjectIsTypedObject(inArray), 1.1079 + "Map/From called on non-object or untyped input array."); 1.1080 + assert(TypeDescrIsArrayType(outputType), 1.1081 + "Map/From called on non array-type outputType"); 1.1082 + assert(typeof depth === "number", 1.1083 + "Map/From called with non-numeric depth"); 1.1084 + assert(IsCallable(func), 1.1085 + "Map/From called on something not callable"); 1.1086 + 1.1087 + var inArrayType = TypeOfTypedObject(inArray); 1.1088 + 1.1089 + if (ShouldForceSequential() || 1.1090 + depth <= 0 || 1.1091 + TO_INT32(depth) !== depth || 1.1092 + !TypeDescrIsArrayType(inArrayType) || 1.1093 + !TypeDescrIsUnsizedArrayType(outputType)) 1.1094 + { 1.1095 + // defer error cases to seq implementation: 1.1096 + return MapTypedSeqImpl(inArray, depth, outputType, func); 1.1097 + } 1.1098 + 1.1099 + switch (depth) { 1.1100 + case 1: 1.1101 + return MapTypedParImplDepth1(inArray, inArrayType, outputType, func); 1.1102 + default: 1.1103 + return MapTypedSeqImpl(inArray, depth, outputType, func); 1.1104 + } 1.1105 +} 1.1106 + 1.1107 +function RedirectPointer(typedObj, offset, outputIsScalar) { 1.1108 + if (!outputIsScalar || !InParallelSection()) { 1.1109 + // ^ Subtle note: always check InParallelSection() last, because 1.1110 + // otherwise the other if conditions will not execute during 1.1111 + // sequential mode and we will not gather enough type 1.1112 + // information. 1.1113 + 1.1114 + // Here `typedObj` represents the input or output pointer we will 1.1115 + // pass to the user function. Ideally, we will just update the 1.1116 + // offset of `typedObj` in place so that it moves along the 1.1117 + // input/output buffer without incurring any allocation costs. But 1.1118 + // we can only do this if these changes are invisible to the user. 1.1119 + // 1.1120 + // Under normal uses, such changes *should* be invisible -- the 1.1121 + // in/out pointers are only intended to be used during the 1.1122 + // callback and then discarded, but of course in the general case 1.1123 + // nothing prevents them from escaping. 1.1124 + // 1.1125 + // However, if we are in parallel mode, we know that the pointers 1.1126 + // will not escape into global state. They could still escape by 1.1127 + // being returned into the resulting array, but even that avenue 1.1128 + // is impossible if the result array cannot contain objects. 1.1129 + // 1.1130 + // Therefore, we reuse a pointer if we are both in parallel mode 1.1131 + // and we have a transparent output type. It'd be nice to loosen 1.1132 + // this condition later by using fancy ion optimizations that 1.1133 + // assume the value won't escape and copy it if it does. But those 1.1134 + // don't exist yet. Moreover, checking if the type is transparent 1.1135 + // is an overapproximation: users can manually declare opaque 1.1136 + // types that nonetheless only contain scalar data. 1.1137 + 1.1138 + typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj), 1.1139 + typedObj, 0); 1.1140 + } 1.1141 + 1.1142 + SetTypedObjectOffset(typedObj, offset); 1.1143 + return typedObj; 1.1144 +} 1.1145 +SetScriptHints(RedirectPointer, { inline: true }); 1.1146 + 1.1147 +function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) { 1.1148 + assert(IsObject(inArrayType) && ObjectIsTypeDescr(inArrayType) && 1.1149 + TypeDescrIsArrayType(inArrayType), 1.1150 + "DoMapTypedParDepth1: invalid inArrayType"); 1.1151 + assert(IsObject(outArrayType) && ObjectIsTypeDescr(outArrayType) && 1.1152 + TypeDescrIsArrayType(outArrayType), 1.1153 + "DoMapTypedParDepth1: invalid outArrayType"); 1.1154 + assert(IsObject(inArray) && ObjectIsTypedObject(inArray), 1.1155 + "DoMapTypedParDepth1: invalid inArray"); 1.1156 + 1.1157 + // Determine the grain types of the input and output. 1.1158 + const inGrainType = inArrayType.elementType; 1.1159 + const outGrainType = outArrayType.elementType; 1.1160 + const inGrainTypeSize = DESCR_SIZE(inGrainType); 1.1161 + const outGrainTypeSize = DESCR_SIZE(outGrainType); 1.1162 + const inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType); 1.1163 + const outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType); 1.1164 + 1.1165 + const length = inArray.length; 1.1166 + const mode = undefined; 1.1167 + 1.1168 + const outArray = new outArrayType(length); 1.1169 + if (length === 0) 1.1170 + return outArray; 1.1171 + 1.1172 + const outGrainTypeIsTransparent = ObjectIsTransparentTypedObject(outArray); 1.1173 + 1.1174 + // Construct the slices and initial pointers for each worker: 1.1175 + const slicesInfo = ComputeSlicesInfo(length); 1.1176 + const numWorkers = ForkJoinNumWorkers(); 1.1177 + assert(numWorkers > 0, "Should have at least the main thread"); 1.1178 + const pointers = []; 1.1179 + for (var i = 0; i < numWorkers; i++) { 1.1180 + const inTypedObject = TypedObjectGetDerivedIf(inGrainType, inArray, 0, 1.1181 + inGrainTypeIsComplex); 1.1182 + const outTypedObject = TypedObjectGetOpaqueIf(outGrainType, outArray, 0, 1.1183 + outGrainTypeIsComplex); 1.1184 + ARRAY_PUSH(pointers, ({ inTypedObject: inTypedObject, 1.1185 + outTypedObject: outTypedObject })); 1.1186 + } 1.1187 + 1.1188 + // Below we will be adjusting offsets within the input to point at 1.1189 + // successive entries; we'll need to know the offset of inArray 1.1190 + // relative to its owner (which is often but not always 0). 1.1191 + const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray); 1.1192 + 1.1193 + ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode)); 1.1194 + return outArray; 1.1195 + 1.1196 + function mapThread(workerId, sliceStart, sliceEnd) { 1.1197 + assert(TO_INT32(workerId) === workerId, 1.1198 + "workerId not int: " + workerId); 1.1199 + assert(workerId < pointers.length, 1.1200 + "workerId too large: " + workerId + " >= " + pointers.length); 1.1201 + 1.1202 + var pointerIndex = InParallelSection() ? workerId : 0; 1.1203 + assert(!!pointers[pointerIndex], 1.1204 + "no pointer data for workerId: " + workerId); 1.1205 + 1.1206 + const { inTypedObject, outTypedObject } = pointers[pointerIndex]; 1.1207 + const sliceShift = slicesInfo.shift; 1.1208 + var sliceId; 1.1209 + 1.1210 + while (GET_SLICE(sliceStart, sliceEnd, sliceId)) { 1.1211 + const indexStart = SLICE_START_INDEX(sliceShift, sliceId); 1.1212 + const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length); 1.1213 + 1.1214 + var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart); 1.1215 + var outOffset = std_Math_imul(outGrainTypeSize, indexStart); 1.1216 + 1.1217 + // Set the target region so that user is only permitted to write 1.1218 + // within the range set aside for this slice. This prevents user 1.1219 + // from writing to typed objects that escaped from prior slices 1.1220 + // during sequential iteration. Note that, for any particular 1.1221 + // iteration of the loop below, it's only valid to write to the 1.1222 + // memory range corresponding to the index `i` -- however, since 1.1223 + // the different iterations cannot communicate typed object 1.1224 + // pointers to one another during parallel exec, we need only 1.1225 + // fear escaped typed objects from *other* slices, so we can 1.1226 + // just set the target region once. 1.1227 + const endOffset = std_Math_imul(outGrainTypeSize, indexEnd); 1.1228 + SetForkJoinTargetRegion(outArray, outOffset, endOffset); 1.1229 + 1.1230 + for (var i = indexStart; i < indexEnd; i++) { 1.1231 + var inVal = (inGrainTypeIsComplex 1.1232 + ? RedirectPointer(inTypedObject, inOffset, 1.1233 + outGrainTypeIsTransparent) 1.1234 + : inArray[i]); 1.1235 + var outVal = (outGrainTypeIsComplex 1.1236 + ? RedirectPointer(outTypedObject, outOffset, 1.1237 + outGrainTypeIsTransparent) 1.1238 + : undefined); 1.1239 + const r = func(inVal, i, inArray, outVal); 1.1240 + if (r !== undefined) { 1.1241 + if (outGrainTypeIsComplex) 1.1242 + SetTypedObjectValue(outGrainType, outArray, outOffset, r); 1.1243 + else 1.1244 + UnsafePutElements(outArray, i, r); 1.1245 + } 1.1246 + inOffset += inGrainTypeSize; 1.1247 + outOffset += outGrainTypeSize; 1.1248 + 1.1249 + // A transparent result type cannot contain references, and 1.1250 + // hence there is no way for a pointer to a thread-local object 1.1251 + // to escape. 1.1252 + if (outGrainTypeIsTransparent) 1.1253 + ClearThreadLocalArenas(); 1.1254 + } 1.1255 + } 1.1256 + 1.1257 + return sliceId; 1.1258 + } 1.1259 + 1.1260 + return undefined; 1.1261 +} 1.1262 +SetScriptHints(MapTypedParImplDepth1, { cloneAtCallsite: true }); 1.1263 + 1.1264 +function ReduceTypedSeqImpl(array, outputType, func, initial) { 1.1265 + assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array."); 1.1266 + assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType"); 1.1267 + 1.1268 + var start, value; 1.1269 + 1.1270 + if (initial === undefined && array.length < 1) 1.1271 + // RangeError("reduce requires array of length > 0") 1.1272 + ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); 1.1273 + 1.1274 + // FIXME bug 950106 Should reduce method supply an outptr handle? 1.1275 + // For now, reduce never supplies an outptr, regardless of outputType. 1.1276 + 1.1277 + if (TypeDescrIsSimpleType(outputType)) { 1.1278 + if (initial === undefined) { 1.1279 + start = 1; 1.1280 + value = array[0]; 1.1281 + } else { 1.1282 + start = 0; 1.1283 + value = outputType(initial); 1.1284 + } 1.1285 + 1.1286 + for (var i = start; i < array.length; i++) 1.1287 + value = outputType(func(value, array[i])); 1.1288 + 1.1289 + } else { 1.1290 + if (initial === undefined) { 1.1291 + start = 1; 1.1292 + value = new outputType(array[0]); 1.1293 + } else { 1.1294 + start = 0; 1.1295 + value = initial; 1.1296 + } 1.1297 + 1.1298 + for (var i = start; i < array.length; i++) 1.1299 + value = func(value, array[i]); 1.1300 + } 1.1301 + 1.1302 + return value; 1.1303 +} 1.1304 + 1.1305 +function ScatterTypedSeqImpl(array, outputType, indices, defaultValue, conflictFunc) { 1.1306 + assert(IsObject(array) && ObjectIsTypedObject(array), "Scatter called on non-object or untyped input array."); 1.1307 + assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Scatter called on non-type-object outputType"); 1.1308 + assert(TypeDescrIsSizedArrayType(outputType), "Scatter called on non-sized array type"); 1.1309 + assert(conflictFunc === undefined || typeof conflictFunc === "function", "Scatter called with invalid conflictFunc"); 1.1310 + 1.1311 + var result = new outputType(); 1.1312 + var bitvec = new Uint8Array(result.length); 1.1313 + var elemType = outputType.elementType; 1.1314 + var i, j; 1.1315 + if (defaultValue !== elemType(undefined)) { 1.1316 + for (i = 0; i < result.length; i++) { 1.1317 + result[i] = defaultValue; 1.1318 + } 1.1319 + } 1.1320 + 1.1321 + for (i = 0; i < indices.length; i++) { 1.1322 + j = indices[i]; 1.1323 + if (!GET_BIT(bitvec, j)) { 1.1324 + result[j] = array[i]; 1.1325 + SET_BIT(bitvec, j); 1.1326 + } else if (conflictFunc === undefined) { 1.1327 + ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); 1.1328 + } else { 1.1329 + result[j] = conflictFunc(result[j], elemType(array[i])); 1.1330 + } 1.1331 + } 1.1332 + return result; 1.1333 +} 1.1334 + 1.1335 +function FilterTypedSeqImpl(array, func) { 1.1336 + assert(IsObject(array) && ObjectIsTypedObject(array), "Filter called on non-object or untyped input array."); 1.1337 + assert(typeof func === "function", "Filter called with non-function predicate"); 1.1338 + 1.1339 + var arrayType = TypeOfTypedObject(array); 1.1340 + if (!TypeDescrIsArrayType(arrayType)) 1.1341 + ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); 1.1342 + 1.1343 + var elementType = arrayType.elementType; 1.1344 + var flags = new Uint8Array(NUM_BYTES(array.length)); 1.1345 + var count = 0; 1.1346 + var size = DESCR_SIZE(elementType); 1.1347 + var inOffset = 0; 1.1348 + for (var i = 0; i < array.length; i++) { 1.1349 + var v = TypedObjectGet(elementType, array, inOffset); 1.1350 + if (func(v, i, array)) { 1.1351 + SET_BIT(flags, i); 1.1352 + count++; 1.1353 + } 1.1354 + inOffset += size; 1.1355 + } 1.1356 + 1.1357 + var resultType = (arrayType.variable ? arrayType : arrayType.unsized); 1.1358 + var result = new resultType(count); 1.1359 + for (var i = 0, j = 0; i < array.length; i++) { 1.1360 + if (GET_BIT(flags, i)) 1.1361 + result[j++] = array[i]; 1.1362 + } 1.1363 + return result; 1.1364 +}