js/src/builtin/TypedObject.js

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

mercurial