summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/TypedObject.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/builtin/TypedObject.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/builtin/TypedObject.h')
-rw-r--r--js/src/builtin/TypedObject.h1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h
new file mode 100644
index 000000000..6da62b656
--- /dev/null
+++ b/js/src/builtin/TypedObject.h
@@ -0,0 +1,1072 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef builtin_TypedObject_h
+#define builtin_TypedObject_h
+
+#include "jsobj.h"
+#include "jsweakmap.h"
+
+#include "builtin/TypedObjectConstants.h"
+#include "js/Conversions.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/ShapedObject.h"
+
+/*
+ * -------------
+ * Typed Objects
+ * -------------
+ *
+ * Typed objects are a special kind of JS object where the data is
+ * given well-structured form. To use a typed object, users first
+ * create *type objects* (no relation to the type objects used in TI)
+ * that define the type layout. For example, a statement like:
+ *
+ * var PointType = new StructType({x: uint8, y: uint8});
+ *
+ * would create a type object PointType that is a struct with
+ * two fields, each of uint8 type.
+ *
+ * This comment typically assumes familiary with the API. For more
+ * info on the API itself, see the Harmony wiki page at
+ * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the
+ * ES6 spec (not finalized at the time of this writing).
+ *
+ * - Initialization:
+ *
+ * Currently, all "globals" related to typed objects are packaged
+ * within a single "module" object `TypedObject`. This module has its
+ * own js::Class and when that class is initialized, we also create
+ * and define all other values (in `js::InitTypedObjectModuleClass()`).
+ *
+ * - Type objects, meta type objects, and type representations:
+ *
+ * There are a number of pre-defined type objects, one for each
+ * scalar type (`uint8` etc). Each of these has its own class_,
+ * defined in `DefineNumericClass()`.
+ *
+ * There are also meta type objects (`ArrayType`, `StructType`).
+ * These constructors are not themselves type objects but rather the
+ * means for the *user* to construct new typed objects.
+ *
+ * Each type object is associated with a *type representation* (see
+ * TypeRepresentation.h). Type representations are canonical versions
+ * of type objects. We attach them to TI type objects and (eventually)
+ * use them for shape guards etc. They are purely internal to the
+ * engine and are not exposed to end users (though self-hosted code
+ * sometimes accesses them).
+ *
+ * - Typed objects:
+ *
+ * A typed object is an instance of a *type object* (note the past participle).
+ * Typed objects can be either transparent or opaque, depending on whether
+ * their underlying buffer can be accessed. Transparent and opaque typed
+ * objects have different classes, and can have different physical layouts.
+ * The following layouts are possible:
+ *
+ * InlineTypedObject: Typed objects whose data immediately follows the object's
+ * header are inline typed objects. The buffer for these objects is created
+ * lazily and stored via the compartment's LazyArrayBufferTable, and points
+ * back into the object's internal data.
+ *
+ * OutlineTypedObject: Typed objects whose data is owned by another object,
+ * which can be either an array buffer or an inline typed object. Outline
+ * typed objects may be attached or unattached. An unattached typed object
+ * has no data associated with it. When first created, objects are always
+ * attached, but they can become unattached if their buffer becomes detached.
+ *
+ * Note that whether a typed object is opaque is not directly
+ * connected to its type. That is, opaque types are *always*
+ * represented by opaque typed objects, but you may have opaque typed
+ * objects for transparent types too. This can occur for two reasons:
+ * (1) a transparent type may be embedded within an opaque type or (2)
+ * users can choose to convert transparent typed objects into opaque
+ * ones to avoid giving access to the buffer itself.
+ *
+ * Typed objects (no matter their class) are non-native objects that
+ * fully override the property accessors etc. The overridden accessor
+ * methods are the same in each and are defined in methods of
+ * TypedObject.
+ */
+
+namespace js {
+
+/*
+ * Helper method for converting a double into other scalar
+ * types in the same way that JavaScript would. In particular,
+ * simple C casting from double to int32_t gets things wrong
+ * for values like 0xF0000000.
+ */
+template <typename T>
+static T ConvertScalar(double d)
+{
+ if (TypeIsFloatingPoint<T>())
+ return T(d);
+ if (TypeIsUnsigned<T>()) {
+ uint32_t n = JS::ToUint32(d);
+ return T(n);
+ }
+ int32_t n = JS::ToInt32(d);
+ return T(n);
+}
+
+namespace type {
+
+enum Kind {
+ Scalar = JS_TYPEREPR_SCALAR_KIND,
+ Reference = JS_TYPEREPR_REFERENCE_KIND,
+ Simd = JS_TYPEREPR_SIMD_KIND,
+ Struct = JS_TYPEREPR_STRUCT_KIND,
+ Array = JS_TYPEREPR_ARRAY_KIND
+};
+
+} // namespace type
+
+///////////////////////////////////////////////////////////////////////////
+// Typed Prototypes
+
+class SimpleTypeDescr;
+class ComplexTypeDescr;
+class SimdTypeDescr;
+class StructTypeDescr;
+class TypedProto;
+
+/*
+ * The prototype for a typed object.
+ */
+class TypedProto : public NativeObject
+{
+ public:
+ static const Class class_;
+};
+
+class TypeDescr : public NativeObject
+{
+ public:
+ TypedProto& typedProto() const {
+ return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
+ }
+
+ JSAtom& stringRepr() const {
+ return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom();
+ }
+
+ type::Kind kind() const {
+ return (type::Kind) getReservedSlot(JS_DESCR_SLOT_KIND).toInt32();
+ }
+
+ bool opaque() const {
+ return getReservedSlot(JS_DESCR_SLOT_OPAQUE).toBoolean();
+ }
+
+ bool transparent() const {
+ return !opaque();
+ }
+
+ uint32_t alignment() const {
+ int32_t i = getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32();
+ MOZ_ASSERT(i >= 0);
+ return uint32_t(i);
+ }
+
+ uint32_t size() const {
+ int32_t i = getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32();
+ MOZ_ASSERT(i >= 0);
+ return uint32_t(i);
+ }
+
+ // Whether id is an 'own' property of objects with this descriptor.
+ MOZ_MUST_USE bool hasProperty(const JSAtomState& names, jsid id);
+
+ // Type descriptors may contain a list of their references for use during
+ // scanning. Marking code is optimized to use this list to mark inline
+ // typed objects, rather than the slower trace hook. This list is only
+ // specified when (a) the descriptor is short enough that it can fit in an
+ // InlineTypedObject, and (b) the descriptor contains at least one
+ // reference. Otherwise its value is undefined.
+ //
+ // The list is three consecutive arrays of int32_t offsets, with each array
+ // terminated by -1. The arrays store offsets of string, object, and value
+ // references in the descriptor, in that order.
+ MOZ_MUST_USE bool hasTraceList() const {
+ return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
+ }
+ const int32_t* traceList() const {
+ MOZ_ASSERT(hasTraceList());
+ return reinterpret_cast<int32_t*>(getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
+ }
+
+ void initInstances(const JSRuntime* rt, uint8_t* mem, size_t length);
+ void traceInstances(JSTracer* trace, uint8_t* mem, size_t length);
+
+ static void finalize(FreeOp* fop, JSObject* obj);
+};
+
+typedef Handle<TypeDescr*> HandleTypeDescr;
+
+class SimpleTypeDescr : public TypeDescr
+{
+};
+
+// Type for scalar type constructors like `uint8`. All such type
+// constructors share a common js::Class and JSFunctionSpec. Scalar
+// types are non-opaque (their storage is visible unless combined with
+// an opaque reference type.)
+class ScalarTypeDescr : public SimpleTypeDescr
+{
+ public:
+ typedef Scalar::Type Type;
+
+ static const type::Kind Kind = type::Scalar;
+ static const bool Opaque = false;
+ static uint32_t size(Type t);
+ static uint32_t alignment(Type t);
+ static const char* typeName(Type type);
+
+ static const Class class_;
+ static const JSFunctionSpec typeObjectMethods[];
+
+ Type type() const {
+ // Make sure the values baked into TypedObjectConstants.h line up with
+ // the Scalar::Type enum. We don't define Scalar::Type directly in
+ // terms of these constants to avoid making TypedObjectConstants.h a
+ // public header file.
+ static_assert(Scalar::Int8 == JS_SCALARTYPEREPR_INT8,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Uint8 == JS_SCALARTYPEREPR_UINT8,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Int16 == JS_SCALARTYPEREPR_INT16,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Uint16 == JS_SCALARTYPEREPR_UINT16,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Int32 == JS_SCALARTYPEREPR_INT32,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Uint32 == JS_SCALARTYPEREPR_UINT32,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Float32 == JS_SCALARTYPEREPR_FLOAT32,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Float64 == JS_SCALARTYPEREPR_FLOAT64,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Uint8Clamped == JS_SCALARTYPEREPR_UINT8_CLAMPED,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Float32x4 == JS_SCALARTYPEREPR_FLOAT32X4,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Int8x16 == JS_SCALARTYPEREPR_INT8X16,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Int16x8 == JS_SCALARTYPEREPR_INT16X8,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+ static_assert(Scalar::Int32x4 == JS_SCALARTYPEREPR_INT32X4,
+ "TypedObjectConstants.h must be consistent with Scalar::Type");
+
+ return Type(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
+ }
+
+ static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
+};
+
+// Enumerates the cases of ScalarTypeDescr::Type which have
+// unique C representation. In particular, omits Uint8Clamped since it
+// is just a Uint8.
+#define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \
+ macro_(Scalar::Int8, int8_t, int8) \
+ macro_(Scalar::Uint8, uint8_t, uint8) \
+ macro_(Scalar::Int16, int16_t, int16) \
+ macro_(Scalar::Uint16, uint16_t, uint16) \
+ macro_(Scalar::Int32, int32_t, int32) \
+ macro_(Scalar::Uint32, uint32_t, uint32) \
+ macro_(Scalar::Float32, float, float32) \
+ macro_(Scalar::Float64, double, float64)
+
+// Must be in same order as the enum ScalarTypeDescr::Type:
+#define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \
+ JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \
+ macro_(Scalar::Uint8Clamped, uint8_t, uint8Clamped)
+
+// Type for reference type constructors like `Any`, `String`, and
+// `Object`. All such type constructors share a common js::Class and
+// JSFunctionSpec. All these types are opaque.
+class ReferenceTypeDescr : public SimpleTypeDescr
+{
+ public:
+ // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below
+ enum Type {
+ TYPE_ANY = JS_REFERENCETYPEREPR_ANY,
+ TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT,
+ TYPE_STRING = JS_REFERENCETYPEREPR_STRING,
+ };
+ static const int32_t TYPE_MAX = TYPE_STRING + 1;
+ static const char* typeName(Type type);
+
+ static const type::Kind Kind = type::Reference;
+ static const bool Opaque = true;
+ static const Class class_;
+ static uint32_t size(Type t);
+ static uint32_t alignment(Type t);
+ static const JSFunctionSpec typeObjectMethods[];
+
+ ReferenceTypeDescr::Type type() const {
+ return (ReferenceTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
+ }
+
+ const char* typeName() const {
+ return typeName(type());
+ }
+
+ static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
+};
+
+#define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \
+ macro_(ReferenceTypeDescr::TYPE_ANY, GCPtrValue, Any) \
+ macro_(ReferenceTypeDescr::TYPE_OBJECT, GCPtrObject, Object) \
+ macro_(ReferenceTypeDescr::TYPE_STRING, GCPtrString, string)
+
+// Type descriptors whose instances are objects and hence which have
+// an associated `prototype` property.
+class ComplexTypeDescr : public TypeDescr
+{
+ public:
+ // Returns the prototype that instances of this type descriptor
+ // will have.
+ TypedProto& instancePrototype() const {
+ return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
+ }
+};
+
+enum class SimdType;
+
+/*
+ * SIMD Type descriptors.
+ */
+class SimdTypeDescr : public ComplexTypeDescr
+{
+ public:
+ static const type::Kind Kind = type::Simd;
+ static const bool Opaque = false;
+ static const Class class_;
+ static uint32_t size(SimdType t);
+ static uint32_t alignment(SimdType t);
+ static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
+ static bool is(const Value& v);
+
+ SimdType type() const;
+};
+
+bool IsTypedObjectClass(const Class* clasp); // Defined below
+bool IsTypedObjectArray(JSObject& obj);
+
+MOZ_MUST_USE bool CreateUserSizeAndAlignmentProperties(JSContext* cx, HandleTypeDescr obj);
+
+class ArrayTypeDescr;
+
+/*
+ * Properties and methods of the `ArrayType` meta type object. There
+ * is no `class_` field because `ArrayType` is just a native
+ * constructor function.
+ */
+class ArrayMetaTypeDescr : public NativeObject
+{
+ private:
+ // Helper for creating a new ArrayType object.
+ //
+ // - `arrayTypePrototype` - prototype for the new object to be created
+ // - `elementType` - type object for the elements in the array
+ // - `stringRepr` - canonical string representation for the array
+ // - `size` - length of the array
+ static ArrayTypeDescr* create(JSContext* cx,
+ HandleObject arrayTypePrototype,
+ HandleTypeDescr elementType,
+ HandleAtom stringRepr,
+ int32_t size,
+ int32_t length);
+
+ public:
+ // Properties and methods to be installed on ArrayType.prototype,
+ // and hence inherited by all array type objects:
+ static const JSPropertySpec typeObjectProperties[];
+ static const JSFunctionSpec typeObjectMethods[];
+
+ // Properties and methods to be installed on ArrayType.prototype.prototype,
+ // and hence inherited by all array *typed* objects:
+ static const JSPropertySpec typedObjectProperties[];
+ static const JSFunctionSpec typedObjectMethods[];
+
+ // This is the function that gets called when the user
+ // does `new ArrayType(elem)`. It produces an array type object.
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+};
+
+/*
+ * Type descriptor created by `new ArrayType(type, n)`
+ */
+class ArrayTypeDescr : public ComplexTypeDescr
+{
+ public:
+ static const Class class_;
+ static const type::Kind Kind = type::Array;
+
+ TypeDescr& elementType() const {
+ return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as<TypeDescr>();
+ }
+
+ uint32_t length() const {
+ int32_t i = getReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH).toInt32();
+ MOZ_ASSERT(i >= 0);
+ return uint32_t(i);
+ }
+
+ static int32_t offsetOfLength() {
+ return getFixedSlotOffset(JS_DESCR_SLOT_ARRAY_LENGTH);
+ }
+};
+
+/*
+ * Properties and methods of the `StructType` meta type object. There
+ * is no `class_` field because `StructType` is just a native
+ * constructor function.
+ */
+class StructMetaTypeDescr : public NativeObject
+{
+ private:
+ static JSObject* create(JSContext* cx, HandleObject structTypeGlobal,
+ HandleObject fields);
+
+ public:
+ // Properties and methods to be installed on StructType.prototype,
+ // and hence inherited by all struct type objects:
+ static const JSPropertySpec typeObjectProperties[];
+ static const JSFunctionSpec typeObjectMethods[];
+
+ // Properties and methods to be installed on StructType.prototype.prototype,
+ // and hence inherited by all struct *typed* objects:
+ static const JSPropertySpec typedObjectProperties[];
+ static const JSFunctionSpec typedObjectMethods[];
+
+ // This is the function that gets called when the user
+ // does `new StructType(...)`. It produces a struct type object.
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+};
+
+class StructTypeDescr : public ComplexTypeDescr
+{
+ public:
+ static const Class class_;
+
+ // Returns the number of fields defined in this struct.
+ size_t fieldCount() const;
+
+ // Set `*out` to the index of the field named `id` and returns true,
+ // or return false if no such field exists.
+ MOZ_MUST_USE bool fieldIndex(jsid id, size_t* out) const;
+
+ // Return the name of the field at index `index`.
+ JSAtom& fieldName(size_t index) const;
+
+ // Return the type descr of the field at index `index`.
+ TypeDescr& fieldDescr(size_t index) const;
+
+ // Return the offset of the field at index `index`.
+ size_t fieldOffset(size_t index) const;
+
+ private:
+ ArrayObject& fieldInfoObject(size_t slot) const {
+ return getReservedSlot(slot).toObject().as<ArrayObject>();
+ }
+};
+
+typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
+
+/*
+ * This object exists in order to encapsulate the typed object types
+ * somewhat, rather than sticking them all into the global object.
+ * Eventually it will go away and become a module.
+ */
+class TypedObjectModuleObject : public NativeObject {
+ public:
+ enum Slot {
+ ArrayTypePrototype,
+ StructTypePrototype,
+ SlotCount
+ };
+
+ static const Class class_;
+};
+
+/* Base type for transparent and opaque typed objects. */
+class TypedObject : public ShapedObject
+{
+ static const bool IsTypedObjectClass = true;
+
+ static MOZ_MUST_USE bool obj_getArrayElement(JSContext* cx,
+ Handle<TypedObject*> typedObj,
+ Handle<TypeDescr*> typeDescr,
+ uint32_t index,
+ MutableHandleValue vp);
+
+ protected:
+ static const ObjectOps objectOps_;
+
+ static MOZ_MUST_USE bool obj_lookupProperty(JSContext* cx, HandleObject obj,
+ HandleId id, MutableHandleObject objp,
+ MutableHandleShape propp);
+
+ static MOZ_MUST_USE bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result);
+
+ static MOZ_MUST_USE bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
+ bool* foundp);
+
+ static MOZ_MUST_USE bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
+ HandleId id, MutableHandleValue vp);
+
+ static MOZ_MUST_USE bool obj_getElement(JSContext* cx, HandleObject obj, HandleValue receiver,
+ uint32_t index, MutableHandleValue vp);
+
+ static MOZ_MUST_USE bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
+ HandleValue v, HandleValue receiver,
+ ObjectOpResult& result);
+
+ static MOZ_MUST_USE bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj,
+ HandleId id,
+ MutableHandle<PropertyDescriptor> desc);
+
+ static MOZ_MUST_USE bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
+ ObjectOpResult& result);
+
+ static MOZ_MUST_USE bool obj_enumerate(JSContext* cx, HandleObject obj,
+ AutoIdVector& properties, bool enumerableOnly);
+
+
+ uint8_t* typedMem() const;
+ uint8_t* typedMemBase() const;
+
+ public:
+ TypedProto& typedProto() const {
+ // Typed objects' prototypes can't be modified.
+ return staticPrototype()->as<TypedProto>();
+ }
+
+ TypeDescr& typeDescr() const {
+ return group()->typeDescr();
+ }
+
+ uint32_t offset() const;
+ uint32_t length() const;
+ uint8_t* typedMem(const JS::AutoRequireNoGC&) const { return typedMem(); }
+ bool isAttached() const;
+
+ uint32_t size() const {
+ return typeDescr().size();
+ }
+
+ uint8_t* typedMem(size_t offset, const JS::AutoRequireNoGC& nogc) const {
+ // It seems a bit surprising that one might request an offset
+ // == size(), but it can happen when taking the "address of" a
+ // 0-sized value. (In other words, we maintain the invariant
+ // that `offset + size <= size()` -- this is always checked in
+ // the caller's side.)
+ MOZ_ASSERT(offset <= (size_t) size());
+ return typedMem(nogc) + offset;
+ }
+
+ inline MOZ_MUST_USE bool opaque() const;
+
+ // Creates a new typed object whose memory is freshly allocated and
+ // initialized with zeroes (or, in the case of references, an appropriate
+ // default value).
+ static TypedObject* createZeroed(JSContext* cx, HandleTypeDescr typeObj, int32_t length,
+ gc::InitialHeap heap = gc::DefaultHeap);
+
+ // User-accessible constructor (`new TypeDescriptor(...)`). Note that the
+ // callee here is the type descriptor.
+ static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
+
+ /* Accessors for self hosted code. */
+ static MOZ_MUST_USE bool GetBuffer(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool GetByteOffset(JSContext* cx, unsigned argc, Value* vp);
+
+ Shape** addressOfShapeFromGC() { return shape_.unsafeUnbarrieredForTracing(); }
+};
+
+typedef Handle<TypedObject*> HandleTypedObject;
+
+class OutlineTypedObject : public TypedObject
+{
+ // The object which owns the data this object points to. Because this
+ // pointer is managed in tandem with |data|, this is not a GCPtr and
+ // barriers are managed directly.
+ JSObject* owner_;
+
+ // Data pointer to some offset in the owner's contents.
+ uint8_t* data_;
+
+ void setOwnerAndData(JSObject* owner, uint8_t* data);
+
+ public:
+ // JIT accessors.
+ static size_t offsetOfData() { return offsetof(OutlineTypedObject, data_); }
+ static size_t offsetOfOwner() { return offsetof(OutlineTypedObject, owner_); }
+
+ JSObject& owner() const {
+ MOZ_ASSERT(owner_);
+ return *owner_;
+ }
+
+ JSObject* maybeOwner() const {
+ return owner_;
+ }
+
+ uint8_t* outOfLineTypedMem() const {
+ return data_;
+ }
+
+ void setData(uint8_t* data) {
+ data_ = data;
+ }
+
+ void resetOffset(size_t offset) {
+ MOZ_ASSERT(offset <= (size_t) size());
+ setData(typedMemBase() + offset);
+ }
+
+ // Helper for createUnattached()
+ static OutlineTypedObject* createUnattachedWithClass(JSContext* cx,
+ const Class* clasp,
+ HandleTypeDescr type,
+ int32_t length,
+ gc::InitialHeap heap = gc::DefaultHeap);
+
+ // Creates an unattached typed object or handle (depending on the
+ // type parameter T). Note that it is only legal for unattached
+ // handles to escape to the end user; for non-handles, the caller
+ // should always invoke one of the `attach()` methods below.
+ //
+ // Arguments:
+ // - type: type object for resulting object
+ // - length: 0 unless this is an array, otherwise the length
+ static OutlineTypedObject* createUnattached(JSContext* cx, HandleTypeDescr type,
+ int32_t length, gc::InitialHeap heap = gc::DefaultHeap);
+
+ // Creates a typedObj that aliases the memory pointed at by `owner`
+ // at the given offset. The typedObj will be a handle iff type is a
+ // handle and a typed object otherwise.
+ static OutlineTypedObject* createDerived(JSContext* cx,
+ HandleTypeDescr type,
+ Handle<TypedObject*> typedContents,
+ uint32_t offset);
+
+ // Use this method when `buffer` is the owner of the memory.
+ void attach(JSContext* cx, ArrayBufferObject& buffer, uint32_t offset);
+
+ // Otherwise, use this to attach to memory referenced by another typedObj.
+ void attach(JSContext* cx, TypedObject& typedObj, uint32_t offset);
+
+ // Invoked when array buffer is transferred elsewhere
+ void notifyBufferDetached(void* newData);
+
+ static void obj_trace(JSTracer* trace, JSObject* object);
+};
+
+// Class for a transparent typed object whose owner is an array buffer.
+class OutlineTransparentTypedObject : public OutlineTypedObject
+{
+ public:
+ static const Class class_;
+
+ ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
+};
+
+// Class for an opaque typed object whose owner may be either an array buffer
+// or an opaque inlined typed object.
+class OutlineOpaqueTypedObject : public OutlineTypedObject
+{
+ public:
+ static const Class class_;
+};
+
+// Class for a typed object whose data is allocated inline.
+class InlineTypedObject : public TypedObject
+{
+ friend class TypedObject;
+
+ // Start of the inline data, which immediately follows the shape and type.
+ uint8_t data_[1];
+
+ protected:
+ uint8_t* inlineTypedMem() const {
+ return (uint8_t*) &data_;
+ }
+
+ public:
+ static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
+
+ static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
+ size_t nbytes = descr->size();
+ MOZ_ASSERT(nbytes <= MaximumSize);
+
+ return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
+ }
+
+ uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
+ return inlineTypedMem();
+ }
+
+ uint8_t* inlineTypedMemForGC() const {
+ return inlineTypedMem();
+ }
+
+ static void obj_trace(JSTracer* trace, JSObject* object);
+ static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
+
+ static size_t offsetOfDataStart() {
+ return offsetof(InlineTypedObject, data_);
+ }
+
+ static InlineTypedObject* create(JSContext* cx, HandleTypeDescr descr,
+ gc::InitialHeap heap = gc::DefaultHeap);
+ static InlineTypedObject* createCopy(JSContext* cx, Handle<InlineTypedObject*> templateObject,
+ gc::InitialHeap heap);
+};
+
+// Class for a transparent typed object with inline data, which may have a
+// lazily allocated array buffer.
+class InlineTransparentTypedObject : public InlineTypedObject
+{
+ public:
+ static const Class class_;
+
+ ArrayBufferObject* getOrCreateBuffer(JSContext* cx);
+
+ uint8_t* inlineTypedMem() const {
+ return InlineTypedObject::inlineTypedMem();
+ }
+};
+
+// Class for an opaque typed object with inline data and no array buffer.
+class InlineOpaqueTypedObject : public InlineTypedObject
+{
+ public:
+ static const Class class_;
+};
+
+// Class for the global SIMD object.
+class SimdObject : public JSObject
+{
+ public:
+ static const Class class_;
+ static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
+ static MOZ_MUST_USE bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId,
+ bool* resolved);
+};
+
+/*
+ * Usage: NewOpaqueTypedObject(typeObj)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+MOZ_MUST_USE bool NewOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: NewDerivedTypedObject(typeObj, owner, offset)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+MOZ_MUST_USE bool NewDerivedTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: AttachTypedObject(typedObj, newDatum, newOffset)
+ *
+ * Moves `typedObj` to point at the memory referenced by `newDatum` with
+ * the offset `newOffset`.
+ */
+MOZ_MUST_USE bool AttachTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: SetTypedObjectOffset(typedObj, offset)
+ *
+ * Changes the offset for `typedObj` within its buffer to `offset`.
+ * `typedObj` must already be attached.
+ */
+MOZ_MUST_USE bool SetTypedObjectOffset(JSContext*, unsigned argc, Value* vp);
+
+/*
+ * Usage: ObjectIsTypeDescr(obj)
+ *
+ * True if `obj` is a type object.
+ */
+MOZ_MUST_USE bool ObjectIsTypeDescr(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: ObjectIsTypedObject(obj)
+ *
+ * True if `obj` is a transparent or opaque typed object.
+ */
+MOZ_MUST_USE bool ObjectIsTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: ObjectIsOpaqueTypedObject(obj)
+ *
+ * True if `obj` is an opaque typed object.
+ */
+MOZ_MUST_USE bool ObjectIsOpaqueTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: ObjectIsTransparentTypedObject(obj)
+ *
+ * True if `obj` is a transparent typed object.
+ */
+MOZ_MUST_USE bool ObjectIsTransparentTypedObject(JSContext* cx, unsigned argc, Value* vp);
+
+/* Predicates on type descriptor objects. In all cases, 'obj' must be a type descriptor. */
+
+MOZ_MUST_USE bool TypeDescrIsSimpleType(JSContext*, unsigned argc, Value* vp);
+
+MOZ_MUST_USE bool TypeDescrIsArrayType(JSContext*, unsigned argc, Value* vp);
+
+/*
+ * Usage: TypedObjectIsAttached(obj)
+ *
+ * Given a TypedObject `obj`, returns true if `obj` is
+ * "attached" (i.e., its data pointer is nullptr).
+ */
+MOZ_MUST_USE bool TypedObjectIsAttached(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: TypedObjectTypeDescr(obj)
+ *
+ * Given a TypedObject `obj`, returns the object's type descriptor.
+ */
+MOZ_MUST_USE bool TypedObjectTypeDescr(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: ClampToUint8(v)
+ *
+ * Same as the C function ClampDoubleToUint8. `v` must be a number.
+ */
+MOZ_MUST_USE bool ClampToUint8(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: GetTypedObjectModule()
+ *
+ * Returns the global "typed object" module, which provides access
+ * to the various builtin type descriptors. These are currently
+ * exported as immutable properties so it is safe for self-hosted code
+ * to access them; eventually this should be linked into the module
+ * system.
+ */
+MOZ_MUST_USE bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: GetSimdTypeDescr(simdTypeRepr)
+ *
+ * Returns one of the SIMD type objects, identified by `simdTypeRepr` which must
+ * be one of the JS_SIMDTYPEREPR_* constants.
+ *
+ * The SIMD pseudo-module must have been initialized for this to be safe.
+ */
+MOZ_MUST_USE bool GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp);
+
+/*
+ * Usage: Store_int8(targetDatum, targetOffset, value)
+ * ...
+ * Store_uint8(targetDatum, targetOffset, value)
+ * ...
+ * Store_float32(targetDatum, targetOffset, value)
+ * Store_float64(targetDatum, targetOffset, value)
+ *
+ * Intrinsic function. Stores `value` into the memory referenced by
+ * `targetDatum` at the offset `targetOffset`.
+ *
+ * Assumes (and asserts) that:
+ * - `targetDatum` is attached
+ * - `targetOffset` is a valid offset within the bounds of `targetDatum`
+ * - `value` is a number
+ */
+#define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \
+class StoreScalar##T { \
+ public: \
+ static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
+ static const JSJitInfo JitInfo; \
+};
+
+/*
+ * Usage: Store_Any(targetDatum, targetOffset, fieldName, value)
+ * Store_Object(targetDatum, targetOffset, fieldName, value)
+ * Store_string(targetDatum, targetOffset, fieldName, value)
+ *
+ * Intrinsic function. Stores `value` into the memory referenced by
+ * `targetDatum` at the offset `targetOffset`.
+ *
+ * Assumes (and asserts) that:
+ * - `targetDatum` is attached
+ * - `targetOffset` is a valid offset within the bounds of `targetDatum`
+ * - `value` is an object or null (`Store_Object`) or string (`Store_string`).
+ */
+#define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \
+class StoreReference##_name { \
+ private: \
+ static MOZ_MUST_USE bool store(JSContext* cx, T* heap, const Value& v, \
+ TypedObject* obj, jsid id); \
+ \
+ public: \
+ static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
+ static const JSJitInfo JitInfo; \
+};
+
+/*
+ * Usage: LoadScalar(targetDatum, targetOffset, value)
+ *
+ * Intrinsic function. Loads value (which must be an int32 or uint32)
+ * by `scalarTypeRepr` (which must be a type repr obj) and loads the
+ * value at the memory for `targetDatum` at offset `targetOffset`.
+ * `targetDatum` must be attached.
+ */
+#define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name) \
+class LoadScalar##T { \
+ public: \
+ static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
+ static const JSJitInfo JitInfo; \
+};
+
+/*
+ * Usage: LoadReference(targetDatum, targetOffset, value)
+ *
+ * Intrinsic function. Stores value (which must be an int32 or uint32)
+ * by `scalarTypeRepr` (which must be a type repr obj) and stores the
+ * value at the memory for `targetDatum` at offset `targetOffset`.
+ * `targetDatum` must be attached.
+ */
+#define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \
+class LoadReference##_name { \
+ private: \
+ static void load(T* heap, MutableHandleValue v); \
+ \
+ public: \
+ static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp); \
+ static const JSJitInfo JitInfo; \
+};
+
+// I was using templates for this stuff instead of macros, but ran
+// into problems with the Unagi compiler.
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN)
+JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN)
+JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN)
+JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN)
+
+inline bool
+IsTypedObjectClass(const Class* class_)
+{
+ return class_ == &OutlineTransparentTypedObject::class_ ||
+ class_ == &InlineTransparentTypedObject::class_ ||
+ class_ == &OutlineOpaqueTypedObject::class_ ||
+ class_ == &InlineOpaqueTypedObject::class_;
+}
+
+inline bool
+IsOpaqueTypedObjectClass(const Class* class_)
+{
+ return class_ == &OutlineOpaqueTypedObject::class_ ||
+ class_ == &InlineOpaqueTypedObject::class_;
+}
+
+inline bool
+IsOutlineTypedObjectClass(const Class* class_)
+{
+ return class_ == &OutlineOpaqueTypedObject::class_ ||
+ class_ == &OutlineTransparentTypedObject::class_;
+}
+
+inline bool
+IsInlineTypedObjectClass(const Class* class_)
+{
+ return class_ == &InlineOpaqueTypedObject::class_ ||
+ class_ == &InlineTransparentTypedObject::class_;
+}
+
+inline const Class*
+GetOutlineTypedObjectClass(bool opaque)
+{
+ return opaque ? &OutlineOpaqueTypedObject::class_ : &OutlineTransparentTypedObject::class_;
+}
+
+inline bool
+IsSimpleTypeDescrClass(const Class* clasp)
+{
+ return clasp == &ScalarTypeDescr::class_ ||
+ clasp == &ReferenceTypeDescr::class_;
+}
+
+inline bool
+IsComplexTypeDescrClass(const Class* clasp)
+{
+ return clasp == &StructTypeDescr::class_ ||
+ clasp == &ArrayTypeDescr::class_ ||
+ clasp == &SimdTypeDescr::class_;
+}
+
+inline bool
+IsTypeDescrClass(const Class* clasp)
+{
+ return IsSimpleTypeDescrClass(clasp) ||
+ IsComplexTypeDescrClass(clasp);
+}
+
+inline bool
+TypedObject::opaque() const
+{
+ return IsOpaqueTypedObjectClass(getClass());
+}
+
+JSObject*
+InitTypedObjectModuleObject(JSContext* cx, JS::HandleObject obj);
+
+} // namespace js
+
+template <>
+inline bool
+JSObject::is<js::SimpleTypeDescr>() const
+{
+ return IsSimpleTypeDescrClass(getClass());
+}
+
+template <>
+inline bool
+JSObject::is<js::ComplexTypeDescr>() const
+{
+ return IsComplexTypeDescrClass(getClass());
+}
+
+template <>
+inline bool
+JSObject::is<js::TypeDescr>() const
+{
+ return IsTypeDescrClass(getClass());
+}
+
+template <>
+inline bool
+JSObject::is<js::TypedObject>() const
+{
+ return IsTypedObjectClass(getClass());
+}
+
+template <>
+inline bool
+JSObject::is<js::OutlineTypedObject>() const
+{
+ return getClass() == &js::OutlineTransparentTypedObject::class_ ||
+ getClass() == &js::OutlineOpaqueTypedObject::class_;
+}
+
+template <>
+inline bool
+JSObject::is<js::InlineTypedObject>() const
+{
+ return getClass() == &js::InlineTransparentTypedObject::class_ ||
+ getClass() == &js::InlineOpaqueTypedObject::class_;
+}
+
+#endif /* builtin_TypedObject_h */