1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "builtin/TypedObject-inl.h"
8 
9 #include "mozilla/Casting.h"
10 #include "mozilla/CheckedInt.h"
11 
12 #include "jsutil.h"
13 
14 #include "builtin/SIMD.h"
15 #include "gc/Marking.h"
16 #include "js/Vector.h"
17 #include "util/StringBuffer.h"
18 #include "vm/GlobalObject.h"
19 #include "vm/JSCompartment.h"
20 #include "vm/JSFunction.h"
21 #include "vm/StringType.h"
22 #include "vm/TypedArrayObject.h"
23 
24 #include "gc/Nursery-inl.h"
25 #include "gc/StoreBuffer-inl.h"
26 #include "vm/JSAtom-inl.h"
27 #include "vm/JSObject-inl.h"
28 #include "vm/NativeObject-inl.h"
29 #include "vm/Shape-inl.h"
30 
31 using mozilla::AssertedCast;
32 using mozilla::CheckedInt32;
33 using mozilla::IsPowerOfTwo;
34 using mozilla::PodCopy;
35 using mozilla::PointerRangeSize;
36 
37 using namespace js;
38 
39 const Class js::TypedObjectModuleObject::class_ = {
40     "TypedObject", JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
41                        JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject)};
42 
43 static const JSFunctionSpec TypedObjectMethods[] = {
44     JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0),
45     JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0), JS_FS_END};
46 
ReportCannotConvertTo(JSContext * cx,HandleValue fromValue,const char * toType)47 static void ReportCannotConvertTo(JSContext* cx, HandleValue fromValue,
48                                   const char* toType) {
49   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
50                             InformalValueTypeName(fromValue), toType);
51 }
52 
53 template <class T>
ToObjectIf(HandleValue value)54 static inline T* ToObjectIf(HandleValue value) {
55   if (!value.isObject()) return nullptr;
56 
57   if (!value.toObject().is<T>()) return nullptr;
58 
59   return &value.toObject().as<T>();
60 }
61 
RoundUpToAlignment(CheckedInt32 address,uint32_t align)62 static inline CheckedInt32 RoundUpToAlignment(CheckedInt32 address,
63                                               uint32_t align) {
64   MOZ_ASSERT(IsPowerOfTwo(align));
65 
66   // Note: Be careful to order operators such that we first make the
67   // value smaller and then larger, so that we don't get false
68   // overflow errors due to (e.g.) adding `align` and then
69   // subtracting `1` afterwards when merely adding `align-1` would
70   // not have overflowed. Note that due to the nature of two's
71   // complement representation, if `address` is already aligned,
72   // then adding `align-1` cannot itself cause an overflow.
73 
74   return ((address + (align - 1)) / align) * align;
75 }
76 
77 /*
78  * Overwrites the contents of `typedObj` at offset `offset` with `val`
79  * converted to the type `typeObj`. This is done by delegating to
80  * self-hosted code. This is used for assignments and initializations.
81  *
82  * For example, consider the final assignment in this snippet:
83  *
84  *    var Point = new StructType({x: float32, y: float32});
85  *    var Line = new StructType({from: Point, to: Point});
86  *    var line = new Line();
87  *    line.to = {x: 22, y: 44};
88  *
89  * This would result in a call to `ConvertAndCopyTo`
90  * where:
91  * - typeObj = Point
92  * - typedObj = line
93  * - offset = sizeof(Point) == 8
94  * - val = {x: 22, y: 44}
95  * This would result in loading the value of `x`, converting
96  * it to a float32, and hen storing it at the appropriate offset,
97  * and then doing the same for `y`.
98  *
99  * Note that the type of `typeObj` may not be the
100  * type of `typedObj` but rather some subcomponent of `typedObj`.
101  */
ConvertAndCopyTo(JSContext * cx,HandleTypeDescr typeObj,HandleTypedObject typedObj,int32_t offset,HandleAtom name,HandleValue val)102 static bool ConvertAndCopyTo(JSContext* cx, HandleTypeDescr typeObj,
103                              HandleTypedObject typedObj, int32_t offset,
104                              HandleAtom name, HandleValue val) {
105   RootedFunction func(cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
106   if (!func) return false;
107 
108   FixedInvokeArgs<5> args(cx);
109 
110   args[0].setObject(*typeObj);
111   args[1].setObject(*typedObj);
112   args[2].setInt32(offset);
113   if (name)
114     args[3].setString(name);
115   else
116     args[3].setNull();
117   args[4].set(val);
118 
119   RootedValue fval(cx, ObjectValue(*func));
120   RootedValue dummy(cx);  // ignored by ConvertAndCopyTo
121   return js::Call(cx, fval, dummy, args, &dummy);
122 }
123 
ConvertAndCopyTo(JSContext * cx,HandleTypedObject typedObj,HandleValue val)124 static bool ConvertAndCopyTo(JSContext* cx, HandleTypedObject typedObj,
125                              HandleValue val) {
126   Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
127   return ConvertAndCopyTo(cx, type, typedObj, 0, nullptr, val);
128 }
129 
130 /*
131  * Overwrites the contents of `typedObj` at offset `offset` with `val`
132  * converted to the type `typeObj`
133  */
Reify(JSContext * cx,HandleTypeDescr type,HandleTypedObject typedObj,size_t offset,MutableHandleValue to)134 static bool Reify(JSContext* cx, HandleTypeDescr type,
135                   HandleTypedObject typedObj, size_t offset,
136                   MutableHandleValue to) {
137   RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
138   if (!func) return false;
139 
140   FixedInvokeArgs<3> args(cx);
141 
142   args[0].setObject(*type);
143   args[1].setObject(*typedObj);
144   args[2].setInt32(offset);
145 
146   RootedValue fval(cx, ObjectValue(*func));
147   return js::Call(cx, fval, UndefinedHandleValue, args, to);
148 }
149 
150 // Extracts the `prototype` property from `obj`, throwing if it is
151 // missing or not an object.
GetPrototype(JSContext * cx,HandleObject obj)152 static JSObject* GetPrototype(JSContext* cx, HandleObject obj) {
153   RootedValue prototypeVal(cx);
154   if (!GetProperty(cx, obj, obj, cx->names().prototype, &prototypeVal)) {
155     return nullptr;
156   }
157   if (!prototypeVal.isObject()) {
158     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
159                               JSMSG_INVALID_PROTOTYPE);
160     return nullptr;
161   }
162   return &prototypeVal.toObject();
163 }
164 
165 /***************************************************************************
166  * Typed Prototypes
167  *
168  * Every type descriptor has an associated prototype. Instances of
169  * that type descriptor use this as their prototype. Per the spec,
170  * typed object prototypes cannot be mutated.
171  */
172 
173 const Class js::TypedProto::class_ = {
174     "TypedProto", JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS)};
175 
176 /***************************************************************************
177  * Scalar type objects
178  *
179  * Scalar type objects like `uint8`, `uint16`, are all instances of
180  * the ScalarTypeDescr class. Like all type objects, they have a reserved
181  * slot pointing to a TypeRepresentation object, which is used to
182  * distinguish which scalar type object this actually is.
183  */
184 
185 static const ClassOps ScalarTypeDescrClassOps = {nullptr, /* addProperty */
186                                                  nullptr, /* delProperty */
187                                                  nullptr, /* enumerate */
188                                                  nullptr, /* newEnumerate */
189                                                  nullptr, /* resolve */
190                                                  nullptr, /* mayResolve */
191                                                  TypeDescr::finalize,
192                                                  ScalarTypeDescr::call};
193 
194 const Class js::ScalarTypeDescr::class_ = {
195     "Scalar",
196     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
197     &ScalarTypeDescrClassOps};
198 
199 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
200     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
201     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
202     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), JS_FS_END};
203 
size(Type t)204 uint32_t ScalarTypeDescr::size(Type t) {
205   return AssertedCast<uint32_t>(Scalar::byteSize(t));
206 }
207 
alignment(Type t)208 uint32_t ScalarTypeDescr::alignment(Type t) {
209   return AssertedCast<uint32_t>(Scalar::byteSize(t));
210 }
211 
typeName(Type type)212 /*static*/ const char* ScalarTypeDescr::typeName(Type type) {
213   switch (type) {
214 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
215   case constant_:                                       \
216     return #name_;
217     JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
218 #undef NUMERIC_TYPE_TO_STRING
219     case Scalar::Int64:
220     case Scalar::Float32x4:
221     case Scalar::Int8x16:
222     case Scalar::Int16x8:
223     case Scalar::Int32x4:
224     case Scalar::MaxTypedArrayViewType:
225       break;
226   }
227   MOZ_CRASH("Invalid type");
228 }
229 
call(JSContext * cx,unsigned argc,Value * vp)230 bool ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
231   CallArgs args = CallArgsFromVp(argc, vp);
232   if (args.length() < 1) {
233     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
234                               JSMSG_MORE_ARGS_NEEDED,
235                               args.callee().getClass()->name, "0", "s");
236     return false;
237   }
238 
239   Rooted<ScalarTypeDescr*> descr(cx, &args.callee().as<ScalarTypeDescr>());
240   ScalarTypeDescr::Type type = descr->type();
241 
242   double number;
243   if (!ToNumber(cx, args[0], &number)) return false;
244 
245   if (type == Scalar::Uint8Clamped) number = ClampDoubleToUint8(number);
246 
247   switch (type) {
248 #define SCALARTYPE_CALL(constant_, type_, name_)    \
249   case constant_: {                                 \
250     type_ converted = ConvertScalar<type_>(number); \
251     args.rval().setNumber((double)converted);       \
252     return true;                                    \
253   }
254 
255     JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
256 #undef SCALARTYPE_CALL
257     case Scalar::Int64:
258     case Scalar::Float32x4:
259     case Scalar::Int8x16:
260     case Scalar::Int16x8:
261     case Scalar::Int32x4:
262     case Scalar::MaxTypedArrayViewType:
263       MOZ_CRASH();
264   }
265   return true;
266 }
267 
268 /***************************************************************************
269  * Reference type objects
270  *
271  * Reference type objects like `Any` or `Object` basically work the
272  * same way that the scalar type objects do. There is one class with
273  * many instances, and each instance has a reserved slot with a
274  * TypeRepresentation object, which is used to distinguish which
275  * reference type object this actually is.
276  */
277 
278 static const ClassOps ReferenceTypeDescrClassOps = {nullptr, /* addProperty */
279                                                     nullptr, /* delProperty */
280                                                     nullptr, /* enumerate */
281                                                     nullptr, /* newEnumerate */
282                                                     nullptr, /* resolve */
283                                                     nullptr, /* mayResolve */
284                                                     TypeDescr::finalize,
285                                                     ReferenceTypeDescr::call};
286 
287 const Class js::ReferenceTypeDescr::class_ = {
288     "Reference",
289     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
290     &ReferenceTypeDescrClassOps};
291 
292 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
293     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
294     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
295     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
296     JS_FS_END};
297 
298 static const uint32_t ReferenceSizes[] = {
299 #define REFERENCE_SIZE(_kind, _type, _name) sizeof(_type),
300     JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0
301 #undef REFERENCE_SIZE
302 };
303 
size(Type t)304 uint32_t ReferenceTypeDescr::size(Type t) { return ReferenceSizes[t]; }
305 
alignment(Type t)306 uint32_t ReferenceTypeDescr::alignment(Type t) { return ReferenceSizes[t]; }
307 
typeName(Type type)308 /*static*/ const char* ReferenceTypeDescr::typeName(Type type) {
309   switch (type) {
310 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
311   case constant_:                                       \
312     return #name_;
313     JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
314 #undef NUMERIC_TYPE_TO_STRING
315   }
316   MOZ_CRASH("Invalid type");
317 }
318 
call(JSContext * cx,unsigned argc,Value * vp)319 bool js::ReferenceTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
320   CallArgs args = CallArgsFromVp(argc, vp);
321 
322   MOZ_ASSERT(args.callee().is<ReferenceTypeDescr>());
323   Rooted<ReferenceTypeDescr*> descr(cx,
324                                     &args.callee().as<ReferenceTypeDescr>());
325 
326   if (args.length() < 1) {
327     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
328                               JSMSG_MORE_ARGS_NEEDED, descr->typeName(), "0",
329                               "s");
330     return false;
331   }
332 
333   switch (descr->type()) {
334     case ReferenceTypeDescr::TYPE_ANY:
335       args.rval().set(args[0]);
336       return true;
337 
338     case ReferenceTypeDescr::TYPE_OBJECT: {
339       RootedObject obj(cx, ToObject(cx, args[0]));
340       if (!obj) return false;
341       args.rval().setObject(*obj);
342       return true;
343     }
344 
345     case ReferenceTypeDescr::TYPE_STRING: {
346       RootedString obj(cx, ToString<CanGC>(cx, args[0]));
347       if (!obj) return false;
348       args.rval().setString(&*obj);
349       return true;
350     }
351   }
352 
353   MOZ_CRASH("Unhandled Reference type");
354 }
355 
356 /***************************************************************************
357  * SIMD type objects
358  *
359  * Note: these are partially defined in SIMD.cpp
360  */
361 
type() const362 SimdType SimdTypeDescr::type() const {
363   uint32_t t = uint32_t(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
364   MOZ_ASSERT(t < uint32_t(SimdType::Count));
365   return SimdType(t);
366 }
367 
size(SimdType t)368 uint32_t SimdTypeDescr::size(SimdType t) {
369   MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
370   switch (t) {
371     case SimdType::Int8x16:
372     case SimdType::Int16x8:
373     case SimdType::Int32x4:
374     case SimdType::Uint8x16:
375     case SimdType::Uint16x8:
376     case SimdType::Uint32x4:
377     case SimdType::Float32x4:
378     case SimdType::Float64x2:
379     case SimdType::Bool8x16:
380     case SimdType::Bool16x8:
381     case SimdType::Bool32x4:
382     case SimdType::Bool64x2:
383       return 16;
384     case SimdType::Count:
385       break;
386   }
387   MOZ_CRASH("unexpected SIMD type");
388 }
389 
alignment(SimdType t)390 uint32_t SimdTypeDescr::alignment(SimdType t) {
391   MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
392   return size(t);
393 }
394 
395 /***************************************************************************
396  * ArrayMetaTypeDescr class
397  */
398 
399 /*
400  * For code like:
401  *
402  *   var A = new TypedObject.ArrayType(uint8, 10);
403  *   var S = new TypedObject.StructType({...});
404  *
405  * As usual, the [[Prototype]] of A is
406  * TypedObject.ArrayType.prototype.  This permits adding methods to
407  * all ArrayType types, by setting
408  * TypedObject.ArrayType.prototype.methodName = function() { ... }.
409  * The same holds for S with respect to TypedObject.StructType.
410  *
411  * We may also want to add methods to *instances* of an ArrayType:
412  *
413  *   var a = new A();
414  *   var s = new S();
415  *
416  * As usual, the [[Prototype]] of a is A.prototype.  What's
417  * A.prototype?  It's an empty object, and you can set
418  * A.prototype.methodName = function() { ... } to add a method to all
419  * A instances.  (And the same with respect to s and S.)
420  *
421  * But what if you want to add a method to all ArrayType instances,
422  * not just all A instances?  (Or to all StructType instances.)  The
423  * [[Prototype]] of the A.prototype empty object is
424  * TypedObject.ArrayType.prototype.prototype (two .prototype levels!).
425  * So just set TypedObject.ArrayType.prototype.prototype.methodName =
426  * function() { ... } to add a method to all ArrayType instances.
427  * (And, again, same with respect to s and S.)
428  *
429  * This function creates the A.prototype/S.prototype object. It returns an
430  * empty object with the .prototype.prototype object as its [[Prototype]].
431  */
CreatePrototypeObjectForComplexTypeInstance(JSContext * cx,HandleObject ctorPrototype)432 static TypedProto* CreatePrototypeObjectForComplexTypeInstance(
433     JSContext* cx, HandleObject ctorPrototype) {
434   RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
435   if (!ctorPrototypePrototype) return nullptr;
436 
437   return NewObjectWithGivenProto<TypedProto>(cx, ctorPrototypePrototype,
438                                              SingletonObject);
439 }
440 
441 static const ClassOps ArrayTypeDescrClassOps = {nullptr, /* addProperty */
442                                                 nullptr, /* delProperty */
443                                                 nullptr, /* enumerate */
444                                                 nullptr, /* newEnumerate */
445                                                 nullptr, /* resolve */
446                                                 nullptr, /* mayResolve */
447                                                 TypeDescr::finalize,
448                                                 nullptr, /* call */
449                                                 nullptr, /* hasInstance */
450                                                 TypedObject::construct};
451 
452 const Class ArrayTypeDescr::class_ = {
453     "ArrayType",
454     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
455     &ArrayTypeDescrClassOps};
456 
457 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
458 
459 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
460     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
461     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
462     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
463     JS_SELF_HOSTED_FN("build", "TypedObjectArrayTypeBuild", 3, 0),
464     JS_SELF_HOSTED_FN("from", "TypedObjectArrayTypeFrom", 3, 0),
465     JS_FS_END};
466 
467 const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = {JS_PS_END};
468 
469 const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = {
470     {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
471     {"redimension", {nullptr, nullptr}, 1, 0, "TypedObjectArrayRedimension"},
472     JS_SELF_HOSTED_FN("map", "TypedObjectArrayMap", 2, 0),
473     JS_SELF_HOSTED_FN("reduce", "TypedObjectArrayReduce", 2, 0),
474     JS_SELF_HOSTED_FN("filter", "TypedObjectArrayFilter", 1, 0),
475     JS_FS_END};
476 
CreateUserSizeAndAlignmentProperties(JSContext * cx,HandleTypeDescr descr)477 bool js::CreateUserSizeAndAlignmentProperties(JSContext* cx,
478                                               HandleTypeDescr descr) {
479   // If data is transparent, also store the public slots.
480   if (descr->transparent()) {
481     // byteLength
482     RootedValue typeByteLength(
483         cx, Int32Value(AssertedCast<int32_t>(descr->size())));
484     if (!DefineDataProperty(cx, descr, cx->names().byteLength, typeByteLength,
485                             JSPROP_READONLY | JSPROP_PERMANENT)) {
486       return false;
487     }
488 
489     // byteAlignment
490     RootedValue typeByteAlignment(cx, Int32Value(descr->alignment()));
491     if (!DefineDataProperty(cx, descr, cx->names().byteAlignment,
492                             typeByteAlignment,
493                             JSPROP_READONLY | JSPROP_PERMANENT)) {
494       return false;
495     }
496   } else {
497     // byteLength
498     if (!DefineDataProperty(cx, descr, cx->names().byteLength,
499                             UndefinedHandleValue,
500                             JSPROP_READONLY | JSPROP_PERMANENT)) {
501       return false;
502     }
503 
504     // byteAlignment
505     if (!DefineDataProperty(cx, descr, cx->names().byteAlignment,
506                             UndefinedHandleValue,
507                             JSPROP_READONLY | JSPROP_PERMANENT)) {
508       return false;
509     }
510   }
511 
512   return true;
513 }
514 
515 static bool CreateTraceList(JSContext* cx, HandleTypeDescr descr);
516 
create(JSContext * cx,HandleObject arrayTypePrototype,HandleTypeDescr elementType,HandleAtom stringRepr,int32_t size,int32_t length)517 ArrayTypeDescr* ArrayMetaTypeDescr::create(JSContext* cx,
518                                            HandleObject arrayTypePrototype,
519                                            HandleTypeDescr elementType,
520                                            HandleAtom stringRepr, int32_t size,
521                                            int32_t length) {
522   MOZ_ASSERT(arrayTypePrototype);
523   Rooted<ArrayTypeDescr*> obj(cx);
524   obj = NewObjectWithGivenProto<ArrayTypeDescr>(cx, arrayTypePrototype,
525                                                 SingletonObject);
526   if (!obj) return nullptr;
527 
528   obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
529   obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
530   obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
531                         Int32Value(elementType->alignment()));
532   obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
533   obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE,
534                         BooleanValue(elementType->opaque()));
535   obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE,
536                         ObjectValue(*elementType));
537   obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
538 
539   RootedValue elementTypeVal(cx, ObjectValue(*elementType));
540   if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
541                           JSPROP_READONLY | JSPROP_PERMANENT)) {
542     return nullptr;
543   }
544 
545   RootedValue lengthValue(cx, NumberValue(length));
546   if (!DefineDataProperty(cx, obj, cx->names().length, lengthValue,
547                           JSPROP_READONLY | JSPROP_PERMANENT)) {
548     return nullptr;
549   }
550 
551   if (!CreateUserSizeAndAlignmentProperties(cx, obj)) return nullptr;
552 
553   // All arrays with the same element type have the same prototype. This
554   // prototype is created lazily and stored in the element type descriptor.
555   Rooted<TypedProto*> prototypeObj(cx);
556   if (elementType->getReservedSlot(JS_DESCR_SLOT_ARRAYPROTO).isObject()) {
557     prototypeObj = &elementType->getReservedSlot(JS_DESCR_SLOT_ARRAYPROTO)
558                         .toObject()
559                         .as<TypedProto>();
560   } else {
561     prototypeObj =
562         CreatePrototypeObjectForComplexTypeInstance(cx, arrayTypePrototype);
563     if (!prototypeObj) return nullptr;
564     elementType->setReservedSlot(JS_DESCR_SLOT_ARRAYPROTO,
565                                  ObjectValue(*prototypeObj));
566   }
567 
568   obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
569 
570   if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) return nullptr;
571 
572   if (!CreateTraceList(cx, obj)) return nullptr;
573 
574   if (!cx->zone()->addTypeDescrObject(cx, obj)) {
575     ReportOutOfMemory(cx);
576     return nullptr;
577   }
578 
579   return obj;
580 }
581 
construct(JSContext * cx,unsigned argc,Value * vp)582 bool ArrayMetaTypeDescr::construct(JSContext* cx, unsigned argc, Value* vp) {
583   CallArgs args = CallArgsFromVp(argc, vp);
584 
585   if (!ThrowIfNotConstructing(cx, args, "ArrayType")) return false;
586 
587   RootedObject arrayTypeGlobal(cx, &args.callee());
588 
589   // Expect two arguments. The first is a type object, the second is a length.
590   if (args.length() < 2) {
591     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
592                               JSMSG_MORE_ARGS_NEEDED, "ArrayType", "1", "");
593     return false;
594   }
595 
596   if (!args[0].isObject() || !args[0].toObject().is<TypeDescr>()) {
597     ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
598     return false;
599   }
600 
601   if (!args[1].isInt32() || args[1].toInt32() < 0) {
602     ReportCannotConvertTo(cx, args[1], "ArrayType length specifier");
603     return false;
604   }
605 
606   Rooted<TypeDescr*> elementType(cx, &args[0].toObject().as<TypeDescr>());
607 
608   int32_t length = args[1].toInt32();
609 
610   // Compute the byte size.
611   CheckedInt32 size = CheckedInt32(elementType->size()) * length;
612   if (!size.isValid()) {
613     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
614                               JSMSG_TYPEDOBJECT_TOO_BIG);
615     return false;
616   }
617 
618   // Construct a canonical string `new ArrayType(<elementType>, N)`:
619   StringBuffer contents(cx);
620   if (!contents.append("new ArrayType(")) return false;
621   if (!contents.append(&elementType->stringRepr())) return false;
622   if (!contents.append(", ")) return false;
623   if (!NumberValueToStringBuffer(cx, NumberValue(length), contents))
624     return false;
625   if (!contents.append(")")) return false;
626   RootedAtom stringRepr(cx, contents.finishAtom());
627   if (!stringRepr) return false;
628 
629   // Extract ArrayType.prototype
630   RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
631   if (!arrayTypePrototype) return false;
632 
633   // Create the instance of ArrayType
634   Rooted<ArrayTypeDescr*> obj(cx);
635   obj = create(cx, arrayTypePrototype, elementType, stringRepr, size.value(),
636                length);
637   if (!obj) return false;
638 
639   args.rval().setObject(*obj);
640   return true;
641 }
642 
IsTypedObjectArray(JSObject & obj)643 bool js::IsTypedObjectArray(JSObject& obj) {
644   if (!obj.is<TypedObject>()) return false;
645   TypeDescr& d = obj.as<TypedObject>().typeDescr();
646   return d.is<ArrayTypeDescr>();
647 }
648 
649 /*********************************
650  * StructType class
651  */
652 
653 static const ClassOps StructTypeDescrClassOps = {nullptr, /* addProperty */
654                                                  nullptr, /* delProperty */
655                                                  nullptr, /* enumerate */
656                                                  nullptr, /* newEnumerate */
657                                                  nullptr, /* resolve */
658                                                  nullptr, /* mayResolve */
659                                                  TypeDescr::finalize,
660                                                  nullptr, /* call */
661                                                  nullptr, /* hasInstance */
662                                                  TypedObject::construct};
663 
664 const Class StructTypeDescr::class_ = {
665     "StructType",
666     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
667     &StructTypeDescrClassOps};
668 
669 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
670 
671 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
672     {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
673     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
674     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
675     JS_FS_END};
676 
677 const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {JS_PS_END};
678 
679 const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {JS_FS_END};
680 
create(JSContext * cx,HandleObject metaTypeDescr,HandleObject fields)681 JSObject* StructMetaTypeDescr::create(JSContext* cx, HandleObject metaTypeDescr,
682                                       HandleObject fields) {
683   // Obtain names of fields, which are the own properties of `fields`
684   AutoIdVector ids(cx);
685   if (!GetPropertyKeys(cx, fields, JSITER_OWNONLY | JSITER_SYMBOLS, &ids))
686     return nullptr;
687 
688   // Iterate through each field. Collect values for the various
689   // vectors below and also track total size and alignment. Be wary
690   // of overflow!
691   StringBuffer stringBuffer(cx);      // Canonical string repr
692   AutoValueVector fieldNames(cx);     // Name of each field.
693   AutoValueVector fieldTypeObjs(cx);  // Type descriptor of each field.
694   AutoValueVector fieldOffsets(cx);   // Offset of each field field.
695   RootedObject userFieldOffsets(cx);  // User-exposed {f:offset} object
696   RootedObject userFieldTypes(cx);    // User-exposed {f:descr} object.
697   CheckedInt32 sizeSoFar(0);          // Size of struct thus far.
698   uint32_t alignment = 1;             // Alignment of struct.
699   bool opaque = false;                // Opacity of struct.
700 
701   userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
702   if (!userFieldOffsets) return nullptr;
703 
704   userFieldTypes = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
705   if (!userFieldTypes) return nullptr;
706 
707   if (!stringBuffer.append("new StructType({")) return nullptr;
708 
709   RootedValue fieldTypeVal(cx);
710   RootedId id(cx);
711   Rooted<TypeDescr*> fieldType(cx);
712   for (unsigned int i = 0; i < ids.length(); i++) {
713     id = ids[i];
714 
715     // Check that all the property names are non-numeric strings.
716     uint32_t unused;
717     if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
718       RootedValue idValue(cx, IdToValue(id));
719       ReportCannotConvertTo(cx, idValue, "StructType field name");
720       return nullptr;
721     }
722 
723     // Load the value for the current field from the `fields` object.
724     // The value should be a type descriptor.
725     if (!GetProperty(cx, fields, fields, id, &fieldTypeVal)) return nullptr;
726     fieldType = ToObjectIf<TypeDescr>(fieldTypeVal);
727     if (!fieldType) {
728       ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
729       return nullptr;
730     }
731 
732     // Collect field name and type object
733     RootedValue fieldName(cx, IdToValue(id));
734     if (!fieldNames.append(fieldName)) return nullptr;
735     if (!fieldTypeObjs.append(ObjectValue(*fieldType))) return nullptr;
736 
737     // userFieldTypes[id] = typeObj
738     if (!DefineDataProperty(cx, userFieldTypes, id, fieldTypeObjs[i],
739                             JSPROP_READONLY | JSPROP_PERMANENT)) {
740       return nullptr;
741     }
742 
743     // Append "f:Type" to the string repr
744     if (i > 0 && !stringBuffer.append(", ")) return nullptr;
745     if (!stringBuffer.append(JSID_TO_ATOM(id))) return nullptr;
746     if (!stringBuffer.append(": ")) return nullptr;
747     if (!stringBuffer.append(&fieldType->stringRepr())) return nullptr;
748 
749     // Offset of this field is the current total size adjusted for
750     // the field's alignment.
751     CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldType->alignment());
752     if (!offset.isValid()) {
753       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
754                                 JSMSG_TYPEDOBJECT_TOO_BIG);
755       return nullptr;
756     }
757     MOZ_ASSERT(offset.value() >= 0);
758     if (!fieldOffsets.append(Int32Value(offset.value()))) return nullptr;
759 
760     // userFieldOffsets[id] = offset
761     RootedValue offsetValue(cx, Int32Value(offset.value()));
762     if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
763                             JSPROP_READONLY | JSPROP_PERMANENT)) {
764       return nullptr;
765     }
766 
767     // Add space for this field to the total struct size.
768     sizeSoFar = offset + fieldType->size();
769     if (!sizeSoFar.isValid()) {
770       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
771                                 JSMSG_TYPEDOBJECT_TOO_BIG);
772       return nullptr;
773     }
774 
775     // Struct is opaque if any field is opaque
776     if (fieldType->opaque()) opaque = true;
777 
778     // Alignment of the struct is the max of the alignment of its fields.
779     alignment = js::Max(alignment, fieldType->alignment());
780   }
781 
782   // Complete string representation.
783   if (!stringBuffer.append("})")) return nullptr;
784 
785   RootedAtom stringRepr(cx, stringBuffer.finishAtom());
786   if (!stringRepr) return nullptr;
787 
788   // Adjust the total size to be a multiple of the final alignment.
789   CheckedInt32 totalSize = RoundUpToAlignment(sizeSoFar, alignment);
790   if (!totalSize.isValid()) {
791     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
792                               JSMSG_TYPEDOBJECT_TOO_BIG);
793     return nullptr;
794   }
795 
796   // Now create the resulting type descriptor.
797   RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
798   if (!structTypePrototype) return nullptr;
799 
800   Rooted<StructTypeDescr*> descr(cx);
801   descr = NewObjectWithGivenProto<StructTypeDescr>(cx, structTypePrototype,
802                                                    SingletonObject);
803   if (!descr) return nullptr;
804 
805   descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
806   descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
807   descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
808                           Int32Value(AssertedCast<int32_t>(alignment)));
809   descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
810   descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
811 
812   // Construct for internal use an array with the name for each field.
813   {
814     RootedObject fieldNamesVec(cx);
815     fieldNamesVec = NewDenseCopiedArray(
816         cx, fieldNames.length(), fieldNames.begin(), nullptr, TenuredObject);
817     if (!fieldNamesVec) return nullptr;
818     descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
819                             ObjectValue(*fieldNamesVec));
820   }
821 
822   // Construct for internal use an array with the type object for each field.
823   RootedObject fieldTypeVec(cx);
824   fieldTypeVec =
825       NewDenseCopiedArray(cx, fieldTypeObjs.length(), fieldTypeObjs.begin(),
826                           nullptr, TenuredObject);
827   if (!fieldTypeVec) return nullptr;
828   descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
829                           ObjectValue(*fieldTypeVec));
830 
831   // Construct for internal use an array with the offset for each field.
832   {
833     RootedObject fieldOffsetsVec(cx);
834     fieldOffsetsVec =
835         NewDenseCopiedArray(cx, fieldOffsets.length(), fieldOffsets.begin(),
836                             nullptr, TenuredObject);
837     if (!fieldOffsetsVec) return nullptr;
838     descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
839                             ObjectValue(*fieldOffsetsVec));
840   }
841 
842   // Create data properties fieldOffsets and fieldTypes
843   if (!FreezeObject(cx, userFieldOffsets)) return nullptr;
844   if (!FreezeObject(cx, userFieldTypes)) return nullptr;
845   RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
846   if (!DefineDataProperty(cx, descr, cx->names().fieldOffsets,
847                           userFieldOffsetsValue,
848                           JSPROP_READONLY | JSPROP_PERMANENT)) {
849     return nullptr;
850   }
851   RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
852   if (!DefineDataProperty(cx, descr, cx->names().fieldTypes,
853                           userFieldTypesValue,
854                           JSPROP_READONLY | JSPROP_PERMANENT)) {
855     return nullptr;
856   }
857 
858   if (!CreateUserSizeAndAlignmentProperties(cx, descr)) return nullptr;
859 
860   Rooted<TypedProto*> prototypeObj(cx);
861   prototypeObj =
862       CreatePrototypeObjectForComplexTypeInstance(cx, structTypePrototype);
863   if (!prototypeObj) return nullptr;
864 
865   descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
866 
867   if (!LinkConstructorAndPrototype(cx, descr, prototypeObj)) return nullptr;
868 
869   if (!CreateTraceList(cx, descr)) return nullptr;
870 
871   if (!cx->zone()->addTypeDescrObject(cx, descr)) {
872     ReportOutOfMemory(cx);
873     return nullptr;
874   }
875 
876   return descr;
877 }
878 
construct(JSContext * cx,unsigned int argc,Value * vp)879 bool StructMetaTypeDescr::construct(JSContext* cx, unsigned int argc,
880                                     Value* vp) {
881   CallArgs args = CallArgsFromVp(argc, vp);
882 
883   if (!ThrowIfNotConstructing(cx, args, "StructType")) return false;
884 
885   if (args.length() >= 1 && args[0].isObject()) {
886     RootedObject metaTypeDescr(cx, &args.callee());
887     RootedObject fields(cx, &args[0].toObject());
888     RootedObject obj(cx, create(cx, metaTypeDescr, fields));
889     if (!obj) return false;
890     args.rval().setObject(*obj);
891     return true;
892   }
893 
894   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
895                             JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
896   return false;
897 }
898 
fieldCount() const899 size_t StructTypeDescr::fieldCount() const {
900   return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
901       .getDenseInitializedLength();
902 }
903 
fieldIndex(jsid id,size_t * out) const904 bool StructTypeDescr::fieldIndex(jsid id, size_t* out) const {
905   ArrayObject& fieldNames = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
906   size_t l = fieldNames.getDenseInitializedLength();
907   for (size_t i = 0; i < l; i++) {
908     JSAtom& a = fieldNames.getDenseElement(i).toString()->asAtom();
909     if (JSID_IS_ATOM(id, &a)) {
910       *out = i;
911       return true;
912     }
913   }
914   return false;
915 }
916 
fieldName(size_t index) const917 JSAtom& StructTypeDescr::fieldName(size_t index) const {
918   return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
919       .getDenseElement(index)
920       .toString()
921       ->asAtom();
922 }
923 
fieldOffset(size_t index) const924 size_t StructTypeDescr::fieldOffset(size_t index) const {
925   ArrayObject& fieldOffsets =
926       fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
927   MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
928   return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
929 }
930 
fieldDescr(size_t index) const931 TypeDescr& StructTypeDescr::fieldDescr(size_t index) const {
932   ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
933   MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
934   return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
935 }
936 
937 /******************************************************************************
938  * Creating the TypedObject "module"
939  *
940  * We create one global, `TypedObject`, which contains the following
941  * members:
942  *
943  * 1. uint8, uint16, etc
944  * 2. ArrayType
945  * 3. StructType
946  *
947  * Each of these is a function and hence their prototype is
948  * `Function.__proto__` (in terms of the JS Engine, they are not
949  * JSFunctions but rather instances of their own respective JSClasses
950  * which override the call and construct operations).
951  *
952  * Each type object also has its own `prototype` field. Therefore,
953  * using `StructType` as an example, the basic setup is:
954  *
955  *   StructType --__proto__--> Function.__proto__
956  *        |
957  *    prototype -- prototype --> { }
958  *        |
959  *        v
960  *       { } -----__proto__--> Function.__proto__
961  *
962  * When a new type object (e.g., an instance of StructType) is created,
963  * it will look as follows:
964  *
965  *   MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__
966  *        |                          |
967  *        |                     prototype
968  *        |                          |
969  *        |                          v
970  *    prototype -----__proto__----> { }
971  *        |
972  *        v
973  *       { } --__proto__-> Object.prototype
974  *
975  * Finally, when an instance of `MyStruct` is created, its
976  * structure is as follows:
977  *
978  *    object -__proto__->
979  *      MyStruct.prototype -__proto__->
980  *        StructType.prototype.prototype -__proto__->
981  *          Object.prototype
982  */
983 
984 // Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr`
985 template <typename T>
DefineSimpleTypeDescr(JSContext * cx,Handle<GlobalObject * > global,HandleObject module,typename T::Type type,HandlePropertyName className)986 static bool DefineSimpleTypeDescr(JSContext* cx, Handle<GlobalObject*> global,
987                                   HandleObject module, typename T::Type type,
988                                   HandlePropertyName className) {
989   RootedObject objProto(cx,
990                         GlobalObject::getOrCreateObjectPrototype(cx, global));
991   if (!objProto) return false;
992 
993   RootedObject funcProto(
994       cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
995   if (!funcProto) return false;
996 
997   Rooted<T*> descr(cx);
998   descr = NewObjectWithGivenProto<T>(cx, funcProto, SingletonObject);
999   if (!descr) return false;
1000 
1001   descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
1002   descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
1003   descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
1004                           Int32Value(T::alignment(type)));
1005   descr->initReservedSlot(JS_DESCR_SLOT_SIZE,
1006                           Int32Value(AssertedCast<int32_t>(T::size(type))));
1007   descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
1008   descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(type));
1009 
1010   if (!CreateUserSizeAndAlignmentProperties(cx, descr)) return false;
1011 
1012   if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods)) return false;
1013 
1014   // Create the typed prototype for the scalar type. This winds up
1015   // not being user accessible, but we still create one for consistency.
1016   Rooted<TypedProto*> proto(cx);
1017   proto = NewObjectWithGivenProto<TypedProto>(cx, objProto, TenuredObject);
1018   if (!proto) return false;
1019   descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
1020 
1021   RootedValue descrValue(cx, ObjectValue(*descr));
1022   if (!DefineDataProperty(cx, module, className, descrValue, 0)) return false;
1023 
1024   if (!CreateTraceList(cx, descr)) return false;
1025 
1026   if (!cx->zone()->addTypeDescrObject(cx, descr)) return false;
1027 
1028   return true;
1029 }
1030 
1031 ///////////////////////////////////////////////////////////////////////////
1032 
1033 template <typename T>
DefineMetaTypeDescr(JSContext * cx,const char * name,Handle<GlobalObject * > global,Handle<TypedObjectModuleObject * > module,TypedObjectModuleObject::Slot protoSlot)1034 static JSObject* DefineMetaTypeDescr(JSContext* cx, const char* name,
1035                                      Handle<GlobalObject*> global,
1036                                      Handle<TypedObjectModuleObject*> module,
1037                                      TypedObjectModuleObject::Slot protoSlot) {
1038   RootedAtom className(cx, Atomize(cx, name, strlen(name)));
1039   if (!className) return nullptr;
1040 
1041   RootedObject funcProto(
1042       cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
1043   if (!funcProto) return nullptr;
1044 
1045   // Create ctor.prototype, which inherits from Function.__proto__
1046 
1047   RootedObject proto(
1048       cx, NewObjectWithGivenProto<PlainObject>(cx, funcProto, SingletonObject));
1049   if (!proto) return nullptr;
1050 
1051   // Create ctor.prototype.prototype, which inherits from Object.__proto__
1052 
1053   RootedObject objProto(cx,
1054                         GlobalObject::getOrCreateObjectPrototype(cx, global));
1055   if (!objProto) return nullptr;
1056   RootedObject protoProto(cx);
1057   protoProto =
1058       NewObjectWithGivenProto<PlainObject>(cx, objProto, SingletonObject);
1059   if (!protoProto) return nullptr;
1060 
1061   RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
1062   if (!DefineDataProperty(cx, proto, cx->names().prototype, protoProtoValue,
1063                           JSPROP_READONLY | JSPROP_PERMANENT)) {
1064     return nullptr;
1065   }
1066 
1067   // Create ctor itself
1068 
1069   const int constructorLength = 2;
1070   RootedFunction ctor(cx);
1071   ctor = GlobalObject::createConstructor(cx, T::construct, className,
1072                                          constructorLength);
1073   if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) ||
1074       !DefinePropertiesAndFunctions(cx, proto, T::typeObjectProperties,
1075                                     T::typeObjectMethods) ||
1076       !DefinePropertiesAndFunctions(cx, protoProto, T::typedObjectProperties,
1077                                     T::typedObjectMethods)) {
1078     return nullptr;
1079   }
1080 
1081   module->initReservedSlot(protoSlot, ObjectValue(*proto));
1082 
1083   return ctor;
1084 }
1085 
1086 /*  The initialization strategy for TypedObjects is mildly unusual
1087  * compared to other classes. Because all of the types are members
1088  * of a single global, `TypedObject`, we basically make the
1089  * initializer for the `TypedObject` class populate the
1090  * `TypedObject` global (which is referred to as "module" herein).
1091  */
initTypedObjectModule(JSContext * cx,Handle<GlobalObject * > global)1092 /* static */ bool GlobalObject::initTypedObjectModule(
1093     JSContext* cx, Handle<GlobalObject*> global) {
1094   RootedObject objProto(cx,
1095                         GlobalObject::getOrCreateObjectPrototype(cx, global));
1096   if (!objProto) return false;
1097 
1098   Rooted<TypedObjectModuleObject*> module(cx);
1099   module = NewObjectWithGivenProto<TypedObjectModuleObject>(cx, objProto,
1100                                                             SingletonObject);
1101   if (!module) return false;
1102 
1103   if (!JS_DefineFunctions(cx, module, TypedObjectMethods)) return false;
1104 
1105     // uint8, uint16, any, etc
1106 
1107 #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_)                    \
1108   if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_, \
1109                                               cx->names().name_))            \
1110     return false;
1111   JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
1112 #undef BINARYDATA_SCALAR_DEFINE
1113 
1114 #define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_) \
1115   if (!DefineSimpleTypeDescr<ReferenceTypeDescr>(            \
1116           cx, global, module, constant_, cx->names().name_)) \
1117     return false;
1118   JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
1119 #undef BINARYDATA_REFERENCE_DEFINE
1120 
1121   // ArrayType.
1122 
1123   RootedObject arrayType(cx);
1124   arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
1125       cx, "ArrayType", global, module,
1126       TypedObjectModuleObject::ArrayTypePrototype);
1127   if (!arrayType) return false;
1128 
1129   RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
1130   if (!DefineDataProperty(cx, module, cx->names().ArrayType, arrayTypeValue,
1131                           JSPROP_READONLY | JSPROP_PERMANENT)) {
1132     return false;
1133   }
1134 
1135   // StructType.
1136 
1137   RootedObject structType(cx);
1138   structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
1139       cx, "StructType", global, module,
1140       TypedObjectModuleObject::StructTypePrototype);
1141   if (!structType) return false;
1142 
1143   RootedValue structTypeValue(cx, ObjectValue(*structType));
1144   if (!DefineDataProperty(cx, module, cx->names().StructType, structTypeValue,
1145                           JSPROP_READONLY | JSPROP_PERMANENT)) {
1146     return false;
1147   }
1148 
1149   // Everything is setup, install module on the global object:
1150   RootedValue moduleValue(cx, ObjectValue(*module));
1151   if (!DefineDataProperty(cx, global, cx->names().TypedObject, moduleValue,
1152                           JSPROP_RESOLVING)) {
1153     return false;
1154   }
1155 
1156   global->setConstructor(JSProto_TypedObject, moduleValue);
1157 
1158   return module;
1159 }
1160 
InitTypedObjectModuleObject(JSContext * cx,HandleObject obj)1161 JSObject* js::InitTypedObjectModuleObject(JSContext* cx, HandleObject obj) {
1162   Handle<GlobalObject*> global = obj.as<GlobalObject>();
1163   return GlobalObject::getOrCreateTypedObjectModule(cx, global);
1164 }
1165 
1166 /******************************************************************************
1167  * Typed objects
1168  */
1169 
offset() const1170 uint32_t TypedObject::offset() const {
1171   if (is<InlineTypedObject>()) return 0;
1172   return PointerRangeSize(typedMemBase(), typedMem());
1173 }
1174 
length() const1175 uint32_t TypedObject::length() const {
1176   return typeDescr().as<ArrayTypeDescr>().length();
1177 }
1178 
typedMem() const1179 uint8_t* TypedObject::typedMem() const {
1180   MOZ_ASSERT(isAttached());
1181 
1182   if (is<InlineTypedObject>()) return as<InlineTypedObject>().inlineTypedMem();
1183   return as<OutlineTypedObject>().outOfLineTypedMem();
1184 }
1185 
typedMemBase() const1186 uint8_t* TypedObject::typedMemBase() const {
1187   MOZ_ASSERT(isAttached());
1188   MOZ_ASSERT(is<OutlineTypedObject>());
1189 
1190   JSObject& owner = as<OutlineTypedObject>().owner();
1191   if (owner.is<ArrayBufferObject>())
1192     return owner.as<ArrayBufferObject>().dataPointer();
1193   return owner.as<InlineTypedObject>().inlineTypedMem();
1194 }
1195 
isAttached() const1196 bool TypedObject::isAttached() const {
1197   if (is<InlineTransparentTypedObject>()) {
1198     ObjectWeakMap* table = compartment()->lazyArrayBuffers;
1199     if (table) {
1200       JSObject* buffer = table->lookup(this);
1201       if (buffer) return !buffer->as<ArrayBufferObject>().isDetached();
1202     }
1203     return true;
1204   }
1205   if (is<InlineOpaqueTypedObject>()) return true;
1206   if (!as<OutlineTypedObject>().outOfLineTypedMem()) return false;
1207   JSObject& owner = as<OutlineTypedObject>().owner();
1208   if (owner.is<ArrayBufferObject>() &&
1209       owner.as<ArrayBufferObject>().isDetached())
1210     return false;
1211   return true;
1212 }
1213 
GetBuffer(JSContext * cx,unsigned argc,Value * vp)1214 /* static */ bool TypedObject::GetBuffer(JSContext* cx, unsigned argc,
1215                                          Value* vp) {
1216   CallArgs args = CallArgsFromVp(argc, vp);
1217   JSObject& obj = args[0].toObject();
1218   ArrayBufferObject* buffer;
1219   if (obj.is<OutlineTransparentTypedObject>())
1220     buffer = obj.as<OutlineTransparentTypedObject>().getOrCreateBuffer(cx);
1221   else
1222     buffer = obj.as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
1223   if (!buffer) return false;
1224   args.rval().setObject(*buffer);
1225   return true;
1226 }
1227 
GetByteOffset(JSContext * cx,unsigned argc,Value * vp)1228 /* static */ bool TypedObject::GetByteOffset(JSContext* cx, unsigned argc,
1229                                              Value* vp) {
1230   CallArgs args = CallArgsFromVp(argc, vp);
1231   args.rval().setInt32(
1232       AssertedCast<int32_t>(args[0].toObject().as<TypedObject>().offset()));
1233   return true;
1234 }
1235 
1236 /******************************************************************************
1237  * Outline typed objects
1238  */
1239 
createUnattached(JSContext * cx,HandleTypeDescr descr,int32_t length,gc::InitialHeap heap)1240 /*static*/ OutlineTypedObject* OutlineTypedObject::createUnattached(
1241     JSContext* cx, HandleTypeDescr descr, int32_t length,
1242     gc::InitialHeap heap) {
1243   if (descr->opaque())
1244     return createUnattachedWithClass(cx, &OutlineOpaqueTypedObject::class_,
1245                                      descr, length, heap);
1246   else
1247     return createUnattachedWithClass(cx, &OutlineTransparentTypedObject::class_,
1248                                      descr, length, heap);
1249 }
1250 
setOwnerAndData(JSObject * owner,uint8_t * data)1251 void OutlineTypedObject::setOwnerAndData(JSObject* owner, uint8_t* data) {
1252   // Make sure we don't associate with array buffers whose data is from an
1253   // inline typed object, see obj_trace.
1254   MOZ_ASSERT_IF(owner && owner->is<ArrayBufferObject>(),
1255                 !owner->as<ArrayBufferObject>().forInlineTypedObject());
1256 
1257   // Typed objects cannot move from one owner to another, so don't worry
1258   // about pre barriers during this initialization.
1259   owner_ = owner;
1260   data_ = data;
1261 
1262   // Trigger a post barrier when attaching an object outside the nursery to
1263   // one that is inside it.
1264   if (owner && !IsInsideNursery(this) && IsInsideNursery(owner))
1265     zone()->group()->storeBuffer().putWholeCell(this);
1266 }
1267 
createUnattachedWithClass(JSContext * cx,const Class * clasp,HandleTypeDescr descr,int32_t length,gc::InitialHeap heap)1268 /*static*/ OutlineTypedObject* OutlineTypedObject::createUnattachedWithClass(
1269     JSContext* cx, const Class* clasp, HandleTypeDescr descr, int32_t length,
1270     gc::InitialHeap heap) {
1271   MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
1272              clasp == &OutlineOpaqueTypedObject::class_);
1273 
1274   AutoSetNewObjectMetadata metadata(cx);
1275 
1276   RootedObjectGroup group(
1277       cx, ObjectGroup::defaultNewGroup(
1278               cx, clasp, TaggedProto(&descr->typedProto()), descr));
1279   if (!group) return nullptr;
1280 
1281   NewObjectKind newKind =
1282       (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
1283   OutlineTypedObject* obj = NewObjectWithGroup<OutlineTypedObject>(
1284       cx, group, gc::AllocKind::OBJECT0, newKind);
1285   if (!obj) return nullptr;
1286 
1287   obj->setOwnerAndData(nullptr, nullptr);
1288   return obj;
1289 }
1290 
attach(JSContext * cx,ArrayBufferObject & buffer,uint32_t offset)1291 void OutlineTypedObject::attach(JSContext* cx, ArrayBufferObject& buffer,
1292                                 uint32_t offset) {
1293   MOZ_ASSERT(!isAttached());
1294   MOZ_ASSERT(offset <= buffer.byteLength());
1295   MOZ_ASSERT(size() <= buffer.byteLength() - offset);
1296 
1297   // If the owner's data is from an inline typed object, associate this with
1298   // the inline typed object instead, to simplify tracing.
1299   if (buffer.forInlineTypedObject()) {
1300     InlineTypedObject& realOwner = buffer.firstView()->as<InlineTypedObject>();
1301     attach(cx, realOwner, offset);
1302     return;
1303   }
1304 
1305   buffer.setHasTypedObjectViews();
1306 
1307   {
1308     AutoEnterOOMUnsafeRegion oomUnsafe;
1309     if (!buffer.addView(cx, this)) oomUnsafe.crash("TypedObject::attach");
1310   }
1311 
1312   setOwnerAndData(&buffer, buffer.dataPointer() + offset);
1313 }
1314 
attach(JSContext * cx,TypedObject & typedObj,uint32_t offset)1315 void OutlineTypedObject::attach(JSContext* cx, TypedObject& typedObj,
1316                                 uint32_t offset) {
1317   MOZ_ASSERT(!isAttached());
1318   MOZ_ASSERT(typedObj.isAttached());
1319 
1320   JSObject* owner = &typedObj;
1321   if (typedObj.is<OutlineTypedObject>()) {
1322     owner = &typedObj.as<OutlineTypedObject>().owner();
1323     MOZ_ASSERT(typedObj.offset() <= UINT32_MAX - offset);
1324     offset += typedObj.offset();
1325   }
1326 
1327   if (owner->is<ArrayBufferObject>()) {
1328     attach(cx, owner->as<ArrayBufferObject>(), offset);
1329   } else {
1330     MOZ_ASSERT(owner->is<InlineTypedObject>());
1331     JS::AutoCheckCannotGC nogc(cx);
1332     setOwnerAndData(
1333         owner, owner->as<InlineTypedObject>().inlineTypedMem(nogc) + offset);
1334   }
1335 }
1336 
1337 // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
1338 // the type `type`.
TypedObjLengthFromType(TypeDescr & descr)1339 static uint32_t TypedObjLengthFromType(TypeDescr& descr) {
1340   switch (descr.kind()) {
1341     case type::Scalar:
1342     case type::Reference:
1343     case type::Struct:
1344     case type::Simd:
1345       return 0;
1346 
1347     case type::Array:
1348       return descr.as<ArrayTypeDescr>().length();
1349   }
1350   MOZ_CRASH("Invalid kind");
1351 }
1352 
createDerived(JSContext * cx,HandleTypeDescr type,HandleTypedObject typedObj,uint32_t offset)1353 /*static*/ OutlineTypedObject* OutlineTypedObject::createDerived(
1354     JSContext* cx, HandleTypeDescr type, HandleTypedObject typedObj,
1355     uint32_t offset) {
1356   MOZ_ASSERT(offset <= typedObj->size());
1357   MOZ_ASSERT(offset + type->size() <= typedObj->size());
1358 
1359   int32_t length = TypedObjLengthFromType(*type);
1360 
1361   const js::Class* clasp = typedObj->opaque()
1362                                ? &OutlineOpaqueTypedObject::class_
1363                                : &OutlineTransparentTypedObject::class_;
1364   Rooted<OutlineTypedObject*> obj(cx);
1365   obj = createUnattachedWithClass(cx, clasp, type, length);
1366   if (!obj) return nullptr;
1367 
1368   obj->attach(cx, *typedObj, offset);
1369   return obj;
1370 }
1371 
createZeroed(JSContext * cx,HandleTypeDescr descr,int32_t length,gc::InitialHeap heap)1372 /*static*/ TypedObject* TypedObject::createZeroed(JSContext* cx,
1373                                                   HandleTypeDescr descr,
1374                                                   int32_t length,
1375                                                   gc::InitialHeap heap) {
1376   // If possible, create an object with inline data.
1377   if (descr->size() <= InlineTypedObject::MaximumSize) {
1378     AutoSetNewObjectMetadata metadata(cx);
1379 
1380     InlineTypedObject* obj = InlineTypedObject::create(cx, descr, heap);
1381     if (!obj) return nullptr;
1382     JS::AutoCheckCannotGC nogc(cx);
1383     descr->initInstances(cx->runtime(), obj->inlineTypedMem(nogc), 1);
1384     return obj;
1385   }
1386 
1387   // Create unattached wrapper object.
1388   Rooted<OutlineTypedObject*> obj(
1389       cx, OutlineTypedObject::createUnattached(cx, descr, length, heap));
1390   if (!obj) return nullptr;
1391 
1392   // Allocate and initialize the memory for this instance.
1393   size_t totalSize = descr->size();
1394   Rooted<ArrayBufferObject*> buffer(cx);
1395   buffer = ArrayBufferObject::create(cx, totalSize);
1396   if (!buffer) return nullptr;
1397   descr->initInstances(cx->runtime(), buffer->dataPointer(), 1);
1398   obj->attach(cx, *buffer, 0);
1399   return obj;
1400 }
1401 
ReportTypedObjTypeError(JSContext * cx,const unsigned errorNumber,HandleTypedObject obj)1402 static bool ReportTypedObjTypeError(JSContext* cx, const unsigned errorNumber,
1403                                     HandleTypedObject obj) {
1404   // Serialize type string of obj
1405   RootedAtom typeReprAtom(cx, &obj->typeDescr().stringRepr());
1406   UniqueChars typeReprStr(JS_EncodeStringToUTF8(cx, typeReprAtom));
1407   if (!typeReprStr) return false;
1408 
1409   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
1410                            typeReprStr.get());
1411   return false;
1412 }
1413 
obj_trace(JSTracer * trc,JSObject * object)1414 /* static */ void OutlineTypedObject::obj_trace(JSTracer* trc,
1415                                                 JSObject* object) {
1416   OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
1417 
1418   TraceEdge(trc, typedObj.shapePtr(), "OutlineTypedObject_shape");
1419 
1420   if (!typedObj.owner_) return;
1421 
1422   TypeDescr& descr = typedObj.typeDescr();
1423 
1424   // Mark the owner, watching in case it is moved by the tracer.
1425   JSObject* oldOwner = typedObj.owner_;
1426   TraceManuallyBarrieredEdge(trc, &typedObj.owner_, "typed object owner");
1427   JSObject* owner = typedObj.owner_;
1428 
1429   uint8_t* oldData = typedObj.outOfLineTypedMem();
1430   uint8_t* newData = oldData;
1431 
1432   // Update the data pointer if the owner moved and the owner's data is
1433   // inline with it. Note that an array buffer pointing to data in an inline
1434   // typed object will never be used as an owner for another outline typed
1435   // object. In such cases, the owner will be the inline typed object itself.
1436   MakeAccessibleAfterMovingGC(owner);
1437   MOZ_ASSERT_IF(owner->is<ArrayBufferObject>(),
1438                 !owner->as<ArrayBufferObject>().forInlineTypedObject());
1439   if (owner != oldOwner && (owner->is<InlineTypedObject>() ||
1440                             owner->as<ArrayBufferObject>().hasInlineData())) {
1441     newData += reinterpret_cast<uint8_t*>(owner) -
1442                reinterpret_cast<uint8_t*>(oldOwner);
1443     typedObj.setData(newData);
1444 
1445     if (trc->isTenuringTracer()) {
1446       Nursery& nursery = typedObj.zoneFromAnyThread()->group()->nursery();
1447       nursery.maybeSetForwardingPointer(trc, oldData, newData,
1448                                         /* direct = */ false);
1449     }
1450   }
1451 
1452   if (!descr.opaque() || !typedObj.isAttached()) return;
1453 
1454   descr.traceInstances(trc, newData, 1);
1455 }
1456 
hasProperty(const JSAtomState & names,jsid id)1457 bool TypeDescr::hasProperty(const JSAtomState& names, jsid id) {
1458   switch (kind()) {
1459     case type::Scalar:
1460     case type::Reference:
1461     case type::Simd:
1462       return false;
1463 
1464     case type::Array: {
1465       uint32_t index;
1466       return IdIsIndex(id, &index) || JSID_IS_ATOM(id, names.length);
1467     }
1468 
1469     case type::Struct: {
1470       size_t index;
1471       return as<StructTypeDescr>().fieldIndex(id, &index);
1472     }
1473   }
1474 
1475   MOZ_CRASH("Unexpected kind");
1476 }
1477 
obj_lookupProperty(JSContext * cx,HandleObject obj,HandleId id,MutableHandleObject objp,MutableHandle<PropertyResult> propp)1478 /* static */ bool TypedObject::obj_lookupProperty(
1479     JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp,
1480     MutableHandle<PropertyResult> propp) {
1481   if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
1482     propp.setNonNativeProperty();
1483     objp.set(obj);
1484     return true;
1485   }
1486 
1487   RootedObject proto(cx, obj->staticPrototype());
1488   if (!proto) {
1489     objp.set(nullptr);
1490     propp.setNotFound();
1491     return true;
1492   }
1493 
1494   return LookupProperty(cx, proto, id, objp, propp);
1495 }
1496 
ReportPropertyError(JSContext * cx,const unsigned errorNumber,HandleId id)1497 static bool ReportPropertyError(JSContext* cx, const unsigned errorNumber,
1498                                 HandleId id) {
1499   RootedValue idVal(cx, IdToValue(id));
1500   RootedString str(cx, ValueToSource(cx, idVal));
1501   if (!str) return false;
1502 
1503   UniqueChars propName(JS_EncodeStringToUTF8(cx, str));
1504   if (!propName) return false;
1505 
1506   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
1507                            propName.get());
1508   return false;
1509 }
1510 
obj_defineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result)1511 bool TypedObject::obj_defineProperty(JSContext* cx, HandleObject obj,
1512                                      HandleId id,
1513                                      Handle<PropertyDescriptor> desc,
1514                                      ObjectOpResult& result) {
1515   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1516   return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
1517 }
1518 
obj_hasProperty(JSContext * cx,HandleObject obj,HandleId id,bool * foundp)1519 bool TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
1520                                   bool* foundp) {
1521   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1522   switch (typedObj->typeDescr().kind()) {
1523     case type::Scalar:
1524     case type::Reference:
1525     case type::Simd:
1526       break;
1527 
1528     case type::Array: {
1529       if (JSID_IS_ATOM(id, cx->names().length)) {
1530         *foundp = true;
1531         return true;
1532       }
1533       uint32_t index;
1534       // Elements are not inherited from the prototype.
1535       if (IdIsIndex(id, &index)) {
1536         *foundp = (index < uint32_t(typedObj->length()));
1537         return true;
1538       }
1539       break;
1540     }
1541 
1542     case type::Struct:
1543       size_t index;
1544       if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) {
1545         *foundp = true;
1546         return true;
1547       }
1548   }
1549 
1550   RootedObject proto(cx, obj->staticPrototype());
1551   if (!proto) {
1552     *foundp = false;
1553     return true;
1554   }
1555 
1556   return HasProperty(cx, proto, id, foundp);
1557 }
1558 
obj_getProperty(JSContext * cx,HandleObject obj,HandleValue receiver,HandleId id,MutableHandleValue vp)1559 bool TypedObject::obj_getProperty(JSContext* cx, HandleObject obj,
1560                                   HandleValue receiver, HandleId id,
1561                                   MutableHandleValue vp) {
1562   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1563 
1564   // Dispatch elements to obj_getElement:
1565   uint32_t index;
1566   if (IdIsIndex(id, &index))
1567     return obj_getElement(cx, obj, receiver, index, vp);
1568 
1569   // Handle everything else here:
1570 
1571   switch (typedObj->typeDescr().kind()) {
1572     case type::Scalar:
1573     case type::Reference:
1574       break;
1575 
1576     case type::Simd:
1577       break;
1578 
1579     case type::Array:
1580       if (JSID_IS_ATOM(id, cx->names().length)) {
1581         if (!typedObj->isAttached()) {
1582           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1583                                     JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
1584           return false;
1585         }
1586 
1587         vp.setInt32(typedObj->length());
1588         return true;
1589       }
1590       break;
1591 
1592     case type::Struct: {
1593       Rooted<StructTypeDescr*> descr(
1594           cx, &typedObj->typeDescr().as<StructTypeDescr>());
1595 
1596       size_t fieldIndex;
1597       if (!descr->fieldIndex(id, &fieldIndex)) break;
1598 
1599       size_t offset = descr->fieldOffset(fieldIndex);
1600       Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
1601       return Reify(cx, fieldType, typedObj, offset, vp);
1602     }
1603   }
1604 
1605   RootedObject proto(cx, obj->staticPrototype());
1606   if (!proto) {
1607     vp.setUndefined();
1608     return true;
1609   }
1610 
1611   return GetProperty(cx, proto, receiver, id, vp);
1612 }
1613 
obj_getElement(JSContext * cx,HandleObject obj,HandleValue receiver,uint32_t index,MutableHandleValue vp)1614 bool TypedObject::obj_getElement(JSContext* cx, HandleObject obj,
1615                                  HandleValue receiver, uint32_t index,
1616                                  MutableHandleValue vp) {
1617   MOZ_ASSERT(obj->is<TypedObject>());
1618   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1619   Rooted<TypeDescr*> descr(cx, &typedObj->typeDescr());
1620 
1621   switch (descr->kind()) {
1622     case type::Scalar:
1623     case type::Reference:
1624     case type::Simd:
1625     case type::Struct:
1626       break;
1627 
1628     case type::Array:
1629       return obj_getArrayElement(cx, typedObj, descr, index, vp);
1630   }
1631 
1632   RootedObject proto(cx, obj->staticPrototype());
1633   if (!proto) {
1634     vp.setUndefined();
1635     return true;
1636   }
1637 
1638   return GetElement(cx, proto, receiver, index, vp);
1639 }
1640 
obj_getArrayElement(JSContext * cx,Handle<TypedObject * > typedObj,Handle<TypeDescr * > typeDescr,uint32_t index,MutableHandleValue vp)1641 /*static*/ bool TypedObject::obj_getArrayElement(JSContext* cx,
1642                                                  Handle<TypedObject*> typedObj,
1643                                                  Handle<TypeDescr*> typeDescr,
1644                                                  uint32_t index,
1645                                                  MutableHandleValue vp) {
1646   // Elements are not inherited from the prototype.
1647   if (index >= (size_t)typedObj->length()) {
1648     vp.setUndefined();
1649     return true;
1650   }
1651 
1652   Rooted<TypeDescr*> elementType(
1653       cx, &typeDescr->as<ArrayTypeDescr>().elementType());
1654   size_t offset = elementType->size() * index;
1655   return Reify(cx, elementType, typedObj, offset, vp);
1656 }
1657 
obj_setProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)1658 bool TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
1659                                   HandleValue v, HandleValue receiver,
1660                                   ObjectOpResult& result) {
1661   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1662 
1663   switch (typedObj->typeDescr().kind()) {
1664     case type::Scalar:
1665     case type::Reference:
1666       break;
1667 
1668     case type::Simd:
1669       break;
1670 
1671     case type::Array: {
1672       if (JSID_IS_ATOM(id, cx->names().length)) {
1673         if (receiver.isObject() && obj == &receiver.toObject()) {
1674           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1675                                     JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
1676           return false;
1677         }
1678         return result.failReadOnly();
1679       }
1680 
1681       uint32_t index;
1682       if (IdIsIndex(id, &index)) {
1683         if (!receiver.isObject() || obj != &receiver.toObject())
1684           return SetPropertyByDefining(cx, id, v, receiver, result);
1685 
1686         if (index >= uint32_t(typedObj->length())) {
1687           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1688                                     JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
1689           return false;
1690         }
1691 
1692         Rooted<TypeDescr*> elementType(cx);
1693         elementType = &typedObj->typeDescr().as<ArrayTypeDescr>().elementType();
1694         size_t offset = elementType->size() * index;
1695         if (!ConvertAndCopyTo(cx, elementType, typedObj, offset, nullptr, v))
1696           return false;
1697         return result.succeed();
1698       }
1699       break;
1700     }
1701 
1702     case type::Struct: {
1703       Rooted<StructTypeDescr*> descr(
1704           cx, &typedObj->typeDescr().as<StructTypeDescr>());
1705 
1706       size_t fieldIndex;
1707       if (!descr->fieldIndex(id, &fieldIndex)) break;
1708 
1709       if (!receiver.isObject() || obj != &receiver.toObject())
1710         return SetPropertyByDefining(cx, id, v, receiver, result);
1711 
1712       size_t offset = descr->fieldOffset(fieldIndex);
1713       Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
1714       RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
1715       if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, v))
1716         return false;
1717       return result.succeed();
1718     }
1719   }
1720 
1721   return SetPropertyOnProto(cx, obj, id, v, receiver, result);
1722 }
1723 
obj_getOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<PropertyDescriptor> desc)1724 bool TypedObject::obj_getOwnPropertyDescriptor(
1725     JSContext* cx, HandleObject obj, HandleId id,
1726     MutableHandle<PropertyDescriptor> desc) {
1727   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1728   if (!typedObj->isAttached()) {
1729     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1730                               JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
1731     return false;
1732   }
1733 
1734   Rooted<TypeDescr*> descr(cx, &typedObj->typeDescr());
1735   switch (descr->kind()) {
1736     case type::Scalar:
1737     case type::Reference:
1738     case type::Simd:
1739       break;
1740 
1741     case type::Array: {
1742       uint32_t index;
1743       if (IdIsIndex(id, &index)) {
1744         if (!obj_getArrayElement(cx, typedObj, descr, index, desc.value()))
1745           return false;
1746         desc.setAttributes(JSPROP_ENUMERATE | JSPROP_PERMANENT);
1747         desc.object().set(obj);
1748         return true;
1749       }
1750 
1751       if (JSID_IS_ATOM(id, cx->names().length)) {
1752         desc.value().setInt32(typedObj->length());
1753         desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
1754         desc.object().set(obj);
1755         return true;
1756       }
1757       break;
1758     }
1759 
1760     case type::Struct: {
1761       Rooted<StructTypeDescr*> descr(
1762           cx, &typedObj->typeDescr().as<StructTypeDescr>());
1763 
1764       size_t fieldIndex;
1765       if (!descr->fieldIndex(id, &fieldIndex)) break;
1766 
1767       size_t offset = descr->fieldOffset(fieldIndex);
1768       Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
1769       if (!Reify(cx, fieldType, typedObj, offset, desc.value())) return false;
1770 
1771       desc.setAttributes(JSPROP_ENUMERATE | JSPROP_PERMANENT);
1772       desc.object().set(obj);
1773       return true;
1774     }
1775   }
1776 
1777   desc.object().set(nullptr);
1778   return true;
1779 }
1780 
IsOwnId(JSContext * cx,HandleObject obj,HandleId id)1781 static bool IsOwnId(JSContext* cx, HandleObject obj, HandleId id) {
1782   uint32_t index;
1783   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1784   switch (typedObj->typeDescr().kind()) {
1785     case type::Scalar:
1786     case type::Reference:
1787     case type::Simd:
1788       return false;
1789 
1790     case type::Array:
1791       return IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
1792 
1793     case type::Struct:
1794       size_t index;
1795       if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index))
1796         return true;
1797   }
1798 
1799   return false;
1800 }
1801 
obj_deleteProperty(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)1802 bool TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj,
1803                                      HandleId id, ObjectOpResult& result) {
1804   if (IsOwnId(cx, obj, id))
1805     return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
1806 
1807   RootedObject proto(cx, obj->staticPrototype());
1808   if (!proto) return result.succeed();
1809 
1810   return DeleteProperty(cx, proto, id, result);
1811 }
1812 
obj_newEnumerate(JSContext * cx,HandleObject obj,AutoIdVector & properties,bool enumerableOnly)1813 bool TypedObject::obj_newEnumerate(JSContext* cx, HandleObject obj,
1814                                    AutoIdVector& properties,
1815                                    bool enumerableOnly) {
1816   MOZ_ASSERT(obj->is<TypedObject>());
1817   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
1818   Rooted<TypeDescr*> descr(cx, &typedObj->typeDescr());
1819 
1820   RootedId id(cx);
1821   switch (descr->kind()) {
1822     case type::Scalar:
1823     case type::Reference:
1824     case type::Simd: {
1825       // Nothing to enumerate.
1826       break;
1827     }
1828 
1829     case type::Array: {
1830       if (!properties.reserve(typedObj->length())) return false;
1831 
1832       for (uint32_t index = 0; index < typedObj->length(); index++) {
1833         id = INT_TO_JSID(index);
1834         properties.infallibleAppend(id);
1835       }
1836       break;
1837     }
1838 
1839     case type::Struct: {
1840       size_t fieldCount = descr->as<StructTypeDescr>().fieldCount();
1841       if (!properties.reserve(fieldCount)) return false;
1842 
1843       for (size_t index = 0; index < fieldCount; index++) {
1844         id = AtomToId(&descr->as<StructTypeDescr>().fieldName(index));
1845         properties.infallibleAppend(id);
1846       }
1847       break;
1848     }
1849   }
1850 
1851   return true;
1852 }
1853 
notifyBufferDetached(void * newData)1854 void OutlineTypedObject::notifyBufferDetached(void* newData) {
1855   setData(reinterpret_cast<uint8_t*>(newData));
1856 }
1857 
1858 /******************************************************************************
1859  * Inline typed objects
1860  */
1861 
create(JSContext * cx,HandleTypeDescr descr,gc::InitialHeap heap)1862 /* static */ InlineTypedObject* InlineTypedObject::create(
1863     JSContext* cx, HandleTypeDescr descr, gc::InitialHeap heap) {
1864   gc::AllocKind allocKind = allocKindForTypeDescriptor(descr);
1865 
1866   const Class* clasp = descr->opaque() ? &InlineOpaqueTypedObject::class_
1867                                        : &InlineTransparentTypedObject::class_;
1868 
1869   RootedObjectGroup group(
1870       cx, ObjectGroup::defaultNewGroup(
1871               cx, clasp, TaggedProto(&descr->typedProto()), descr));
1872   if (!group) return nullptr;
1873 
1874   NewObjectKind newKind =
1875       (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
1876   return NewObjectWithGroup<InlineTypedObject>(cx, group, allocKind, newKind);
1877 }
1878 
createCopy(JSContext * cx,Handle<InlineTypedObject * > templateObject,gc::InitialHeap heap)1879 /* static */ InlineTypedObject* InlineTypedObject::createCopy(
1880     JSContext* cx, Handle<InlineTypedObject*> templateObject,
1881     gc::InitialHeap heap) {
1882   AutoSetNewObjectMetadata metadata(cx);
1883 
1884   Rooted<TypeDescr*> descr(cx, &templateObject->typeDescr());
1885   InlineTypedObject* res = create(cx, descr, heap);
1886   if (!res) return nullptr;
1887 
1888   memcpy(res->inlineTypedMem(), templateObject->inlineTypedMem(),
1889          templateObject->size());
1890   return res;
1891 }
1892 
obj_trace(JSTracer * trc,JSObject * object)1893 /* static */ void InlineTypedObject::obj_trace(JSTracer* trc,
1894                                                JSObject* object) {
1895   InlineTypedObject& typedObj = object->as<InlineTypedObject>();
1896 
1897   TraceEdge(trc, typedObj.shapePtr(), "InlineTypedObject_shape");
1898 
1899   // Inline transparent objects do not have references and do not need more
1900   // tracing. If there is an entry in the compartment's LazyArrayBufferTable,
1901   // tracing that reference will be taken care of by the table itself.
1902   if (typedObj.is<InlineTransparentTypedObject>()) return;
1903 
1904   typedObj.typeDescr().traceInstances(trc, typedObj.inlineTypedMem(), 1);
1905 }
1906 
obj_moved(JSObject * dst,JSObject * src)1907 /* static */ size_t InlineTypedObject::obj_moved(JSObject* dst, JSObject* src) {
1908   if (!IsInsideNursery(src)) return 0;
1909 
1910   // Inline typed object element arrays can be preserved on the stack by Ion
1911   // and need forwarding pointers created during a minor GC. We can't do this
1912   // in the trace hook because we don't have any stale data to determine
1913   // whether this object moved and where it was moved from.
1914   TypeDescr& descr = dst->as<InlineTypedObject>().typeDescr();
1915   if (descr.kind() == type::Array) {
1916     // The forwarding pointer can be direct as long as there is enough
1917     // space for it. Other objects might point into the object's buffer,
1918     // but they will not set any direct forwarding pointers.
1919     uint8_t* oldData = reinterpret_cast<uint8_t*>(src) + offsetOfDataStart();
1920     uint8_t* newData = dst->as<InlineTypedObject>().inlineTypedMem();
1921     auto& nursery = dst->zone()->group()->nursery();
1922     bool direct = descr.size() >= sizeof(uintptr_t);
1923     nursery.setForwardingPointerWhileTenuring(oldData, newData, direct);
1924   }
1925 
1926   return 0;
1927 }
1928 
getOrCreateBuffer(JSContext * cx)1929 ArrayBufferObject* InlineTransparentTypedObject::getOrCreateBuffer(
1930     JSContext* cx) {
1931   ObjectWeakMap*& table = cx->compartment()->lazyArrayBuffers;
1932   if (!table) {
1933     table = cx->new_<ObjectWeakMap>(cx);
1934     if (!table || !table->init()) return nullptr;
1935   }
1936 
1937   JSObject* obj = table->lookup(this);
1938   if (obj) return &obj->as<ArrayBufferObject>();
1939 
1940   ArrayBufferObject::BufferContents contents =
1941       ArrayBufferObject::BufferContents::createPlain(inlineTypedMem());
1942   size_t nbytes = typeDescr().size();
1943 
1944   // Prevent GC under ArrayBufferObject::create, which might move this object
1945   // and its contents.
1946   gc::AutoSuppressGC suppress(cx);
1947 
1948   ArrayBufferObject* buffer = ArrayBufferObject::create(
1949       cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
1950   if (!buffer) return nullptr;
1951 
1952   // The owning object must always be the array buffer's first view. This
1953   // both prevents the memory from disappearing out from under the buffer
1954   // (the first view is held strongly by the buffer) and is used by the
1955   // buffer marking code to detect whether its data pointer needs to be
1956   // relocated.
1957   JS_ALWAYS_TRUE(buffer->addView(cx, this));
1958 
1959   buffer->setForInlineTypedObject();
1960   buffer->setHasTypedObjectViews();
1961 
1962   if (!table->add(cx, this, buffer)) return nullptr;
1963 
1964   if (IsInsideNursery(this)) {
1965     // Make sure the buffer is traced by the next generational collection,
1966     // so that its data pointer is updated after this typed object moves.
1967     zone()->group()->storeBuffer().putWholeCell(buffer);
1968   }
1969 
1970   return buffer;
1971 }
1972 
getOrCreateBuffer(JSContext * cx)1973 ArrayBufferObject* OutlineTransparentTypedObject::getOrCreateBuffer(
1974     JSContext* cx) {
1975   if (owner().is<ArrayBufferObject>()) return &owner().as<ArrayBufferObject>();
1976   return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
1977 }
1978 
1979 /******************************************************************************
1980  * Typed object classes
1981  */
1982 
1983 const ObjectOps TypedObject::objectOps_ = {
1984     TypedObject::obj_lookupProperty,
1985     TypedObject::obj_defineProperty,
1986     TypedObject::obj_hasProperty,
1987     TypedObject::obj_getProperty,
1988     TypedObject::obj_setProperty,
1989     TypedObject::obj_getOwnPropertyDescriptor,
1990     TypedObject::obj_deleteProperty,
1991     nullptr, /* getElements */
1992     nullptr, /* thisValue */
1993 };
1994 
1995 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved)                          \
1996   static const ClassOps Name##ClassOps = {                                 \
1997       nullptr, /* addProperty */                                           \
1998       nullptr, /* delProperty */                                           \
1999       nullptr, /* enumerate   */                                           \
2000       TypedObject::obj_newEnumerate,                                       \
2001       nullptr, /* resolve     */                                           \
2002       nullptr, /* mayResolve  */                                           \
2003       nullptr, /* finalize    */                                           \
2004       nullptr, /* call        */                                           \
2005       nullptr, /* hasInstance */                                           \
2006       nullptr, /* construct   */                                           \
2007       Trace,                                                               \
2008   };                                                                       \
2009   static const ClassExtension Name##ClassExt = {                           \
2010       nullptr, /* weakmapKeyDelegateOp */                                  \
2011       Moved    /* objectMovedOp */                                         \
2012   };                                                                       \
2013   const Class Name::class_ = {                                             \
2014       #Name,           Class::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER, \
2015       &Name##ClassOps, JS_NULL_CLASS_SPEC,                                 \
2016       &Name##ClassExt, &TypedObject::objectOps_}
2017 
2018 DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject,
2019                       OutlineTypedObject::obj_trace, nullptr);
2020 DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject, OutlineTypedObject::obj_trace,
2021                       nullptr);
2022 DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,
2023                       InlineTypedObject::obj_trace,
2024                       InlineTypedObject::obj_moved);
2025 DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject, InlineTypedObject::obj_trace,
2026                       InlineTypedObject::obj_moved);
2027 
LengthForType(TypeDescr & descr)2028 static int32_t LengthForType(TypeDescr& descr) {
2029   switch (descr.kind()) {
2030     case type::Scalar:
2031     case type::Reference:
2032     case type::Struct:
2033     case type::Simd:
2034       return 0;
2035 
2036     case type::Array:
2037       return descr.as<ArrayTypeDescr>().length();
2038   }
2039 
2040   MOZ_CRASH("Invalid kind");
2041 }
2042 
CheckOffset(uint32_t offset,uint32_t size,uint32_t alignment,uint32_t bufferLength)2043 static bool CheckOffset(uint32_t offset, uint32_t size, uint32_t alignment,
2044                         uint32_t bufferLength) {
2045   // Offset (plus size) must be fully contained within the buffer.
2046   if (offset > bufferLength) return false;
2047   if (offset + size < offset) return false;
2048   if (offset + size > bufferLength) return false;
2049 
2050   // Offset must be aligned.
2051   if ((offset % alignment) != 0) return false;
2052 
2053   return true;
2054 }
2055 
2056 template <typename T, typename U, typename V, typename W>
2057 inline bool CheckOffset(T, U, V, W) = delete;
2058 
construct(JSContext * cx,unsigned int argc,Value * vp)2059 /*static*/ bool TypedObject::construct(JSContext* cx, unsigned int argc,
2060                                        Value* vp) {
2061   CallArgs args = CallArgsFromVp(argc, vp);
2062 
2063   MOZ_ASSERT(args.callee().is<TypeDescr>());
2064   Rooted<TypeDescr*> callee(cx, &args.callee().as<TypeDescr>());
2065 
2066   // Typed object constructors are overloaded in three ways, in order of
2067   // precedence:
2068   //
2069   //   new TypeObj()
2070   //   new TypeObj(buffer, [offset])
2071   //   new TypeObj(data)
2072 
2073   // Zero argument constructor:
2074   if (args.length() == 0) {
2075     int32_t length = LengthForType(*callee);
2076     Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2077     if (!obj) return false;
2078     args.rval().setObject(*obj);
2079     return true;
2080   }
2081 
2082   // Buffer constructor.
2083   if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
2084     Rooted<ArrayBufferObject*> buffer(cx);
2085     buffer = &args[0].toObject().as<ArrayBufferObject>();
2086 
2087     if (callee->opaque() || buffer->isDetached()) {
2088       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2089                                 JSMSG_TYPEDOBJECT_BAD_ARGS);
2090       return false;
2091     }
2092 
2093     uint32_t offset;
2094     if (args.length() >= 2 && !args[1].isUndefined()) {
2095       if (!args[1].isInt32() || args[1].toInt32() < 0) {
2096         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2097                                   JSMSG_TYPEDOBJECT_BAD_ARGS);
2098         return false;
2099       }
2100 
2101       offset = args[1].toInt32();
2102     } else {
2103       offset = 0;
2104     }
2105 
2106     if (args.length() >= 3 && !args[2].isUndefined()) {
2107       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2108                                 JSMSG_TYPEDOBJECT_BAD_ARGS);
2109       return false;
2110     }
2111 
2112     if (!CheckOffset(offset, callee->size(), callee->alignment(),
2113                      buffer->byteLength())) {
2114       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2115                                 JSMSG_TYPEDOBJECT_BAD_ARGS);
2116       return false;
2117     }
2118 
2119     Rooted<OutlineTypedObject*> obj(cx);
2120     obj = OutlineTypedObject::createUnattached(cx, callee,
2121                                                LengthForType(*callee));
2122     if (!obj) return false;
2123 
2124     obj->attach(cx, *buffer, offset);
2125     args.rval().setObject(*obj);
2126     return true;
2127   }
2128 
2129   // Data constructor.
2130   if (args[0].isObject()) {
2131     // Create the typed object.
2132     int32_t length = LengthForType(*callee);
2133     Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2134     if (!obj) return false;
2135 
2136     // Initialize from `arg`.
2137     if (!ConvertAndCopyTo(cx, obj, args[0])) return false;
2138     args.rval().setObject(*obj);
2139     return true;
2140   }
2141 
2142   // Something bogus.
2143   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2144                             JSMSG_TYPEDOBJECT_BAD_ARGS);
2145   return false;
2146 }
2147 
create(JSContext * cx,js::gc::AllocKind kind,js::gc::InitialHeap heap,js::HandleShape shape,js::HandleObjectGroup group)2148 /* static */ JS::Result<TypedObject*, JS::OOM&> TypedObject::create(
2149     JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
2150     js::HandleShape shape, js::HandleObjectGroup group) {
2151   debugCheckNewObject(group, shape, kind, heap);
2152 
2153   const js::Class* clasp = group->clasp();
2154   MOZ_ASSERT(::IsTypedObjectClass(clasp));
2155 
2156   JSObject* obj =
2157       js::Allocate<JSObject>(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
2158   if (!obj) return cx->alreadyReportedOOM();
2159 
2160   TypedObject* tobj = static_cast<TypedObject*>(obj);
2161   tobj->initGroup(group);
2162   tobj->initShape(shape);
2163 
2164   MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
2165   cx->compartment()->setObjectPendingMetadata(cx, tobj);
2166 
2167   js::gc::TraceCreateObject(tobj);
2168 
2169   return tobj;
2170 }
2171 
2172 /******************************************************************************
2173  * Intrinsics
2174  */
2175 
NewOpaqueTypedObject(JSContext * cx,unsigned argc,Value * vp)2176 bool js::NewOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp) {
2177   CallArgs args = CallArgsFromVp(argc, vp);
2178   MOZ_ASSERT(args.length() == 1);
2179   MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypeDescr>());
2180 
2181   Rooted<TypeDescr*> descr(cx, &args[0].toObject().as<TypeDescr>());
2182   int32_t length = TypedObjLengthFromType(*descr);
2183   Rooted<OutlineTypedObject*> obj(cx);
2184   obj = OutlineTypedObject::createUnattachedWithClass(
2185       cx, &OutlineOpaqueTypedObject::class_, descr, length);
2186   if (!obj) return false;
2187   args.rval().setObject(*obj);
2188   return true;
2189 }
2190 
NewDerivedTypedObject(JSContext * cx,unsigned argc,Value * vp)2191 bool js::NewDerivedTypedObject(JSContext* cx, unsigned argc, Value* vp) {
2192   CallArgs args = CallArgsFromVp(argc, vp);
2193   MOZ_ASSERT(args.length() == 3);
2194   MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypeDescr>());
2195   MOZ_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
2196   MOZ_ASSERT(args[2].isInt32());
2197 
2198   Rooted<TypeDescr*> descr(cx, &args[0].toObject().as<TypeDescr>());
2199   Rooted<TypedObject*> typedObj(cx, &args[1].toObject().as<TypedObject>());
2200   uint32_t offset = AssertedCast<uint32_t>(args[2].toInt32());
2201 
2202   Rooted<TypedObject*> obj(cx);
2203   obj = OutlineTypedObject::createDerived(cx, descr, typedObj, offset);
2204   if (!obj) return false;
2205 
2206   args.rval().setObject(*obj);
2207   return true;
2208 }
2209 
AttachTypedObject(JSContext * cx,unsigned argc,Value * vp)2210 bool js::AttachTypedObject(JSContext* cx, unsigned argc, Value* vp) {
2211   CallArgs args = CallArgsFromVp(argc, vp);
2212   MOZ_ASSERT(args.length() == 3);
2213   MOZ_ASSERT(args[2].isInt32());
2214 
2215   OutlineTypedObject& handle = args[0].toObject().as<OutlineTypedObject>();
2216   TypedObject& target = args[1].toObject().as<TypedObject>();
2217   MOZ_ASSERT(!handle.isAttached());
2218   uint32_t offset = AssertedCast<uint32_t>(args[2].toInt32());
2219 
2220   handle.attach(cx, target, offset);
2221 
2222   return true;
2223 }
2224 
SetTypedObjectOffset(JSContext *,unsigned argc,Value * vp)2225 bool js::SetTypedObjectOffset(JSContext*, unsigned argc, Value* vp) {
2226   CallArgs args = CallArgsFromVp(argc, vp);
2227   MOZ_ASSERT(args.length() == 2);
2228   MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
2229   MOZ_ASSERT(args[1].isInt32());
2230 
2231   OutlineTypedObject& typedObj = args[0].toObject().as<OutlineTypedObject>();
2232   int32_t offset = args[1].toInt32();
2233 
2234   MOZ_ASSERT(typedObj.isAttached());
2235   typedObj.resetOffset(offset);
2236   args.rval().setUndefined();
2237   return true;
2238 }
2239 
ObjectIsTypeDescr(JSContext *,unsigned argc,Value * vp)2240 bool js::ObjectIsTypeDescr(JSContext*, unsigned argc, Value* vp) {
2241   CallArgs args = CallArgsFromVp(argc, vp);
2242   MOZ_ASSERT(args.length() == 1);
2243   MOZ_ASSERT(args[0].isObject());
2244   args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
2245   return true;
2246 }
2247 
ObjectIsTypedObject(JSContext *,unsigned argc,Value * vp)2248 bool js::ObjectIsTypedObject(JSContext*, unsigned argc, Value* vp) {
2249   CallArgs args = CallArgsFromVp(argc, vp);
2250   MOZ_ASSERT(args.length() == 1);
2251   MOZ_ASSERT(args[0].isObject());
2252   args.rval().setBoolean(args[0].toObject().is<TypedObject>());
2253   return true;
2254 }
2255 
ObjectIsOpaqueTypedObject(JSContext *,unsigned argc,Value * vp)2256 bool js::ObjectIsOpaqueTypedObject(JSContext*, unsigned argc, Value* vp) {
2257   CallArgs args = CallArgsFromVp(argc, vp);
2258   MOZ_ASSERT(args.length() == 1);
2259   JSObject& obj = args[0].toObject();
2260   args.rval().setBoolean(obj.is<TypedObject>() &&
2261                          obj.as<TypedObject>().opaque());
2262   return true;
2263 }
2264 
ObjectIsTransparentTypedObject(JSContext *,unsigned argc,Value * vp)2265 bool js::ObjectIsTransparentTypedObject(JSContext*, unsigned argc, Value* vp) {
2266   CallArgs args = CallArgsFromVp(argc, vp);
2267   MOZ_ASSERT(args.length() == 1);
2268   JSObject& obj = args[0].toObject();
2269   args.rval().setBoolean(obj.is<TypedObject>() &&
2270                          !obj.as<TypedObject>().opaque());
2271   return true;
2272 }
2273 
TypeDescrIsSimpleType(JSContext *,unsigned argc,Value * vp)2274 bool js::TypeDescrIsSimpleType(JSContext*, unsigned argc, Value* vp) {
2275   CallArgs args = CallArgsFromVp(argc, vp);
2276   MOZ_ASSERT(args.length() == 1);
2277   MOZ_ASSERT(args[0].isObject());
2278   MOZ_ASSERT(args[0].toObject().is<js::TypeDescr>());
2279   args.rval().setBoolean(args[0].toObject().is<js::SimpleTypeDescr>());
2280   return true;
2281 }
2282 
TypeDescrIsArrayType(JSContext *,unsigned argc,Value * vp)2283 bool js::TypeDescrIsArrayType(JSContext*, unsigned argc, Value* vp) {
2284   CallArgs args = CallArgsFromVp(argc, vp);
2285   MOZ_ASSERT(args.length() == 1);
2286   MOZ_ASSERT(args[0].isObject());
2287   MOZ_ASSERT(args[0].toObject().is<js::TypeDescr>());
2288   JSObject& obj = args[0].toObject();
2289   args.rval().setBoolean(obj.is<js::ArrayTypeDescr>());
2290   return true;
2291 }
2292 
TypedObjectIsAttached(JSContext * cx,unsigned argc,Value * vp)2293 bool js::TypedObjectIsAttached(JSContext* cx, unsigned argc, Value* vp) {
2294   CallArgs args = CallArgsFromVp(argc, vp);
2295   TypedObject& typedObj = args[0].toObject().as<TypedObject>();
2296   args.rval().setBoolean(typedObj.isAttached());
2297   return true;
2298 }
2299 
TypedObjectTypeDescr(JSContext * cx,unsigned argc,Value * vp)2300 bool js::TypedObjectTypeDescr(JSContext* cx, unsigned argc, Value* vp) {
2301   CallArgs args = CallArgsFromVp(argc, vp);
2302   TypedObject& typedObj = args[0].toObject().as<TypedObject>();
2303   args.rval().setObject(typedObj.typeDescr());
2304   return true;
2305 }
2306 
ClampToUint8(JSContext *,unsigned argc,Value * vp)2307 bool js::ClampToUint8(JSContext*, unsigned argc, Value* vp) {
2308   CallArgs args = CallArgsFromVp(argc, vp);
2309   MOZ_ASSERT(args.length() == 1);
2310   MOZ_ASSERT(args[0].isNumber());
2311   args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
2312   return true;
2313 }
2314 
GetTypedObjectModule(JSContext * cx,unsigned argc,Value * vp)2315 bool js::GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp) {
2316   CallArgs args = CallArgsFromVp(argc, vp);
2317   Rooted<GlobalObject*> global(cx, cx->global());
2318   MOZ_ASSERT(global);
2319   args.rval().setObject(global->getTypedObjectModule());
2320   return true;
2321 }
2322 
GetSimdTypeDescr(JSContext * cx,unsigned argc,Value * vp)2323 bool js::GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp) {
2324   CallArgs args = CallArgsFromVp(argc, vp);
2325   MOZ_ASSERT(args.length() == 1);
2326   MOZ_ASSERT(args[0].isInt32());
2327   // One of the JS_SIMDTYPEREPR_* constants / a SimdType enum value.
2328   // getOrCreateSimdTypeDescr() will do the range check.
2329   int32_t simdTypeRepr = args[0].toInt32();
2330   Rooted<GlobalObject*> global(cx, cx->global());
2331   MOZ_ASSERT(global);
2332   auto* obj = GlobalObject::getOrCreateSimdTypeDescr(cx, global,
2333                                                      SimdType(simdTypeRepr));
2334   args.rval().setObject(*obj);
2335   return true;
2336 }
2337 
2338 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                     \
2339   bool js::StoreScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) {  \
2340     CallArgs args = CallArgsFromVp(argc, vp);                               \
2341     MOZ_ASSERT(args.length() == 3);                                         \
2342     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2343     MOZ_ASSERT(args[1].isInt32());                                          \
2344     MOZ_ASSERT(args[2].isNumber());                                         \
2345                                                                             \
2346     TypedObject& typedObj = args[0].toObject().as<TypedObject>();           \
2347     int32_t offset = args[1].toInt32();                                     \
2348                                                                             \
2349     /* Should be guaranteed by the typed objects API: */                    \
2350     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                               \
2351                                                                             \
2352     JS::AutoCheckCannotGC nogc(cx);                                         \
2353     T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));      \
2354     double d = args[2].toNumber();                                          \
2355     *target = ConvertScalar<T>(d);                                          \
2356     args.rval().setUndefined();                                             \
2357     return true;                                                            \
2358   }
2359 
2360 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                  \
2361   bool js::StoreReference##_name::Func(JSContext* cx, unsigned argc,        \
2362                                        Value* vp) {                         \
2363     CallArgs args = CallArgsFromVp(argc, vp);                               \
2364     MOZ_ASSERT(args.length() == 4);                                         \
2365     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2366     MOZ_ASSERT(args[1].isInt32());                                          \
2367     MOZ_ASSERT(args[2].isString() || args[2].isNull());                     \
2368                                                                             \
2369     TypedObject& typedObj = args[0].toObject().as<TypedObject>();           \
2370     int32_t offset = args[1].toInt32();                                     \
2371                                                                             \
2372     jsid id = args[2].isString()                                            \
2373                   ? IdToTypeId(AtomToId(&args[2].toString()->asAtom()))     \
2374                   : JSID_VOID;                                              \
2375                                                                             \
2376     /* Should be guaranteed by the typed objects API: */                    \
2377     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                               \
2378                                                                             \
2379     JS::AutoCheckCannotGC nogc(cx);                                         \
2380     T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));      \
2381     if (!store(cx, target, args[3], &typedObj, id)) return false;           \
2382     args.rval().setUndefined();                                             \
2383     return true;                                                            \
2384   }
2385 
2386 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                      \
2387   bool js::LoadScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) {   \
2388     CallArgs args = CallArgsFromVp(argc, vp);                               \
2389     MOZ_ASSERT(args.length() == 2);                                         \
2390     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2391     MOZ_ASSERT(args[1].isInt32());                                          \
2392                                                                             \
2393     TypedObject& typedObj = args[0].toObject().as<TypedObject>();           \
2394     int32_t offset = args[1].toInt32();                                     \
2395                                                                             \
2396     /* Should be guaranteed by the typed objects API: */                    \
2397     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                               \
2398                                                                             \
2399     JS::AutoCheckCannotGC nogc(cx);                                         \
2400     T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));      \
2401     args.rval().setNumber(JS::CanonicalizeNaN((double)*target));            \
2402     return true;                                                            \
2403   }
2404 
2405 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                   \
2406   bool js::LoadReference##_name::Func(JSContext* cx, unsigned argc,         \
2407                                       Value* vp) {                          \
2408     CallArgs args = CallArgsFromVp(argc, vp);                               \
2409     MOZ_ASSERT(args.length() == 2);                                         \
2410     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2411     MOZ_ASSERT(args[1].isInt32());                                          \
2412                                                                             \
2413     TypedObject& typedObj = args[0].toObject().as<TypedObject>();           \
2414     int32_t offset = args[1].toInt32();                                     \
2415                                                                             \
2416     /* Should be guaranteed by the typed objects API: */                    \
2417     MOZ_ASSERT(offset % MOZ_ALIGNOF(T) == 0);                               \
2418                                                                             \
2419     JS::AutoCheckCannotGC nogc(cx);                                         \
2420     T* target = reinterpret_cast<T*>(typedObj.typedMem(offset, nogc));      \
2421     load(target, args.rval());                                              \
2422     return true;                                                            \
2423   }
2424 
2425 // Because the precise syntax for storing values/objects/strings
2426 // differs, we abstract it away using specialized variants of the
2427 // private methods `store()` and `load()`.
2428 
store(JSContext * cx,GCPtrValue * heap,const Value & v,TypedObject * obj,jsid id)2429 bool StoreReferenceAny::store(JSContext* cx, GCPtrValue* heap, const Value& v,
2430                               TypedObject* obj, jsid id) {
2431   // Undefined values are not included in type inference information for
2432   // value properties of typed objects, as these properties are always
2433   // considered to contain undefined.
2434   if (!v.isUndefined()) {
2435     if (!cx->helperThread())
2436       AddTypePropertyId(cx, obj, id, v);
2437     else if (!HasTypePropertyId(obj, id, v))
2438       return false;
2439   }
2440 
2441   *heap = v;
2442   return true;
2443 }
2444 
store(JSContext * cx,GCPtrObject * heap,const Value & v,TypedObject * obj,jsid id)2445 bool StoreReferenceObject::store(JSContext* cx, GCPtrObject* heap,
2446                                  const Value& v, TypedObject* obj, jsid id) {
2447   MOZ_ASSERT(v.isObjectOrNull());  // or else Store_object is being misused
2448 
2449   // Null pointers are not included in type inference information for
2450   // object properties of typed objects, as these properties are always
2451   // considered to contain null.
2452   if (v.isObject()) {
2453     if (!cx->helperThread())
2454       AddTypePropertyId(cx, obj, id, v);
2455     else if (!HasTypePropertyId(obj, id, v))
2456       return false;
2457   }
2458 
2459   *heap = v.toObjectOrNull();
2460   return true;
2461 }
2462 
store(JSContext * cx,GCPtrString * heap,const Value & v,TypedObject * obj,jsid id)2463 bool StoreReferencestring::store(JSContext* cx, GCPtrString* heap,
2464                                  const Value& v, TypedObject* obj, jsid id) {
2465   MOZ_ASSERT(v.isString());  // or else Store_string is being misused
2466 
2467   // Note: string references are not reflected in type information for the
2468   // object.
2469   *heap = v.toString();
2470 
2471   return true;
2472 }
2473 
load(GCPtrValue * heap,MutableHandleValue v)2474 void LoadReferenceAny::load(GCPtrValue* heap, MutableHandleValue v) {
2475   v.set(*heap);
2476 }
2477 
load(GCPtrObject * heap,MutableHandleValue v)2478 void LoadReferenceObject::load(GCPtrObject* heap, MutableHandleValue v) {
2479   if (*heap)
2480     v.setObject(**heap);
2481   else
2482     v.setNull();
2483 }
2484 
load(GCPtrString * heap,MutableHandleValue v)2485 void LoadReferencestring::load(GCPtrString* heap, MutableHandleValue v) {
2486   v.setString(*heap);
2487 }
2488 
2489 // I was using templates for this stuff instead of macros, but ran
2490 // into problems with the Unagi compiler.
2491 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)2492 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
2493 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_IMPL)
2494 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_IMPL)
2495 
2496 ///////////////////////////////////////////////////////////////////////////
2497 // Walking memory
2498 
2499 template <typename V>
2500 static void visitReferences(TypeDescr& descr, uint8_t* mem, V& visitor) {
2501   if (descr.transparent()) return;
2502 
2503   switch (descr.kind()) {
2504     case type::Scalar:
2505     case type::Simd:
2506       return;
2507 
2508     case type::Reference:
2509       visitor.visitReference(descr.as<ReferenceTypeDescr>(), mem);
2510       return;
2511 
2512     case type::Array: {
2513       ArrayTypeDescr& arrayDescr = descr.as<ArrayTypeDescr>();
2514       TypeDescr& elementDescr = arrayDescr.elementType();
2515       for (uint32_t i = 0; i < arrayDescr.length(); i++) {
2516         visitReferences(elementDescr, mem, visitor);
2517         mem += elementDescr.size();
2518       }
2519       return;
2520     }
2521 
2522     case type::Struct: {
2523       StructTypeDescr& structDescr = descr.as<StructTypeDescr>();
2524       for (size_t i = 0; i < structDescr.fieldCount(); i++) {
2525         TypeDescr& descr = structDescr.fieldDescr(i);
2526         size_t offset = structDescr.fieldOffset(i);
2527         visitReferences(descr, mem + offset, visitor);
2528       }
2529       return;
2530     }
2531   }
2532 
2533   MOZ_CRASH("Invalid type repr kind");
2534 }
2535 
2536 ///////////////////////////////////////////////////////////////////////////
2537 // Initializing instances
2538 
2539 namespace {
2540 
2541 class MemoryInitVisitor {
2542   const JSRuntime* rt_;
2543 
2544  public:
MemoryInitVisitor(const JSRuntime * rt)2545   explicit MemoryInitVisitor(const JSRuntime* rt) : rt_(rt) {}
2546 
2547   void visitReference(ReferenceTypeDescr& descr, uint8_t* mem);
2548 };
2549 
2550 }  // namespace
2551 
visitReference(ReferenceTypeDescr & descr,uint8_t * mem)2552 void MemoryInitVisitor::visitReference(ReferenceTypeDescr& descr,
2553                                        uint8_t* mem) {
2554   switch (descr.type()) {
2555     case ReferenceTypeDescr::TYPE_ANY: {
2556       js::GCPtrValue* heapValue = reinterpret_cast<js::GCPtrValue*>(mem);
2557       heapValue->init(UndefinedValue());
2558       return;
2559     }
2560 
2561     case ReferenceTypeDescr::TYPE_OBJECT: {
2562       js::GCPtrObject* objectPtr = reinterpret_cast<js::GCPtrObject*>(mem);
2563       objectPtr->init(nullptr);
2564       return;
2565     }
2566 
2567     case ReferenceTypeDescr::TYPE_STRING: {
2568       js::GCPtrString* stringPtr = reinterpret_cast<js::GCPtrString*>(mem);
2569       stringPtr->init(rt_->emptyString);
2570       return;
2571     }
2572   }
2573 
2574   MOZ_CRASH("Invalid kind");
2575 }
2576 
initInstances(const JSRuntime * rt,uint8_t * mem,size_t length)2577 void TypeDescr::initInstances(const JSRuntime* rt, uint8_t* mem,
2578                               size_t length) {
2579   MOZ_ASSERT(length >= 1);
2580 
2581   MemoryInitVisitor visitor(rt);
2582 
2583   // Initialize the 0th instance
2584   memset(mem, 0, size());
2585   if (opaque()) visitReferences(*this, mem, visitor);
2586 
2587   // Stamp out N copies of later instances
2588   uint8_t* target = mem;
2589   for (size_t i = 1; i < length; i++) {
2590     target += size();
2591     memcpy(target, mem, size());
2592   }
2593 }
2594 
2595 ///////////////////////////////////////////////////////////////////////////
2596 // Tracing instances
2597 
2598 namespace {
2599 
2600 class MemoryTracingVisitor {
2601   JSTracer* trace_;
2602 
2603  public:
MemoryTracingVisitor(JSTracer * trace)2604   explicit MemoryTracingVisitor(JSTracer* trace) : trace_(trace) {}
2605 
2606   void visitReference(ReferenceTypeDescr& descr, uint8_t* mem);
2607 };
2608 
2609 }  // namespace
2610 
visitReference(ReferenceTypeDescr & descr,uint8_t * mem)2611 void MemoryTracingVisitor::visitReference(ReferenceTypeDescr& descr,
2612                                           uint8_t* mem) {
2613   switch (descr.type()) {
2614     case ReferenceTypeDescr::TYPE_ANY: {
2615       GCPtrValue* heapValue = reinterpret_cast<js::GCPtrValue*>(mem);
2616       TraceEdge(trace_, heapValue, "reference-val");
2617       return;
2618     }
2619 
2620     case ReferenceTypeDescr::TYPE_OBJECT: {
2621       GCPtrObject* objectPtr = reinterpret_cast<js::GCPtrObject*>(mem);
2622       TraceNullableEdge(trace_, objectPtr, "reference-obj");
2623       return;
2624     }
2625 
2626     case ReferenceTypeDescr::TYPE_STRING: {
2627       GCPtrString* stringPtr = reinterpret_cast<js::GCPtrString*>(mem);
2628       TraceNullableEdge(trace_, stringPtr, "reference-str");
2629       return;
2630     }
2631   }
2632 
2633   MOZ_CRASH("Invalid kind");
2634 }
2635 
traceInstances(JSTracer * trace,uint8_t * mem,size_t length)2636 void TypeDescr::traceInstances(JSTracer* trace, uint8_t* mem, size_t length) {
2637   MemoryTracingVisitor visitor(trace);
2638 
2639   for (size_t i = 0; i < length; i++) {
2640     visitReferences(*this, mem, visitor);
2641     mem += size();
2642   }
2643 }
2644 
2645 namespace {
2646 
2647 struct TraceListVisitor {
2648   typedef Vector<int32_t, 0, SystemAllocPolicy> VectorType;
2649   VectorType stringOffsets, objectOffsets, valueOffsets;
2650 
2651   void visitReference(ReferenceTypeDescr& descr, uint8_t* mem);
2652 
2653   bool fillList(Vector<int32_t>& entries);
2654 };
2655 
2656 }  // namespace
2657 
visitReference(ReferenceTypeDescr & descr,uint8_t * mem)2658 void TraceListVisitor::visitReference(ReferenceTypeDescr& descr, uint8_t* mem) {
2659   VectorType* offsets;
2660   switch (descr.type()) {
2661     case ReferenceTypeDescr::TYPE_ANY:
2662       offsets = &valueOffsets;
2663       break;
2664     case ReferenceTypeDescr::TYPE_OBJECT:
2665       offsets = &objectOffsets;
2666       break;
2667     case ReferenceTypeDescr::TYPE_STRING:
2668       offsets = &stringOffsets;
2669       break;
2670     default:
2671       MOZ_CRASH("Invalid kind");
2672   }
2673 
2674   AutoEnterOOMUnsafeRegion oomUnsafe;
2675   if (!offsets->append((uintptr_t)mem))
2676     oomUnsafe.crash("TraceListVisitor::visitReference");
2677 }
2678 
fillList(Vector<int32_t> & entries)2679 bool TraceListVisitor::fillList(Vector<int32_t>& entries) {
2680   return entries.appendAll(stringOffsets) && entries.append(-1) &&
2681          entries.appendAll(objectOffsets) && entries.append(-1) &&
2682          entries.appendAll(valueOffsets) && entries.append(-1);
2683 }
2684 
CreateTraceList(JSContext * cx,HandleTypeDescr descr)2685 static bool CreateTraceList(JSContext* cx, HandleTypeDescr descr) {
2686   // Trace lists are only used for inline typed objects. We don't use them
2687   // for larger objects, both to limit the size of the trace lists and
2688   // because tracing outline typed objects is considerably more complicated
2689   // than inline ones.
2690   if (descr->size() > InlineTypedObject::MaximumSize || descr->transparent())
2691     return true;
2692 
2693   TraceListVisitor visitor;
2694   visitReferences(*descr, nullptr, visitor);
2695 
2696   Vector<int32_t> entries(cx);
2697   if (!visitor.fillList(entries)) return false;
2698 
2699   // Trace lists aren't necessary for descriptors with no references.
2700   MOZ_ASSERT(entries.length() >= 3);
2701   if (entries.length() == 3) return true;
2702 
2703   int32_t* list = cx->pod_malloc<int32_t>(entries.length());
2704   if (!list) return false;
2705 
2706   PodCopy(list, entries.begin(), entries.length());
2707 
2708   descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(list));
2709   return true;
2710 }
2711 
finalize(FreeOp * fop,JSObject * obj)2712 /* static */ void TypeDescr::finalize(FreeOp* fop, JSObject* obj) {
2713   TypeDescr& descr = obj->as<TypeDescr>();
2714   if (descr.hasTraceList()) js_free(const_cast<int32_t*>(descr.traceList()));
2715 }
2716