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