Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "builtin/TypedObject-inl.h"
8
9
#include "mozilla/Casting.h"
10
#include "mozilla/CheckedInt.h"
11
12
#include <algorithm>
13
14
#include "gc/Marking.h"
15
#include "js/CharacterEncoding.h"
16
#include "js/PropertySpec.h"
17
#include "js/Vector.h"
18
#include "util/StringBuffer.h"
19
#include "vm/GlobalObject.h"
20
#include "vm/JSFunction.h"
21
#include "vm/JSObject.h"
22
#include "vm/Realm.h"
23
#include "vm/SelfHosting.h"
24
#include "vm/StringType.h"
25
#include "vm/TypedArrayObject.h"
26
27
#include "gc/Nursery-inl.h"
28
#include "gc/StoreBuffer-inl.h"
29
#include "vm/JSAtom-inl.h"
30
#include "vm/JSObject-inl.h"
31
#include "vm/NativeObject-inl.h"
32
#include "vm/Shape-inl.h"
33
34
using mozilla::AssertedCast;
35
using mozilla::CheckedInt32;
36
using mozilla::IsPowerOfTwo;
37
using mozilla::PodCopy;
38
using mozilla::PointerRangeSize;
39
40
using namespace js;
41
42
const JSClass js::TypedObjectModuleObject::class_ = {
43
"TypedObject", JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
44
JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject)};
45
46
static const JSFunctionSpec TypedObjectMethods[] = {
47
JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0), JS_FS_END};
48
49
static void ReportCannotConvertTo(JSContext* cx, HandleValue fromValue,
50
const char* toType) {
51
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
52
InformalValueTypeName(fromValue), toType);
53
}
54
55
template <typename T>
56
BigInt* CreateBigInt(JSContext* cx, T i);
57
template <>
58
BigInt* CreateBigInt<int64_t>(JSContext* cx, int64_t i) {
59
return BigInt::createFromInt64(cx, i);
60
}
61
template <>
62
BigInt* CreateBigInt<uint64_t>(JSContext* cx, uint64_t u) {
63
return BigInt::createFromUint64(cx, u);
64
}
65
66
template <typename T>
67
T ConvertBigInt(BigInt* bi);
68
template <>
69
int64_t ConvertBigInt<int64_t>(BigInt* bi) {
70
return BigInt::toInt64(bi);
71
}
72
template <>
73
uint64_t ConvertBigInt<uint64_t>(BigInt* bi) {
74
return BigInt::toUint64(bi);
75
}
76
77
template <class T>
78
static inline T* ToObjectIf(HandleValue value) {
79
if (!value.isObject()) {
80
return nullptr;
81
}
82
83
if (!value.toObject().is<T>()) {
84
return nullptr;
85
}
86
87
return &value.toObject().as<T>();
88
}
89
90
static inline CheckedInt32 RoundUpToAlignment(CheckedInt32 address,
91
uint32_t align) {
92
MOZ_ASSERT(IsPowerOfTwo(align));
93
94
// Note: Be careful to order operators such that we first make the
95
// value smaller and then larger, so that we don't get false
96
// overflow errors due to (e.g.) adding `align` and then
97
// subtracting `1` afterwards when merely adding `align-1` would
98
// not have overflowed. Note that due to the nature of two's
99
// complement representation, if `address` is already aligned,
100
// then adding `align-1` cannot itself cause an overflow.
101
102
return ((address + (align - 1)) / align) * align;
103
}
104
105
/*
106
* Overwrites the contents of `typedObj` at offset `offset` with `val`
107
* converted to the type `typeObj`. This is done by delegating to
108
* self-hosted code. This is used for assignments and initializations.
109
*
110
* For example, consider the final assignment in this snippet:
111
*
112
* var Point = new StructType({x: float32, y: float32});
113
* var Line = new StructType({from: Point, to: Point});
114
* var line = new Line();
115
* line.to = {x: 22, y: 44};
116
*
117
* This would result in a call to `ConvertAndCopyTo`
118
* where:
119
* - typeObj = Point
120
* - typedObj = line
121
* - offset = sizeof(Point) == 8
122
* - val = {x: 22, y: 44}
123
* This would result in loading the value of `x`, converting
124
* it to a float32, and hen storing it at the appropriate offset,
125
* and then doing the same for `y`.
126
*
127
* Note that the type of `typeObj` may not be the
128
* type of `typedObj` but rather some subcomponent of `typedObj`.
129
*/
130
static bool ConvertAndCopyTo(JSContext* cx, HandleTypeDescr typeObj,
131
HandleTypedObject typedObj, int32_t offset,
132
HandleAtom name, HandleValue val) {
133
FixedInvokeArgs<5> args(cx);
134
135
args[0].setObject(*typeObj);
136
args[1].setObject(*typedObj);
137
args[2].setInt32(offset);
138
if (name) {
139
args[3].setString(name);
140
} else {
141
args[3].setNull();
142
}
143
args[4].set(val);
144
145
RootedValue dummy(cx); // ignored by ConvertAndCopyTo
146
return CallSelfHostedFunction(cx, cx->names().ConvertAndCopyTo, dummy, args,
147
&dummy);
148
}
149
150
static bool ConvertAndCopyTo(JSContext* cx, HandleTypedObject typedObj,
151
HandleValue val) {
152
Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
153
return ConvertAndCopyTo(cx, type, typedObj, 0, nullptr, val);
154
}
155
156
/*
157
* Overwrites the contents of `typedObj` at offset `offset` with `val`
158
* converted to the type `typeObj`
159
*/
160
static bool Reify(JSContext* cx, HandleTypeDescr type,
161
HandleTypedObject typedObj, size_t offset,
162
MutableHandleValue to) {
163
FixedInvokeArgs<3> args(cx);
164
165
args[0].setObject(*type);
166
args[1].setObject(*typedObj);
167
args[2].setInt32(offset);
168
169
return CallSelfHostedFunction(cx, cx->names().Reify, UndefinedHandleValue,
170
args, to);
171
}
172
173
// Extracts the `prototype` property from `obj`, throwing if it is
174
// missing or not an object.
175
static JSObject* GetPrototype(JSContext* cx, HandleObject obj) {
176
RootedValue prototypeVal(cx);
177
if (!GetProperty(cx, obj, obj, cx->names().prototype, &prototypeVal)) {
178
return nullptr;
179
}
180
if (!prototypeVal.isObject()) {
181
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
182
JSMSG_INVALID_PROTOTYPE);
183
return nullptr;
184
}
185
return &prototypeVal.toObject();
186
}
187
188
/***************************************************************************
189
* Typed Prototypes
190
*
191
* Every type descriptor has an associated prototype. Instances of
192
* that type descriptor use this as their prototype. Per the spec,
193
* typed object prototypes cannot be mutated.
194
*/
195
196
const JSClass js::TypedProto::class_ = {
197
"TypedProto", JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS)};
198
199
/***************************************************************************
200
* Scalar type objects
201
*
202
* Scalar type objects like `uint8`, `uint16`, are all instances of
203
* the ScalarTypeDescr class. Like all type objects, they have a reserved
204
* slot pointing to a TypeRepresentation object, which is used to
205
* distinguish which scalar type object this actually is.
206
*/
207
208
static const JSClassOps ScalarTypeDescrClassOps = {nullptr, /* addProperty */
209
nullptr, /* delProperty */
210
nullptr, /* enumerate */
211
nullptr, /* newEnumerate */
212
nullptr, /* resolve */
213
nullptr, /* mayResolve */
214
TypeDescr::finalize,
215
ScalarTypeDescr::call};
216
217
const JSClass js::ScalarTypeDescr::class_ = {
218
"Scalar",
219
JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
220
&ScalarTypeDescrClassOps};
221
222
const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
223
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
224
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
225
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), JS_FS_END};
226
227
uint32_t ScalarTypeDescr::size(Type t) {
228
return AssertedCast<uint32_t>(Scalar::byteSize(t));
229
}
230
231
uint32_t ScalarTypeDescr::alignment(Type t) {
232
return AssertedCast<uint32_t>(Scalar::byteSize(t));
233
}
234
235
/*static*/ const char* ScalarTypeDescr::typeName(Type type) {
236
switch (type) {
237
#define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
238
case constant_: \
239
return #name_;
240
JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
241
#undef NUMERIC_TYPE_TO_STRING
242
case Scalar::Int64:
243
case Scalar::MaxTypedArrayViewType:
244
break;
245
}
246
MOZ_CRASH("Invalid type");
247
}
248
249
bool ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
250
CallArgs args = CallArgsFromVp(argc, vp);
251
if (!args.requireAtLeast(cx, args.callee().getClass()->name, 1)) {
252
return false;
253
}
254
255
Rooted<ScalarTypeDescr*> descr(cx, &args.callee().as<ScalarTypeDescr>());
256
ScalarTypeDescr::Type type = descr->type();
257
258
switch (type) {
259
#define NUMBER_CALL(constant_, type_, name_) \
260
case constant_: { \
261
double number; \
262
if (!ToNumber(cx, args[0], &number)) { \
263
return false; \
264
} \
265
if (type == Scalar::Uint8Clamped) { \
266
number = ClampDoubleToUint8(number); \
267
} \
268
type_ converted = ConvertScalar<type_>(number); \
269
args.rval().setNumber((double)converted); \
270
return true; \
271
}
272
JS_FOR_EACH_SCALAR_NUMBER_TYPE_REPR(NUMBER_CALL)
273
#undef NUMBER_CALL
274
#define BIGINT_CALL(constant_, type_, name_) \
275
case constant_: { \
276
BigInt* bi = ToBigInt(cx, args[0]); \
277
if (!bi) { \
278
return false; \
279
} \
280
type_ converted = ConvertBigInt<type_>(bi); \
281
BigInt* ret = CreateBigInt<type_>(cx, converted); \
282
if (!ret) { \
283
return false; \
284
} \
285
args.rval().setBigInt(ret); \
286
return true; \
287
}
288
JS_FOR_EACH_SCALAR_BIGINT_TYPE_REPR(BIGINT_CALL)
289
#undef BIGINT_CALL
290
case Scalar::Int64:
291
case Scalar::MaxTypedArrayViewType:
292
MOZ_CRASH();
293
}
294
return true;
295
}
296
297
/* static */
298
TypeDescr* GlobalObject::getOrCreateScalarTypeDescr(
299
JSContext* cx, Handle<GlobalObject*> global, Scalar::Type scalarType) {
300
int32_t slot = 0;
301
switch (scalarType) {
302
case Scalar::Int32:
303
slot = TypedObjectModuleObject::Int32Desc;
304
break;
305
case Scalar::Int64:
306
slot = TypedObjectModuleObject::Int64Desc;
307
break;
308
case Scalar::Float32:
309
slot = TypedObjectModuleObject::Float32Desc;
310
break;
311
case Scalar::Float64:
312
slot = TypedObjectModuleObject::Float64Desc;
313
break;
314
default:
315
MOZ_CRASH("NYI");
316
}
317
318
Rooted<TypedObjectModuleObject*> module(
319
cx, &GlobalObject::getOrCreateTypedObjectModule(cx, global)
320
->as<TypedObjectModuleObject>());
321
if (!module) {
322
return nullptr;
323
}
324
return &module->getReservedSlot(slot).toObject().as<TypeDescr>();
325
}
326
327
/* static */
328
TypeDescr* GlobalObject::getOrCreateReferenceTypeDescr(
329
JSContext* cx, Handle<GlobalObject*> global, ReferenceType type) {
330
int32_t slot = 0;
331
switch (type) {
332
case ReferenceType::TYPE_OBJECT:
333
slot = TypedObjectModuleObject::ObjectDesc;
334
break;
335
case ReferenceType::TYPE_WASM_ANYREF:
336
slot = TypedObjectModuleObject::WasmAnyRefDesc;
337
break;
338
default:
339
MOZ_CRASH("NYI");
340
}
341
342
Rooted<TypedObjectModuleObject*> module(
343
cx, &GlobalObject::getOrCreateTypedObjectModule(cx, global)
344
->as<TypedObjectModuleObject>());
345
if (!module) {
346
return nullptr;
347
}
348
return &module->getReservedSlot(slot).toObject().as<TypeDescr>();
349
}
350
351
/***************************************************************************
352
* Reference type objects
353
*
354
* Reference type objects like `Any` or `Object` basically work the
355
* same way that the scalar type objects do. There is one class with
356
* many instances, and each instance has a reserved slot with a
357
* TypeRepresentation object, which is used to distinguish which
358
* reference type object this actually is.
359
*/
360
361
static const JSClassOps ReferenceTypeDescrClassOps = {
362
nullptr, /* addProperty */
363
nullptr, /* delProperty */
364
nullptr, /* enumerate */
365
nullptr, /* newEnumerate */
366
nullptr, /* resolve */
367
nullptr, /* mayResolve */
368
TypeDescr::finalize,
369
ReferenceTypeDescr::call};
370
371
const JSClass js::ReferenceTypeDescr::class_ = {
372
"Reference",
373
JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
374
&ReferenceTypeDescrClassOps};
375
376
const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
377
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
378
{JSFunctionSpec::Name("array"), {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
379
{JSFunctionSpec::Name("equivalent"),
380
{nullptr, nullptr},
381
1,
382
0,
383
"TypeDescrEquivalent"},
384
JS_FS_END};
385
386
static const uint32_t ReferenceSizes[] = {
387
#define REFERENCE_SIZE(_kind, _type, _name) sizeof(_type),
388
JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0
389
#undef REFERENCE_SIZE
390
};
391
392
uint32_t ReferenceTypeDescr::size(Type t) {
393
return ReferenceSizes[uint32_t(t)];
394
}
395
396
uint32_t ReferenceTypeDescr::alignment(Type t) {
397
return ReferenceSizes[uint32_t(t)];
398
}
399
400
/*static*/ const char* ReferenceTypeDescr::typeName(Type type) {
401
switch (type) {
402
#define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
403
case constant_: \
404
return #name_;
405
JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
406
#undef NUMERIC_TYPE_TO_STRING
407
}
408
MOZ_CRASH("Invalid type");
409
}
410
411
bool js::ReferenceTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
412
CallArgs args = CallArgsFromVp(argc, vp);
413
414
MOZ_ASSERT(args.callee().is<ReferenceTypeDescr>());
415
Rooted<ReferenceTypeDescr*> descr(cx,
416
&args.callee().as<ReferenceTypeDescr>());
417
418
if (!args.requireAtLeast(cx, descr->typeName(), 1)) {
419
return false;
420
}
421
422
switch (descr->type()) {
423
case ReferenceType::TYPE_ANY:
424
args.rval().set(args[0]);
425
return true;
426
427
case ReferenceType::TYPE_WASM_ANYREF:
428
// As a cast in JS, anyref is an identity operation.
429
args.rval().set(args[0]);
430
return true;
431
432
case ReferenceType::TYPE_OBJECT: {
433
RootedObject obj(cx, ToObject(cx, args[0]));
434
if (!obj) {
435
return false;
436
}
437
args.rval().setObject(*obj);
438
return true;
439
}
440
441
case ReferenceType::TYPE_STRING: {
442
RootedString obj(cx, ToString<CanGC>(cx, args[0]));
443
if (!obj) {
444
return false;
445
}
446
args.rval().setString(&*obj);
447
return true;
448
}
449
}
450
451
MOZ_CRASH("Unhandled Reference type");
452
}
453
454
/***************************************************************************
455
* ArrayMetaTypeDescr class
456
*/
457
458
/*
459
* For code like:
460
*
461
* var A = new TypedObject.ArrayType(uint8, 10);
462
* var S = new TypedObject.StructType({...});
463
*
464
* As usual, the [[Prototype]] of A is
465
* TypedObject.ArrayType.prototype. This permits adding methods to
466
* all ArrayType types, by setting
467
* TypedObject.ArrayType.prototype.methodName = function() { ... }.
468
* The same holds for S with respect to TypedObject.StructType.
469
*
470
* We may also want to add methods to *instances* of an ArrayType:
471
*
472
* var a = new A();
473
* var s = new S();
474
*
475
* As usual, the [[Prototype]] of a is A.prototype. What's
476
* A.prototype? It's an empty object, and you can set
477
* A.prototype.methodName = function() { ... } to add a method to all
478
* A instances. (And the same with respect to s and S.)
479
*
480
* But what if you want to add a method to all ArrayType instances,
481
* not just all A instances? (Or to all StructType instances.) The
482
* [[Prototype]] of the A.prototype empty object is
483
* TypedObject.ArrayType.prototype.prototype (two .prototype levels!).
484
* So just set TypedObject.ArrayType.prototype.prototype.methodName =
485
* function() { ... } to add a method to all ArrayType instances.
486
* (And, again, same with respect to s and S.)
487
*
488
* This function creates the A.prototype/S.prototype object. It returns an
489
* empty object with the .prototype.prototype object as its [[Prototype]].
490
*/
491
static TypedProto* CreatePrototypeObjectForComplexTypeInstance(
492
JSContext* cx, HandleObject ctorPrototype) {
493
RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
494
if (!ctorPrototypePrototype) {
495
return nullptr;
496
}
497
498
return NewObjectWithGivenProto<TypedProto>(cx, ctorPrototypePrototype,
499
SingletonObject);
500
}
501
502
static const JSClassOps ArrayTypeDescrClassOps = {nullptr, /* addProperty */
503
nullptr, /* delProperty */
504
nullptr, /* enumerate */
505
nullptr, /* newEnumerate */
506
nullptr, /* resolve */
507
nullptr, /* mayResolve */
508
TypeDescr::finalize,
509
nullptr, /* call */
510
nullptr, /* hasInstance */
511
TypedObject::construct};
512
513
const JSClass ArrayTypeDescr::class_ = {
514
"ArrayType",
515
JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
516
&ArrayTypeDescrClassOps};
517
518
const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
519
520
const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
521
{JSFunctionSpec::Name("array"), {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
522
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
523
{JSFunctionSpec::Name("equivalent"),
524
{nullptr, nullptr},
525
1,
526
0,
527
"TypeDescrEquivalent"},
528
JS_SELF_HOSTED_FN("build", "TypedObjectArrayTypeBuild", 3, 0),
529
JS_SELF_HOSTED_FN("from", "TypedObjectArrayTypeFrom", 3, 0),
530
JS_FS_END};
531
532
const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = {JS_PS_END};
533
534
const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = {
535
{JSFunctionSpec::Name("forEach"), {nullptr, nullptr}, 1, 0, "ArrayForEach"},
536
{JSFunctionSpec::Name("redimension"),
537
{nullptr, nullptr},
538
1,
539
0,
540
"TypedObjectArrayRedimension"},
541
JS_SELF_HOSTED_FN("map", "TypedObjectArrayMap", 2, 0),
542
JS_SELF_HOSTED_FN("reduce", "TypedObjectArrayReduce", 2, 0),
543
JS_SELF_HOSTED_FN("filter", "TypedObjectArrayFilter", 1, 0),
544
JS_FS_END};
545
546
bool js::CreateUserSizeAndAlignmentProperties(JSContext* cx,
547
HandleTypeDescr descr) {
548
// If data is transparent, also store the public slots.
549
if (descr->transparent()) {
550
// byteLength
551
RootedValue typeByteLength(
552
cx, Int32Value(AssertedCast<int32_t>(descr->size())));
553
if (!DefineDataProperty(cx, descr, cx->names().byteLength, typeByteLength,
554
JSPROP_READONLY | JSPROP_PERMANENT)) {
555
return false;
556
}
557
558
// byteAlignment
559
RootedValue typeByteAlignment(cx, Int32Value(descr->alignment()));
560
if (!DefineDataProperty(cx, descr, cx->names().byteAlignment,
561
typeByteAlignment,
562
JSPROP_READONLY | JSPROP_PERMANENT)) {
563
return false;
564
}
565
} else {
566
// byteLength
567
if (!DefineDataProperty(cx, descr, cx->names().byteLength,
568
UndefinedHandleValue,
569
JSPROP_READONLY | JSPROP_PERMANENT)) {
570
return false;
571
}
572
573
// byteAlignment
574
if (!DefineDataProperty(cx, descr, cx->names().byteAlignment,
575
UndefinedHandleValue,
576
JSPROP_READONLY | JSPROP_PERMANENT)) {
577
return false;
578
}
579
}
580
581
return true;
582
}
583
584
static bool CreateTraceList(JSContext* cx, HandleTypeDescr descr);
585
586
ArrayTypeDescr* ArrayMetaTypeDescr::create(JSContext* cx,
587
HandleObject arrayTypePrototype,
588
HandleTypeDescr elementType,
589
HandleAtom stringRepr, int32_t size,
590
int32_t length) {
591
MOZ_ASSERT(arrayTypePrototype);
592
Rooted<ArrayTypeDescr*> obj(cx);
593
obj = NewObjectWithGivenProto<ArrayTypeDescr>(cx, arrayTypePrototype,
594
SingletonObject);
595
if (!obj) {
596
return nullptr;
597
}
598
599
obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
600
obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
601
obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
602
Int32Value(elementType->alignment()));
603
obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
604
obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE,
605
BooleanValue(elementType->opaque()));
606
obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE,
607
ObjectValue(*elementType));
608
obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH, Int32Value(length));
609
obj->initReservedSlot(JS_DESCR_SLOT_FLAGS,
610
Int32Value(JS_DESCR_FLAG_ALLOW_CONSTRUCT));
611
612
RootedValue elementTypeVal(cx, ObjectValue(*elementType));
613
if (!DefineDataProperty(cx, obj, cx->names().elementType, elementTypeVal,
614
JSPROP_READONLY | JSPROP_PERMANENT)) {
615
return nullptr;
616
}
617
618
RootedValue lengthValue(cx, NumberValue(length));
619
if (!DefineDataProperty(cx, obj, cx->names().length, lengthValue,
620
JSPROP_READONLY | JSPROP_PERMANENT)) {
621
return nullptr;
622
}
623
624
if (!CreateUserSizeAndAlignmentProperties(cx, obj)) {
625
return nullptr;
626
}
627
628
// All arrays with the same element type have the same prototype. This
629
// prototype is created lazily and stored in the element type descriptor.
630
Rooted<TypedProto*> prototypeObj(cx);
631
if (elementType->getReservedSlot(JS_DESCR_SLOT_ARRAYPROTO).isObject()) {
632
prototypeObj = &elementType->getReservedSlot(JS_DESCR_SLOT_ARRAYPROTO)
633
.toObject()
634
.as<TypedProto>();
635
} else {
636
prototypeObj =
637
CreatePrototypeObjectForComplexTypeInstance(cx, arrayTypePrototype);
638
if (!prototypeObj) {
639
return nullptr;
640
}
641
elementType->setReservedSlot(JS_DESCR_SLOT_ARRAYPROTO,
642
ObjectValue(*prototypeObj));
643
}
644
645
obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
646
647
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj)) {
648
return nullptr;
649
}
650
651
if (!CreateTraceList(cx, obj)) {
652
return nullptr;
653
}
654
655
if (!cx->zone()->addTypeDescrObject(cx, obj)) {
656
ReportOutOfMemory(cx);
657
return nullptr;
658
}
659
660
return obj;
661
}
662
663
bool ArrayMetaTypeDescr::construct(JSContext* cx, unsigned argc, Value* vp) {
664
CallArgs args = CallArgsFromVp(argc, vp);
665
666
if (!ThrowIfNotConstructing(cx, args, "ArrayType")) {
667
return false;
668
}
669
670
RootedObject arrayTypeGlobal(cx, &args.callee());
671
672
// Expect two arguments. The first is a type object, the second is a length.
673
if (!args.requireAtLeast(cx, "ArrayType", 2)) {
674
return false;
675
}
676
677
if (!args[0].isObject() || !args[0].toObject().is<TypeDescr>()) {
678
ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
679
return false;
680
}
681
682
if (!args[1].isInt32() || args[1].toInt32() < 0) {
683
ReportCannotConvertTo(cx, args[1], "ArrayType length specifier");
684
return false;
685
}
686
687
Rooted<TypeDescr*> elementType(cx, &args[0].toObject().as<TypeDescr>());
688
689
int32_t length = args[1].toInt32();
690
691
// Compute the byte size.
692
CheckedInt32 size = CheckedInt32(elementType->size()) * length;
693
if (!size.isValid()) {
694
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
695
JSMSG_TYPEDOBJECT_TOO_BIG);
696
return false;
697
}
698
699
// Construct a canonical string `new ArrayType(<elementType>, N)`:
700
StringBuffer contents(cx);
701
if (!contents.append("new ArrayType(")) {
702
return false;
703
}
704
if (!contents.append(&elementType->stringRepr())) {
705
return false;
706
}
707
if (!contents.append(", ")) {
708
return false;
709
}
710
if (!NumberValueToStringBuffer(cx, NumberValue(length), contents)) {
711
return false;
712
}
713
if (!contents.append(")")) {
714
return false;
715
}
716
RootedAtom stringRepr(cx, contents.finishAtom());
717
if (!stringRepr) {
718
return false;
719
}
720
721
// Extract ArrayType.prototype
722
RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
723
if (!arrayTypePrototype) {
724
return false;
725
}
726
727
// Create the instance of ArrayType
728
Rooted<ArrayTypeDescr*> obj(cx);
729
obj = create(cx, arrayTypePrototype, elementType, stringRepr, size.value(),
730
length);
731
if (!obj) {
732
return false;
733
}
734
735
args.rval().setObject(*obj);
736
return true;
737
}
738
739
bool js::IsTypedObjectArray(JSObject& obj) {
740
if (!obj.is<TypedObject>()) {
741
return false;
742
}
743
TypeDescr& d = obj.as<TypedObject>().typeDescr();
744
return d.is<ArrayTypeDescr>();
745
}
746
747
/*********************************
748
* StructType class
749
*/
750
751
static const JSClassOps StructTypeDescrClassOps = {nullptr, /* addProperty */
752
nullptr, /* delProperty */
753
nullptr, /* enumerate */
754
nullptr, /* newEnumerate */
755
nullptr, /* resolve */
756
nullptr, /* mayResolve */
757
TypeDescr::finalize,
758
StructTypeDescr::call,
759
nullptr, /* hasInstance */
760
TypedObject::construct};
761
762
const JSClass StructTypeDescr::class_ = {
763
"StructType",
764
JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
765
&StructTypeDescrClassOps};
766
767
const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
768
769
const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
770
{JSFunctionSpec::Name("array"), {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
771
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
772
{JSFunctionSpec::Name("equivalent"),
773
{nullptr, nullptr},
774
1,
775
0,
776
"TypeDescrEquivalent"},
777
JS_FS_END};
778
779
const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {JS_PS_END};
780
781
const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {JS_FS_END};
782
783
CheckedInt32 StructMetaTypeDescr::Layout::addField(int32_t fieldAlignment,
784
int32_t fieldSize) {
785
// Alignment of the struct is the max of the alignment of its fields.
786
structAlignment = std::max(structAlignment, fieldAlignment);
787
788
// Align the pointer.
789
CheckedInt32 offset = RoundUpToAlignment(sizeSoFar, fieldAlignment);
790
if (!offset.isValid()) {
791
return offset;
792
}
793
794
// Allocate space.
795
sizeSoFar = offset + fieldSize;
796
if (!sizeSoFar.isValid()) {
797
return sizeSoFar;
798
}
799
800
return offset;
801
}
802
803
CheckedInt32 StructMetaTypeDescr::Layout::addScalar(Scalar::Type type) {
804
return addField(ScalarTypeDescr::alignment(type),
805
ScalarTypeDescr::size(type));
806
}
807
808
CheckedInt32 StructMetaTypeDescr::Layout::addReference(ReferenceType type) {
809
return addField(ReferenceTypeDescr::alignment(type),
810
ReferenceTypeDescr::size(type));
811
}
812
813
CheckedInt32 StructMetaTypeDescr::Layout::close(int32_t* alignment) {
814
if (alignment) {
815
*alignment = structAlignment;
816
}
817
return RoundUpToAlignment(sizeSoFar, structAlignment);
818
}
819
820
/* static */
821
JSObject* StructMetaTypeDescr::create(JSContext* cx, HandleObject metaTypeDescr,
822
HandleObject fields) {
823
// Obtain names of fields, which are the own properties of `fields`
824
RootedIdVector ids(cx);
825
if (!GetPropertyKeys(cx, fields, JSITER_OWNONLY | JSITER_SYMBOLS, &ids)) {
826
return nullptr;
827
}
828
829
// Iterate through each field. Collect values for the various
830
// vectors below and also track total size and alignment. Be wary
831
// of overflow!
832
RootedValueVector fieldTypeObjs(cx); // Type descriptor of each field.
833
bool opaque = false; // Opacity of struct.
834
835
Vector<StructFieldProps> fieldProps(cx);
836
837
RootedValue fieldTypeVal(cx);
838
RootedId id(cx);
839
Rooted<TypeDescr*> fieldType(cx);
840
841
for (unsigned int i = 0; i < ids.length(); i++) {
842
id = ids[i];
843
844
// Check that all the property names are non-numeric strings.
845
uint32_t unused;
846
if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
847
RootedValue idValue(cx, IdToValue(id));
848
ReportCannotConvertTo(cx, idValue, "StructType field name");
849
return nullptr;
850
}
851
852
// Load the value for the current field from the `fields` object.
853
// The value should be a type descriptor.
854
if (!GetProperty(cx, fields, fields, id, &fieldTypeVal)) {
855
return nullptr;
856
}
857
fieldType = ToObjectIf<TypeDescr>(fieldTypeVal);
858
if (!fieldType) {
859
ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
860
return nullptr;
861
}
862
863
// Collect field type object
864
if (!fieldTypeObjs.append(ObjectValue(*fieldType))) {
865
return nullptr;
866
}
867
868
// Along this path everything is mutable
869
StructFieldProps props;
870
props.isMutable = true;
871
if (!fieldProps.append(props)) {
872
return nullptr;
873
}
874
875
// Struct is opaque if any field is opaque
876
if (fieldType->opaque()) {
877
opaque = true;
878
}
879
}
880
881
RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
882
if (!structTypePrototype) {
883
return nullptr;
884
}
885
886
return createFromArrays(cx, structTypePrototype, opaque,
887
/* allowConstruct= */ true, ids, fieldTypeObjs,
888
fieldProps);
889
}
890
891
/* static */
892
StructTypeDescr* StructMetaTypeDescr::createFromArrays(
893
JSContext* cx, HandleObject structTypePrototype, bool opaque,
894
bool allowConstruct, HandleIdVector ids,
895
JS::HandleValueVector fieldTypeObjs, Vector<StructFieldProps>& fieldProps) {
896
StringBuffer stringBuffer(cx); // Canonical string repr
897
RootedValueVector fieldNames(cx); // Name of each field.
898
RootedValueVector fieldOffsets(cx); // Offset of each field field.
899
RootedValueVector fieldMuts(cx); // Mutability of each field.
900
RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
901
RootedObject userFieldTypes(cx); // User-exposed {f:descr} object.
902
Layout layout; // Field offsetter
903
904
userFieldOffsets = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
905
if (!userFieldOffsets) {
906
return nullptr;
907
}
908
909
userFieldTypes = NewBuiltinClassInstance<PlainObject>(cx, TenuredObject);
910
if (!userFieldTypes) {
911
return nullptr;
912
}
913
914
if (!stringBuffer.append("new StructType({")) {
915
return nullptr;
916
}
917
918
RootedId id(cx);
919
Rooted<TypeDescr*> fieldType(cx);
920
921
for (unsigned int i = 0; i < ids.length(); i++) {
922
id = ids[i];
923
924
// Collect field name
925
RootedValue fieldName(cx, IdToValue(id));
926
if (!fieldNames.append(fieldName)) {
927
return nullptr;
928
}
929
930
fieldType = ToObjectIf<TypeDescr>(fieldTypeObjs[i]);
931
932
// userFieldTypes[id] = typeObj
933
if (!DefineDataProperty(cx, userFieldTypes, id, fieldTypeObjs[i],
934
JSPROP_READONLY | JSPROP_PERMANENT)) {
935
return nullptr;
936
}
937
938
// Append "f:Type" to the string repr
939
if (i > 0 && !stringBuffer.append(", ")) {
940
return nullptr;
941
}
942
if (!stringBuffer.append(JSID_TO_ATOM(id))) {
943
return nullptr;
944
}
945
if (!stringBuffer.append(": ")) {
946
return nullptr;
947
}
948
if (!stringBuffer.append(&fieldType->stringRepr())) {
949
return nullptr;
950
}
951
952
CheckedInt32 offset = layout.addField(
953
fieldProps[i].alignAsInt64 ? ScalarTypeDescr::alignment(Scalar::Int64)
954
: fieldType->alignment(),
955
fieldType->size());
956
if (!offset.isValid()) {
957
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
958
JSMSG_TYPEDOBJECT_TOO_BIG);
959
return nullptr;
960
}
961
MOZ_ASSERT(offset.value() >= 0);
962
if (!fieldOffsets.append(Int32Value(offset.value()))) {
963
return nullptr;
964
}
965
if (!fieldMuts.append(BooleanValue(fieldProps[i].isMutable))) {
966
return nullptr;
967
}
968
969
// userFieldOffsets[id] = offset
970
RootedValue offsetValue(cx, Int32Value(offset.value()));
971
if (!DefineDataProperty(cx, userFieldOffsets, id, offsetValue,
972
JSPROP_READONLY | JSPROP_PERMANENT)) {
973
return nullptr;
974
}
975
}
976
977
// Complete string representation.
978
if (!stringBuffer.append("})")) {
979
return nullptr;
980
}
981
982
RootedAtom stringRepr(cx, stringBuffer.finishAtom());
983
if (!stringRepr) {
984
return nullptr;
985
}
986
987
int32_t alignment;
988
CheckedInt32 totalSize = layout.close(&alignment);
989
if (!totalSize.isValid()) {
990
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
991
JSMSG_TYPEDOBJECT_TOO_BIG);
992
return nullptr;
993
}
994
995
// Now create the resulting type descriptor.
996
997
Rooted<StructTypeDescr*> descr(cx);
998
descr = NewObjectWithGivenProto<StructTypeDescr>(cx, structTypePrototype,
999
SingletonObject);
1000
if (!descr) {
1001
return nullptr;
1002
}
1003
1004
descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
1005
descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
1006
descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
1007
Int32Value(AssertedCast<int32_t>(alignment)));
1008
descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
1009
descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
1010
descr->initReservedSlot(
1011
JS_DESCR_SLOT_FLAGS,
1012
Int32Value(allowConstruct ? JS_DESCR_FLAG_ALLOW_CONSTRUCT : 0));
1013
1014
// Construct for internal use an array with the name for each field.
1015
{
1016
RootedObject fieldNamesVec(cx);
1017
fieldNamesVec = NewDenseCopiedArray(
1018
cx, fieldNames.length(), fieldNames.begin(), nullptr, TenuredObject);
1019
if (!fieldNamesVec) {
1020
return nullptr;
1021
}
1022
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
1023
ObjectValue(*fieldNamesVec));
1024
}
1025
1026
// Construct for internal use an array with the type object for each field.
1027
RootedObject fieldTypeVec(cx);
1028
fieldTypeVec =
1029
NewDenseCopiedArray(cx, fieldTypeObjs.length(), fieldTypeObjs.begin(),
1030
nullptr, TenuredObject);
1031
if (!fieldTypeVec) {
1032
return nullptr;
1033
}
1034
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
1035
ObjectValue(*fieldTypeVec));
1036
1037
// Construct for internal use an array with the offset for each field.
1038
{
1039
RootedObject fieldOffsetsVec(cx);
1040
fieldOffsetsVec =
1041
NewDenseCopiedArray(cx, fieldOffsets.length(), fieldOffsets.begin(),
1042
nullptr, TenuredObject);
1043
if (!fieldOffsetsVec) {
1044
return nullptr;
1045
}
1046
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
1047
ObjectValue(*fieldOffsetsVec));
1048
}
1049
1050
// Construct for internal use an array with the mutability for each field.
1051
{
1052
RootedObject fieldMutsVec(cx);
1053
fieldMutsVec = NewDenseCopiedArray(
1054
cx, fieldMuts.length(), fieldMuts.begin(), nullptr, TenuredObject);
1055
if (!fieldMutsVec) {
1056
return nullptr;
1057
}
1058
descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_MUTS,
1059
ObjectValue(*fieldMutsVec));
1060
}
1061
1062
// Create data properties fieldOffsets and fieldTypes
1063
// TODO: Probably also want to track mutability here, but not important yet.
1064
if (!FreezeObject(cx, userFieldOffsets)) {
1065
return nullptr;
1066
}
1067
if (!FreezeObject(cx, userFieldTypes)) {
1068
return nullptr;
1069
}
1070
RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
1071
if (!DefineDataProperty(cx, descr, cx->names().fieldOffsets,
1072
userFieldOffsetsValue,
1073
JSPROP_READONLY | JSPROP_PERMANENT)) {
1074
return nullptr;
1075
}
1076
RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
1077
if (!DefineDataProperty(cx, descr, cx->names().fieldTypes,
1078
userFieldTypesValue,
1079
JSPROP_READONLY | JSPROP_PERMANENT)) {
1080
return nullptr;
1081
}
1082
1083
if (!CreateUserSizeAndAlignmentProperties(cx, descr)) {
1084
return nullptr;
1085
}
1086
1087
Rooted<TypedProto*> prototypeObj(cx);
1088
prototypeObj =
1089
CreatePrototypeObjectForComplexTypeInstance(cx, structTypePrototype);
1090
if (!prototypeObj) {
1091
return nullptr;
1092
}
1093
1094
descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
1095
1096
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj)) {
1097
return nullptr;
1098
}
1099
1100
if (!CreateTraceList(cx, descr)) {
1101
return nullptr;
1102
}
1103
1104
if (!cx->zone()->addTypeDescrObject(cx, descr)) {
1105
ReportOutOfMemory(cx);
1106
return nullptr;
1107
}
1108
1109
return descr;
1110
}
1111
1112
bool StructMetaTypeDescr::construct(JSContext* cx, unsigned int argc,
1113
Value* vp) {
1114
CallArgs args = CallArgsFromVp(argc, vp);
1115
1116
if (!ThrowIfNotConstructing(cx, args, "StructType")) {
1117
return false;
1118
}
1119
1120
if (args.length() >= 1 && args[0].isObject()) {
1121
RootedObject metaTypeDescr(cx, &args.callee());
1122
RootedObject fields(cx, &args[0].toObject());
1123
RootedObject obj(cx, create(cx, metaTypeDescr, fields));
1124
if (!obj) {
1125
return false;
1126
}
1127
args.rval().setObject(*obj);
1128
return true;
1129
}
1130
1131
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1132
JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
1133
return false;
1134
}
1135
1136
size_t StructTypeDescr::fieldCount() const {
1137
return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
1138
.getDenseInitializedLength();
1139
}
1140
1141
bool StructTypeDescr::fieldIndex(jsid id, size_t* out) const {
1142
ArrayObject& fieldNames = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
1143
size_t l = fieldNames.getDenseInitializedLength();
1144
for (size_t i = 0; i < l; i++) {
1145
JSAtom& a = fieldNames.getDenseElement(i).toString()->asAtom();
1146
if (JSID_IS_ATOM(id, &a)) {
1147
*out = i;
1148
return true;
1149
}
1150
}
1151
return false;
1152
}
1153
1154
JSAtom& StructTypeDescr::fieldName(size_t index) const {
1155
return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
1156
.getDenseElement(index)
1157
.toString()
1158
->asAtom();
1159
}
1160
1161
size_t StructTypeDescr::fieldOffset(size_t index) const {
1162
ArrayObject& fieldOffsets =
1163
fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
1164
MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
1165
return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
1166
}
1167
1168
bool StructTypeDescr::fieldIsMutable(size_t index) const {
1169
ArrayObject& fieldMuts = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_MUTS);
1170
MOZ_ASSERT(index < fieldMuts.getDenseInitializedLength());
1171
return fieldMuts.getDenseElement(index).toBoolean();
1172
}
1173
1174
TypeDescr& StructTypeDescr::fieldDescr(size_t index) const {
1175
ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
1176
MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
1177
return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
1178
}
1179
1180
bool StructTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) {
1181
// A structure type is a constructor and hence callable, but at present the
1182
// call always throws.
1183
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1184
JSMSG_TYPEDOBJECT_STRUCTTYPE_NOT_CALLABLE);
1185
return false;
1186
}
1187
1188
/******************************************************************************
1189
* Creating the TypedObject "module"
1190
*
1191
* We create one global, `TypedObject`, which contains the following
1192
* members:
1193
*
1194
* 1. uint8, uint16, etc
1195
* 2. ArrayType
1196
* 3. StructType
1197
*
1198
* Each of these is a function and hence their prototype is
1199
* `Function.__proto__` (in terms of the JS Engine, they are not
1200
* JSFunctions but rather instances of their own respective JSClasses
1201
* which override the call and construct operations).
1202
*
1203
* Each type object also has its own `prototype` field. Therefore,
1204
* using `StructType` as an example, the basic setup is:
1205
*
1206
* StructType --__proto__--> Function.__proto__
1207
* |
1208
* prototype -- prototype --> { }
1209
* |
1210
* v
1211
* { } -----__proto__--> Function.__proto__
1212
*
1213
* When a new type object (e.g., an instance of StructType) is created,
1214
* it will look as follows:
1215
*
1216
* MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__
1217
* | |
1218
* | prototype
1219
* | |
1220
* | v
1221
* prototype -----__proto__----> { }
1222
* |
1223
* v
1224
* { } --__proto__-> Object.prototype
1225
*
1226
* Finally, when an instance of `MyStruct` is created, its
1227
* structure is as follows:
1228
*
1229
* object -__proto__->
1230
* MyStruct.prototype -__proto__->
1231
* StructType.prototype.prototype -__proto__->
1232
* Object.prototype
1233
*/
1234
1235
// Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr`
1236
template <typename T>
1237
static bool DefineSimpleTypeDescr(JSContext* cx, Handle<GlobalObject*> global,
1238
HandleObject module, typename T::Type type,
1239
HandlePropertyName className) {
1240
RootedObject objProto(cx,
1241
GlobalObject::getOrCreateObjectPrototype(cx, global));
1242
if (!objProto) {
1243
return false;
1244
}
1245
1246
RootedObject funcProto(
1247
cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
1248
if (!funcProto) {
1249
return false;
1250
}
1251
1252
Rooted<T*> descr(cx);
1253
descr = NewObjectWithGivenProto<T>(cx, funcProto, SingletonObject);
1254
if (!descr) {
1255
return false;
1256
}
1257
1258
descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
1259
descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
1260
descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT,
1261
Int32Value(T::alignment(type)));
1262
descr->initReservedSlot(JS_DESCR_SLOT_SIZE,
1263
Int32Value(AssertedCast<int32_t>(T::size(type))));
1264
descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
1265
descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(int32_t(type)));
1266
descr->initReservedSlot(JS_DESCR_SLOT_FLAGS, Int32Value(0));
1267
1268
if (!CreateUserSizeAndAlignmentProperties(cx, descr)) {
1269
return false;
1270
}
1271
1272
if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods)) {
1273
return false;
1274
}
1275
1276
// Create the typed prototype for the scalar type. This winds up
1277
// not being user accessible, but we still create one for consistency.
1278
Rooted<TypedProto*> proto(cx);
1279
proto = NewObjectWithGivenProto<TypedProto>(cx, objProto, TenuredObject);
1280
if (!proto) {
1281
return false;
1282
}
1283
descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
1284
1285
RootedValue descrValue(cx, ObjectValue(*descr));
1286
if (!DefineDataProperty(cx, module, className, descrValue, 0)) {
1287
return false;
1288
}
1289
1290
if (!CreateTraceList(cx, descr)) {
1291
return false;
1292
}
1293
1294
if (!cx->zone()->addTypeDescrObject(cx, descr)) {
1295
return false;
1296
}
1297
1298
return true;
1299
}
1300
1301
///////////////////////////////////////////////////////////////////////////
1302
1303
template <typename T>
1304
static JSObject* DefineMetaTypeDescr(JSContext* cx, const char* name,
1305
Handle<GlobalObject*> global,
1306
Handle<TypedObjectModuleObject*> module,
1307
TypedObjectModuleObject::Slot protoSlot) {
1308
RootedAtom className(cx, Atomize(cx, name, strlen(name)));
1309
if (!className) {
1310
return nullptr;
1311
}
1312
1313
RootedObject funcProto(
1314
cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
1315
if (!funcProto) {
1316
return nullptr;
1317
}
1318
1319
// Create ctor.prototype, which inherits from Function.__proto__
1320
1321
RootedObject proto(
1322
cx, NewObjectWithGivenProto<PlainObject>(cx, funcProto, SingletonObject));
1323
if (!proto) {
1324
return nullptr;
1325
}
1326
1327
// Create ctor.prototype.prototype, which inherits from Object.__proto__
1328
1329
RootedObject objProto(cx,
1330
GlobalObject::getOrCreateObjectPrototype(cx, global));
1331
if (!objProto) {
1332
return nullptr;
1333
}
1334
RootedObject protoProto(cx);
1335
protoProto =
1336
NewObjectWithGivenProto<PlainObject>(cx, objProto, SingletonObject);
1337
if (!protoProto) {
1338
return nullptr;
1339
}
1340
1341
RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
1342
if (!DefineDataProperty(cx, proto, cx->names().prototype, protoProtoValue,
1343
JSPROP_READONLY | JSPROP_PERMANENT)) {
1344
return nullptr;
1345
}
1346
1347
// Create ctor itself
1348
1349
const int constructorLength = 2;
1350
RootedFunction ctor(cx);
1351
ctor = GlobalObject::createConstructor(cx, T::construct, className,
1352
constructorLength);
1353
if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) ||
1354
!DefinePropertiesAndFunctions(cx, proto, T::typeObjectProperties,
1355
T::typeObjectMethods) ||
1356
!DefinePropertiesAndFunctions(cx, protoProto, T::typedObjectProperties,
1357
T::typedObjectMethods)) {
1358
return nullptr;
1359
}
1360
1361
module->initReservedSlot(protoSlot, ObjectValue(*proto));
1362
1363
return ctor;
1364
}
1365
1366
/* The initialization strategy for TypedObjects is mildly unusual
1367
* compared to other classes. Because all of the types are members
1368
* of a single global, `TypedObject`, we basically make the
1369
* initializer for the `TypedObject` class populate the
1370
* `TypedObject` global (which is referred to as "module" herein).
1371
*/
1372
/* static */
1373
bool GlobalObject::initTypedObjectModule(JSContext* cx,
1374
Handle<GlobalObject*> global) {
1375
RootedObject objProto(cx,
1376
GlobalObject::getOrCreateObjectPrototype(cx, global));
1377
if (!objProto) {
1378
return false;
1379
}
1380
1381
Rooted<TypedObjectModuleObject*> module(cx);
1382
module = NewObjectWithGivenProto<TypedObjectModuleObject>(cx, objProto,
1383
SingletonObject);
1384
if (!module) {
1385
return false;
1386
}
1387
1388
if (!JS_DefineFunctions(cx, module, TypedObjectMethods)) {
1389
return false;
1390
}
1391
1392
// uint8, uint16, any, etc
1393
1394
#define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_) \
1395
if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_, \
1396
cx->names().name_)) \
1397
return false;
1398
JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
1399
#undef BINARYDATA_SCALAR_DEFINE
1400
1401
#define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_) \
1402
if (!DefineSimpleTypeDescr<ReferenceTypeDescr>( \
1403
cx, global, module, constant_, cx->names().name_)) \
1404
return false;
1405
JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
1406
#undef BINARYDATA_REFERENCE_DEFINE
1407
1408
// Tuck away descriptors we will use for wasm.
1409
1410
RootedValue typeDescr(cx);
1411
1412
// The lookups should fail only from atomization-related OOM in
1413
// JS_GetProperty(). The properties themselves will always exist on the
1414
// object.
1415
1416
if (!JS_GetProperty(cx, module, "int32", &typeDescr)) {
1417
return false;
1418
}
1419
module->initReservedSlot(TypedObjectModuleObject::Int32Desc, typeDescr);
1420
1421
if (!JS_GetProperty(cx, module, "int64", &typeDescr)) {
1422
return false;
1423
}
1424
module->initReservedSlot(TypedObjectModuleObject::Int64Desc, typeDescr);
1425
1426
if (!JS_GetProperty(cx, module, "float32", &typeDescr)) {
1427
return false;
1428
}
1429
module->initReservedSlot(TypedObjectModuleObject::Float32Desc, typeDescr);
1430
1431
if (!JS_GetProperty(cx, module, "float64", &typeDescr)) {
1432
return false;
1433
}
1434
module->initReservedSlot(TypedObjectModuleObject::Float64Desc, typeDescr);
1435
1436
if (!JS_GetProperty(cx, module, "Object", &typeDescr)) {
1437
return false;
1438
}
1439
module->initReservedSlot(TypedObjectModuleObject::ObjectDesc, typeDescr);
1440
1441
if (!JS_GetProperty(cx, module, "WasmAnyRef", &typeDescr)) {
1442
return false;
1443
}
1444
module->initReservedSlot(TypedObjectModuleObject::WasmAnyRefDesc, typeDescr);
1445
1446
// ArrayType.
1447
1448
RootedObject arrayType(cx);
1449
arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
1450
cx, "ArrayType", global, module,
1451
TypedObjectModuleObject::ArrayTypePrototype);
1452
if (!arrayType) {
1453
return false;
1454
}
1455
1456
RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
1457
if (!DefineDataProperty(cx, module, cx->names().ArrayType, arrayTypeValue,
1458
JSPROP_READONLY | JSPROP_PERMANENT)) {
1459
return false;
1460
}
1461
1462
// StructType.
1463
1464
RootedObject structType(cx);
1465
structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
1466
cx, "StructType", global, module,
1467
TypedObjectModuleObject::StructTypePrototype);
1468
if (!structType) {
1469
return false;
1470
}
1471
1472
RootedValue structTypeValue(cx, ObjectValue(*structType));
1473
if (!DefineDataProperty(cx, module, cx->names().StructType, structTypeValue,
1474
JSPROP_READONLY | JSPROP_PERMANENT)) {
1475
return false;
1476
}
1477
1478
// Everything is setup, install module on the global object:
1479
RootedValue moduleValue(cx, ObjectValue(*module));
1480
if (!DefineDataProperty(cx, global, cx->names().TypedObject, moduleValue,
1481
JSPROP_RESOLVING)) {
1482
return false;
1483
}
1484
1485
global->setConstructor(JSProto_TypedObject, moduleValue);
1486
1487
return module;
1488
}
1489
1490
JSObject* js::InitTypedObjectModuleObject(JSContext* cx,
1491
Handle<GlobalObject*> global) {
1492
return GlobalObject::getOrCreateTypedObjectModule(cx, global);
1493
}
1494
1495
/******************************************************************************
1496
* Typed objects
1497
*/
1498
1499
uint32_t TypedObject::offset() const {
1500
if (is<InlineTypedObject>()) {
1501
return 0;
1502
}
1503
return PointerRangeSize(typedMemBase(), typedMem());
1504
}
1505
1506
uint32_t TypedObject::length() const {
1507
return typeDescr().as<ArrayTypeDescr>().length();
1508
}
1509
1510
uint8_t* TypedObject::typedMem() const {
1511
MOZ_ASSERT(isAttached());
1512
1513
if (is<InlineTypedObject>()) {
1514
return as<InlineTypedObject>().inlineTypedMem();
1515
}
1516
return as<OutlineTypedObject>().outOfLineTypedMem();
1517
}
1518
1519
uint8_t* TypedObject::typedMemBase() const {
1520
MOZ_ASSERT(isAttached());
1521
MOZ_ASSERT(is<OutlineTypedObject>());
1522
1523
JSObject& owner = as<OutlineTypedObject>().owner();
1524
if (owner.is<ArrayBufferObject>()) {
1525
return owner.as<ArrayBufferObject>().dataPointer();
1526
}
1527
return owner.as<InlineTypedObject>().inlineTypedMem();
1528
}
1529
1530
bool TypedObject::isAttached() const {
1531
if (is<InlineTransparentTypedObject>()) {
1532
ObjectWeakMap* table = ObjectRealm::get(this).lazyArrayBuffers.get();
1533
if (table) {
1534
JSObject* buffer = table->lookup(this);
1535
if (buffer) {
1536
return !buffer->as<ArrayBufferObject>().isDetached();
1537
}
1538
}
1539
return true;
1540
}
1541
if (is<InlineOpaqueTypedObject>()) {
1542
return true;
1543
}
1544
if (!as<OutlineTypedObject>().outOfLineTypedMem()) {
1545
return false;
1546
}
1547
JSObject& owner = as<OutlineTypedObject>().owner();
1548
if (owner.is<ArrayBufferObject>() &&
1549
owner.as<ArrayBufferObject>().isDetached()) {
1550
return false;
1551
}
1552
return true;
1553
}
1554
1555
/* static */
1556
bool TypedObject::GetByteOffset(JSContext* cx, unsigned argc, Value* vp) {
1557
CallArgs args = CallArgsFromVp(argc, vp);
1558
args.rval().setInt32(
1559
AssertedCast<int32_t>(args[0].toObject().as<TypedObject>().offset()));
1560
return true;
1561
}
1562
1563
/******************************************************************************
1564
* Outline typed objects
1565
*/
1566
1567
/*static*/
1568
OutlineTypedObject* OutlineTypedObject::createUnattached(JSContext* cx,
1569
HandleTypeDescr descr,
1570
gc::InitialHeap heap) {
1571
if (descr->opaque()) {
1572
return createUnattachedWithClass(cx, &OutlineOpaqueTypedObject::class_,
1573
descr, heap);
1574
} else {
1575
return createUnattachedWithClass(cx, &OutlineTransparentTypedObject::class_,
1576
descr, heap);
1577
}
1578
}
1579
1580
void OutlineTypedObject::setOwnerAndData(JSObject* owner, uint8_t* data) {
1581
// Typed objects cannot move from one owner to another, so don't worry
1582
// about pre barriers during this initialization.
1583
owner_ = owner;
1584
data_ = data;
1585
1586
if (owner) {
1587
if (!IsInsideNursery(this) && IsInsideNursery(owner)) {
1588
// Trigger a post barrier when attaching an object outside the nursery to
1589
// one that is inside it.
1590
owner->storeBuffer()->putWholeCell(this);
1591
} else if (IsInsideNursery(this) && !IsInsideNursery(owner)) {
1592
// ...and also when attaching an object inside the nursery to one that is
1593
// outside it, for a subtle reason -- the outline object now points to
1594
// the memory owned by 'owner', and can modify object/string references
1595
// stored in that memory, potentially storing nursery pointers in it. If
1596
// the outline object is in the nursery, then the post barrier will do
1597
// nothing; you will be writing a nursery pointer "into" a nursery
1598
// object. But that will result in the tenured owner's data containing a
1599
// nursery pointer, and thus we need a store buffer edge. Since we can't
1600
// catch the actual write, register the owner preemptively now.
1601
storeBuffer()->putWholeCell(owner);
1602
}
1603
}
1604
}
1605
1606
/*static*/
1607
OutlineTypedObject* OutlineTypedObject::createUnattachedWithClass(
1608
JSContext* cx, const JSClass* clasp, HandleTypeDescr descr,
1609
gc::InitialHeap heap) {
1610
MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
1611
clasp == &OutlineOpaqueTypedObject::class_);
1612
1613
AutoSetNewObjectMetadata metadata(cx);
1614
1615
RootedObjectGroup group(
1616
cx, ObjectGroup::defaultNewGroup(
1617
cx, clasp, TaggedProto(&descr->typedProto()), descr));
1618
if (!group) {
1619
return nullptr;
1620
}
1621
1622
NewObjectKind newKind =
1623
(heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
1624
OutlineTypedObject* obj = NewObjectWithGroup<OutlineTypedObject>(
1625
cx, group, gc::AllocKind::OBJECT0, newKind);
1626
if (!obj) {
1627
return nullptr;
1628
}
1629
1630
obj->setOwnerAndData(nullptr, nullptr);
1631
return obj;
1632
}
1633
1634
void OutlineTypedObject::attach(JSContext* cx, ArrayBufferObject& buffer,
1635
uint32_t offset) {
1636
MOZ_ASSERT(!isAttached());
1637
MOZ_ASSERT(offset <= buffer.byteLength());
1638
MOZ_ASSERT(size() <= buffer.byteLength() - offset);
1639
1640
buffer.setHasTypedObjectViews();
1641
1642
{
1643
AutoEnterOOMUnsafeRegion oomUnsafe;
1644
if (!buffer.addView(cx, this)) {
1645
oomUnsafe.crash("TypedObject::attach");
1646
}
1647
}
1648
1649
setOwnerAndData(&buffer, buffer.dataPointer() + offset);
1650
}
1651
1652
void OutlineTypedObject::attach(JSContext* cx, TypedObject& typedObj,
1653
uint32_t offset) {
1654
MOZ_ASSERT(!isAttached());
1655
MOZ_ASSERT(typedObj.isAttached());
1656
1657
JSObject* owner = &typedObj;
1658
if (typedObj.is<OutlineTypedObject>()) {
1659
owner = &typedObj.as<OutlineTypedObject>().owner();
1660
MOZ_ASSERT(typedObj.offset() <= UINT32_MAX - offset);
1661
offset += typedObj.offset();
1662
}
1663
1664
if (owner->is<ArrayBufferObject>()) {
1665
attach(cx, owner->as<ArrayBufferObject>(), offset);
1666
} else {
1667
MOZ_ASSERT(owner->is<InlineTypedObject>());
1668
JS::AutoCheckCannotGC nogc(cx);
1669
setOwnerAndData(
1670
owner, owner->as<InlineTypedObject>().inlineTypedMem(nogc) + offset);
1671
}
1672
}
1673
1674
/*static*/
1675
OutlineTypedObject* OutlineTypedObject::createDerived(
1676
JSContext* cx, HandleTypeDescr type, HandleTypedObject typedObj,
1677
uint32_t offset) {
1678
MOZ_ASSERT(offset <= typedObj->size());
1679
MOZ_ASSERT(offset + type->size() <= typedObj->size());
1680
1681
const JSClass* clasp = typedObj->opaque()
1682
? &OutlineOpaqueTypedObject::class_
1683
: &OutlineTransparentTypedObject::class_;
1684
Rooted<OutlineTypedObject*> obj(cx);
1685
obj = createUnattachedWithClass(cx, clasp, type);
1686
if (!obj) {
1687
return nullptr;
1688
}
1689
1690
obj->attach(cx, *typedObj, offset);
1691
return obj;
1692
}
1693
1694
/*static*/
1695
TypedObject* TypedObject::createZeroed(JSContext* cx, HandleTypeDescr descr,
1696
gc::InitialHeap heap) {
1697
// If possible, create an object with inline data.
1698
if (InlineTypedObject::canAccommodateType(descr)) {
1699
AutoSetNewObjectMetadata metadata(cx);
1700
1701
InlineTypedObject* obj = InlineTypedObject::create(cx, descr, heap);
1702
if (!obj) {
1703
return nullptr;
1704
}
1705
JS::AutoCheckCannotGC nogc(cx);
1706
descr->initInstances(cx->runtime(), obj->inlineTypedMem(nogc), 1);
1707
return obj;
1708
}
1709
1710
// Create unattached wrapper object.
1711
Rooted<OutlineTypedObject*> obj(
1712
cx, OutlineTypedObject::createUnattached(cx, descr, heap));
1713
if (!obj) {
1714
return nullptr;
1715
}
1716
1717
// Allocate and initialize the memory for this instance.
1718
size_t totalSize = descr->size();
1719
Rooted<ArrayBufferObject*> buffer(cx);
1720
buffer = ArrayBufferObject::createZeroed(cx, totalSize);
1721
if (!buffer) {
1722
return nullptr;
1723
}
1724
descr->initInstances(cx->runtime(), buffer->dataPointer(), 1);
1725
obj->attach(cx, *buffer, 0);
1726
return obj;
1727
}
1728
1729
/* static */
1730
void OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object) {
1731
OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
1732
1733
TraceEdge(trc, typedObj.shapePtr(), "OutlineTypedObject_shape");
1734
1735
if (!typedObj.owner_) {
1736
return;
1737
}
1738
1739
TypeDescr& descr = typedObj.typeDescr();
1740
1741
// Mark the owner, watching in case it is moved by the tracer.
1742
JSObject* oldOwner = typedObj.owner_;
1743
TraceManuallyBarrieredEdge(trc, &typedObj.owner_, "typed object owner");
1744
JSObject* owner = typedObj.owner_;
1745
1746
uint8_t* oldData = typedObj.outOfLineTypedMem();
1747
uint8_t* newData = oldData;
1748
1749
// Update the data pointer if the owner moved and the owner's data is
1750
// inline with it.
1751
if (owner != oldOwner && (owner->is<InlineTypedObject>() ||