js/src/builtin/TypedObject.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 #include "TypedObjectConstants.h"
michael@0 2
michael@0 3 ///////////////////////////////////////////////////////////////////////////
michael@0 4 // Getters and setters for various slots.
michael@0 5
michael@0 6 // Type object slots
michael@0 7
michael@0 8 #define DESCR_KIND(obj) \
michael@0 9 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_KIND)
michael@0 10 #define DESCR_STRING_REPR(obj) \
michael@0 11 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRING_REPR)
michael@0 12 #define DESCR_ALIGNMENT(obj) \
michael@0 13 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ALIGNMENT)
michael@0 14 #define DESCR_SIZE(obj) \
michael@0 15 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZE)
michael@0 16 #define DESCR_OPAQUE(obj) \
michael@0 17 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_OPAQUE)
michael@0 18 #define DESCR_TYPE(obj) \
michael@0 19 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_TYPE)
michael@0 20 #define DESCR_ARRAY_ELEMENT_TYPE(obj) \
michael@0 21 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_ARRAY_ELEM_TYPE)
michael@0 22 #define DESCR_SIZED_ARRAY_LENGTH(obj) \
michael@0 23 TO_INT32(UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_SIZED_ARRAY_LENGTH))
michael@0 24 #define DESCR_STRUCT_FIELD_NAMES(obj) \
michael@0 25 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
michael@0 26 #define DESCR_STRUCT_FIELD_TYPES(obj) \
michael@0 27 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES)
michael@0 28 #define DESCR_STRUCT_FIELD_OFFSETS(obj) \
michael@0 29 UnsafeGetReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS)
michael@0 30
michael@0 31 // Typed object slots
michael@0 32
michael@0 33 #define TYPEDOBJ_BYTEOFFSET(obj) \
michael@0 34 TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTEOFFSET))
michael@0 35 #define TYPEDOBJ_BYTELENGTH(obj) \
michael@0 36 TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_BYTELENGTH))
michael@0 37 #define TYPEDOBJ_TYPE_DESCR(obj) \
michael@0 38 UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_DESCR)
michael@0 39 #define TYPEDOBJ_OWNER(obj) \
michael@0 40 UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_OWNER)
michael@0 41 #define TYPEDOBJ_LENGTH(obj) \
michael@0 42 TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_LENGTH))
michael@0 43
michael@0 44 #define HAS_PROPERTY(obj, prop) \
michael@0 45 callFunction(std_Object_hasOwnProperty, obj, prop)
michael@0 46
michael@0 47 ///////////////////////////////////////////////////////////////////////////
michael@0 48 // Getting values
michael@0 49 //
michael@0 50 // The methods in this section read from the memory pointed at
michael@0 51 // by `this` and produce JS values. This process is called *reification*
michael@0 52 // in the spec.
michael@0 53
michael@0 54 // Reifies the value referenced by the pointer, meaning that it
michael@0 55 // returns a new object pointing at the value. If the value is
michael@0 56 // a scalar, it will return a JS number, but otherwise the reified
michael@0 57 // result will be a typedObj of the same class as the ptr's typedObj.
michael@0 58 function TypedObjectGet(descr, typedObj, offset) {
michael@0 59 assert(IsObject(descr) && ObjectIsTypeDescr(descr),
michael@0 60 "get() called with bad type descr");
michael@0 61 assert(TypedObjectIsAttached(typedObj),
michael@0 62 "get() called with unattached typedObj");
michael@0 63
michael@0 64 switch (DESCR_KIND(descr)) {
michael@0 65 case JS_TYPEREPR_SCALAR_KIND:
michael@0 66 return TypedObjectGetScalar(descr, typedObj, offset);
michael@0 67
michael@0 68 case JS_TYPEREPR_REFERENCE_KIND:
michael@0 69 return TypedObjectGetReference(descr, typedObj, offset);
michael@0 70
michael@0 71 case JS_TYPEREPR_X4_KIND:
michael@0 72 return TypedObjectGetX4(descr, typedObj, offset);
michael@0 73
michael@0 74 case JS_TYPEREPR_SIZED_ARRAY_KIND:
michael@0 75 case JS_TYPEREPR_STRUCT_KIND:
michael@0 76 return TypedObjectGetDerived(descr, typedObj, offset);
michael@0 77
michael@0 78 case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
michael@0 79 assert(false, "Unhandled repr kind: " + DESCR_KIND(descr));
michael@0 80 }
michael@0 81
michael@0 82 assert(false, "Unhandled kind: " + DESCR_KIND(descr));
michael@0 83 return undefined;
michael@0 84 }
michael@0 85
michael@0 86 function TypedObjectGetDerived(descr, typedObj, offset) {
michael@0 87 assert(!TypeDescrIsSimpleType(descr),
michael@0 88 "getDerived() used with simple type");
michael@0 89 return NewDerivedTypedObject(descr, typedObj, offset);
michael@0 90 }
michael@0 91
michael@0 92 function TypedObjectGetDerivedIf(descr, typedObj, offset, cond) {
michael@0 93 return (cond ? TypedObjectGetDerived(descr, typedObj, offset) : undefined);
michael@0 94 }
michael@0 95
michael@0 96 function TypedObjectGetOpaque(descr, typedObj, offset) {
michael@0 97 assert(!TypeDescrIsSimpleType(descr),
michael@0 98 "getDerived() used with simple type");
michael@0 99 var opaqueTypedObj = NewOpaqueTypedObject(descr);
michael@0 100 AttachTypedObject(opaqueTypedObj, typedObj, offset);
michael@0 101 return opaqueTypedObj;
michael@0 102 }
michael@0 103
michael@0 104 function TypedObjectGetOpaqueIf(descr, typedObj, offset, cond) {
michael@0 105 return (cond ? TypedObjectGetOpaque(descr, typedObj, offset) : undefined);
michael@0 106 }
michael@0 107
michael@0 108 function TypedObjectGetScalar(descr, typedObj, offset) {
michael@0 109 var type = DESCR_TYPE(descr);
michael@0 110 switch (type) {
michael@0 111 case JS_SCALARTYPEREPR_INT8:
michael@0 112 return Load_int8(typedObj, offset);
michael@0 113
michael@0 114 case JS_SCALARTYPEREPR_UINT8:
michael@0 115 case JS_SCALARTYPEREPR_UINT8_CLAMPED:
michael@0 116 return Load_uint8(typedObj, offset);
michael@0 117
michael@0 118 case JS_SCALARTYPEREPR_INT16:
michael@0 119 return Load_int16(typedObj, offset);
michael@0 120
michael@0 121 case JS_SCALARTYPEREPR_UINT16:
michael@0 122 return Load_uint16(typedObj, offset);
michael@0 123
michael@0 124 case JS_SCALARTYPEREPR_INT32:
michael@0 125 return Load_int32(typedObj, offset);
michael@0 126
michael@0 127 case JS_SCALARTYPEREPR_UINT32:
michael@0 128 return Load_uint32(typedObj, offset);
michael@0 129
michael@0 130 case JS_SCALARTYPEREPR_FLOAT32:
michael@0 131 return Load_float32(typedObj, offset);
michael@0 132
michael@0 133 case JS_SCALARTYPEREPR_FLOAT64:
michael@0 134 return Load_float64(typedObj, offset);
michael@0 135 }
michael@0 136
michael@0 137 assert(false, "Unhandled scalar type: " + type);
michael@0 138 return undefined;
michael@0 139 }
michael@0 140
michael@0 141 function TypedObjectGetReference(descr, typedObj, offset) {
michael@0 142 var type = DESCR_TYPE(descr);
michael@0 143 switch (type) {
michael@0 144 case JS_REFERENCETYPEREPR_ANY:
michael@0 145 return Load_Any(typedObj, offset);
michael@0 146
michael@0 147 case JS_REFERENCETYPEREPR_OBJECT:
michael@0 148 return Load_Object(typedObj, offset);
michael@0 149
michael@0 150 case JS_REFERENCETYPEREPR_STRING:
michael@0 151 return Load_string(typedObj, offset);
michael@0 152 }
michael@0 153
michael@0 154 assert(false, "Unhandled scalar type: " + type);
michael@0 155 return undefined;
michael@0 156 }
michael@0 157
michael@0 158 function TypedObjectGetX4(descr, typedObj, offset) {
michael@0 159 var type = DESCR_TYPE(descr);
michael@0 160 switch (type) {
michael@0 161 case JS_X4TYPEREPR_FLOAT32:
michael@0 162 var x = Load_float32(typedObj, offset + 0);
michael@0 163 var y = Load_float32(typedObj, offset + 4);
michael@0 164 var z = Load_float32(typedObj, offset + 8);
michael@0 165 var w = Load_float32(typedObj, offset + 12);
michael@0 166 return GetFloat32x4TypeDescr()(x, y, z, w);
michael@0 167
michael@0 168 case JS_X4TYPEREPR_INT32:
michael@0 169 var x = Load_int32(typedObj, offset + 0);
michael@0 170 var y = Load_int32(typedObj, offset + 4);
michael@0 171 var z = Load_int32(typedObj, offset + 8);
michael@0 172 var w = Load_int32(typedObj, offset + 12);
michael@0 173 return GetInt32x4TypeDescr()(x, y, z, w);
michael@0 174 }
michael@0 175
michael@0 176 assert(false, "Unhandled x4 type: " + type);
michael@0 177 return undefined;
michael@0 178 }
michael@0 179
michael@0 180 ///////////////////////////////////////////////////////////////////////////
michael@0 181 // Setting values
michael@0 182 //
michael@0 183 // The methods in this section modify the data pointed at by `this`.
michael@0 184
michael@0 185 // Writes `fromValue` into the `typedObj` at offset `offset`, adapting
michael@0 186 // it to `descr` as needed. This is the most general entry point
michael@0 187 // and works for any type.
michael@0 188 function TypedObjectSet(descr, typedObj, offset, fromValue) {
michael@0 189 assert(TypedObjectIsAttached(typedObj), "set() called with unattached typedObj");
michael@0 190
michael@0 191 // Fast path: `fromValue` is a typed object with same type
michael@0 192 // representation as the destination. In that case, we can just do a
michael@0 193 // memcpy.
michael@0 194 if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
michael@0 195 if (!descr.variable && DescrsEquiv(descr, TYPEDOBJ_TYPE_DESCR(fromValue))) {
michael@0 196 if (!TypedObjectIsAttached(fromValue))
michael@0 197 ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
michael@0 198
michael@0 199 var size = DESCR_SIZE(descr);
michael@0 200 Memcpy(typedObj, offset, fromValue, 0, size);
michael@0 201 return;
michael@0 202 }
michael@0 203 }
michael@0 204
michael@0 205 switch (DESCR_KIND(descr)) {
michael@0 206 case JS_TYPEREPR_SCALAR_KIND:
michael@0 207 TypedObjectSetScalar(descr, typedObj, offset, fromValue);
michael@0 208 return;
michael@0 209
michael@0 210 case JS_TYPEREPR_REFERENCE_KIND:
michael@0 211 TypedObjectSetReference(descr, typedObj, offset, fromValue);
michael@0 212 return;
michael@0 213
michael@0 214 case JS_TYPEREPR_X4_KIND:
michael@0 215 TypedObjectSetX4(descr, typedObj, offset, fromValue);
michael@0 216 return;
michael@0 217
michael@0 218 case JS_TYPEREPR_SIZED_ARRAY_KIND:
michael@0 219 var length = DESCR_SIZED_ARRAY_LENGTH(descr);
michael@0 220 if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue))
michael@0 221 return;
michael@0 222 break;
michael@0 223
michael@0 224 case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
michael@0 225 var length = typedObj.length;
michael@0 226 if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue))
michael@0 227 return;
michael@0 228 break;
michael@0 229
michael@0 230 case JS_TYPEREPR_STRUCT_KIND:
michael@0 231 if (!IsObject(fromValue))
michael@0 232 break;
michael@0 233
michael@0 234 // Adapt each field.
michael@0 235 var fieldNames = DESCR_STRUCT_FIELD_NAMES(descr);
michael@0 236 var fieldDescrs = DESCR_STRUCT_FIELD_TYPES(descr);
michael@0 237 var fieldOffsets = DESCR_STRUCT_FIELD_OFFSETS(descr);
michael@0 238 for (var i = 0; i < fieldNames.length; i++) {
michael@0 239 var fieldName = fieldNames[i];
michael@0 240 var fieldDescr = fieldDescrs[i];
michael@0 241 var fieldOffset = fieldOffsets[i];
michael@0 242 var fieldValue = fromValue[fieldName];
michael@0 243 TypedObjectSet(fieldDescr, typedObj, offset + fieldOffset, fieldValue);
michael@0 244 }
michael@0 245 return;
michael@0 246 }
michael@0 247
michael@0 248 ThrowError(JSMSG_CANT_CONVERT_TO,
michael@0 249 typeof(fromValue),
michael@0 250 DESCR_STRING_REPR(descr));
michael@0 251 }
michael@0 252
michael@0 253 function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) {
michael@0 254 if (!IsObject(fromValue))
michael@0 255 return false;
michael@0 256
michael@0 257 // Check that "array-like" fromValue has an appropriate length.
michael@0 258 if (fromValue.length !== length)
michael@0 259 return false;
michael@0 260
michael@0 261 // Adapt each element.
michael@0 262 if (length > 0) {
michael@0 263 var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr);
michael@0 264 var elemSize = DESCR_SIZE(elemDescr);
michael@0 265 var elemOffset = offset;
michael@0 266 for (var i = 0; i < length; i++) {
michael@0 267 TypedObjectSet(elemDescr, typedObj, elemOffset, fromValue[i]);
michael@0 268 elemOffset += elemSize;
michael@0 269 }
michael@0 270 }
michael@0 271 return true;
michael@0 272 }
michael@0 273
michael@0 274 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
michael@0 275 function TypedObjectSetScalar(descr, typedObj, offset, fromValue) {
michael@0 276 assert(DESCR_KIND(descr) === JS_TYPEREPR_SCALAR_KIND,
michael@0 277 "Expected scalar type descriptor");
michael@0 278 var type = DESCR_TYPE(descr);
michael@0 279 switch (type) {
michael@0 280 case JS_SCALARTYPEREPR_INT8:
michael@0 281 return Store_int8(typedObj, offset,
michael@0 282 TO_INT32(fromValue) & 0xFF);
michael@0 283
michael@0 284 case JS_SCALARTYPEREPR_UINT8:
michael@0 285 return Store_uint8(typedObj, offset,
michael@0 286 TO_UINT32(fromValue) & 0xFF);
michael@0 287
michael@0 288 case JS_SCALARTYPEREPR_UINT8_CLAMPED:
michael@0 289 var v = ClampToUint8(+fromValue);
michael@0 290 return Store_int8(typedObj, offset, v);
michael@0 291
michael@0 292 case JS_SCALARTYPEREPR_INT16:
michael@0 293 return Store_int16(typedObj, offset,
michael@0 294 TO_INT32(fromValue) & 0xFFFF);
michael@0 295
michael@0 296 case JS_SCALARTYPEREPR_UINT16:
michael@0 297 return Store_uint16(typedObj, offset,
michael@0 298 TO_UINT32(fromValue) & 0xFFFF);
michael@0 299
michael@0 300 case JS_SCALARTYPEREPR_INT32:
michael@0 301 return Store_int32(typedObj, offset,
michael@0 302 TO_INT32(fromValue));
michael@0 303
michael@0 304 case JS_SCALARTYPEREPR_UINT32:
michael@0 305 return Store_uint32(typedObj, offset,
michael@0 306 TO_UINT32(fromValue));
michael@0 307
michael@0 308 case JS_SCALARTYPEREPR_FLOAT32:
michael@0 309 return Store_float32(typedObj, offset, +fromValue);
michael@0 310
michael@0 311 case JS_SCALARTYPEREPR_FLOAT64:
michael@0 312 return Store_float64(typedObj, offset, +fromValue);
michael@0 313 }
michael@0 314
michael@0 315 assert(false, "Unhandled scalar type: " + type);
michael@0 316 return undefined;
michael@0 317 }
michael@0 318
michael@0 319 function TypedObjectSetReference(descr, typedObj, offset, fromValue) {
michael@0 320 var type = DESCR_TYPE(descr);
michael@0 321 switch (type) {
michael@0 322 case JS_REFERENCETYPEREPR_ANY:
michael@0 323 return Store_Any(typedObj, offset, fromValue);
michael@0 324
michael@0 325 case JS_REFERENCETYPEREPR_OBJECT:
michael@0 326 var value = (fromValue === null ? fromValue : ToObject(fromValue));
michael@0 327 return Store_Object(typedObj, offset, value);
michael@0 328
michael@0 329 case JS_REFERENCETYPEREPR_STRING:
michael@0 330 return Store_string(typedObj, offset, ToString(fromValue));
michael@0 331 }
michael@0 332
michael@0 333 assert(false, "Unhandled scalar type: " + type);
michael@0 334 return undefined;
michael@0 335 }
michael@0 336
michael@0 337 // Sets `fromValue` to `this` assuming that `this` is a scalar type.
michael@0 338 function TypedObjectSetX4(descr, typedObj, offset, fromValue) {
michael@0 339 // It is only permitted to set a float32x4/int32x4 value from another
michael@0 340 // float32x4/int32x4; in that case, the "fast path" that uses memcopy will
michael@0 341 // have already matched. So if we get to this point, we're supposed
michael@0 342 // to "adapt" fromValue, but there are no legal adaptions.
michael@0 343 ThrowError(JSMSG_CANT_CONVERT_TO,
michael@0 344 typeof(fromValue),
michael@0 345 DESCR_STRING_REPR(descr));
michael@0 346 }
michael@0 347
michael@0 348 ///////////////////////////////////////////////////////////////////////////
michael@0 349 // C++ Wrappers
michael@0 350 //
michael@0 351 // These helpers are invoked by C++ code or used as method bodies.
michael@0 352
michael@0 353 // Wrapper for use from C++ code.
michael@0 354 function ConvertAndCopyTo(destDescr,
michael@0 355 destTypedObj,
michael@0 356 destOffset,
michael@0 357 fromValue)
michael@0 358 {
michael@0 359 assert(IsObject(destDescr) && ObjectIsTypeDescr(destDescr),
michael@0 360 "ConvertAndCopyTo: not type obj");
michael@0 361 assert(IsObject(destTypedObj) && ObjectIsTypedObject(destTypedObj),
michael@0 362 "ConvertAndCopyTo: not type typedObj");
michael@0 363
michael@0 364 if (!TypedObjectIsAttached(destTypedObj))
michael@0 365 ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
michael@0 366
michael@0 367 TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue);
michael@0 368 }
michael@0 369
michael@0 370 // Wrapper for use from C++ code.
michael@0 371 function Reify(sourceDescr,
michael@0 372 sourceTypedObj,
michael@0 373 sourceOffset) {
michael@0 374 assert(IsObject(sourceDescr) && ObjectIsTypeDescr(sourceDescr),
michael@0 375 "Reify: not type obj");
michael@0 376 assert(IsObject(sourceTypedObj) && ObjectIsTypedObject(sourceTypedObj),
michael@0 377 "Reify: not type typedObj");
michael@0 378
michael@0 379 if (!TypedObjectIsAttached(sourceTypedObj))
michael@0 380 ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
michael@0 381
michael@0 382 return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset);
michael@0 383 }
michael@0 384
michael@0 385 function FillTypedArrayWithValue(destArray, fromValue) {
michael@0 386 assert(IsObject(handle) && ObjectIsTypedObject(destArray),
michael@0 387 "FillTypedArrayWithValue: not typed handle");
michael@0 388
michael@0 389 var descr = TYPEDOBJ_TYPE_DESCR(destArray);
michael@0 390 var length = DESCR_SIZED_ARRAY_LENGTH(descr);
michael@0 391 if (length === 0)
michael@0 392 return;
michael@0 393
michael@0 394 // Use convert and copy to to produce the first element:
michael@0 395 var elemDescr = DESCR_ARRAY_ELEMENT_TYPE(descr);
michael@0 396 TypedObjectSet(elemDescr, destArray, 0, fromValue);
michael@0 397
michael@0 398 // Stamp out the remaining copies:
michael@0 399 var elemSize = DESCR_SIZE(elemDescr);
michael@0 400 var totalSize = length * elemSize;
michael@0 401 for (var offset = elemSize; offset < totalSize; offset += elemSize)
michael@0 402 Memcpy(destArray, offset, destArray, 0, elemSize);
michael@0 403 }
michael@0 404
michael@0 405 // Warning: user exposed!
michael@0 406 function TypeDescrEquivalent(otherDescr) {
michael@0 407 if (!IsObject(this) || !ObjectIsTypeDescr(this))
michael@0 408 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 409 if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr))
michael@0 410 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 411 return DescrsEquiv(this, otherDescr);
michael@0 412 }
michael@0 413
michael@0 414 // TypedArray.redimension(newArrayType)
michael@0 415 //
michael@0 416 // Method that "repackages" the data from this array into a new typed
michael@0 417 // object whose type is `newArrayType`. Once you strip away all the
michael@0 418 // outer array dimensions, the type of `this` array and `newArrayType`
michael@0 419 // must share the same innermost element type. Moreover, those
michael@0 420 // stripped away dimensions must amount to the same total number of
michael@0 421 // elements.
michael@0 422 //
michael@0 423 // For example, given two equivalent types `T` and `U`, it is legal to
michael@0 424 // interconvert between arrays types like:
michael@0 425 // T[32]
michael@0 426 // U[2][16]
michael@0 427 // U[2][2][8]
michael@0 428 // Because they all share the same total number (32) of equivalent elements.
michael@0 429 // But it would be illegal to convert `T[32]` to `U[31]` or `U[2][17]`, since
michael@0 430 // the number of elements differs. And it's just plain incompatible to convert
michael@0 431 // if the base element types are not equivalent.
michael@0 432 //
michael@0 433 // Warning: user exposed!
michael@0 434 function TypedArrayRedimension(newArrayType) {
michael@0 435 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 436 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 437
michael@0 438 if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType))
michael@0 439 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 440
michael@0 441 // Peel away the outermost array layers from the type of `this` to find
michael@0 442 // the core element type. In the process, count the number of elements.
michael@0 443 var oldArrayType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 444 var oldArrayReprKind = DESCR_KIND(oldArrayType);
michael@0 445 var oldElementType = oldArrayType;
michael@0 446 var oldElementCount = 1;
michael@0 447 switch (oldArrayReprKind) {
michael@0 448 case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
michael@0 449 oldElementCount *= this.length;
michael@0 450 oldElementType = oldElementType.elementType;
michael@0 451 break;
michael@0 452
michael@0 453 case JS_TYPEREPR_SIZED_ARRAY_KIND:
michael@0 454 break;
michael@0 455
michael@0 456 default:
michael@0 457 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 458 }
michael@0 459 while (DESCR_KIND(oldElementType) === JS_TYPEREPR_SIZED_ARRAY_KIND) {
michael@0 460 oldElementCount *= oldElementType.length;
michael@0 461 oldElementType = oldElementType.elementType;
michael@0 462 }
michael@0 463
michael@0 464 // Peel away the outermost array layers from `newArrayType`. In the
michael@0 465 // process, count the number of elements.
michael@0 466 var newElementType = newArrayType;
michael@0 467 var newElementCount = 1;
michael@0 468 while (DESCR_KIND(newElementType) == JS_TYPEREPR_SIZED_ARRAY_KIND) {
michael@0 469 newElementCount *= newElementType.length;
michael@0 470 newElementType = newElementType.elementType;
michael@0 471 }
michael@0 472
michael@0 473 // Check that the total number of elements does not change.
michael@0 474 if (oldElementCount !== newElementCount) {
michael@0 475 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 476 }
michael@0 477
michael@0 478 // Check that the element types are equivalent.
michael@0 479 if (!DescrsEquiv(oldElementType, newElementType)) {
michael@0 480 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 481 }
michael@0 482
michael@0 483 // Together, this should imply that the sizes are unchanged.
michael@0 484 assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType),
michael@0 485 "Byte sizes should be equal");
michael@0 486
michael@0 487 // Rewrap the data from `this` in a new type.
michael@0 488 return NewDerivedTypedObject(newArrayType, this, 0);
michael@0 489 }
michael@0 490
michael@0 491 ///////////////////////////////////////////////////////////////////////////
michael@0 492 // X4
michael@0 493
michael@0 494 function X4ProtoString(type) {
michael@0 495 switch (type) {
michael@0 496 case JS_X4TYPEREPR_INT32:
michael@0 497 return "int32x4";
michael@0 498 case JS_X4TYPEREPR_FLOAT32:
michael@0 499 return "float32x4";
michael@0 500 }
michael@0 501
michael@0 502 assert(false, "Unhandled type constant");
michael@0 503 return undefined;
michael@0 504 }
michael@0 505
michael@0 506 function X4ToSource() {
michael@0 507 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 508 ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
michael@0 509
michael@0 510 var descr = TYPEDOBJ_TYPE_DESCR(this);
michael@0 511
michael@0 512 if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND)
michael@0 513 ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
michael@0 514
michael@0 515 var type = DESCR_TYPE(descr);
michael@0 516 return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
michael@0 517 }
michael@0 518
michael@0 519 ///////////////////////////////////////////////////////////////////////////
michael@0 520 // Miscellaneous
michael@0 521
michael@0 522 function DescrsEquiv(descr1, descr2) {
michael@0 523 assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr");
michael@0 524 assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr");
michael@0 525
michael@0 526 // Potential optimization: these two strings are guaranteed to be
michael@0 527 // atoms, and hence this string comparison can just be a pointer
michael@0 528 // comparison. However, I don't think ion knows that. If this ever
michael@0 529 // becomes a bottleneck, we can add a intrinsic at some point that
michael@0 530 // is treated specially by Ion. (Bug 976688)
michael@0 531
michael@0 532 return DESCR_STRING_REPR(descr1) === DESCR_STRING_REPR(descr2);
michael@0 533 }
michael@0 534
michael@0 535 // toSource() for type descriptors.
michael@0 536 //
michael@0 537 // Warning: user exposed!
michael@0 538 function DescrToSource() {
michael@0 539 if (!IsObject(this) || !ObjectIsTypeDescr(this))
michael@0 540 ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value");
michael@0 541
michael@0 542 return DESCR_STRING_REPR(this);
michael@0 543 }
michael@0 544
michael@0 545 // Warning: user exposed!
michael@0 546 function ArrayShorthand(...dims) {
michael@0 547 if (!IsObject(this) || !ObjectIsTypeDescr(this))
michael@0 548 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 549
michael@0 550 var T = GetTypedObjectModule();
michael@0 551
michael@0 552 if (dims.length == 0)
michael@0 553 return new T.ArrayType(this);
michael@0 554
michael@0 555 var accum = this;
michael@0 556 for (var i = dims.length - 1; i >= 0; i--)
michael@0 557 accum = new T.ArrayType(accum).dimension(dims[i]);
michael@0 558 return accum;
michael@0 559 }
michael@0 560
michael@0 561 // This is the `storage()` function defined in the spec. When
michael@0 562 // provided with a *transparent* typed object, it returns an object
michael@0 563 // containing buffer, byteOffset, byteLength. When given an opaque
michael@0 564 // typed object, it returns null. Otherwise it throws.
michael@0 565 //
michael@0 566 // Warning: user exposed!
michael@0 567 function StorageOfTypedObject(obj) {
michael@0 568 if (IsObject(obj)) {
michael@0 569 if (ObjectIsOpaqueTypedObject(obj))
michael@0 570 return null;
michael@0 571
michael@0 572 if (ObjectIsTransparentTypedObject(obj))
michael@0 573 return { buffer: TYPEDOBJ_OWNER(obj),
michael@0 574 byteLength: TYPEDOBJ_BYTELENGTH(obj),
michael@0 575 byteOffset: TYPEDOBJ_BYTEOFFSET(obj) };
michael@0 576 }
michael@0 577
michael@0 578 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 579 return null; // pacify silly "always returns a value" lint
michael@0 580 }
michael@0 581
michael@0 582 // This is the `objectType()` function defined in the spec.
michael@0 583 // It returns the type of its argument.
michael@0 584 //
michael@0 585 // Warning: user exposed!
michael@0 586 function TypeOfTypedObject(obj) {
michael@0 587 if (IsObject(obj) && ObjectIsTypedObject(obj))
michael@0 588 return TYPEDOBJ_TYPE_DESCR(obj);
michael@0 589
michael@0 590 // Note: Do not create bindings for `Any`, `String`, etc in
michael@0 591 // Utilities.js, but rather access them through
michael@0 592 // `GetTypedObjectModule()`. The reason is that bindings
michael@0 593 // you create in Utilities.js are part of the self-hosted global,
michael@0 594 // vs the user-accessible global, and hence should not escape to
michael@0 595 // user script.
michael@0 596 var T = GetTypedObjectModule();
michael@0 597 switch (typeof obj) {
michael@0 598 case "object": return T.Object;
michael@0 599 case "function": return T.Object;
michael@0 600 case "string": return T.String;
michael@0 601 case "number": return T.float64;
michael@0 602 case "undefined": return T.Any;
michael@0 603 default: return T.Any;
michael@0 604 }
michael@0 605 }
michael@0 606
michael@0 607 ///////////////////////////////////////////////////////////////////////////
michael@0 608 // TypedObject surface API methods (sequential implementations).
michael@0 609
michael@0 610 // Warning: user exposed!
michael@0 611 function TypedObjectArrayTypeBuild(a,b,c) {
michael@0 612 // Arguments (this sized) : [depth], func
michael@0 613 // Arguments (this unsized) : length, [depth], func
michael@0 614
michael@0 615 if (!IsObject(this) || !ObjectIsTypeDescr(this))
michael@0 616 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 617 var kind = DESCR_KIND(this);
michael@0 618 switch (kind) {
michael@0 619 case JS_TYPEREPR_SIZED_ARRAY_KIND:
michael@0 620 if (typeof a === "function") // XXX here and elsewhere: these type dispatches are fragile at best.
michael@0 621 return BuildTypedSeqImpl(this, this.length, 1, a);
michael@0 622 else if (typeof a === "number" && typeof b === "function")
michael@0 623 return BuildTypedSeqImpl(this, this.length, a, b);
michael@0 624 else if (typeof a === "number")
michael@0 625 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 626 else
michael@0 627 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 628 case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
michael@0 629 var len = a;
michael@0 630 if (typeof b === "function")
michael@0 631 return BuildTypedSeqImpl(this, len, 1, b);
michael@0 632 else if (typeof b === "number" && typeof c === "function")
michael@0 633 return BuildTypedSeqImpl(this, len, b, c);
michael@0 634 else if (typeof b === "number")
michael@0 635 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 636 else
michael@0 637 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 638 default:
michael@0 639 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 640 }
michael@0 641 }
michael@0 642
michael@0 643 // Warning: user exposed!
michael@0 644 function TypedObjectArrayTypeFrom(a, b, c) {
michael@0 645 // Arguments: arrayLike, [depth], func
michael@0 646
michael@0 647 if (!IsObject(this) || !ObjectIsTypeDescr(this))
michael@0 648 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 649
michael@0 650 var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a);
michael@0 651
michael@0 652 // for untyped input array, the expectation (in terms of error
michael@0 653 // reporting for invalid parameters) is no-depth, despite
michael@0 654 // supporting an explicit depth of 1; while for typed input array,
michael@0 655 // the expectation is explicit depth.
michael@0 656
michael@0 657 if (untypedInput) {
michael@0 658 var explicitDepth = (b === 1);
michael@0 659 if (explicitDepth && IsCallable(c))
michael@0 660 return MapUntypedSeqImpl(a, this, c);
michael@0 661 else if (IsCallable(b))
michael@0 662 return MapUntypedSeqImpl(a, this, b);
michael@0 663 else
michael@0 664 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 665 } else {
michael@0 666 var explicitDepth = (typeof b === "number");
michael@0 667 if (explicitDepth && IsCallable(c))
michael@0 668 return MapTypedSeqImpl(a, b, this, c);
michael@0 669 else if (IsCallable(b))
michael@0 670 return MapTypedSeqImpl(a, 1, this, b);
michael@0 671 else if (explicitDepth)
michael@0 672 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 673 else
michael@0 674 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 675 }
michael@0 676 }
michael@0 677
michael@0 678 // Warning: user exposed!
michael@0 679 function TypedArrayMap(a, b) {
michael@0 680 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 681 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 682 var thisType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 683 if (!TypeDescrIsArrayType(thisType))
michael@0 684 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 685
michael@0 686 // Arguments: [depth], func
michael@0 687 if (typeof a === "number" && typeof b === "function")
michael@0 688 return MapTypedSeqImpl(this, a, thisType, b);
michael@0 689 else if (typeof a === "function")
michael@0 690 return MapTypedSeqImpl(this, 1, thisType, a);
michael@0 691 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 692 }
michael@0 693
michael@0 694 // Warning: user exposed!
michael@0 695 function TypedArrayMapPar(a, b) {
michael@0 696 // Arguments: [depth], func
michael@0 697
michael@0 698 // Defer to the sequential variant for error cases or
michael@0 699 // when not working with typed objects.
michael@0 700 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 701 return callFunction(TypedArrayMap, this, a, b);
michael@0 702 var thisType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 703 if (!TypeDescrIsArrayType(thisType))
michael@0 704 return callFunction(TypedArrayMap, this, a, b);
michael@0 705
michael@0 706 if (typeof a === "number" && IsCallable(b))
michael@0 707 return MapTypedParImpl(this, a, thisType, b);
michael@0 708 else if (IsCallable(a))
michael@0 709 return MapTypedParImpl(this, 1, thisType, a);
michael@0 710 return callFunction(TypedArrayMap, this, a, b);
michael@0 711 }
michael@0 712
michael@0 713 // Warning: user exposed!
michael@0 714 function TypedArrayReduce(a, b) {
michael@0 715 // Arguments: func, [initial]
michael@0 716 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 717 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 718 var thisType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 719 if (!TypeDescrIsArrayType(thisType))
michael@0 720 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 721
michael@0 722 if (a !== undefined && typeof a !== "function")
michael@0 723 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 724
michael@0 725 var outputType = thisType.elementType;
michael@0 726 return ReduceTypedSeqImpl(this, outputType, a, b);
michael@0 727 }
michael@0 728
michael@0 729 // Warning: user exposed!
michael@0 730 function TypedArrayScatter(a, b, c, d) {
michael@0 731 // Arguments: outputArrayType, indices, defaultValue, conflictFunction
michael@0 732 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 733 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 734 var thisType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 735 if (!TypeDescrIsArrayType(thisType))
michael@0 736 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 737
michael@0 738 if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a))
michael@0 739 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 740
michael@0 741 if (d !== undefined && typeof d !== "function")
michael@0 742 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 743
michael@0 744 return ScatterTypedSeqImpl(this, a, b, c, d);
michael@0 745 }
michael@0 746
michael@0 747 // Warning: user exposed!
michael@0 748 function TypedArrayFilter(func) {
michael@0 749 // Arguments: predicate
michael@0 750 if (!IsObject(this) || !ObjectIsTypedObject(this))
michael@0 751 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 752 var thisType = TYPEDOBJ_TYPE_DESCR(this);
michael@0 753 if (!TypeDescrIsArrayType(thisType))
michael@0 754 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 755
michael@0 756 if (typeof func !== "function")
michael@0 757 return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 758
michael@0 759 return FilterTypedSeqImpl(this, func);
michael@0 760 }
michael@0 761
michael@0 762 // placeholders
michael@0 763
michael@0 764 // Warning: user exposed!
michael@0 765 function TypedObjectArrayTypeBuildPar(a,b,c) {
michael@0 766 return callFunction(TypedObjectArrayTypeBuild, this, a, b, c);
michael@0 767 }
michael@0 768
michael@0 769 // Warning: user exposed!
michael@0 770 function TypedObjectArrayTypeFromPar(a,b,c) {
michael@0 771 // Arguments: arrayLike, [depth], func
michael@0 772
michael@0 773 // Use the sequential version for error cases or when arrayLike is
michael@0 774 // not a typed object.
michael@0 775 if (!IsObject(this) || !ObjectIsTypeDescr(this) || !TypeDescrIsArrayType(this))
michael@0 776 return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
michael@0 777 if (!IsObject(a) || !ObjectIsTypedObject(a))
michael@0 778 return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
michael@0 779
michael@0 780 // Detect whether an explicit depth is supplied.
michael@0 781 if (typeof b === "number" && IsCallable(c))
michael@0 782 return MapTypedParImpl(a, b, this, c);
michael@0 783 if (IsCallable(b))
michael@0 784 return MapTypedParImpl(a, 1, this, b);
michael@0 785 return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
michael@0 786 }
michael@0 787
michael@0 788 // Warning: user exposed!
michael@0 789 function TypedArrayReducePar(a, b) {
michael@0 790 return callFunction(TypedArrayReduce, this, a, b);
michael@0 791 }
michael@0 792
michael@0 793 // Warning: user exposed!
michael@0 794 function TypedArrayScatterPar(a, b, c, d) {
michael@0 795 return callFunction(TypedArrayScatter, this, a, b, c, d);
michael@0 796 }
michael@0 797
michael@0 798 // Warning: user exposed!
michael@0 799 function TypedArrayFilterPar(func) {
michael@0 800 return callFunction(TypedArrayFilter, this, func);
michael@0 801 }
michael@0 802
michael@0 803 // should eventually become macros
michael@0 804 function NUM_BYTES(bits) {
michael@0 805 return (bits + 7) >> 3;
michael@0 806 }
michael@0 807 function SET_BIT(data, index) {
michael@0 808 var word = index >> 3;
michael@0 809 var mask = 1 << (index & 0x7);
michael@0 810 data[word] |= mask;
michael@0 811 }
michael@0 812 function GET_BIT(data, index) {
michael@0 813 var word = index >> 3;
michael@0 814 var mask = 1 << (index & 0x7);
michael@0 815 return (data[word] & mask) != 0;
michael@0 816 }
michael@0 817
michael@0 818 // Bug 956914: make performance-tuned variants tailored to 1, 2, and 3 dimensions.
michael@0 819 function BuildTypedSeqImpl(arrayType, len, depth, func) {
michael@0 820 assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object");
michael@0 821
michael@0 822 if (depth <= 0 || TO_INT32(depth) !== depth)
michael@0 823 // RangeError("bad depth")
michael@0 824 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 825
michael@0 826 // For example, if we have as input
michael@0 827 // ArrayType(ArrayType(T, 4), 5)
michael@0 828 // and a depth of 2, we get
michael@0 829 // grainType = T
michael@0 830 // iterationSpace = [5, 4]
michael@0 831 var [iterationSpace, grainType, totalLength] =
michael@0 832 ComputeIterationSpace(arrayType, depth, len);
michael@0 833
michael@0 834 // Create a zeroed instance with no data
michael@0 835 var result = arrayType.variable ? new arrayType(len) : new arrayType();
michael@0 836
michael@0 837 var indices = NewDenseArray(depth);
michael@0 838 for (var i = 0; i < depth; i++) {
michael@0 839 indices[i] = 0;
michael@0 840 }
michael@0 841
michael@0 842 var grainTypeIsComplex = !TypeDescrIsSimpleType(grainType);
michael@0 843 var size = DESCR_SIZE(grainType);
michael@0 844 var outOffset = 0;
michael@0 845 for (i = 0; i < totalLength; i++) {
michael@0 846 // Position out-pointer to point at &result[...indices], if appropriate.
michael@0 847 var userOutPointer = TypedObjectGetOpaqueIf(grainType, result, outOffset,
michael@0 848 grainTypeIsComplex);
michael@0 849
michael@0 850 // Invoke func(...indices, userOutPointer) and store the result
michael@0 851 callFunction(std_Array_push, indices, userOutPointer);
michael@0 852 var r = callFunction(std_Function_apply, func, undefined, indices);
michael@0 853 callFunction(std_Array_pop, indices);
michael@0 854 if (r !== undefined)
michael@0 855 TypedObjectSet(grainType, result, outOffset, r); // result[...indices] = r;
michael@0 856
michael@0 857 // Increment indices.
michael@0 858 IncrementIterationSpace(indices, iterationSpace);
michael@0 859 outOffset += size;
michael@0 860 }
michael@0 861
michael@0 862 return result;
michael@0 863 }
michael@0 864
michael@0 865 function ComputeIterationSpace(arrayType, depth, len) {
michael@0 866 assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "ComputeIterationSpace called on non-type-object");
michael@0 867 assert(TypeDescrIsArrayType(arrayType), "ComputeIterationSpace called on non-array-type");
michael@0 868 assert(depth > 0, "ComputeIterationSpace called on non-positive depth");
michael@0 869 var iterationSpace = NewDenseArray(depth);
michael@0 870 iterationSpace[0] = len;
michael@0 871 var totalLength = len;
michael@0 872 var grainType = arrayType.elementType;
michael@0 873
michael@0 874 for (var i = 1; i < depth; i++) {
michael@0 875 if (TypeDescrIsArrayType(grainType)) {
michael@0 876 var grainLen = grainType.length;
michael@0 877 iterationSpace[i] = grainLen;
michael@0 878 totalLength *= grainLen;
michael@0 879 grainType = grainType.elementType;
michael@0 880 } else {
michael@0 881 // RangeError("Depth "+depth+" too high");
michael@0 882 ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
michael@0 883 }
michael@0 884 }
michael@0 885 return [iterationSpace, grainType, totalLength];
michael@0 886 }
michael@0 887
michael@0 888 function IncrementIterationSpace(indices, iterationSpace) {
michael@0 889 // Increment something like
michael@0 890 // [5, 5, 7, 8]
michael@0 891 // in an iteration space of
michael@0 892 // [9, 9, 9, 9]
michael@0 893 // to
michael@0 894 // [5, 5, 8, 0]
michael@0 895
michael@0 896 assert(indices.length === iterationSpace.length,
michael@0 897 "indices dimension must equal iterationSpace dimension.");
michael@0 898 var n = indices.length - 1;
michael@0 899 while (true) {
michael@0 900 indices[n] += 1;
michael@0 901 if (indices[n] < iterationSpace[n])
michael@0 902 return;
michael@0 903
michael@0 904 assert(indices[n] === iterationSpace[n],
michael@0 905 "Components of indices must match those of iterationSpace.");
michael@0 906 indices[n] = 0;
michael@0 907 if (n == 0)
michael@0 908 return;
michael@0 909
michael@0 910 n -= 1;
michael@0 911 }
michael@0 912 }
michael@0 913
michael@0 914 // Implements |from| method for untyped |inArray|. (Depth is implicitly 1 for untyped input.)
michael@0 915 function MapUntypedSeqImpl(inArray, outputType, maybeFunc) {
michael@0 916 assert(IsObject(outputType), "1. Map/From called on non-object outputType");
michael@0 917 assert(ObjectIsTypeDescr(outputType), "1. Map/From called on non-type-object outputType");
michael@0 918 inArray = ToObject(inArray);
michael@0 919 assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType");
michael@0 920
michael@0 921 if (!IsCallable(maybeFunc))
michael@0 922 ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc));
michael@0 923 var func = maybeFunc;
michael@0 924
michael@0 925 // Skip check for compatible iteration spaces; any normal JS array
michael@0 926 // is trivially compatible with any iteration space of depth 1.
michael@0 927
michael@0 928 var outLength = outputType.variable ? inArray.length : outputType.length;
michael@0 929 var outGrainType = outputType.elementType;
michael@0 930
michael@0 931 // Create a zeroed instance with no data
michael@0 932 var result = outputType.variable ? new outputType(inArray.length) : new outputType();
michael@0 933
michael@0 934 var outUnitSize = DESCR_SIZE(outGrainType);
michael@0 935 var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
michael@0 936 var outOffset = 0;
michael@0 937
michael@0 938 // Core of map computation starts here (comparable to
michael@0 939 // DoMapTypedSeqDepth1 and DoMapTypedSeqDepthN below).
michael@0 940
michael@0 941 for (var i = 0; i < outLength; i++) {
michael@0 942 // In this loop, since depth is 1, "indices" denotes singleton array [i].
michael@0 943
michael@0 944 if (i in inArray) { // Check for holes (only needed for untyped case).
michael@0 945 // Extract element value.
michael@0 946 var element = inArray[i];
michael@0 947
michael@0 948 // Create out pointer to point at &array[...indices] for result array.
michael@0 949 var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset,
michael@0 950 outGrainTypeIsComplex);
michael@0 951
michael@0 952 // Invoke: var r = func(element, ...indices, collection, out);
michael@0 953 var r = func(element, i, inArray, out);
michael@0 954
michael@0 955 if (r !== undefined)
michael@0 956 TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r
michael@0 957 }
michael@0 958
michael@0 959 // Update offset and (implicitly) increment indices.
michael@0 960 outOffset += outUnitSize;
michael@0 961 }
michael@0 962
michael@0 963 return result;
michael@0 964 }
michael@0 965
michael@0 966 // Implements |map| and |from| methods for typed |inArray|.
michael@0 967 function MapTypedSeqImpl(inArray, depth, outputType, func) {
michael@0 968 assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "2. Map/From called on non-type-object outputType");
michael@0 969 assert(IsObject(inArray) && ObjectIsTypedObject(inArray), "Map/From called on non-object or untyped input array.");
michael@0 970 assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType");
michael@0 971
michael@0 972 if (depth <= 0 || TO_INT32(depth) !== depth)
michael@0 973 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 974
michael@0 975 // Compute iteration space for input and output and check for compatibility.
michael@0 976 var inputType = TypeOfTypedObject(inArray);
michael@0 977 var [inIterationSpace, inGrainType, _] =
michael@0 978 ComputeIterationSpace(inputType, depth, inArray.length);
michael@0 979 if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType))
michael@0 980 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 981 var [iterationSpace, outGrainType, totalLength] =
michael@0 982 ComputeIterationSpace(outputType, depth, outputType.variable ? inArray.length : outputType.length);
michael@0 983 for (var i = 0; i < depth; i++)
michael@0 984 if (inIterationSpace[i] !== iterationSpace[i])
michael@0 985 // TypeError("Incompatible iteration space in input and output type");
michael@0 986 ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
michael@0 987
michael@0 988 // Create a zeroed instance with no data
michael@0 989 var result = outputType.variable ? new outputType(inArray.length) : new outputType();
michael@0 990
michael@0 991 var inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType);
michael@0 992 var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
michael@0 993
michael@0 994 var inOffset = 0;
michael@0 995 var outOffset = 0;
michael@0 996
michael@0 997 var isDepth1Simple = depth == 1 && !(inGrainTypeIsComplex || outGrainTypeIsComplex);
michael@0 998
michael@0 999 var inUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(inGrainType);
michael@0 1000 var outUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(outGrainType);
michael@0 1001
michael@0 1002 // Bug 956914: add additional variants for depth = 2, 3, etc.
michael@0 1003
michael@0 1004 function DoMapTypedSeqDepth1() {
michael@0 1005 for (var i = 0; i < totalLength; i++) {
michael@0 1006 // In this loop, since depth is 1, "indices" denotes singleton array [i].
michael@0 1007
michael@0 1008 // Prepare input element/handle and out pointer
michael@0 1009 var element = TypedObjectGet(inGrainType, inArray, inOffset);
michael@0 1010 var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset,
michael@0 1011 outGrainTypeIsComplex);
michael@0 1012
michael@0 1013 // Invoke: var r = func(element, ...indices, collection, out);
michael@0 1014 var r = func(element, i, inArray, out);
michael@0 1015 if (r !== undefined)
michael@0 1016 TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r
michael@0 1017
michael@0 1018 // Update offsets and (implicitly) increment indices.
michael@0 1019 inOffset += inUnitSize;
michael@0 1020 outOffset += outUnitSize;
michael@0 1021 }
michael@0 1022
michael@0 1023 return result;
michael@0 1024 }
michael@0 1025
michael@0 1026 function DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result) {
michael@0 1027 for (var i = 0; i < totalLength; i++) {
michael@0 1028 var r = func(inArray[i], i, inArray, undefined);
michael@0 1029 if (r !== undefined)
michael@0 1030 result[i] = r;
michael@0 1031 }
michael@0 1032
michael@0 1033 return result;
michael@0 1034 }
michael@0 1035
michael@0 1036 function DoMapTypedSeqDepthN() {
michael@0 1037 var indices = new Uint32Array(depth);
michael@0 1038
michael@0 1039 for (var i = 0; i < totalLength; i++) {
michael@0 1040 // Prepare input element and out pointer
michael@0 1041 var element = TypedObjectGet(inGrainType, inArray, inOffset);
michael@0 1042 var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset,
michael@0 1043 outGrainTypeIsComplex);
michael@0 1044
michael@0 1045 // Invoke: var r = func(element, ...indices, collection, out);
michael@0 1046 var args = [element];
michael@0 1047 callFunction(std_Function_apply, std_Array_push, args, indices);
michael@0 1048 callFunction(std_Array_push, args, inArray, out);
michael@0 1049 var r = callFunction(std_Function_apply, func, void 0, args);
michael@0 1050 if (r !== undefined)
michael@0 1051 TypedObjectSet(outGrainType, result, outOffset, r); // result[...indices] = r
michael@0 1052
michael@0 1053 // Update offsets and explicitly increment indices.
michael@0 1054 inOffset += inUnitSize;
michael@0 1055 outOffset += outUnitSize;
michael@0 1056 IncrementIterationSpace(indices, iterationSpace);
michael@0 1057 }
michael@0 1058
michael@0 1059 return result;
michael@0 1060 }
michael@0 1061
michael@0 1062 if (isDepth1Simple)
michael@0 1063 return DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result);
michael@0 1064
michael@0 1065 if (depth == 1)
michael@0 1066 return DoMapTypedSeqDepth1();
michael@0 1067
michael@0 1068 return DoMapTypedSeqDepthN();
michael@0 1069 }
michael@0 1070
michael@0 1071 // Implements |map| and |from| methods for typed |inArray|.
michael@0 1072 function MapTypedParImpl(inArray, depth, outputType, func) {
michael@0 1073 assert(IsObject(outputType) && ObjectIsTypeDescr(outputType),
michael@0 1074 "Map/From called on non-type-object outputType");
michael@0 1075 assert(IsObject(inArray) && ObjectIsTypedObject(inArray),
michael@0 1076 "Map/From called on non-object or untyped input array.");
michael@0 1077 assert(TypeDescrIsArrayType(outputType),
michael@0 1078 "Map/From called on non array-type outputType");
michael@0 1079 assert(typeof depth === "number",
michael@0 1080 "Map/From called with non-numeric depth");
michael@0 1081 assert(IsCallable(func),
michael@0 1082 "Map/From called on something not callable");
michael@0 1083
michael@0 1084 var inArrayType = TypeOfTypedObject(inArray);
michael@0 1085
michael@0 1086 if (ShouldForceSequential() ||
michael@0 1087 depth <= 0 ||
michael@0 1088 TO_INT32(depth) !== depth ||
michael@0 1089 !TypeDescrIsArrayType(inArrayType) ||
michael@0 1090 !TypeDescrIsUnsizedArrayType(outputType))
michael@0 1091 {
michael@0 1092 // defer error cases to seq implementation:
michael@0 1093 return MapTypedSeqImpl(inArray, depth, outputType, func);
michael@0 1094 }
michael@0 1095
michael@0 1096 switch (depth) {
michael@0 1097 case 1:
michael@0 1098 return MapTypedParImplDepth1(inArray, inArrayType, outputType, func);
michael@0 1099 default:
michael@0 1100 return MapTypedSeqImpl(inArray, depth, outputType, func);
michael@0 1101 }
michael@0 1102 }
michael@0 1103
michael@0 1104 function RedirectPointer(typedObj, offset, outputIsScalar) {
michael@0 1105 if (!outputIsScalar || !InParallelSection()) {
michael@0 1106 // ^ Subtle note: always check InParallelSection() last, because
michael@0 1107 // otherwise the other if conditions will not execute during
michael@0 1108 // sequential mode and we will not gather enough type
michael@0 1109 // information.
michael@0 1110
michael@0 1111 // Here `typedObj` represents the input or output pointer we will
michael@0 1112 // pass to the user function. Ideally, we will just update the
michael@0 1113 // offset of `typedObj` in place so that it moves along the
michael@0 1114 // input/output buffer without incurring any allocation costs. But
michael@0 1115 // we can only do this if these changes are invisible to the user.
michael@0 1116 //
michael@0 1117 // Under normal uses, such changes *should* be invisible -- the
michael@0 1118 // in/out pointers are only intended to be used during the
michael@0 1119 // callback and then discarded, but of course in the general case
michael@0 1120 // nothing prevents them from escaping.
michael@0 1121 //
michael@0 1122 // However, if we are in parallel mode, we know that the pointers
michael@0 1123 // will not escape into global state. They could still escape by
michael@0 1124 // being returned into the resulting array, but even that avenue
michael@0 1125 // is impossible if the result array cannot contain objects.
michael@0 1126 //
michael@0 1127 // Therefore, we reuse a pointer if we are both in parallel mode
michael@0 1128 // and we have a transparent output type. It'd be nice to loosen
michael@0 1129 // this condition later by using fancy ion optimizations that
michael@0 1130 // assume the value won't escape and copy it if it does. But those
michael@0 1131 // don't exist yet. Moreover, checking if the type is transparent
michael@0 1132 // is an overapproximation: users can manually declare opaque
michael@0 1133 // types that nonetheless only contain scalar data.
michael@0 1134
michael@0 1135 typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj),
michael@0 1136 typedObj, 0);
michael@0 1137 }
michael@0 1138
michael@0 1139 SetTypedObjectOffset(typedObj, offset);
michael@0 1140 return typedObj;
michael@0 1141 }
michael@0 1142 SetScriptHints(RedirectPointer, { inline: true });
michael@0 1143
michael@0 1144 function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
michael@0 1145 assert(IsObject(inArrayType) && ObjectIsTypeDescr(inArrayType) &&
michael@0 1146 TypeDescrIsArrayType(inArrayType),
michael@0 1147 "DoMapTypedParDepth1: invalid inArrayType");
michael@0 1148 assert(IsObject(outArrayType) && ObjectIsTypeDescr(outArrayType) &&
michael@0 1149 TypeDescrIsArrayType(outArrayType),
michael@0 1150 "DoMapTypedParDepth1: invalid outArrayType");
michael@0 1151 assert(IsObject(inArray) && ObjectIsTypedObject(inArray),
michael@0 1152 "DoMapTypedParDepth1: invalid inArray");
michael@0 1153
michael@0 1154 // Determine the grain types of the input and output.
michael@0 1155 const inGrainType = inArrayType.elementType;
michael@0 1156 const outGrainType = outArrayType.elementType;
michael@0 1157 const inGrainTypeSize = DESCR_SIZE(inGrainType);
michael@0 1158 const outGrainTypeSize = DESCR_SIZE(outGrainType);
michael@0 1159 const inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType);
michael@0 1160 const outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
michael@0 1161
michael@0 1162 const length = inArray.length;
michael@0 1163 const mode = undefined;
michael@0 1164
michael@0 1165 const outArray = new outArrayType(length);
michael@0 1166 if (length === 0)
michael@0 1167 return outArray;
michael@0 1168
michael@0 1169 const outGrainTypeIsTransparent = ObjectIsTransparentTypedObject(outArray);
michael@0 1170
michael@0 1171 // Construct the slices and initial pointers for each worker:
michael@0 1172 const slicesInfo = ComputeSlicesInfo(length);
michael@0 1173 const numWorkers = ForkJoinNumWorkers();
michael@0 1174 assert(numWorkers > 0, "Should have at least the main thread");
michael@0 1175 const pointers = [];
michael@0 1176 for (var i = 0; i < numWorkers; i++) {
michael@0 1177 const inTypedObject = TypedObjectGetDerivedIf(inGrainType, inArray, 0,
michael@0 1178 inGrainTypeIsComplex);
michael@0 1179 const outTypedObject = TypedObjectGetOpaqueIf(outGrainType, outArray, 0,
michael@0 1180 outGrainTypeIsComplex);
michael@0 1181 ARRAY_PUSH(pointers, ({ inTypedObject: inTypedObject,
michael@0 1182 outTypedObject: outTypedObject }));
michael@0 1183 }
michael@0 1184
michael@0 1185 // Below we will be adjusting offsets within the input to point at
michael@0 1186 // successive entries; we'll need to know the offset of inArray
michael@0 1187 // relative to its owner (which is often but not always 0).
michael@0 1188 const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray);
michael@0 1189
michael@0 1190 ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
michael@0 1191 return outArray;
michael@0 1192
michael@0 1193 function mapThread(workerId, sliceStart, sliceEnd) {
michael@0 1194 assert(TO_INT32(workerId) === workerId,
michael@0 1195 "workerId not int: " + workerId);
michael@0 1196 assert(workerId < pointers.length,
michael@0 1197 "workerId too large: " + workerId + " >= " + pointers.length);
michael@0 1198
michael@0 1199 var pointerIndex = InParallelSection() ? workerId : 0;
michael@0 1200 assert(!!pointers[pointerIndex],
michael@0 1201 "no pointer data for workerId: " + workerId);
michael@0 1202
michael@0 1203 const { inTypedObject, outTypedObject } = pointers[pointerIndex];
michael@0 1204 const sliceShift = slicesInfo.shift;
michael@0 1205 var sliceId;
michael@0 1206
michael@0 1207 while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
michael@0 1208 const indexStart = SLICE_START_INDEX(sliceShift, sliceId);
michael@0 1209 const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
michael@0 1210
michael@0 1211 var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart);
michael@0 1212 var outOffset = std_Math_imul(outGrainTypeSize, indexStart);
michael@0 1213
michael@0 1214 // Set the target region so that user is only permitted to write
michael@0 1215 // within the range set aside for this slice. This prevents user
michael@0 1216 // from writing to typed objects that escaped from prior slices
michael@0 1217 // during sequential iteration. Note that, for any particular
michael@0 1218 // iteration of the loop below, it's only valid to write to the
michael@0 1219 // memory range corresponding to the index `i` -- however, since
michael@0 1220 // the different iterations cannot communicate typed object
michael@0 1221 // pointers to one another during parallel exec, we need only
michael@0 1222 // fear escaped typed objects from *other* slices, so we can
michael@0 1223 // just set the target region once.
michael@0 1224 const endOffset = std_Math_imul(outGrainTypeSize, indexEnd);
michael@0 1225 SetForkJoinTargetRegion(outArray, outOffset, endOffset);
michael@0 1226
michael@0 1227 for (var i = indexStart; i < indexEnd; i++) {
michael@0 1228 var inVal = (inGrainTypeIsComplex
michael@0 1229 ? RedirectPointer(inTypedObject, inOffset,
michael@0 1230 outGrainTypeIsTransparent)
michael@0 1231 : inArray[i]);
michael@0 1232 var outVal = (outGrainTypeIsComplex
michael@0 1233 ? RedirectPointer(outTypedObject, outOffset,
michael@0 1234 outGrainTypeIsTransparent)
michael@0 1235 : undefined);
michael@0 1236 const r = func(inVal, i, inArray, outVal);
michael@0 1237 if (r !== undefined) {
michael@0 1238 if (outGrainTypeIsComplex)
michael@0 1239 SetTypedObjectValue(outGrainType, outArray, outOffset, r);
michael@0 1240 else
michael@0 1241 UnsafePutElements(outArray, i, r);
michael@0 1242 }
michael@0 1243 inOffset += inGrainTypeSize;
michael@0 1244 outOffset += outGrainTypeSize;
michael@0 1245
michael@0 1246 // A transparent result type cannot contain references, and
michael@0 1247 // hence there is no way for a pointer to a thread-local object
michael@0 1248 // to escape.
michael@0 1249 if (outGrainTypeIsTransparent)
michael@0 1250 ClearThreadLocalArenas();
michael@0 1251 }
michael@0 1252 }
michael@0 1253
michael@0 1254 return sliceId;
michael@0 1255 }
michael@0 1256
michael@0 1257 return undefined;
michael@0 1258 }
michael@0 1259 SetScriptHints(MapTypedParImplDepth1, { cloneAtCallsite: true });
michael@0 1260
michael@0 1261 function ReduceTypedSeqImpl(array, outputType, func, initial) {
michael@0 1262 assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array.");
michael@0 1263 assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Reduce called on non-type-object outputType");
michael@0 1264
michael@0 1265 var start, value;
michael@0 1266
michael@0 1267 if (initial === undefined && array.length < 1)
michael@0 1268 // RangeError("reduce requires array of length > 0")
michael@0 1269 ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
michael@0 1270
michael@0 1271 // FIXME bug 950106 Should reduce method supply an outptr handle?
michael@0 1272 // For now, reduce never supplies an outptr, regardless of outputType.
michael@0 1273
michael@0 1274 if (TypeDescrIsSimpleType(outputType)) {
michael@0 1275 if (initial === undefined) {
michael@0 1276 start = 1;
michael@0 1277 value = array[0];
michael@0 1278 } else {
michael@0 1279 start = 0;
michael@0 1280 value = outputType(initial);
michael@0 1281 }
michael@0 1282
michael@0 1283 for (var i = start; i < array.length; i++)
michael@0 1284 value = outputType(func(value, array[i]));
michael@0 1285
michael@0 1286 } else {
michael@0 1287 if (initial === undefined) {
michael@0 1288 start = 1;
michael@0 1289 value = new outputType(array[0]);
michael@0 1290 } else {
michael@0 1291 start = 0;
michael@0 1292 value = initial;
michael@0 1293 }
michael@0 1294
michael@0 1295 for (var i = start; i < array.length; i++)
michael@0 1296 value = func(value, array[i]);
michael@0 1297 }
michael@0 1298
michael@0 1299 return value;
michael@0 1300 }
michael@0 1301
michael@0 1302 function ScatterTypedSeqImpl(array, outputType, indices, defaultValue, conflictFunc) {
michael@0 1303 assert(IsObject(array) && ObjectIsTypedObject(array), "Scatter called on non-object or untyped input array.");
michael@0 1304 assert(IsObject(outputType) && ObjectIsTypeDescr(outputType), "Scatter called on non-type-object outputType");
michael@0 1305 assert(TypeDescrIsSizedArrayType(outputType), "Scatter called on non-sized array type");
michael@0 1306 assert(conflictFunc === undefined || typeof conflictFunc === "function", "Scatter called with invalid conflictFunc");
michael@0 1307
michael@0 1308 var result = new outputType();
michael@0 1309 var bitvec = new Uint8Array(result.length);
michael@0 1310 var elemType = outputType.elementType;
michael@0 1311 var i, j;
michael@0 1312 if (defaultValue !== elemType(undefined)) {
michael@0 1313 for (i = 0; i < result.length; i++) {
michael@0 1314 result[i] = defaultValue;
michael@0 1315 }
michael@0 1316 }
michael@0 1317
michael@0 1318 for (i = 0; i < indices.length; i++) {
michael@0 1319 j = indices[i];
michael@0 1320 if (!GET_BIT(bitvec, j)) {
michael@0 1321 result[j] = array[i];
michael@0 1322 SET_BIT(bitvec, j);
michael@0 1323 } else if (conflictFunc === undefined) {
michael@0 1324 ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT);
michael@0 1325 } else {
michael@0 1326 result[j] = conflictFunc(result[j], elemType(array[i]));
michael@0 1327 }
michael@0 1328 }
michael@0 1329 return result;
michael@0 1330 }
michael@0 1331
michael@0 1332 function FilterTypedSeqImpl(array, func) {
michael@0 1333 assert(IsObject(array) && ObjectIsTypedObject(array), "Filter called on non-object or untyped input array.");
michael@0 1334 assert(typeof func === "function", "Filter called with non-function predicate");
michael@0 1335
michael@0 1336 var arrayType = TypeOfTypedObject(array);
michael@0 1337 if (!TypeDescrIsArrayType(arrayType))
michael@0 1338 ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
michael@0 1339
michael@0 1340 var elementType = arrayType.elementType;
michael@0 1341 var flags = new Uint8Array(NUM_BYTES(array.length));
michael@0 1342 var count = 0;
michael@0 1343 var size = DESCR_SIZE(elementType);
michael@0 1344 var inOffset = 0;
michael@0 1345 for (var i = 0; i < array.length; i++) {
michael@0 1346 var v = TypedObjectGet(elementType, array, inOffset);
michael@0 1347 if (func(v, i, array)) {
michael@0 1348 SET_BIT(flags, i);
michael@0 1349 count++;
michael@0 1350 }
michael@0 1351 inOffset += size;
michael@0 1352 }
michael@0 1353
michael@0 1354 var resultType = (arrayType.variable ? arrayType : arrayType.unsized);
michael@0 1355 var result = new resultType(count);
michael@0 1356 for (var i = 0, j = 0; i < array.length; i++) {
michael@0 1357 if (GET_BIT(flags, i))
michael@0 1358 result[j++] = array[i];
michael@0 1359 }
michael@0 1360 return result;
michael@0 1361 }

mercurial