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