js/src/builtin/TypedObject.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 #include "TypedObjectConstants.h"
     3 ///////////////////////////////////////////////////////////////////////////
     4 // Getters and setters for various slots.
     6 // Type object slots
     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)
    31 // Typed object slots
    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))
    44 #define HAS_PROPERTY(obj, prop) \
    45     callFunction(std_Object_hasOwnProperty, obj, prop)
    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.
    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");
    64   switch (DESCR_KIND(descr)) {
    65   case JS_TYPEREPR_SCALAR_KIND:
    66     return TypedObjectGetScalar(descr, typedObj, offset);
    68   case JS_TYPEREPR_REFERENCE_KIND:
    69     return TypedObjectGetReference(descr, typedObj, offset);
    71   case JS_TYPEREPR_X4_KIND:
    72     return TypedObjectGetX4(descr, typedObj, offset);
    74   case JS_TYPEREPR_SIZED_ARRAY_KIND:
    75   case JS_TYPEREPR_STRUCT_KIND:
    76     return TypedObjectGetDerived(descr, typedObj, offset);
    78   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
    79     assert(false, "Unhandled repr kind: " + DESCR_KIND(descr));
    80   }
    82   assert(false, "Unhandled kind: " + DESCR_KIND(descr));
    83   return undefined;
    84 }
    86 function TypedObjectGetDerived(descr, typedObj, offset) {
    87   assert(!TypeDescrIsSimpleType(descr),
    88          "getDerived() used with simple type");
    89   return NewDerivedTypedObject(descr, typedObj, offset);
    90 }
    92 function TypedObjectGetDerivedIf(descr, typedObj, offset, cond) {
    93   return (cond ? TypedObjectGetDerived(descr, typedObj, offset) : undefined);
    94 }
    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 }
   104 function TypedObjectGetOpaqueIf(descr, typedObj, offset, cond) {
   105   return (cond ? TypedObjectGetOpaque(descr, typedObj, offset) : undefined);
   106 }
   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);
   114   case JS_SCALARTYPEREPR_UINT8:
   115   case JS_SCALARTYPEREPR_UINT8_CLAMPED:
   116     return Load_uint8(typedObj, offset);
   118   case JS_SCALARTYPEREPR_INT16:
   119     return Load_int16(typedObj, offset);
   121   case JS_SCALARTYPEREPR_UINT16:
   122     return Load_uint16(typedObj, offset);
   124   case JS_SCALARTYPEREPR_INT32:
   125     return Load_int32(typedObj, offset);
   127   case JS_SCALARTYPEREPR_UINT32:
   128     return Load_uint32(typedObj, offset);
   130   case JS_SCALARTYPEREPR_FLOAT32:
   131     return Load_float32(typedObj, offset);
   133   case JS_SCALARTYPEREPR_FLOAT64:
   134     return Load_float64(typedObj, offset);
   135   }
   137   assert(false, "Unhandled scalar type: " + type);
   138   return undefined;
   139 }
   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);
   147   case JS_REFERENCETYPEREPR_OBJECT:
   148     return Load_Object(typedObj, offset);
   150   case JS_REFERENCETYPEREPR_STRING:
   151     return Load_string(typedObj, offset);
   152   }
   154   assert(false, "Unhandled scalar type: " + type);
   155   return undefined;
   156 }
   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);
   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   }
   176   assert(false, "Unhandled x4 type: " + type);
   177   return undefined;
   178 }
   180 ///////////////////////////////////////////////////////////////////////////
   181 // Setting values
   182 //
   183 // The methods in this section modify the data pointed at by `this`.
   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");
   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);
   199       var size = DESCR_SIZE(descr);
   200       Memcpy(typedObj, offset, fromValue, 0, size);
   201       return;
   202     }
   203   }
   205   switch (DESCR_KIND(descr)) {
   206   case JS_TYPEREPR_SCALAR_KIND:
   207     TypedObjectSetScalar(descr, typedObj, offset, fromValue);
   208     return;
   210   case JS_TYPEREPR_REFERENCE_KIND:
   211     TypedObjectSetReference(descr, typedObj, offset, fromValue);
   212     return;
   214   case JS_TYPEREPR_X4_KIND:
   215     TypedObjectSetX4(descr, typedObj, offset, fromValue);
   216     return;
   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;
   224   case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
   225     var length = typedObj.length;
   226     if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue))
   227       return;
   228     break;
   230   case JS_TYPEREPR_STRUCT_KIND:
   231     if (!IsObject(fromValue))
   232       break;
   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   }
   248   ThrowError(JSMSG_CANT_CONVERT_TO,
   249              typeof(fromValue),
   250              DESCR_STRING_REPR(descr));
   251 }
   253 function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) {
   254   if (!IsObject(fromValue))
   255     return false;
   257   // Check that "array-like" fromValue has an appropriate length.
   258   if (fromValue.length !== length)
   259     return false;
   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 }
   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);
   284   case JS_SCALARTYPEREPR_UINT8:
   285     return Store_uint8(typedObj, offset,
   286                       TO_UINT32(fromValue) & 0xFF);
   288   case JS_SCALARTYPEREPR_UINT8_CLAMPED:
   289     var v = ClampToUint8(+fromValue);
   290     return Store_int8(typedObj, offset, v);
   292   case JS_SCALARTYPEREPR_INT16:
   293     return Store_int16(typedObj, offset,
   294                       TO_INT32(fromValue) & 0xFFFF);
   296   case JS_SCALARTYPEREPR_UINT16:
   297     return Store_uint16(typedObj, offset,
   298                        TO_UINT32(fromValue) & 0xFFFF);
   300   case JS_SCALARTYPEREPR_INT32:
   301     return Store_int32(typedObj, offset,
   302                       TO_INT32(fromValue));
   304   case JS_SCALARTYPEREPR_UINT32:
   305     return Store_uint32(typedObj, offset,
   306                        TO_UINT32(fromValue));
   308   case JS_SCALARTYPEREPR_FLOAT32:
   309     return Store_float32(typedObj, offset, +fromValue);
   311   case JS_SCALARTYPEREPR_FLOAT64:
   312     return Store_float64(typedObj, offset, +fromValue);
   313   }
   315   assert(false, "Unhandled scalar type: " + type);
   316   return undefined;
   317 }
   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);
   325   case JS_REFERENCETYPEREPR_OBJECT:
   326     var value = (fromValue === null ? fromValue : ToObject(fromValue));
   327     return Store_Object(typedObj, offset, value);
   329   case JS_REFERENCETYPEREPR_STRING:
   330     return Store_string(typedObj, offset, ToString(fromValue));
   331   }
   333   assert(false, "Unhandled scalar type: " + type);
   334   return undefined;
   335 }
   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 }
   348 ///////////////////////////////////////////////////////////////////////////
   349 // C++ Wrappers
   350 //
   351 // These helpers are invoked by C++ code or used as method bodies.
   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");
   364   if (!TypedObjectIsAttached(destTypedObj))
   365     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
   367   TypedObjectSet(destDescr, destTypedObj, destOffset, fromValue);
   368 }
   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");
   379   if (!TypedObjectIsAttached(sourceTypedObj))
   380     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
   382   return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset);
   383 }
   385 function FillTypedArrayWithValue(destArray, fromValue) {
   386   assert(IsObject(handle) && ObjectIsTypedObject(destArray),
   387          "FillTypedArrayWithValue: not typed handle");
   389   var descr = TYPEDOBJ_TYPE_DESCR(destArray);
   390   var length = DESCR_SIZED_ARRAY_LENGTH(descr);
   391   if (length === 0)
   392     return;
   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);
   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 }
   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 }
   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);
   438   if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType))
   439     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   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;
   453   case JS_TYPEREPR_SIZED_ARRAY_KIND:
   454     break;
   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   }
   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   }
   473   // Check that the total number of elements does not change.
   474   if (oldElementCount !== newElementCount) {
   475     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   476   }
   478   // Check that the element types are equivalent.
   479   if (!DescrsEquiv(oldElementType, newElementType)) {
   480     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   481   }
   483   // Together, this should imply that the sizes are unchanged.
   484   assert(DESCR_SIZE(oldArrayType) == DESCR_SIZE(newArrayType),
   485          "Byte sizes should be equal");
   487   // Rewrap the data from `this` in a new type.
   488   return NewDerivedTypedObject(newArrayType, this, 0);
   489 }
   491 ///////////////////////////////////////////////////////////////////////////
   492 // X4
   494 function X4ProtoString(type) {
   495   switch (type) {
   496   case JS_X4TYPEREPR_INT32:
   497     return "int32x4";
   498   case JS_X4TYPEREPR_FLOAT32:
   499     return "float32x4";
   500   }
   502   assert(false, "Unhandled type constant");
   503   return undefined;
   504 }
   506 function X4ToSource() {
   507   if (!IsObject(this) || !ObjectIsTypedObject(this))
   508     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
   510   var descr = TYPEDOBJ_TYPE_DESCR(this);
   512   if (DESCR_KIND(descr) != JS_TYPEREPR_X4_KIND)
   513     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "X4", "toSource", typeof this);
   515   var type = DESCR_TYPE(descr);
   516   return X4ProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
   517 }
   519 ///////////////////////////////////////////////////////////////////////////
   520 // Miscellaneous
   522 function DescrsEquiv(descr1, descr2) {
   523   assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr");
   524   assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr");
   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)
   532   return DESCR_STRING_REPR(descr1) === DESCR_STRING_REPR(descr2);
   533 }
   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");
   542   return DESCR_STRING_REPR(this);
   543 }
   545 // Warning: user exposed!
   546 function ArrayShorthand(...dims) {
   547   if (!IsObject(this) || !ObjectIsTypeDescr(this))
   548     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   550   var T = GetTypedObjectModule();
   552   if (dims.length == 0)
   553     return new T.ArrayType(this);
   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 }
   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;
   572     if (ObjectIsTransparentTypedObject(obj))
   573       return { buffer: TYPEDOBJ_OWNER(obj),
   574                byteLength: TYPEDOBJ_BYTELENGTH(obj),
   575                byteOffset: TYPEDOBJ_BYTEOFFSET(obj) };
   576   }
   578   ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   579   return null; // pacify silly "always returns a value" lint
   580 }
   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);
   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 }
   607 ///////////////////////////////////////////////////////////////////////////
   608 // TypedObject surface API methods (sequential implementations).
   610 // Warning: user exposed!
   611 function TypedObjectArrayTypeBuild(a,b,c) {
   612   // Arguments (this sized) : [depth], func
   613   // Arguments (this unsized) : length, [depth], func
   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 }
   643 // Warning: user exposed!
   644 function TypedObjectArrayTypeFrom(a, b, c) {
   645   // Arguments: arrayLike, [depth], func
   647   if (!IsObject(this) || !ObjectIsTypeDescr(this))
   648     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   650   var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a);
   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.
   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 }
   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);
   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 }
   694 // Warning: user exposed!
   695 function TypedArrayMapPar(a, b) {
   696   // Arguments: [depth], func
   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);
   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 }
   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);
   722   if (a !== undefined && typeof a !== "function")
   723     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   725   var outputType = thisType.elementType;
   726   return ReduceTypedSeqImpl(this, outputType, a, b);
   727 }
   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);
   738   if (!IsObject(a) || !ObjectIsTypeDescr(a) || !TypeDescrIsSizedArrayType(a))
   739     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   741   if (d !== undefined && typeof d !== "function")
   742     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   744   return ScatterTypedSeqImpl(this, a, b, c, d);
   745 }
   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);
   756   if (typeof func !== "function")
   757     return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   759   return FilterTypedSeqImpl(this, func);
   760 }
   762 // placeholders
   764 // Warning: user exposed!
   765 function TypedObjectArrayTypeBuildPar(a,b,c) {
   766   return callFunction(TypedObjectArrayTypeBuild, this, a, b, c);
   767 }
   769 // Warning: user exposed!
   770 function TypedObjectArrayTypeFromPar(a,b,c) {
   771   // Arguments: arrayLike, [depth], func
   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);
   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 }
   788 // Warning: user exposed!
   789 function TypedArrayReducePar(a, b) {
   790   return callFunction(TypedArrayReduce, this, a, b);
   791 }
   793 // Warning: user exposed!
   794 function TypedArrayScatterPar(a, b, c, d) {
   795   return callFunction(TypedArrayScatter, this, a, b, c, d);
   796 }
   798 // Warning: user exposed!
   799 function TypedArrayFilterPar(func) {
   800   return callFunction(TypedArrayFilter, this, func);
   801 }
   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 }
   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");
   822   if (depth <= 0 || TO_INT32(depth) !== depth)
   823     // RangeError("bad depth")
   824     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   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);
   834   // Create a zeroed instance with no data
   835   var result = arrayType.variable ? new arrayType(len) : new arrayType();
   837   var indices = NewDenseArray(depth);
   838   for (var i = 0; i < depth; i++) {
   839     indices[i] = 0;
   840   }
   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);
   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;
   857     // Increment indices.
   858     IncrementIterationSpace(indices, iterationSpace);
   859     outOffset += size;
   860   }
   862   return result;
   863 }
   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;
   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 }
   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]
   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;
   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;
   910     n -= 1;
   911   }
   912 }
   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");
   921   if (!IsCallable(maybeFunc))
   922     ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc));
   923   var func = maybeFunc;
   925   // Skip check for compatible iteration spaces; any normal JS array
   926   // is trivially compatible with any iteration space of depth 1.
   928   var outLength = outputType.variable ? inArray.length : outputType.length;
   929   var outGrainType = outputType.elementType;
   931   // Create a zeroed instance with no data
   932   var result = outputType.variable ? new outputType(inArray.length) : new outputType();
   934   var outUnitSize = DESCR_SIZE(outGrainType);
   935   var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
   936   var outOffset = 0;
   938   // Core of map computation starts here (comparable to
   939   // DoMapTypedSeqDepth1 and DoMapTypedSeqDepthN below).
   941   for (var i = 0; i < outLength; i++) {
   942     // In this loop, since depth is 1, "indices" denotes singleton array [i].
   944     if (i in inArray) { // Check for holes (only needed for untyped case).
   945       // Extract element value.
   946       var element = inArray[i];
   948       // Create out pointer to point at &array[...indices] for result array.
   949       var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset,
   950                                        outGrainTypeIsComplex);
   952       // Invoke: var r = func(element, ...indices, collection, out);
   953       var r = func(element, i, inArray, out);
   955       if (r !== undefined)
   956         TypedObjectSet(outGrainType, result, outOffset, r); // result[i] = r
   957     }
   959     // Update offset and (implicitly) increment indices.
   960     outOffset += outUnitSize;
   961   }
   963   return result;
   964 }
   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");
   972   if (depth <= 0 || TO_INT32(depth) !== depth)
   973     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
   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);
   988   // Create a zeroed instance with no data
   989   var result = outputType.variable ? new outputType(inArray.length) : new outputType();
   991   var inGrainTypeIsComplex = !TypeDescrIsSimpleType(inGrainType);
   992   var outGrainTypeIsComplex = !TypeDescrIsSimpleType(outGrainType);
   994   var inOffset = 0;
   995   var outOffset = 0;
   997   var isDepth1Simple = depth == 1 && !(inGrainTypeIsComplex || outGrainTypeIsComplex);
   999   var inUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(inGrainType);
  1000   var outUnitSize = isDepth1Simple ? 0 : DESCR_SIZE(outGrainType);
  1002   // Bug 956914: add additional variants for depth = 2, 3, etc.
  1004   function DoMapTypedSeqDepth1() {
  1005     for (var i = 0; i < totalLength; i++) {
  1006       // In this loop, since depth is 1, "indices" denotes singleton array [i].
  1008       // Prepare input element/handle and out pointer
  1009       var element = TypedObjectGet(inGrainType, inArray, inOffset);
  1010       var out = TypedObjectGetOpaqueIf(outGrainType, result, outOffset,
  1011                                        outGrainTypeIsComplex);
  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
  1018       // Update offsets and (implicitly) increment indices.
  1019       inOffset += inUnitSize;
  1020       outOffset += outUnitSize;
  1023     return result;
  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;
  1033     return result;
  1036   function DoMapTypedSeqDepthN() {
  1037     var indices = new Uint32Array(depth);
  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);
  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
  1053       // Update offsets and explicitly increment indices.
  1054       inOffset += inUnitSize;
  1055       outOffset += outUnitSize;
  1056       IncrementIterationSpace(indices, iterationSpace);
  1059     return result;
  1062   if (isDepth1Simple)
  1063     return DoMapTypedSeqDepth1Simple(inArray, totalLength, func, result);
  1065   if (depth == 1)
  1066     return DoMapTypedSeqDepth1();
  1068   return DoMapTypedSeqDepthN();
  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");
  1084   var inArrayType = TypeOfTypedObject(inArray);
  1086   if (ShouldForceSequential() ||
  1087       depth <= 0 ||
  1088       TO_INT32(depth) !== depth ||
  1089       !TypeDescrIsArrayType(inArrayType) ||
  1090       !TypeDescrIsUnsizedArrayType(outputType))
  1092     // defer error cases to seq implementation:
  1093     return MapTypedSeqImpl(inArray, depth, outputType, func);
  1096   switch (depth) {
  1097   case 1:
  1098     return MapTypedParImplDepth1(inArray, inArrayType, outputType, func);
  1099   default:
  1100     return MapTypedSeqImpl(inArray, depth, outputType, func);
  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.
  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.
  1135     typedObj = NewDerivedTypedObject(TYPEDOBJ_TYPE_DESCR(typedObj),
  1136                                      typedObj, 0);
  1139   SetTypedObjectOffset(typedObj, offset);
  1140   return typedObj;
  1142 SetScriptHints(RedirectPointer,         { inline: true });
  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");
  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);
  1162   const length = inArray.length;
  1163   const mode = undefined;
  1165   const outArray = new outArrayType(length);
  1166   if (length === 0)
  1167     return outArray;
  1169   const outGrainTypeIsTransparent = ObjectIsTransparentTypedObject(outArray);
  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 }));
  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);
  1190   ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
  1191   return outArray;
  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);
  1199     var pointerIndex = InParallelSection() ? workerId : 0;
  1200     assert(!!pointers[pointerIndex],
  1201           "no pointer data for workerId: " + workerId);
  1203     const { inTypedObject, outTypedObject } = pointers[pointerIndex];
  1204     const sliceShift = slicesInfo.shift;
  1205     var sliceId;
  1207     while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
  1208       const indexStart = SLICE_START_INDEX(sliceShift, sliceId);
  1209       const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
  1211       var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart);
  1212       var outOffset = std_Math_imul(outGrainTypeSize, indexStart);
  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);
  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);
  1243         inOffset += inGrainTypeSize;
  1244         outOffset += outGrainTypeSize;
  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();
  1254     return sliceId;
  1257   return undefined;
  1259 SetScriptHints(MapTypedParImplDepth1,         { cloneAtCallsite: true });
  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");
  1265   var start, value;
  1267   if (initial === undefined && array.length < 1)
  1268     // RangeError("reduce requires array of length > 0")
  1269     ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
  1271   // FIXME bug 950106 Should reduce method supply an outptr handle?
  1272   // For now, reduce never supplies an outptr, regardless of outputType.
  1274   if (TypeDescrIsSimpleType(outputType)) {
  1275     if (initial === undefined) {
  1276       start = 1;
  1277       value = array[0];
  1278     } else {
  1279       start = 0;
  1280       value = outputType(initial);
  1283     for (var i = start; i < array.length; i++)
  1284       value = outputType(func(value, array[i]));
  1286   } else {
  1287     if (initial === undefined) {
  1288       start = 1;
  1289       value = new outputType(array[0]);
  1290     } else {
  1291       start = 0;
  1292       value = initial;
  1295     for (var i = start; i < array.length; i++)
  1296       value = func(value, array[i]);
  1299   return value;
  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");
  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;
  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]));
  1329   return result;
  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");
  1336   var arrayType = TypeOfTypedObject(array);
  1337   if (!TypeDescrIsArrayType(arrayType))
  1338     ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
  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++;
  1351     inOffset += size;
  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];
  1360   return result;

mercurial