Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef builtin_TypedObject_h
8 : #define builtin_TypedObject_h
9 :
10 : #include "jsobj.h"
11 : #include "jsweakmap.h"
12 :
13 : #include "builtin/TypedObjectConstants.h"
14 : #include "js/Conversions.h"
15 : #include "vm/ArrayBufferObject.h"
16 : #include "vm/ShapedObject.h"
17 :
18 : /*
19 : * -------------
20 : * 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
35 : * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the
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 js::Class 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 0 : static T ConvertScalar(double d)
105 : {
106 0 : if (TypeIsFloatingPoint<T>())
107 0 : return T(d);
108 0 : if (TypeIsUnsigned<T>()) {
109 0 : uint32_t n = JS::ToUint32(d);
110 0 : return T(n);
111 : }
112 0 : int32_t n = JS::ToInt32(d);
113 0 : 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 : Simd = JS_TYPEREPR_SIMD_KIND,
122 : Struct = JS_TYPEREPR_STRUCT_KIND,
123 : Array = JS_TYPEREPR_ARRAY_KIND
124 : };
125 :
126 : } // namespace type
127 :
128 : ///////////////////////////////////////////////////////////////////////////
129 : // Typed Prototypes
130 :
131 : class SimpleTypeDescr;
132 : class ComplexTypeDescr;
133 : class SimdTypeDescr;
134 : class StructTypeDescr;
135 : class TypedProto;
136 :
137 : /*
138 : * The prototype for a typed object.
139 : */
140 : class TypedProto : public NativeObject
141 : {
142 : public:
143 : static const Class class_;
144 : };
145 :
146 : class TypeDescr : public NativeObject
147 : {
148 : public:
149 0 : TypedProto& typedProto() const {
150 0 : return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
151 : }
152 :
153 0 : JSAtom& stringRepr() const {
154 0 : return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom();
155 : }
156 :
157 18 : type::Kind kind() const {
158 18 : return (type::Kind) getReservedSlot(JS_DESCR_SLOT_KIND).toInt32();
159 : }
160 :
161 162 : bool opaque() const {
162 162 : return getReservedSlot(JS_DESCR_SLOT_OPAQUE).toBoolean();
163 : }
164 :
165 162 : bool transparent() const {
166 162 : return !opaque();
167 : }
168 :
169 54 : uint32_t alignment() const {
170 54 : int32_t i = getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32();
171 54 : MOZ_ASSERT(i >= 0);
172 54 : return uint32_t(i);
173 : }
174 :
175 126 : uint32_t size() const {
176 126 : int32_t i = getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32();
177 126 : MOZ_ASSERT(i >= 0);
178 126 : return uint32_t(i);
179 : }
180 :
181 : // Whether id is an 'own' property of objects with this descriptor.
182 : MOZ_MUST_USE bool hasProperty(const JSAtomState& names, jsid id);
183 :
184 : // Type descriptors may contain a list of their references for use during
185 : // scanning. Marking code is optimized to use this list to mark inline
186 : // typed objects, rather than the slower trace hook. This list is only
187 : // specified when (a) the descriptor is short enough that it can fit in an
188 : // InlineTypedObject, and (b) the descriptor contains at least one
189 : // reference. Otherwise its value is undefined.
190 : //
191 : // The list is three consecutive arrays of int32_t offsets, with each array
192 : // terminated by -1. The arrays store offsets of string, object, and value
193 : // references in the descriptor, in that order.
194 0 : MOZ_MUST_USE bool hasTraceList() const {
195 0 : return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
196 : }
197 0 : const int32_t* traceList() const {
198 0 : MOZ_ASSERT(hasTraceList());
199 0 : return reinterpret_cast<int32_t*>(getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
200 : }
201 :
202 : void initInstances(const JSRuntime* rt, uint8_t* mem, size_t length);
203 : void traceInstances(JSTracer* trace, uint8_t* mem, size_t length);
204 :
205 : static void finalize(FreeOp* fop, JSObject* obj);
206 : };
207 :
208 : typedef Handle<TypeDescr*> HandleTypeDescr;
209 :
210 : class SimpleTypeDescr : public TypeDescr
211 : {
212 : };
213 :
214 : // Type for scalar type constructors like `uint8`. All such type
215 : // constructors share a common js::Class and JSFunctionSpec. Scalar
216 : // types are non-opaque (their storage is visible unless combined with
217 : // an opaque reference type.)
218 : class ScalarTypeDescr : public SimpleTypeDescr
219 : {
220 : public:
221 : typedef Scalar::Type Type;
222 :
223 : static const type::Kind Kind = type::Scalar;
224 : static const bool Opaque = false;
225 : static uint32_t size(Type t);
226 : static uint32_t alignment(Type t);
227 : static const char* typeName(Type type);
228 :
229 : static const Class class_;
230 : static const JSFunctionSpec typeObjectMethods[];
231 :
232 0 : Type type() const {
233 : // Make sure the values baked into TypedObjectConstants.h line up with
234 : // the Scalar::Type enum. We don't define Scalar::Type directly in
235 : // terms of these constants to avoid making TypedObjectConstants.h a
236 : // public header file.
237 : static_assert(Scalar::Int8 == JS_SCALARTYPEREPR_INT8,
238 : "TypedObjectConstants.h must be consistent with Scalar::Type");
239 : static_assert(Scalar::Uint8 == JS_SCALARTYPEREPR_UINT8,
240 : "TypedObjectConstants.h must be consistent with Scalar::Type");
241 : static_assert(Scalar::Int16 == JS_SCALARTYPEREPR_INT16,
242 : "TypedObjectConstants.h must be consistent with Scalar::Type");
243 : static_assert(Scalar::Uint16 == JS_SCALARTYPEREPR_UINT16,
244 : "TypedObjectConstants.h must be consistent with Scalar::Type");
245 : static_assert(Scalar::Int32 == JS_SCALARTYPEREPR_INT32,
246 : "TypedObjectConstants.h must be consistent with Scalar::Type");
247 : static_assert(Scalar::Uint32 == JS_SCALARTYPEREPR_UINT32,
248 : "TypedObjectConstants.h must be consistent with Scalar::Type");
249 : static_assert(Scalar::Float32 == JS_SCALARTYPEREPR_FLOAT32,
250 : "TypedObjectConstants.h must be consistent with Scalar::Type");
251 : static_assert(Scalar::Float64 == JS_SCALARTYPEREPR_FLOAT64,
252 : "TypedObjectConstants.h must be consistent with Scalar::Type");
253 : static_assert(Scalar::Uint8Clamped == JS_SCALARTYPEREPR_UINT8_CLAMPED,
254 : "TypedObjectConstants.h must be consistent with Scalar::Type");
255 : static_assert(Scalar::Float32x4 == JS_SCALARTYPEREPR_FLOAT32X4,
256 : "TypedObjectConstants.h must be consistent with Scalar::Type");
257 : static_assert(Scalar::Int8x16 == JS_SCALARTYPEREPR_INT8X16,
258 : "TypedObjectConstants.h must be consistent with Scalar::Type");
259 : static_assert(Scalar::Int16x8 == JS_SCALARTYPEREPR_INT16X8,
260 : "TypedObjectConstants.h must be consistent with Scalar::Type");
261 : static_assert(Scalar::Int32x4 == JS_SCALARTYPEREPR_INT32X4,
262 : "TypedObjectConstants.h must be consistent with Scalar::Type");
263 :
264 0 : return Type(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
265 : }
266 :
267 : static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
268 : };
269 :
270 : // Enumerates the cases of ScalarTypeDescr::Type which have
271 : // unique C representation. In particular, omits Uint8Clamped since it
272 : // is just a Uint8.
273 : #define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \
274 : macro_(Scalar::Int8, int8_t, int8) \
275 : macro_(Scalar::Uint8, uint8_t, uint8) \
276 : macro_(Scalar::Int16, int16_t, int16) \
277 : macro_(Scalar::Uint16, uint16_t, uint16) \
278 : macro_(Scalar::Int32, int32_t, int32) \
279 : macro_(Scalar::Uint32, uint32_t, uint32) \
280 : macro_(Scalar::Float32, float, float32) \
281 : macro_(Scalar::Float64, double, float64)
282 :
283 : // Must be in same order as the enum ScalarTypeDescr::Type:
284 : #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \
285 : JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \
286 : macro_(Scalar::Uint8Clamped, uint8_t, uint8Clamped)
287 :
288 : // Type for reference type constructors like `Any`, `String`, and
289 : // `Object`. All such type constructors share a common js::Class and
290 : // JSFunctionSpec. All these types are opaque.
291 : class ReferenceTypeDescr : public SimpleTypeDescr
292 : {
293 : public:
294 : // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below
295 : enum Type {
296 : TYPE_ANY = JS_REFERENCETYPEREPR_ANY,
297 : TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT,
298 : TYPE_STRING = JS_REFERENCETYPEREPR_STRING,
299 : };
300 : static const int32_t TYPE_MAX = TYPE_STRING + 1;
301 : static const char* typeName(Type type);
302 :
303 : static const type::Kind Kind = type::Reference;
304 : static const bool Opaque = true;
305 : static const Class class_;
306 : static uint32_t size(Type t);
307 : static uint32_t alignment(Type t);
308 : static const JSFunctionSpec typeObjectMethods[];
309 :
310 18 : ReferenceTypeDescr::Type type() const {
311 18 : return (ReferenceTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
312 : }
313 :
314 0 : const char* typeName() const {
315 0 : return typeName(type());
316 : }
317 :
318 : static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
319 : };
320 :
321 : #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \
322 : macro_(ReferenceTypeDescr::TYPE_ANY, GCPtrValue, Any) \
323 : macro_(ReferenceTypeDescr::TYPE_OBJECT, GCPtrObject, Object) \
324 : macro_(ReferenceTypeDescr::TYPE_STRING, GCPtrString, string)
325 :
326 : // Type descriptors whose instances are objects and hence which have
327 : // an associated `prototype` property.
328 : class ComplexTypeDescr : public TypeDescr
329 : {
330 : public:
331 : // Returns the prototype that instances of this type descriptor
332 : // will have.
333 0 : TypedProto& instancePrototype() const {
334 0 : return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
335 : }
336 : };
337 :
338 : enum class SimdType;
339 :
340 : /*
341 : * SIMD Type descriptors.
342 : */
343 : class SimdTypeDescr : public ComplexTypeDescr
344 : {
345 : public:
346 : static const type::Kind Kind = type::Simd;
347 : static const bool Opaque = false;
348 : static const Class class_;
349 : static uint32_t size(SimdType t);
350 : static uint32_t alignment(SimdType t);
351 : static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
352 : static bool is(const Value& v);
353 :
354 : SimdType type() const;
355 : };
356 :
357 : bool IsTypedObjectClass(const Class* clasp); // Defined below
358 : bool IsTypedObjectArray(JSObject& obj);
359 :
360 : MOZ_MUST_USE bool CreateUserSizeAndAlignmentProperties(JSContext* cx, 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 : {
371 : private:
372 : // Helper for creating a new ArrayType object.
373 : //
374 : // - `arrayTypePrototype` - prototype for the new object to be created
375 : // - `elementType` - type object for the elements in the array
376 : // - `stringRepr` - canonical string representation for the array
377 : // - `size` - length of the array
378 : static ArrayTypeDescr* create(JSContext* cx,
379 : HandleObject arrayTypePrototype,
380 : HandleTypeDescr elementType,
381 : HandleAtom stringRepr,
382 : int32_t size,
383 : int32_t length);
384 :
385 : public:
386 : // Properties and methods to be installed on ArrayType.prototype,
387 : // and hence inherited by all array type objects:
388 : static const JSPropertySpec typeObjectProperties[];
389 : static const JSFunctionSpec typeObjectMethods[];
390 :
391 : // Properties and methods to be installed on ArrayType.prototype.prototype,
392 : // and hence inherited by all array *typed* objects:
393 : static const JSPropertySpec typedObjectProperties[];
394 : static const JSFunctionSpec typedObjectMethods[];
395 :
396 : // This is the function that gets called when the user
397 : // does `new ArrayType(elem)`. It produces an array type object.
398 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
399 : };
400 :
401 : /*
402 : * Type descriptor created by `new ArrayType(type, n)`
403 : */
404 : class ArrayTypeDescr : public ComplexTypeDescr
405 : {
406 : public:
407 : static const Class class_;
408 : static const type::Kind Kind = type::Array;
409 :
410 0 : TypeDescr& elementType() const {
411 0 : return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as<TypeDescr>();
412 : }
413 :
414 0 : uint32_t length() const {
415 0 : int32_t i = getReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH).toInt32();
416 0 : MOZ_ASSERT(i >= 0);
417 0 : return uint32_t(i);
418 : }
419 :
420 0 : static int32_t offsetOfLength() {
421 0 : return getFixedSlotOffset(JS_DESCR_SLOT_ARRAY_LENGTH);
422 : }
423 : };
424 :
425 : /*
426 : * Properties and methods of the `StructType` meta type object. There
427 : * is no `class_` field because `StructType` is just a native
428 : * constructor function.
429 : */
430 : class StructMetaTypeDescr : public NativeObject
431 : {
432 : private:
433 : static JSObject* create(JSContext* cx, HandleObject structTypeGlobal,
434 : HandleObject fields);
435 :
436 : public:
437 : // Properties and methods to be installed on StructType.prototype,
438 : // and hence inherited by all struct type objects:
439 : static const JSPropertySpec typeObjectProperties[];
440 : static const JSFunctionSpec typeObjectMethods[];
441 :
442 : // Properties and methods to be installed on StructType.prototype.prototype,
443 : // and hence inherited by all struct *typed* objects:
444 : static const JSPropertySpec typedObjectProperties[];
445 : static const JSFunctionSpec typedObjectMethods[];
446 :
447 : // This is the function that gets called when the user
448 : // does `new StructType(...)`. It produces a struct type object.
449 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
450 : };
451 :
452 : class StructTypeDescr : public ComplexTypeDescr
453 : {
454 : public:
455 : static const Class class_;
456 :
457 : // Returns the number of fields defined in this struct.
458 : size_t fieldCount() const;
459 :
460 : // Set `*out` to the index of the field named `id` and returns true,
461 : // or return false if no such field exists.
462 : MOZ_MUST_USE bool fieldIndex(jsid id, size_t* out) const;
463 :
464 : // Return the name of the field at index `index`.
465 : JSAtom& fieldName(size_t index) const;
466 :
467 : // Return the type descr of the field at index `index`.
468 : TypeDescr& fieldDescr(size_t index) const;
469 :
470 : // Return the offset of the field at index `index`.
471 : size_t fieldOffset(size_t index) const;
472 :
473 : private:
474 0 : ArrayObject& fieldInfoObject(size_t slot) const {
475 0 : return getReservedSlot(slot).toObject().as<ArrayObject>();
476 : }
477 : };
478 :
479 : typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
480 :
481 : /*
482 : * This object exists in order to encapsulate the typed object types
483 : * somewhat, rather than sticking them all into the global object.
484 : * Eventually it will go away and become a module.
485 : */
486 : class TypedObjectModuleObject : public NativeObject {
487 : public:
488 : enum Slot {
489 : ArrayTypePrototype,
490 : StructTypePrototype,
491 : SlotCount
492 : };
493 :
494 : static const Class class_;
495 : };
496 :
497 : /* Base type for transparent and opaque typed objects. */
498 : class TypedObject : public ShapedObject
499 : {
500 : static const bool IsTypedObjectClass = true;
501 :
502 : static MOZ_MUST_USE bool obj_getArrayElement(JSContext* cx,
503 : Handle<TypedObject*> typedObj,
504 : Handle<TypeDescr*> typeDescr,
505 : uint32_t index,
506 : MutableHandleValue vp);
507 :
508 : protected:
509 : static const ObjectOps objectOps_;
510 :
511 : static MOZ_MUST_USE bool obj_lookupProperty(JSContext* cx, HandleObject obj,
512 : HandleId id, MutableHandleObject objp,
513 : MutableHandle<PropertyResult> propp);
514 :
515 : static MOZ_MUST_USE bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
516 : Handle<PropertyDescriptor> desc,
517 : ObjectOpResult& result);
518 :
519 : static MOZ_MUST_USE bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
520 : bool* foundp);
521 :
522 : static MOZ_MUST_USE bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
523 : HandleId id, MutableHandleValue vp);
524 :
525 : static MOZ_MUST_USE bool obj_getElement(JSContext* cx, HandleObject obj, HandleValue receiver,
526 : uint32_t index, MutableHandleValue vp);
527 :
528 : static MOZ_MUST_USE bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
529 : HandleValue v, HandleValue receiver,
530 : ObjectOpResult& result);
531 :
532 : static MOZ_MUST_USE bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj,
533 : HandleId id,
534 : MutableHandle<PropertyDescriptor> desc);
535 :
536 : static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
537 : ObjectOpResult& result);
538 :
539 :
540 : uint8_t* typedMem() const;
541 : uint8_t* typedMemBase() const;
542 :
543 : public:
544 : static MOZ_MUST_USE bool obj_newEnumerate(JSContext* cx, HandleObject obj,
545 : AutoIdVector& properties, bool enumerableOnly);
546 :
547 : TypedProto& typedProto() const {
548 : // Typed objects' prototypes can't be modified.
549 : return staticPrototype()->as<TypedProto>();
550 : }
551 :
552 0 : TypeDescr& typeDescr() const {
553 0 : return group()->typeDescr();
554 : }
555 :
556 : static JS::Result<TypedObject*, JS::OOM&>
557 : create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
558 : js::HandleShape shape, js::HandleObjectGroup group);
559 :
560 : uint32_t offset() const;
561 : uint32_t length() const;
562 0 : uint8_t* typedMem(const JS::AutoRequireNoGC&) const { return typedMem(); }
563 : bool isAttached() const;
564 :
565 0 : uint32_t size() const {
566 0 : return typeDescr().size();
567 : }
568 :
569 0 : uint8_t* typedMem(size_t offset, const JS::AutoRequireNoGC& nogc) const {
570 : // It seems a bit surprising that one might request an offset
571 : // == size(), but it can happen when taking the "address of" a
572 : // 0-sized value. (In other words, we maintain the invariant
573 : // that `offset + size <= size()` -- this is always checked in
574 : // the caller's side.)
575 0 : MOZ_ASSERT(offset <= (size_t) size());
576 0 : return typedMem(nogc) + offset;
577 : }
578 :
579 : inline MOZ_MUST_USE bool opaque() const;
580 :
581 : // Creates a new typed object whose memory is freshly allocated and
582 : // initialized with zeroes (or, in the case of references, an appropriate
583 : // default value).
584 : static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj, int32_t length,
585 : gc::InitialHeap heap = gc::DefaultHeap);
586 :
587 : // User-accessible constructor (`new TypeDescriptor(...)`). Note that the
588 : // callee here is the type descriptor.
589 : static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
590 :
591 : /* Accessors for self hosted code. */
592 : static MOZ_MUST_USE bool GetBuffer(JSContext* cx, unsigned argc, Value* vp);
593 : static MOZ_MUST_USE bool GetByteOffset(JSContext* cx, unsigned argc, Value* vp);
594 :
595 0 : Shape** addressOfShapeFromGC() { return shape_.unsafeUnbarrieredForTracing(); }
596 : };
597 :
598 : typedef Handle<TypedObject*> HandleTypedObject;
599 :
600 : class OutlineTypedObject : public TypedObject
601 : {
602 : // The object which owns the data this object points to. Because this
603 : // pointer is managed in tandem with |data|, this is not a GCPtr and
604 : // barriers are managed directly.
605 : JSObject* owner_;
606 :
607 : // Data pointer to some offset in the owner's contents.
608 : uint8_t* data_;
609 :
610 : void setOwnerAndData(JSObject* owner, uint8_t* data);
611 :
612 : public:
613 : // JIT accessors.
614 0 : static size_t offsetOfData() { return offsetof(OutlineTypedObject, data_); }
615 0 : static size_t offsetOfOwner() { return offsetof(OutlineTypedObject, owner_); }
616 :
617 0 : JSObject& owner() const {
618 0 : MOZ_ASSERT(owner_);
619 0 : return *owner_;
620 : }
621 :
622 : JSObject* maybeOwner() const {
623 : return owner_;
624 : }
625 :
626 0 : uint8_t* outOfLineTypedMem() const {
627 0 : return data_;
628 : }
629 :
630 0 : void setData(uint8_t* data) {
631 0 : data_ = data;
632 0 : }
633 :
634 0 : void resetOffset(size_t offset) {
635 0 : MOZ_ASSERT(offset <= (size_t) size());
636 0 : setData(typedMemBase() + offset);
637 0 : }
638 :
639 : // Helper for createUnattached()
640 : static OutlineTypedObject* createUnattachedWithClass(JSContext* cx,
641 : const Class* clasp,
642 : HandleTypeDescr type,
643 : int32_t length,
644 : gc::InitialHeap heap = gc::DefaultHeap);
645 :
646 : // Creates an unattached typed object or handle (depending on the
647 : // type parameter T). Note that it is only legal for unattached
648 : // handles to escape to the end user; for non-handles, the caller
649 : // should always invoke one of the `attach()` methods below.
650 : //
651 : // Arguments:
652 : // - type: type object for resulting object
653 : // - length: 0 unless this is an array, otherwise the length
654 : static OutlineTypedObject* createUnattached(JSContext* cx, HandleTypeDescr type,
655 : int32_t length, gc::InitialHeap heap = gc::DefaultHeap);
656 :
657 : // Creates a typedObj that aliases the memory pointed at by `owner`
658 : // at the given offset. The typedObj will be a handle iff type is a
659 : // handle and a typed object otherwise.
660 : static OutlineTypedObject* createDerived(JSContext* cx,
661 : HandleTypeDescr type,
662 : Handle<TypedObject*> typedContents,
663 : uint32_t offset);
664 :
665 : // Use this method when `buffer` is the owner of the memory.
666 : void attach(JSContext* cx, ArrayBufferObject& buffer, uint32_t offset);
667 :
668 : // Otherwise, use this to attach to memory referenced by another typedObj.
669 : void attach(JSContext* cx, TypedObject& typedObj, uint32_t offset);
670 :
671 : // Invoked when array buffer is transferred elsewhere
672 : void notifyBufferDetached(void* newData);
673 :
674 : static void obj_trace(JSTracer* trace, JSObject* object);
675 : };
676 :
677 : // Class for a transparent typed object whose owner is an array buffer.
678 : class OutlineTransparentTypedObject : public OutlineTypedObject
679 : {
680 : public:
681 : static const Class class_;
682 :
683 : ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
684 : };
685 :
686 : // Class for an opaque typed object whose owner may be either an array buffer
687 : // or an opaque inlined typed object.
688 : class OutlineOpaqueTypedObject : public OutlineTypedObject
689 : {
690 : public:
691 : static const Class class_;
692 : };
693 :
694 : // Class for a typed object whose data is allocated inline.
695 : class InlineTypedObject : public TypedObject
696 : {
697 : friend class TypedObject;
698 :
699 : // Start of the inline data, which immediately follows the shape and type.
700 : uint8_t data_[1];
701 :
702 : protected:
703 0 : uint8_t* inlineTypedMem() const {
704 0 : return (uint8_t*) &data_;
705 : }
706 :
707 : public:
708 : static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
709 :
710 0 : static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
711 0 : size_t nbytes = descr->size();
712 0 : MOZ_ASSERT(nbytes <= MaximumSize);
713 :
714 0 : return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
715 : }
716 :
717 0 : uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
718 0 : return inlineTypedMem();
719 : }
720 :
721 0 : uint8_t* inlineTypedMemForGC() const {
722 0 : return inlineTypedMem();
723 : }
724 :
725 : static void obj_trace(JSTracer* trace, JSObject* object);
726 : static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
727 :
728 0 : static size_t offsetOfDataStart() {
729 0 : return offsetof(InlineTypedObject, data_);
730 : }
731 :
732 : static InlineTypedObject* create(JSContext* cx, HandleTypeDescr descr,
733 : gc::InitialHeap heap = gc::DefaultHeap);
734 : static InlineTypedObject* createCopy(JSContext* cx, Handle<InlineTypedObject*> templateObject,
735 : gc::InitialHeap heap);
736 : };
737 :
738 : // Class for a transparent typed object with inline data, which may have a
739 : // lazily allocated array buffer.
740 : class InlineTransparentTypedObject : public InlineTypedObject
741 : {
742 : public:
743 : static const Class class_;
744 :
745 : ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
746 :
747 0 : uint8_t* inlineTypedMem() const {
748 0 : return InlineTypedObject::inlineTypedMem();
749 : }
750 : };
751 :
752 : // Class for an opaque typed object with inline data and no array buffer.
753 : class InlineOpaqueTypedObject : public InlineTypedObject
754 : {
755 : public:
756 : static const Class class_;
757 : };
758 :
759 : // Class for the global SIMD object.
760 : class SimdObject : public JSObject
761 : {
762 : public:
763 : static const Class class_;
764 : static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
765 : static MOZ_MUST_USE bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId,
766 : bool* resolved);
767 : };
768 :
769 : /*
770 : * Usage: NewOpaqueTypedObject(typeObj)
771 : *
772 : * Constructs a new, unattached instance of `Handle`.
773 : */
774 : MOZ_MUST_USE bool NewOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp);
775 :
776 : /*
777 : * Usage: NewDerivedTypedObject(typeObj, owner, offset)
778 : *
779 : * Constructs a new, unattached instance of `Handle`.
780 : */
781 : MOZ_MUST_USE bool NewDerivedTypedObject(JSContext* cx, unsigned argc, Value* vp);
782 :
783 : /*
784 : * Usage: AttachTypedObject(typedObj, newDatum, newOffset)
785 : *
786 : * Moves `typedObj` to point at the memory referenced by `newDatum` with
787 : * the offset `newOffset`.
788 : */
789 : MOZ_MUST_USE bool AttachTypedObject(JSContext* cx, unsigned argc, Value* vp);
790 :
791 : /*
792 : * Usage: SetTypedObjectOffset(typedObj, offset)
793 : *
794 : * Changes the offset for `typedObj` within its buffer to `offset`.
795 : * `typedObj` must already be attached.
796 : */
797 : MOZ_MUST_USE bool SetTypedObjectOffset(JSContext*, unsigned argc, Value* vp);
798 :
799 : /*
800 : * Usage: ObjectIsTypeDescr(obj)
801 : *
802 : * True if `obj` is a type object.
803 : */
804 : MOZ_MUST_USE bool ObjectIsTypeDescr(JSContext* cx, unsigned argc, Value* vp);
805 :
806 : /*
807 : * Usage: ObjectIsTypedObject(obj)
808 : *
809 : * True if `obj` is a transparent or opaque typed object.
810 : */
811 : MOZ_MUST_USE bool ObjectIsTypedObject(JSContext* cx, unsigned argc, Value* vp);
812 :
813 : /*
814 : * Usage: ObjectIsOpaqueTypedObject(obj)
815 : *
816 : * True if `obj` is an opaque typed object.
817 : */
818 : MOZ_MUST_USE bool ObjectIsOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp);
819 :
820 : /*
821 : * Usage: ObjectIsTransparentTypedObject(obj)
822 : *
823 : * True if `obj` is a transparent typed object.
824 : */
825 : MOZ_MUST_USE bool ObjectIsTransparentTypedObject(JSContext* cx, unsigned argc, Value* vp);
826 :
827 : /* Predicates on type descriptor objects. In all cases, 'obj' must be a type descriptor. */
828 :
829 : MOZ_MUST_USE bool TypeDescrIsSimpleType(JSContext*, unsigned argc, Value* vp);
830 :
831 : MOZ_MUST_USE bool TypeDescrIsArrayType(JSContext*, unsigned argc, Value* vp);
832 :
833 : /*
834 : * Usage: TypedObjectIsAttached(obj)
835 : *
836 : * Given a TypedObject `obj`, returns true if `obj` is
837 : * "attached" (i.e., its data pointer is nullptr).
838 : */
839 : MOZ_MUST_USE bool TypedObjectIsAttached(JSContext* cx, unsigned argc, Value* vp);
840 :
841 : /*
842 : * Usage: TypedObjectTypeDescr(obj)
843 : *
844 : * Given a TypedObject `obj`, returns the object's type descriptor.
845 : */
846 : MOZ_MUST_USE bool TypedObjectTypeDescr(JSContext* cx, unsigned argc, Value* vp);
847 :
848 : /*
849 : * Usage: ClampToUint8(v)
850 : *
851 : * Same as the C function ClampDoubleToUint8. `v` must be a number.
852 : */
853 : MOZ_MUST_USE bool ClampToUint8(JSContext* cx, unsigned argc, Value* vp);
854 :
855 : /*
856 : * Usage: GetTypedObjectModule()
857 : *
858 : * Returns the global "typed object" module, which provides access
859 : * to the various builtin type descriptors. These are currently
860 : * exported as immutable properties so it is safe for self-hosted code
861 : * to access them; eventually this should be linked into the module
862 : * system.
863 : */
864 : MOZ_MUST_USE bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
865 :
866 : /*
867 : * Usage: GetSimdTypeDescr(simdTypeRepr)
868 : *
869 : * Returns one of the SIMD type objects, identified by `simdTypeRepr` which must
870 : * be one of the JS_SIMDTYPEREPR_* constants.
871 : *
872 : * The SIMD pseudo-module must have been initialized for this to be safe.
873 : */
874 : MOZ_MUST_USE bool GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp);
875 :
876 : /*
877 : * Usage: Store_int8(targetDatum, targetOffset, value)
878 : * ...
879 : * Store_uint8(targetDatum, targetOffset, value)
880 : * ...
881 : * Store_float32(targetDatum, targetOffset, value)
882 : * Store_float64(targetDatum, targetOffset, value)
883 : *
884 : * Intrinsic function. Stores `value` into the memory referenced by
885 : * `targetDatum` at the offset `targetOffset`.
886 : *
887 : * Assumes (and asserts) that:
888 : * - `targetDatum` is attached
889 : * - `targetOffset` is a valid offset within the bounds of `targetDatum`
890 : * - `value` is a number
891 : */
892 : #define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \
893 : class StoreScalar##T { \
894 : public: \
895 : static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
896 : static const JSJitInfo JitInfo; \
897 : };
898 :
899 : /*
900 : * Usage: Store_Any(targetDatum, targetOffset, fieldName, value)
901 : * Store_Object(targetDatum, targetOffset, fieldName, value)
902 : * Store_string(targetDatum, targetOffset, fieldName, value)
903 : *
904 : * Intrinsic function. Stores `value` into the memory referenced by
905 : * `targetDatum` at the offset `targetOffset`.
906 : *
907 : * Assumes (and asserts) that:
908 : * - `targetDatum` is attached
909 : * - `targetOffset` is a valid offset within the bounds of `targetDatum`
910 : * - `value` is an object or null (`Store_Object`) or string (`Store_string`).
911 : */
912 : #define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \
913 : class StoreReference##_name { \
914 : private: \
915 : static MOZ_MUST_USE bool store(JSContext* cx, T* heap, const Value& v, \
916 : TypedObject* obj, jsid id); \
917 : \
918 : public: \
919 : static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
920 : static const JSJitInfo JitInfo; \
921 : };
922 :
923 : /*
924 : * Usage: LoadScalar(targetDatum, targetOffset, value)
925 : *
926 : * Intrinsic function. Loads value (which must be an int32 or uint32)
927 : * by `scalarTypeRepr` (which must be a type repr obj) and loads the
928 : * value at the memory for `targetDatum` at offset `targetOffset`.
929 : * `targetDatum` must be attached.
930 : */
931 : #define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name) \
932 : class LoadScalar##T { \
933 : public: \
934 : static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
935 : static const JSJitInfo JitInfo; \
936 : };
937 :
938 : /*
939 : * Usage: LoadReference(targetDatum, targetOffset, value)
940 : *
941 : * Intrinsic function. Stores value (which must be an int32 or uint32)
942 : * by `scalarTypeRepr` (which must be a type repr obj) and stores the
943 : * value at the memory for `targetDatum` at offset `targetOffset`.
944 : * `targetDatum` must be attached.
945 : */
946 : #define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \
947 : class LoadReference##_name { \
948 : private: \
949 : static void load(T* heap, MutableHandleValue v); \
950 : \
951 : public: \
952 : static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
953 : static const JSJitInfo JitInfo; \
954 : };
955 :
956 : // I was using templates for this stuff instead of macros, but ran
957 : // into problems with the Unagi compiler.
958 : JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
959 : JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
960 : JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN)
961 : JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
962 :
963 : inline bool
964 7539 : IsTypedObjectClass(const Class* class_)
965 : {
966 7539 : return class_ == &OutlineTransparentTypedObject::class_ ||
967 7539 : class_ == &InlineTransparentTypedObject::class_ ||
968 15078 : class_ == &OutlineOpaqueTypedObject::class_ ||
969 7539 : class_ == &InlineOpaqueTypedObject::class_;
970 : }
971 :
972 : inline bool
973 0 : IsOpaqueTypedObjectClass(const Class* class_)
974 : {
975 0 : return class_ == &OutlineOpaqueTypedObject::class_ ||
976 0 : class_ == &InlineOpaqueTypedObject::class_;
977 : }
978 :
979 : inline bool
980 0 : IsOutlineTypedObjectClass(const Class* class_)
981 : {
982 0 : return class_ == &OutlineOpaqueTypedObject::class_ ||
983 0 : class_ == &OutlineTransparentTypedObject::class_;
984 : }
985 :
986 : inline bool
987 0 : IsInlineTypedObjectClass(const Class* class_)
988 : {
989 0 : return class_ == &InlineOpaqueTypedObject::class_ ||
990 0 : class_ == &InlineTransparentTypedObject::class_;
991 : }
992 :
993 : inline const Class*
994 0 : GetOutlineTypedObjectClass(bool opaque)
995 : {
996 0 : return opaque ? &OutlineOpaqueTypedObject::class_ : &OutlineTransparentTypedObject::class_;
997 : }
998 :
999 : inline bool
1000 528 : IsSimpleTypeDescrClass(const Class* clasp)
1001 : {
1002 528 : return clasp == &ScalarTypeDescr::class_ ||
1003 528 : clasp == &ReferenceTypeDescr::class_;
1004 : }
1005 :
1006 : inline bool
1007 528 : IsComplexTypeDescrClass(const Class* clasp)
1008 : {
1009 528 : return clasp == &StructTypeDescr::class_ ||
1010 1056 : clasp == &ArrayTypeDescr::class_ ||
1011 528 : clasp == &SimdTypeDescr::class_;
1012 : }
1013 :
1014 : inline bool
1015 528 : IsTypeDescrClass(const Class* clasp)
1016 : {
1017 1056 : return IsSimpleTypeDescrClass(clasp) ||
1018 1056 : IsComplexTypeDescrClass(clasp);
1019 : }
1020 :
1021 : inline bool
1022 0 : TypedObject::opaque() const
1023 : {
1024 0 : return IsOpaqueTypedObjectClass(getClass());
1025 : }
1026 :
1027 : JSObject*
1028 : InitTypedObjectModuleObject(JSContext* cx, JS::HandleObject obj);
1029 :
1030 : } // namespace js
1031 :
1032 : template <>
1033 : inline bool
1034 0 : JSObject::is<js::SimpleTypeDescr>() const
1035 : {
1036 0 : return IsSimpleTypeDescrClass(getClass());
1037 : }
1038 :
1039 : template <>
1040 : inline bool
1041 0 : JSObject::is<js::ComplexTypeDescr>() const
1042 : {
1043 0 : return IsComplexTypeDescrClass(getClass());
1044 : }
1045 :
1046 : template <>
1047 : inline bool
1048 528 : JSObject::is<js::TypeDescr>() const
1049 : {
1050 528 : return IsTypeDescrClass(getClass());
1051 : }
1052 :
1053 : template <>
1054 : inline bool
1055 7384 : JSObject::is<js::TypedObject>() const
1056 : {
1057 7384 : return IsTypedObjectClass(getClass());
1058 : }
1059 :
1060 : template <>
1061 : inline bool
1062 7999 : JSObject::is<js::OutlineTypedObject>() const
1063 : {
1064 15998 : return getClass() == &js::OutlineTransparentTypedObject::class_ ||
1065 15998 : getClass() == &js::OutlineOpaqueTypedObject::class_;
1066 : }
1067 :
1068 : template <>
1069 : inline bool
1070 30700 : JSObject::is<js::InlineTypedObject>() const
1071 : {
1072 61400 : return getClass() == &js::InlineTransparentTypedObject::class_ ||
1073 61400 : getClass() == &js::InlineOpaqueTypedObject::class_;
1074 : }
1075 :
1076 : #endif /* builtin_TypedObject_h */
|