|
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 } |