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
#ifndef builtin_TypedObject_h
8
#define builtin_TypedObject_h
9
10
#include "mozilla/CheckedInt.h"
11
12
#include "builtin/TypedObjectConstants.h"
13
#include "gc/WeakMap.h"
14
#include "js/Conversions.h"
15
#include "vm/ArrayBufferObject.h"
16
#include "vm/JSObject.h"
17
18
/*
19
* -------------
20
* [SMDOC] Typed Objects
21
* -------------
22
*
23
* Typed objects are a special kind of JS object where the data is
24
* given well-structured form. To use a typed object, users first
25
* create *type objects* (no relation to the type objects used in TI)
26
* that define the type layout. For example, a statement like:
27
*
28
* var PointType = new StructType({x: uint8, y: uint8});
29
*
30
* would create a type object PointType that is a struct with
31
* two fields, each of uint8 type.
32
*
33
* This comment typically assumes familiary with the API. For more
34
* info on the API itself, see the Harmony wiki page at
36
* ES6 spec (not finalized at the time of this writing).
37
*
38
* - Initialization:
39
*
40
* Currently, all "globals" related to typed objects are packaged
41
* within a single "module" object `TypedObject`. This module has its
42
* own JSClass and when that class is initialized, we also create
43
* and define all other values (in `js::InitTypedObjectModuleClass()`).
44
*
45
* - Type objects, meta type objects, and type representations:
46
*
47
* There are a number of pre-defined type objects, one for each
48
* scalar type (`uint8` etc). Each of these has its own class_,
49
* defined in `DefineNumericClass()`.
50
*
51
* There are also meta type objects (`ArrayType`, `StructType`).
52
* These constructors are not themselves type objects but rather the
53
* means for the *user* to construct new typed objects.
54
*
55
* Each type object is associated with a *type representation* (see
56
* TypeRepresentation.h). Type representations are canonical versions
57
* of type objects. We attach them to TI type objects and (eventually)
58
* use them for shape guards etc. They are purely internal to the
59
* engine and are not exposed to end users (though self-hosted code
60
* sometimes accesses them).
61
*
62
* - Typed objects:
63
*
64
* A typed object is an instance of a *type object* (note the past participle).
65
* Typed objects can be either transparent or opaque, depending on whether
66
* their underlying buffer can be accessed. Transparent and opaque typed
67
* objects have different classes, and can have different physical layouts.
68
* The following layouts are possible:
69
*
70
* InlineTypedObject: Typed objects whose data immediately follows the object's
71
* header are inline typed objects. The buffer for these objects is created
72
* lazily and stored via the compartment's LazyArrayBufferTable, and points
73
* back into the object's internal data.
74
*
75
* OutlineTypedObject: Typed objects whose data is owned by another object,
76
* which can be either an array buffer or an inline typed object. Outline
77
* typed objects may be attached or unattached. An unattached typed object
78
* has no data associated with it. When first created, objects are always
79
* attached, but they can become unattached if their buffer becomes detached.
80
*
81
* Note that whether a typed object is opaque is not directly
82
* connected to its type. That is, opaque types are *always*
83
* represented by opaque typed objects, but you may have opaque typed
84
* objects for transparent types too. This can occur for two reasons:
85
* (1) a transparent type may be embedded within an opaque type or (2)
86
* users can choose to convert transparent typed objects into opaque
87
* ones to avoid giving access to the buffer itself.
88
*
89
* Typed objects (no matter their class) are non-native objects that
90
* fully override the property accessors etc. The overridden accessor
91
* methods are the same in each and are defined in methods of
92
* TypedObject.
93
*/
94
95
namespace js {
96
97
/*
98
* Helper method for converting a double into other scalar
99
* types in the same way that JavaScript would. In particular,
100
* simple C casting from double to int32_t gets things wrong
101
* for values like 0xF0000000.
102
*/
103
template <typename T>
104
static T ConvertScalar(double d) {
105
if (TypeIsFloatingPoint<T>()) {
106
return T(d);
107
}
108
if (TypeIsUnsigned<T>()) {
109
uint32_t n = JS::ToUint32(d);
110
return T(n);
111
}
112
int32_t n = JS::ToInt32(d);
113
return T(n);
114
}
115
116
namespace type {
117
118
enum Kind {
119
Scalar = JS_TYPEREPR_SCALAR_KIND,
120
Reference = JS_TYPEREPR_REFERENCE_KIND,
121
Struct = JS_TYPEREPR_STRUCT_KIND,
122
Array = JS_TYPEREPR_ARRAY_KIND
123
};
124
125
} // namespace type
126
127
///////////////////////////////////////////////////////////////////////////
128
// Typed Prototypes
129
130
class SimpleTypeDescr;
131
class ComplexTypeDescr;
132
class StructTypeDescr;
133
class TypedProto;
134
135
/*
136
* The prototype for a typed object.
137
*/
138
class TypedProto : public NativeObject {
139
public:
140
static const JSClass class_;
141
};
142
143
class TypeDescr : public NativeObject {
144
public:
145
TypedProto& typedProto() const {
146
return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
147
}
148
149
JSAtom& stringRepr() const {
150
return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom();
151
}
152
153
type::Kind kind() const {
154
return (type::Kind)getReservedSlot(JS_DESCR_SLOT_KIND).toInt32();
155
}
156
157
bool opaque() const {
158
return getReservedSlot(JS_DESCR_SLOT_OPAQUE).toBoolean();
159
}
160
161
bool transparent() const { return !opaque(); }
162
163
uint32_t alignment() const {
164
int32_t i = getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32();
165
MOZ_ASSERT(i >= 0);
166
return uint32_t(i);
167
}
168
169
uint32_t size() const {
170
int32_t i = getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32();
171
MOZ_ASSERT(i >= 0);
172
return uint32_t(i);
173
}
174
175
// Whether id is an 'own' property of objects with this descriptor.
176
MOZ_MUST_USE bool hasProperty(const JSAtomState& names, jsid id);
177
178
// Type descriptors may contain a list of their references for use during
179
// scanning. Marking code is optimized to use this list to mark inline
180
// typed objects, rather than the slower trace hook. This list is only
181
// specified when (a) the descriptor is short enough that it can fit in an
182
// InlineTypedObject, and (b) the descriptor contains at least one
183
// reference. Otherwise its value is undefined.
184
//
185
// The list is three consecutive arrays of uint32_t offsets, preceded by a
186
// header consisting of the length of each array. The arrays store offsets of
187
// string, object/anyref, and value references in the descriptor, in that
188
// order.
189
// TODO/AnyRef-boxing: once anyref has a more complicated structure, we must
190
// revisit this.
191
MOZ_MUST_USE bool hasTraceList() const {
192
return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
193
}
194
const uint32_t* traceList() const {
195
MOZ_ASSERT(hasTraceList());
196
return reinterpret_cast<uint32_t*>(
197
getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
198
}
199
200
void initInstances(const JSRuntime* rt, uint8_t* mem, size_t length);
201
void traceInstances(JSTracer* trace, uint8_t* mem, size_t length);
202
203
static void finalize(JSFreeOp* fop, JSObject* obj);
204
};
205
206
typedef Handle<TypeDescr*> HandleTypeDescr;
207
208
class SimpleTypeDescr : public TypeDescr {};
209
210
// Type for scalar type constructors like `uint8`. All such type
211
// constructors share a common JSClass and JSFunctionSpec. Scalar
212
// types are non-opaque (their storage is visible unless combined with
213
// an opaque reference type.)
214
class ScalarTypeDescr : public SimpleTypeDescr {
215
public:
216
typedef Scalar::Type Type;
217
218
static const type::Kind Kind = type::Scalar;
219
static const bool Opaque = false;
220
static uint32_t size(Type t);
221
static uint32_t alignment(Type t);
222
static const char* typeName(Type type);
223
224
static const JSClass class_;
225
static const JSFunctionSpec typeObjectMethods[];
226
227
Type type() const {
228
// Make sure the values baked into TypedObjectConstants.h line up with
229
// the Scalar::Type enum. We don't define Scalar::Type directly in
230
// terms of these constants to avoid making TypedObjectConstants.h a
231
// public header file.
232
static_assert(
233
Scalar::Int8 == JS_SCALARTYPEREPR_INT8,
234
"TypedObjectConstants.h must be consistent with Scalar::Type");
235
static_assert(
236
Scalar::Uint8 == JS_SCALARTYPEREPR_UINT8,
237
"TypedObjectConstants.h must be consistent with Scalar::Type");
238
static_assert(
239
Scalar::Int16 == JS_SCALARTYPEREPR_INT16,
240
"TypedObjectConstants.h must be consistent with Scalar::Type");
241
static_assert(
242
Scalar::Uint16 == JS_SCALARTYPEREPR_UINT16,
243
"TypedObjectConstants.h must be consistent with Scalar::Type");
244
static_assert(
245
Scalar::Int32 == JS_SCALARTYPEREPR_INT32,
246
"TypedObjectConstants.h must be consistent with Scalar::Type");
247
static_assert(
248
Scalar::Uint32 == JS_SCALARTYPEREPR_UINT32,
249
"TypedObjectConstants.h must be consistent with Scalar::Type");
250
static_assert(
251
Scalar::BigInt64 == JS_SCALARTYPEREPR_BIGINT64,
252
"TypedObjectConstants.h must be consistent with Scalar::Type");
253
static_assert(
254
Scalar::BigUint64 == JS_SCALARTYPEREPR_BIGUINT64,
255
"TypedObjectConstants.h must be consistent with Scalar::Type");
256
static_assert(
257
Scalar::Float32 == JS_SCALARTYPEREPR_FLOAT32,
258
"TypedObjectConstants.h must be consistent with Scalar::Type");
259
static_assert(
260
Scalar::Float64 == JS_SCALARTYPEREPR_FLOAT64,
261
"TypedObjectConstants.h must be consistent with Scalar::Type");
262
static_assert(
263
Scalar::Uint8Clamped == JS_SCALARTYPEREPR_UINT8_CLAMPED,
264
"TypedObjectConstants.h must be consistent with Scalar::Type");
265
266
return Type(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
267
}
268
269
static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
270
};
271
272
// Enumerates the cases of ScalarTypeDescr::Type which have unique C
273
// representation and which are representable as JS Number values. In
274
// particular, omits Uint8Clamped since it is just a Uint8.
275
#define JS_FOR_EACH_UNIQUE_SCALAR_NUMBER_TYPE_REPR_CTYPE(MACRO_) \
276
MACRO_(Scalar::Int8, int8_t, int8) \
277
MACRO_(Scalar::Uint8, uint8_t, uint8) \
278
MACRO_(Scalar::Int16, int16_t, int16) \
279
MACRO_(Scalar::Uint16, uint16_t, uint16) \
280
MACRO_(Scalar::Int32, int32_t, int32) \
281
MACRO_(Scalar::Uint32, uint32_t, uint32) \
282
MACRO_(Scalar::Float32, float, float32) \
283
MACRO_(Scalar::Float64, double, float64)
284
285
// Must be in same order as the enum ScalarTypeDescr::Type:
286
#define JS_FOR_EACH_SCALAR_NUMBER_TYPE_REPR(MACRO_) \
287
JS_FOR_EACH_UNIQUE_SCALAR_NUMBER_TYPE_REPR_CTYPE(MACRO_) \
288
MACRO_(Scalar::Uint8Clamped, uint8_t, uint8Clamped)
289
290
#define JS_FOR_EACH_SCALAR_BIGINT_TYPE_REPR(MACRO_) \
291
MACRO_(Scalar::BigInt64, int64_t, bigint64) \
292
MACRO_(Scalar::BigUint64, uint64_t, biguint64)
293
294
// Must be in same order as the enum ScalarTypeDescr::Type:
295
#define JS_FOR_EACH_SCALAR_TYPE_REPR(MACRO_) \
296
JS_FOR_EACH_SCALAR_NUMBER_TYPE_REPR(MACRO_) \
297
JS_FOR_EACH_SCALAR_BIGINT_TYPE_REPR(MACRO_)
298
299
enum class ReferenceType {
300
TYPE_ANY = JS_REFERENCETYPEREPR_ANY,
301
TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT,
302
TYPE_WASM_ANYREF = JS_REFERENCETYPEREPR_WASM_ANYREF,
303
TYPE_STRING = JS_REFERENCETYPEREPR_STRING
304
};
305
306
// Type for reference type constructors like `Any`, `String`, and
307
// `Object`. All such type constructors share a common JSClass and
308
// JSFunctionSpec. All these types are opaque.
309
class ReferenceTypeDescr : public SimpleTypeDescr {
310
public:
311
// Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below
312
typedef ReferenceType Type;
313
static const char* typeName(Type type);
314
315
static const int32_t TYPE_MAX = int32_t(ReferenceType::TYPE_STRING) + 1;
316
static const type::Kind Kind = type::Reference;
317
static const bool Opaque = true;
318
static const JSClass class_;
319
static uint32_t size(Type t);
320
static uint32_t alignment(Type t);
321
static const JSFunctionSpec typeObjectMethods[];
322
323
ReferenceType type() const {
324
return (ReferenceType)getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
325
}
326
327
const char* typeName() const { return typeName(type()); }
328
329
static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
330
};
331
332
// TODO/AnyRef-boxing: With boxed immediates and strings, GCPtrObject may not be
333
// appropriate.
334
#define JS_FOR_EACH_REFERENCE_TYPE_REPR(MACRO_) \
335
MACRO_(ReferenceType::TYPE_ANY, GCPtrValue, Any) \
336
MACRO_(ReferenceType::TYPE_WASM_ANYREF, GCPtrObject, WasmAnyRef) \
337
MACRO_(ReferenceType::TYPE_OBJECT, GCPtrObject, Object) \
338
MACRO_(ReferenceType::TYPE_STRING, GCPtrString, string)
339
340
// Type descriptors whose instances are objects and hence which have
341
// an associated `prototype` property.
342
class ComplexTypeDescr : public TypeDescr {
343
public:
344
// Returns the prototype that instances of this type descriptor
345
// will have.
346
TypedProto& instancePrototype() const {
347
return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
348
}
349
350
bool allowConstruct() const {
351
return getReservedSlot(JS_DESCR_SLOT_FLAGS).toInt32() &
352
JS_DESCR_FLAG_ALLOW_CONSTRUCT;
353
}
354
};
355
356
bool IsTypedObjectClass(const JSClass* clasp); // Defined below
357
bool IsTypedObjectArray(JSObject& obj);
358
359
MOZ_MUST_USE bool CreateUserSizeAndAlignmentProperties(JSContext* cx,
360
HandleTypeDescr obj);
361
362
class ArrayTypeDescr;
363
364
/*
365
* Properties and methods of the `ArrayType` meta type object. There
366
* is no `class_` field because `ArrayType` is just a native
367
* constructor function.
368
*/
369
class ArrayMetaTypeDescr : public NativeObject {
370
private:
371
// Helper for creating a new ArrayType object.
372
//
373
// - `arrayTypePrototype` - prototype for the new object to be created
374
// - `elementType` - type object for the elements in the array
375
// - `stringRepr` - canonical string representation for the array
376
// - `size` - length of the array
377
static ArrayTypeDescr* create(JSContext* cx, HandleObject arrayTypePrototype,
378
HandleTypeDescr elementType,
379
HandleAtom stringRepr, int32_t size,
380
int32_t length);
381
382
public:
383
// Properties and methods to be installed on ArrayType.prototype,
384
// and hence inherited by all array type objects:
385
static const JSPropertySpec typeObjectProperties[];
386
static const JSFunctionSpec typeObjectMethods[];
387
388
// Properties and methods to be installed on ArrayType.prototype.prototype,
389
// and hence inherited by all array *typed* objects:
390
static const JSPropertySpec typedObjectProperties[];
391
static const JSFunctionSpec typedObjectMethods[];
392
393
// This is the function that gets called when the user
394
// does `new ArrayType(elem)`. It produces an array type object.
395
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
396
};
397
398
/*
399
* Type descriptor created by `new ArrayType(type, n)`
400
*/
401
class ArrayTypeDescr : public ComplexTypeDescr {
402
public:
403
static const JSClass class_;
404
static const type::Kind Kind = type::Array;
405
406
TypeDescr& elementType() const {
407
return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE)
408
.toObject()
409
.as<TypeDescr>();
410
}
411
412
uint32_t length() const {
413
int32_t i = getReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH).toInt32();
414
MOZ_ASSERT(i >= 0);
415
return uint32_t(i);
416
}
417
418
static int32_t offsetOfLength() {
419
return getFixedSlotOffset(JS_DESCR_SLOT_ARRAY_LENGTH);
420
}
421
};
422
423
struct StructFieldProps {
424
StructFieldProps() : isMutable(0), alignAsInt64(0) {}
425
uint32_t isMutable : 1;
426
uint32_t alignAsInt64 : 1;
427
};
428
429
/*
430
* Properties and methods of the `StructType` meta type object. There
431
* is no `class_` field because `StructType` is just a native
432
* constructor function.
433
*/
434
class StructMetaTypeDescr : public NativeObject {
435
private:
436
static JSObject* create(JSContext* cx, HandleObject structTypeGlobal,
437
HandleObject fields);
438
439
public:
440
// The prototype cannot be null.
441
// The names in `ids` must all be non-numeric.
442
// The type objects in `fieldTypeObjs` must all be TypeDescr objects.
443
static StructTypeDescr* createFromArrays(
444
JSContext* cx, HandleObject structTypePrototype, bool opaque,
445
bool allowConstruct, HandleIdVector ids, HandleValueVector fieldTypeObjs,
446
Vector<StructFieldProps>& fieldProps);
447
448
// Properties and methods to be installed on StructType.prototype,
449
// and hence inherited by all struct type objects:
450
static const JSPropertySpec typeObjectProperties[];
451
static const JSFunctionSpec typeObjectMethods[];
452
453
// Properties and methods to be installed on StructType.prototype.prototype,
454
// and hence inherited by all struct *typed* objects:
455
static const JSPropertySpec typedObjectProperties[];
456
static const JSFunctionSpec typedObjectMethods[];
457
458
// This is the function that gets called when the user
459
// does `new StructType(...)`. It produces a struct type object.
460
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
461
462
class Layout {
463
// Can call addField() directly.
464
friend class StructMetaTypeDescr;
465
466
mozilla::CheckedInt32 sizeSoFar = 0;
467
int32_t structAlignment = 1;
468
469
mozilla::CheckedInt32 addField(int32_t fieldAlignment, int32_t fieldSize);
470
471
public:
472
// The field adders return the offset of the the field.
473
mozilla::CheckedInt32 addScalar(Scalar::Type type);
474
mozilla::CheckedInt32 addReference(ReferenceType type);
475
476
// The close method rounds up the structure size to the appropriate
477
// alignment and returns that size. If `alignment` is not NULL then
478
// return the structure alignment through that pointer.
479
mozilla::CheckedInt32 close(int32_t* alignment = nullptr);
480
};
481
};
482
483
class StructTypeDescr : public ComplexTypeDescr {
484
public:
485
static const JSClass class_;
486
487
// Returns the number of fields defined in this struct.
488
size_t fieldCount() const;
489
490
// Set `*out` to the index of the field named `id` and returns true,
491
// or return false if no such field exists.
492
MOZ_MUST_USE bool fieldIndex(jsid id, size_t* out) const;
493
494
// Return the name of the field at index `index`.
495
JSAtom& fieldName(size_t index) const;
496
497
// Return the type descr of the field at index `index`.
498
TypeDescr& fieldDescr(size_t index) const;
499
500
// Return the offset of the field at index `index`.
501
size_t fieldOffset(size_t index) const;
502
503
// Return the mutability of the field at index `index`.
504
bool fieldIsMutable(size_t index) const;
505
506
static bool call(JSContext* cx, unsigned argc, Value* vp);
507
508
private:
509
ArrayObject& fieldInfoObject(size_t slot) const {
510
return getReservedSlot(slot).toObject().as<ArrayObject>();
511
}
512
};
513
514
typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
515
516
/*
517
* This object exists in order to encapsulate the typed object types
518
* somewhat, rather than sticking them all into the global object.
519
* Eventually it will go away and become a module.
520
*/
521
class TypedObjectModuleObject : public NativeObject {
522
public:
523
enum Slot {
524
ArrayTypePrototype,
525
StructTypePrototype,
526
Int32Desc,
527
Int64Desc,
528
Float32Desc,
529
Float64Desc,
530
ObjectDesc,
531
WasmAnyRefDesc,
532
SlotCount
533
};
534
535
static const JSClass class_;
536
537
private:
538
static const ClassSpec classSpec_;
539
};
540
541
/* Base type for transparent and opaque typed objects. */
542
class TypedObject : public JSObject {
543
static const bool IsTypedObjectClass = true;
544
545
static MOZ_MUST_USE bool obj_getArrayElement(JSContext* cx,
546
Handle<TypedObject*> typedObj,
547
Handle<TypeDescr*> typeDescr,
548
uint32_t index,
549
MutableHandleValue vp);
550
551
protected:
552
static const ObjectOps objectOps_;
553
554
static MOZ_MUST_USE bool obj_lookupProperty(
555
JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp,
556
MutableHandle<PropertyResult> propp);
557
558
static MOZ_MUST_USE bool obj_defineProperty(JSContext* cx, HandleObject obj,
559
HandleId id,
560
Handle<PropertyDescriptor> desc,
561
ObjectOpResult& result);
562
563
static MOZ_MUST_USE bool obj_hasProperty(JSContext* cx, HandleObject obj,
564
HandleId id, bool* foundp);
565
566
static MOZ_MUST_USE bool obj_getProperty(JSContext* cx, HandleObject obj,
567
HandleValue receiver, HandleId id,
568
MutableHandleValue vp);
569
570
static MOZ_MUST_USE bool obj_getElement(JSContext* cx, HandleObject obj,
571
HandleValue receiver, uint32_t index,
572
MutableHandleValue vp);
573
574
static MOZ_MUST_USE bool obj_setProperty(JSContext* cx, HandleObject obj,
575
HandleId id, HandleValue v,
576
HandleValue receiver,
577
ObjectOpResult& result);
578
579
static MOZ_MUST_USE bool obj_getOwnPropertyDescriptor(
580
JSContext* cx, HandleObject obj, HandleId id,
581
MutableHandle<PropertyDescriptor> desc);
582
583
static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj,
584
HandleId id,
585
ObjectOpResult& result);
586
587
uint8_t* typedMem() const;
588
uint8_t* typedMemBase() const;
589
590
public:
591
static MOZ_MUST_USE bool obj_newEnumerate(JSContext* cx, HandleObject obj,
592
MutableHandleIdVector properties,
593
bool enumerableOnly);
594
595
TypedProto& typedProto() const {
596
// Typed objects' prototypes can't be modified.
597
return staticPrototype()->as<TypedProto>();
598
}
599
600
TypeDescr& typeDescr() const { return group()->typeDescr(); }
601
602
static JS::Result<TypedObject*, JS::OOM&> create(JSContext* cx,
603
js::gc::AllocKind kind,
604
js::gc::InitialHeap heap,
605
js::HandleShape shape,
606
js::HandleObjectGroup group);
607
608
uint32_t offset() const;
609
uint32_t length() const;
610
uint8_t* typedMem(const JS::AutoRequireNoGC&) const { return typedMem(); }
611
bool isAttached() const;
612
613
uint32_t size() const { return typeDescr().size(); }
614
615
uint8_t* typedMem(size_t offset, const JS::AutoRequireNoGC& nogc) const {
616
// It seems a bit surprising that one might request an offset
617
// == size(), but it can happen when taking the "address of" a
618
// 0-sized value. (In other words, we maintain the invariant
619
// that `offset + size <= size()` -- this is always checked in
620
// the caller's side.)
621
MOZ_ASSERT(offset <= (size_t)size());
622
return typedMem(nogc) + offset;
623
}
624
625
inline MOZ_MUST_USE bool opaque() const;
626
627
// Creates a new typed object whose memory is freshly allocated and
628
// initialized with zeroes (or, in the case of references, an appropriate
629
// default value).
630
static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj,
631
gc::InitialHeap heap = gc::DefaultHeap);
632
633
// User-accessible constructor (`new TypeDescriptor(...)`). Note that the
634
// callee here is the type descriptor.
635
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
636
637
/* Accessors for self hosted code. */
638
static MOZ_MUST_USE bool GetByteOffset(JSContext* cx, unsigned argc,
639
Value* vp);
640
641
Shape** addressOfShapeFromGC() {
642
return shape_.unsafeUnbarrieredForTracing();
643
}
644
};
645
646
typedef Handle<TypedObject*> HandleTypedObject;
647
648
class OutlineTypedObject : public TypedObject {
649
// The object which owns the data this object points to. Because this
650
// pointer is managed in tandem with |data|, this is not a GCPtr and
651
// barriers are managed directly.
652
JSObject* owner_;
653
654
// Data pointer to some offset in the owner's contents.
655
uint8_t* data_;
656
657
void setOwnerAndData(JSObject* owner, uint8_t* data);
658
659
public:
660
// JIT accessors.
661
static size_t offsetOfData() { return offsetof(OutlineTypedObject, data_); }
662
static size_t offsetOfOwner() { return offsetof(OutlineTypedObject, owner_); }
663
664
JSObject& owner() const {
665
MOZ_ASSERT(owner_);
666
return *owner_;
667
}
668
669
JSObject* maybeOwner() const { return owner_; }
670
671
uint8_t* outOfLineTypedMem() const { return data_; }
672
673
void setData(uint8_t* data) { data_ = data; }
674
675
void resetOffset(size_t offset) {
676
MOZ_ASSERT(offset <= (size_t)size());
677
setData(typedMemBase() + offset);
678
}
679
680
// Helper for createUnattached()
681
static OutlineTypedObject* createUnattachedWithClass(
682
JSContext* cx, const JSClass* clasp, HandleTypeDescr type,
683
gc::InitialHeap heap = gc::DefaultHeap);
684
685
// Creates an unattached typed object or handle (depending on the
686
// type parameter T). Note that it is only legal for unattached
687
// handles to escape to the end user; for non-handles, the caller
688
// should always invoke one of the `attach()` methods below.
689
//
690
// Arguments:
691
// - type: type object for resulting object
692
static OutlineTypedObject* createUnattached(
693
JSContext* cx, HandleTypeDescr type,
694
gc::InitialHeap heap = gc::DefaultHeap);
695
696
// Creates a typedObj that aliases the memory pointed at by `owner`
697
// at the given offset. The typedObj will be a handle iff type is a
698
// handle and a typed object otherwise.
699
static OutlineTypedObject* createDerived(JSContext* cx, HandleTypeDescr type,
700
Handle<TypedObject*> typedContents,
701
uint32_t offset);
702
703
// Use this method when `buffer` is the owner of the memory.
704
void attach(JSContext* cx, ArrayBufferObject& buffer, uint32_t offset);
705
706
// Otherwise, use this to attach to memory referenced by another typedObj.
707
void attach(JSContext* cx, TypedObject& typedObj, uint32_t offset);
708
709
// Invoked when array buffer is transferred elsewhere
710
void notifyBufferDetached(void* newData);
711
712
static void obj_trace(JSTracer* trace, JSObject* object);
713
};
714
715
// Class for a transparent typed object whose owner is an array buffer.
716
class OutlineTransparentTypedObject : public OutlineTypedObject {
717
public:
718
static const JSClass class_;
719
};
720
721
// Class for an opaque typed object whose owner may be either an array buffer
722
// or an opaque inlined typed object.
723
class OutlineOpaqueTypedObject : public OutlineTypedObject {
724
public:
725
static const JSClass class_;
726
};
727
728
// Class for a typed object whose data is allocated inline.
729
class InlineTypedObject : public TypedObject {
730
friend class TypedObject;
731
732
// Start of the inline data, which immediately follows the shape and type.
733
uint8_t data_[1];
734
735
static const size_t MaximumSize =
736
JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
737
738
protected:
739
uint8_t* inlineTypedMem() const { return (uint8_t*)&data_; }
740
741
public:
742
static inline gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr);
743
744
static bool canAccommodateSize(size_t size) { return size <= MaximumSize; }
745
746
static bool canAccommodateType(TypeDescr* type) {
747
return type->size() <= MaximumSize;
748
}
749
750
uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
751
return inlineTypedMem();
752
}
753
754
uint8_t* inlineTypedMemForGC() const { return inlineTypedMem(); }
755
756
static void obj_trace(JSTracer* trace, JSObject* object);
757
static size_t obj_moved(JSObject* dst, JSObject* src);
758
759
static size_t offsetOfDataStart() {
760
return offsetof(InlineTypedObject, data_);
761
}
762
763
static InlineTypedObject* create(JSContext* cx, HandleTypeDescr descr,
764
gc::InitialHeap heap = gc::DefaultHeap);
765
static InlineTypedObject* createCopy(
766
JSContext* cx, Handle<InlineTypedObject*> templateObject,
767
gc::InitialHeap heap);
768
};
769
770
// Class for a transparent typed object with inline data, which may have a
771
// lazily allocated array buffer.
772
class InlineTransparentTypedObject : public InlineTypedObject {
773
public:
774
static const JSClass class_;
775
776
uint8_t* inlineTypedMem() const {
777
return InlineTypedObject::inlineTypedMem();
778
}
779
};
780
781
// Class for an opaque typed object with inline data and no array buffer.
782
class InlineOpaqueTypedObject : public InlineTypedObject {
783
public:
784
static const JSClass class_;
785
};
786
787
/*
788
* Usage: NewOpaqueTypedObject(typeObj)
789
*
790
* Constructs a new, unattached instance of `Handle`.
791
*/
792
MOZ_MUST_USE bool NewOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp);
793
794
/*
795
* Usage: NewDerivedTypedObject(typeObj, owner, offset)
796
*
797
* Constructs a new, unattached instance of `Handle`.
798
*/
799
MOZ_MUST_USE bool NewDerivedTypedObject(JSContext* cx, unsigned argc,
800
Value* vp);
801
802
/*
803
* Usage: AttachTypedObject(typedObj, newDatum, newOffset)
804
*
805
* Moves `typedObj` to point at the memory referenced by `newDatum` with
806
* the offset `newOffset`.
807
*/
808
MOZ_MUST_USE bool AttachTypedObject(JSContext* cx, unsigned argc, Value* vp);
809
810
/*
811
* Usage: SetTypedObjectOffset(typedObj, offset)
812
*
813
* Changes the offset for `typedObj` within its buffer to `offset`.
814
* `typedObj` must already be attached.
815
*/
816
MOZ_MUST_USE bool SetTypedObjectOffset(JSContext*, unsigned argc, Value* vp);
817
818
/*
819
* Usage: ObjectIsTypeDescr(obj)
820
*
821
* True if `obj` is a type object.
822
*/
823
MOZ_MUST_USE bool ObjectIsTypeDescr(JSContext* cx, unsigned argc, Value* vp);
824
825
/*
826
* Usage: ObjectIsTypedObject(obj)
827
*
828
* True if `obj` is a transparent or opaque typed object.
829
*/
830
MOZ_MUST_USE bool ObjectIsTypedObject(JSContext* cx, unsigned argc, Value* vp);
831
832
/*
833
* Usage: ObjectIsOpaqueTypedObject(obj)
834
*
835
* True if `obj` is an opaque typed object.
836
*/
837
MOZ_MUST_USE bool ObjectIsOpaqueTypedObject(JSContext* cx, unsigned argc,
838
Value* vp);
839
840
/*
841
* Usage: ObjectIsTransparentTypedObject(obj)
842
*
843
* True if `obj` is a transparent typed object.
844
*/
845
MOZ_MUST_USE bool ObjectIsTransparentTypedObject(JSContext* cx, unsigned argc,
846
Value* vp);
847
848
// Predicates on type descriptor objects. In all cases, 'obj' must be a type
849
// descriptor.
850
851
MOZ_MUST_USE bool TypeDescrIsSimpleType(JSContext*, unsigned argc, Value* vp);
852
853
MOZ_MUST_USE bool TypeDescrIsArrayType(JSContext*, unsigned argc, Value* vp);
854
855
/*
856
* Usage: TypedObjectIsAttached(obj)
857
*
858
* Given a TypedObject `obj`, returns true if `obj` is
859
* "attached" (i.e., its data pointer is nullptr).
860
*/
861
MOZ_MUST_USE bool TypedObjectIsAttached(JSContext* cx, unsigned argc,
862
Value* vp);
863
864
/*
865
* Usage: TypedObjectTypeDescr(obj)
866
*
867
* Given a TypedObject `obj`, returns the object's type descriptor.
868
*/
869
MOZ_MUST_USE bool TypedObjectTypeDescr(JSContext* cx, unsigned argc, Value* vp);
870
871
/*
872
* Usage: ClampToUint8(v)
873
*
874
* Same as the C function ClampDoubleToUint8. `v` must be a number.
875
*/
876
MOZ_MUST_USE bool ClampToUint8(JSContext* cx, unsigned argc, Value* vp);
877
878
/*
879
* Usage: GetTypedObjectModule()
880
*
881
* Returns the global "typed object" module, which provides access
882
* to the various builtin type descriptors. These are currently
883
* exported as immutable properties so it is safe for self-hosted code
884
* to access them; eventually this should be linked into the module
885
* system.
886
*/
887
MOZ_MUST_USE bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
888
889
/*
890
* Usage: IsBoxedWasmAnyRef(Object) -> bool
891
*
892
* Return true iff object is an instance of the Wasm-internal type WasmValueBox.
893
*/
894
MOZ_MUST_USE bool IsBoxedWasmAnyRef(JSContext* cx, unsigned argc, Value* vp);
895
896
/*
897
* Usage: IsBoxableWasmAnyRef(Value) -> bool
898
*
899
* Return true iff the value must be boxed into a WasmValueBox in order to be
900
* stored into an anyref field. Values for which false is returned may be
901
* passed as they are to Store_WasmAnyRef and may therefore appear as results
902
* from Load_WasmAnyRef.
903
*/
904
MOZ_MUST_USE bool IsBoxableWasmAnyRef(JSContext* cx, unsigned argc, Value* vp);
905
906
/*
907
* Usage: BoxWasmAnyRef(Value) -> Object
908
*
909
* Return a new WasmValueBox that holds `value`. The value can be any value at
910
* all.
911
*/
912
MOZ_MUST_USE bool BoxWasmAnyRef(JSContext* cx, unsigned argc, Value* vp);
913
914
/*
915
* Usage: UnboxBoxedWasmAnyRef(Object) -> Value
916
*
917
* The object must be a value for which IsBoxedWasmAnyRef returns true.
918
* Return the value stored in the box.
919
*/
920
MOZ_MUST_USE bool UnboxBoxedWasmAnyRef(JSContext* cx, unsigned argc, Value* vp);
921
922
/*
923
* Usage: Store_int8(targetDatum, targetOffset, value)
924
* ...
925
* Store_uint8(targetDatum, targetOffset, value)
926
* ...
927
* Store_float32(targetDatum, targetOffset, value)
928
* Store_float64(targetDatum, targetOffset, value)
929
*
930
* Intrinsic function. Stores `value` into the memory referenced by
931
* `targetDatum` at the offset `targetOffset`.
932
*
933
* Assumes (and asserts) that:
934
* - `targetDatum` is attached
935
* - `targetOffset` is a valid offset within the bounds of `targetDatum`
936
* - `value` is a number
937
*/
938
#define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \
939
class StoreScalar##T { \
940
public: \
941
static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
942
static const JSJitInfo JitInfo; \
943
};
944
945
/*
946
* Usage: Store_Any(targetDatum, targetOffset, fieldName, value)
947
* Store_Object(targetDatum, targetOffset, fieldName, value)
948
* Store_string(targetDatum, targetOffset, fieldName, value)
949
*
950
* Intrinsic function. Stores `value` into the memory referenced by
951
* `targetDatum` at the offset `targetOffset`.
952
*
953
* Assumes (and asserts) that:
954
* - `targetDatum` is attached
955
* - `targetOffset` is a valid offset within the bounds of `targetDatum`
956
* - `value` is an object or null (`Store_Object`) or string (`Store_string`).
957
*/
958
#define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \
959
class StoreReference##_name { \
960
private: \
961
static MOZ_MUST_USE bool store(JSContext* cx, T* heap, const Value& v, \
962
TypedObject* obj, jsid id); \
963
\
964
public: \
965
static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
966
static const JSJitInfo JitInfo; \
967
};
968
969
/*
970
* Usage: LoadScalar(targetDatum, targetOffset, value)
971
*
972
* Intrinsic function. Loads value (which must be an int32 or uint32)
973
* by `scalarTypeRepr` (which must be a type repr obj) and loads the
974
* value at the memory for `targetDatum` at offset `targetOffset`.
975
* `targetDatum` must be attached.
976
*/
977
#define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name) \
978
class LoadScalar##T { \
979
public: \
980
static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
981
static const JSJitInfo JitInfo; \
982
};
983
984
/*
985
* Usage: LoadReference(targetDatum, targetOffset, value)
986
*
987
* Intrinsic function. Stores value (which must be an int32 or uint32)
988
* by `scalarTypeRepr` (which must be a type repr obj) and stores the
989
* value at the memory for `targetDatum` at offset `targetOffset`.
990
* `targetDatum` must be attached.
991
*/
992
#define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \
993
class LoadReference##_name { \
994
private: \
995
static void load(T* heap, MutableHandleValue v); \
996
\
997
public: \
998
static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
999
static const JSJitInfo JitInfo; \
1000
};
1001
1002
// I was using templates for this stuff instead of macros, but ran
1003
// into problems with the Unagi compiler.
1004
JS_FOR_EACH_UNIQUE_SCALAR_NUMBER_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
1005
JS_FOR_EACH_UNIQUE_SCALAR_NUMBER_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
1006
JS_FOR_EACH_SCALAR_BIGINT_TYPE_REPR(JS_STORE_SCALAR_CLASS_DEFN)
1007
JS_FOR_EACH_SCALAR_BIGINT_TYPE_REPR(JS_LOAD_SCALAR_CLASS_DEFN)
1008
JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN)
1009
JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
1010
1011
inline bool IsTypedObjectClass(const JSClass* class_) {
1012
return class_ == &OutlineTransparentTypedObject::class_ ||
1013
class_ == &InlineTransparentTypedObject::class_ ||
1014
class_ == &OutlineOpaqueTypedObject::class_ ||
1015
class_ == &InlineOpaqueTypedObject::class_;
1016
}
1017
1018
inline bool IsOpaqueTypedObjectClass(const JSClass* class_) {
1019
return class_ == &OutlineOpaqueTypedObject::class_ ||
1020
class_ == &InlineOpaqueTypedObject::class_;
1021
}
1022
1023
inline bool IsOutlineTypedObjectClass(const JSClass* class_) {
1024
return class_ == &OutlineOpaqueTypedObject::class_ ||
1025
class_ == &OutlineTransparentTypedObject::class_;
1026
}
1027
1028
inline bool IsInlineTypedObjectClass(const JSClass* class_) {
1029
return class_ == &InlineOpaqueTypedObject::class_ ||
1030
class_ == &InlineTransparentTypedObject::class_;
1031
}
1032
1033
inline const JSClass* GetOutlineTypedObjectClass(bool opaque) {
1034
return opaque ? &OutlineOpaqueTypedObject::class_
1035
: &OutlineTransparentTypedObject::class_;
1036
}
1037
1038
inline bool IsSimpleTypeDescrClass(const JSClass* clasp) {
1039
return clasp == &ScalarTypeDescr::class_ ||
1040
clasp == &ReferenceTypeDescr::class_;
1041
}
1042
1043
inline bool IsComplexTypeDescrClass(const JSClass* clasp) {
1044
return clasp == &StructTypeDescr::class_ || clasp == &ArrayTypeDescr::class_;
1045
}
1046
1047
inline bool IsTypeDescrClass(const JSClass* clasp) {
1048
return IsSimpleTypeDescrClass(clasp) || IsComplexTypeDescrClass(clasp);
1049
}
1050
1051
inline bool TypedObject::opaque() const {
1052
return IsOpaqueTypedObjectClass(getClass());
1053
}
1054
1055
} // namespace js
1056
1057
template <>
1058
inline bool JSObject::is<js::SimpleTypeDescr>() const {
1059
return js::IsSimpleTypeDescrClass(getClass());
1060
}
1061
1062
template <>
1063
inline bool JSObject::is<js::ComplexTypeDescr>() const {
1064
return js::IsComplexTypeDescrClass(getClass());
1065
}
1066
1067
template <>
1068
inline bool JSObject::is<js::TypeDescr>() const {
1069
return js::IsTypeDescrClass(getClass());
1070
}
1071
1072
template <>
1073
inline bool JSObject::is<js::TypedObject>() const {
1074
return js::IsTypedObjectClass(getClass());
1075
}
1076
1077
template <>
1078
inline bool JSObject::is<js::OutlineTypedObject>() const {
1079
return getClass() == &js::OutlineTransparentTypedObject::class_ ||
1080
getClass() == &js::OutlineOpaqueTypedObject::class_;
1081
}
1082
1083
template <>
1084
inline bool JSObject::is<js::InlineTypedObject>() const {
1085
return getClass() == &js::InlineTransparentTypedObject::class_ ||
1086
getClass() == &js::InlineOpaqueTypedObject::class_;
1087
}
1088
1089
#endif /* builtin_TypedObject_h */