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