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