michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef builtin_TypedObject_h michael@0: #define builtin_TypedObject_h michael@0: michael@0: #include "jsobj.h" michael@0: michael@0: #include "builtin/TypedObjectConstants.h" michael@0: #include "vm/ArrayBufferObject.h" michael@0: michael@0: /* michael@0: * ------------- michael@0: * Typed Objects michael@0: * ------------- michael@0: * michael@0: * Typed objects are a special kind of JS object where the data is michael@0: * given well-structured form. To use a typed object, users first michael@0: * create *type objects* (no relation to the type objects used in TI) michael@0: * that define the type layout. For example, a statement like: michael@0: * michael@0: * var PointType = new StructType({x: uint8, y: uint8}); michael@0: * michael@0: * would create a type object PointType that is a struct with michael@0: * two fields, each of uint8 type. michael@0: * michael@0: * This comment typically assumes familiary with the API. For more michael@0: * info on the API itself, see the Harmony wiki page at michael@0: * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the michael@0: * ES6 spec (not finalized at the time of this writing). michael@0: * michael@0: * - Initialization: michael@0: * michael@0: * Currently, all "globals" related to typed objects are packaged michael@0: * within a single "module" object `TypedObject`. This module has its michael@0: * own js::Class and when that class is initialized, we also create michael@0: * and define all other values (in `js_InitTypedObjectModuleClass()`). michael@0: * michael@0: * - Type objects, meta type objects, and type representations: michael@0: * michael@0: * There are a number of pre-defined type objects, one for each michael@0: * scalar type (`uint8` etc). Each of these has its own class_, michael@0: * defined in `DefineNumericClass()`. michael@0: * michael@0: * There are also meta type objects (`ArrayType`, `StructType`). michael@0: * These constructors are not themselves type objects but rather the michael@0: * means for the *user* to construct new typed objects. michael@0: * michael@0: * Each type object is associated with a *type representation* (see michael@0: * TypeRepresentation.h). Type representations are canonical versions michael@0: * of type objects. We attach them to TI type objects and (eventually) michael@0: * use them for shape guards etc. They are purely internal to the michael@0: * engine and are not exposed to end users (though self-hosted code michael@0: * sometimes accesses them). michael@0: * michael@0: * - Typed objects: michael@0: * michael@0: * A typed object is an instance of a *type object* (note the past michael@0: * participle). There is one class for *transparent* typed objects and michael@0: * one for *opaque* typed objects. These classes are equivalent in michael@0: * basically every way, except that requesting the backing buffer of michael@0: * an opaque typed object yields null. We use distinct js::Classes to michael@0: * avoid the need for an extra slot in every typed object. michael@0: * michael@0: * Note that whether a typed object is opaque is not directly michael@0: * connected to its type. That is, opaque types are *always* michael@0: * represented by opaque typed objects, but you may have opaque typed michael@0: * objects for transparent types too. This can occur for two reasons: michael@0: * (1) a transparent type may be embedded within an opaque type or (2) michael@0: * users can choose to convert transparent typed objects into opaque michael@0: * ones to avoid giving access to the buffer itself. michael@0: * michael@0: * Typed objects (no matter their class) are non-native objects that michael@0: * fully override the property accessors etc. The overridden accessor michael@0: * methods are the same in each and are defined in methods of michael@0: * TypedObject. michael@0: * michael@0: * Typed objects may be attached or unattached. An unattached typed michael@0: * object has no memory associated with it; it is basically a null michael@0: * pointer. When first created, objects are always attached, but they michael@0: * can become unattached if their buffer is neutered (note that this michael@0: * implies that typed objects of opaque types can never be unattached). michael@0: * michael@0: * When a new typed object instance is created, fresh memory is michael@0: * allocated and set as that typed object's private field. The object michael@0: * is then considered the *owner* of that memory: when the object is michael@0: * collected, its finalizer will free the memory. The fact that an michael@0: * object `o` owns its memory is indicated by setting its reserved michael@0: * slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other michael@0: * words). michael@0: * michael@0: * Later, *derived* typed objects can be created, typically via an michael@0: * access like `o.f` where `f` is some complex (non-scalar) type, but michael@0: * also explicitly via Handle objects. In those cases, the memory michael@0: * pointer of the derived object is set to alias the owner's memory michael@0: * pointer, and the owner slot for the derived object is set to the michael@0: * owner object, thus ensuring that the owner is not collected while michael@0: * the derived object is alive. We always maintain the invariant that michael@0: * JS_TYPEDOBJ_SLOT_OWNER is the true owner of the memory, meaning michael@0: * that there is a shallow tree. This prevents an access pattern like michael@0: * `a.b.c.d` from keeping all the intermediate objects alive. michael@0: */ michael@0: michael@0: namespace js { michael@0: michael@0: /* michael@0: * Helper method for converting a double into other scalar michael@0: * types in the same way that JavaScript would. In particular, michael@0: * simple C casting from double to int32_t gets things wrong michael@0: * for values like 0xF0000000. michael@0: */ michael@0: template michael@0: static T ConvertScalar(double d) michael@0: { michael@0: if (TypeIsFloatingPoint()) { michael@0: return T(d); michael@0: } else if (TypeIsUnsigned()) { michael@0: uint32_t n = ToUint32(d); michael@0: return T(n); michael@0: } else { michael@0: int32_t n = ToInt32(d); michael@0: return T(n); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * The prototype for a typed object. Currently, carries a link to the michael@0: * type descriptor. Eventually will carry most of the type information michael@0: * we want. michael@0: */ michael@0: class TypedProto : public JSObject michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: michael@0: inline void initTypeDescrSlot(TypeDescr &descr); michael@0: michael@0: TypeDescr &typeDescr() const { michael@0: return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as(); michael@0: } michael@0: }; michael@0: michael@0: class TypeDescr : public JSObject michael@0: { michael@0: public: michael@0: // This is *intentionally* not defined so as to produce link michael@0: // errors if a is() etc goes wrong. Otherwise, the michael@0: // default implementation resolves this to a reference to michael@0: // FooTypeDescr::class_ which resolves to michael@0: // JSObject::class_. Debugging the resulting errors leads to much michael@0: // fun and rejoicing. michael@0: static const Class class_; michael@0: michael@0: enum Kind { michael@0: Scalar = JS_TYPEREPR_SCALAR_KIND, michael@0: Reference = JS_TYPEREPR_REFERENCE_KIND, michael@0: X4 = JS_TYPEREPR_X4_KIND, michael@0: Struct = JS_TYPEREPR_STRUCT_KIND, michael@0: SizedArray = JS_TYPEREPR_SIZED_ARRAY_KIND, michael@0: UnsizedArray = JS_TYPEREPR_UNSIZED_ARRAY_KIND, michael@0: }; michael@0: michael@0: static bool isSized(Kind kind) { michael@0: return kind > JS_TYPEREPR_MAX_UNSIZED_KIND; michael@0: } michael@0: michael@0: JSAtom &stringRepr() const { michael@0: return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom(); michael@0: } michael@0: michael@0: TypeDescr::Kind kind() const { michael@0: return (TypeDescr::Kind) getReservedSlot(JS_DESCR_SLOT_KIND).toInt32(); michael@0: } michael@0: michael@0: bool opaque() const { michael@0: return getReservedSlot(JS_DESCR_SLOT_OPAQUE).toBoolean(); michael@0: } michael@0: michael@0: bool transparent() const { michael@0: return !opaque(); michael@0: } michael@0: michael@0: int32_t alignment() { michael@0: return getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32(); michael@0: } michael@0: }; michael@0: michael@0: typedef Handle HandleTypeDescr; michael@0: michael@0: class SizedTypeDescr : public TypeDescr michael@0: { michael@0: public: michael@0: int32_t size() { michael@0: return getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32(); michael@0: } michael@0: michael@0: void initInstances(const JSRuntime *rt, uint8_t *mem, size_t length); michael@0: void traceInstances(JSTracer *trace, uint8_t *mem, size_t length); michael@0: }; michael@0: michael@0: typedef Handle HandleSizedTypeDescr; michael@0: michael@0: class SimpleTypeDescr : public SizedTypeDescr michael@0: { michael@0: }; michael@0: michael@0: // Type for scalar type constructors like `uint8`. All such type michael@0: // constructors share a common js::Class and JSFunctionSpec. Scalar michael@0: // types are non-opaque (their storage is visible unless combined with michael@0: // an opaque reference type.) michael@0: class ScalarTypeDescr : public SimpleTypeDescr michael@0: { michael@0: public: michael@0: // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below michael@0: enum Type { michael@0: TYPE_INT8 = JS_SCALARTYPEREPR_INT8, michael@0: TYPE_UINT8 = JS_SCALARTYPEREPR_UINT8, michael@0: TYPE_INT16 = JS_SCALARTYPEREPR_INT16, michael@0: TYPE_UINT16 = JS_SCALARTYPEREPR_UINT16, michael@0: TYPE_INT32 = JS_SCALARTYPEREPR_INT32, michael@0: TYPE_UINT32 = JS_SCALARTYPEREPR_UINT32, michael@0: TYPE_FLOAT32 = JS_SCALARTYPEREPR_FLOAT32, michael@0: TYPE_FLOAT64 = JS_SCALARTYPEREPR_FLOAT64, michael@0: michael@0: /* michael@0: * Special type that's a uint8_t, but assignments are clamped to 0 .. 255. michael@0: * Treat the raw data type as a uint8_t. michael@0: */ michael@0: TYPE_UINT8_CLAMPED = JS_SCALARTYPEREPR_UINT8_CLAMPED, michael@0: }; michael@0: static const int32_t TYPE_MAX = TYPE_UINT8_CLAMPED + 1; michael@0: michael@0: static const TypeDescr::Kind Kind = TypeDescr::Scalar; michael@0: static const bool Opaque = false; michael@0: static int32_t size(Type t); michael@0: static int32_t alignment(Type t); michael@0: static const char *typeName(Type type); michael@0: michael@0: static const Class class_; michael@0: static const JSFunctionSpec typeObjectMethods[]; michael@0: michael@0: ScalarTypeDescr::Type type() const { michael@0: return (ScalarTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); michael@0: } michael@0: michael@0: static bool call(JSContext *cx, unsigned argc, Value *vp); michael@0: }; michael@0: michael@0: // Enumerates the cases of ScalarTypeDescr::Type which have michael@0: // unique C representation. In particular, omits Uint8Clamped since it michael@0: // is just a Uint8. michael@0: #define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ michael@0: macro_(ScalarTypeDescr::TYPE_INT8, int8_t, int8) \ michael@0: macro_(ScalarTypeDescr::TYPE_UINT8, uint8_t, uint8) \ michael@0: macro_(ScalarTypeDescr::TYPE_INT16, int16_t, int16) \ michael@0: macro_(ScalarTypeDescr::TYPE_UINT16, uint16_t, uint16) \ michael@0: macro_(ScalarTypeDescr::TYPE_INT32, int32_t, int32) \ michael@0: macro_(ScalarTypeDescr::TYPE_UINT32, uint32_t, uint32) \ michael@0: macro_(ScalarTypeDescr::TYPE_FLOAT32, float, float32) \ michael@0: macro_(ScalarTypeDescr::TYPE_FLOAT64, double, float64) michael@0: michael@0: // Must be in same order as the enum ScalarTypeDescr::Type: michael@0: #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \ michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ michael@0: macro_(ScalarTypeDescr::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped) michael@0: michael@0: // Type for reference type constructors like `Any`, `String`, and michael@0: // `Object`. All such type constructors share a common js::Class and michael@0: // JSFunctionSpec. All these types are opaque. michael@0: class ReferenceTypeDescr : public SimpleTypeDescr michael@0: { michael@0: public: michael@0: // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below michael@0: enum Type { michael@0: TYPE_ANY = JS_REFERENCETYPEREPR_ANY, michael@0: TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT, michael@0: TYPE_STRING = JS_REFERENCETYPEREPR_STRING, michael@0: }; michael@0: static const int32_t TYPE_MAX = TYPE_STRING + 1; michael@0: static const char *typeName(Type type); michael@0: michael@0: static const TypeDescr::Kind Kind = TypeDescr::Reference; michael@0: static const bool Opaque = true; michael@0: static const Class class_; michael@0: static int32_t size(Type t); michael@0: static int32_t alignment(Type t); michael@0: static const JSFunctionSpec typeObjectMethods[]; michael@0: michael@0: ReferenceTypeDescr::Type type() const { michael@0: return (ReferenceTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); michael@0: } michael@0: michael@0: const char *typeName() const { michael@0: return typeName(type()); michael@0: } michael@0: michael@0: static bool call(JSContext *cx, unsigned argc, Value *vp); michael@0: }; michael@0: michael@0: #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \ michael@0: macro_(ReferenceTypeDescr::TYPE_ANY, HeapValue, Any) \ michael@0: macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \ michael@0: macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string) michael@0: michael@0: // Type descriptors whose instances are objects and hence which have michael@0: // an associated `prototype` property. michael@0: class ComplexTypeDescr : public SizedTypeDescr michael@0: { michael@0: public: michael@0: // Returns the prototype that instances of this type descriptor michael@0: // will have. michael@0: TypedProto &instancePrototype() const { michael@0: return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as(); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Type descriptors `float32x4` and `int32x4` michael@0: */ michael@0: class X4TypeDescr : public ComplexTypeDescr michael@0: { michael@0: public: michael@0: enum Type { michael@0: TYPE_INT32 = JS_X4TYPEREPR_INT32, michael@0: TYPE_FLOAT32 = JS_X4TYPEREPR_FLOAT32, michael@0: }; michael@0: michael@0: static const TypeDescr::Kind Kind = TypeDescr::X4; michael@0: static const bool Opaque = false; michael@0: static const Class class_; michael@0: static int32_t size(Type t); michael@0: static int32_t alignment(Type t); michael@0: michael@0: X4TypeDescr::Type type() const { michael@0: return (X4TypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); michael@0: } michael@0: michael@0: static bool call(JSContext *cx, unsigned argc, Value *vp); michael@0: static bool is(const Value &v); michael@0: }; michael@0: michael@0: #define JS_FOR_EACH_X4_TYPE_REPR(macro_) \ michael@0: macro_(X4TypeDescr::TYPE_INT32, int32_t, int32) \ michael@0: macro_(X4TypeDescr::TYPE_FLOAT32, float, float32) michael@0: michael@0: bool IsTypedObjectClass(const Class *clasp); // Defined below michael@0: bool IsTypedObjectArray(JSObject& obj); michael@0: michael@0: bool CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr obj); michael@0: michael@0: /* michael@0: * Properties and methods of the `ArrayType` meta type object. There michael@0: * is no `class_` field because `ArrayType` is just a native michael@0: * constructor function. michael@0: */ michael@0: class ArrayMetaTypeDescr : public JSObject michael@0: { michael@0: private: michael@0: friend class UnsizedArrayTypeDescr; michael@0: michael@0: // Helper for creating a new ArrayType object, either sized or unsized. michael@0: // The template parameter `T` should be either `UnsizedArrayTypeDescr` michael@0: // or `SizedArrayTypeDescr`. michael@0: // michael@0: // - `arrayTypePrototype` - prototype for the new object to be created, michael@0: // either ArrayType.prototype or michael@0: // unsizedArrayType.__proto__ depending on michael@0: // whether this is a sized or unsized array michael@0: // - `elementType` - type object for the elements in the array michael@0: // - `stringRepr` - canonical string representation for the array michael@0: // - `size` - length of the array (0 if unsized) michael@0: template michael@0: static T *create(JSContext *cx, michael@0: HandleObject arrayTypePrototype, michael@0: HandleSizedTypeDescr elementType, michael@0: HandleAtom stringRepr, michael@0: int32_t size); michael@0: michael@0: public: michael@0: // Properties and methods to be installed on ArrayType.prototype, michael@0: // and hence inherited by all array type objects: michael@0: static const JSPropertySpec typeObjectProperties[]; michael@0: static const JSFunctionSpec typeObjectMethods[]; michael@0: michael@0: // Properties and methods to be installed on ArrayType.prototype.prototype, michael@0: // and hence inherited by all array *typed* objects: michael@0: static const JSPropertySpec typedObjectProperties[]; michael@0: static const JSFunctionSpec typedObjectMethods[]; michael@0: michael@0: // This is the function that gets called when the user michael@0: // does `new ArrayType(elem)`. It produces an array type object. michael@0: static bool construct(JSContext *cx, unsigned argc, Value *vp); michael@0: }; michael@0: michael@0: /* michael@0: * Type descriptor created by `new ArrayType(typeObj)` michael@0: * michael@0: * These have a prototype, and hence *could* be a subclass of michael@0: * `ComplexTypeDescr`, but it would require some reshuffling of the michael@0: * hierarchy, and it's not worth the trouble since they will be going michael@0: * away as part of bug 973238. michael@0: */ michael@0: class UnsizedArrayTypeDescr : public TypeDescr michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: static const TypeDescr::Kind Kind = TypeDescr::UnsizedArray; michael@0: michael@0: // This is the sized method on unsized array type objects. It michael@0: // produces a sized variant. michael@0: static bool dimension(JSContext *cx, unsigned int argc, jsval *vp); michael@0: michael@0: SizedTypeDescr &elementType() { michael@0: return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as(); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Type descriptor created by `unsizedArrayTypeObj.dimension()` michael@0: */ michael@0: class SizedArrayTypeDescr : public ComplexTypeDescr michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: static const TypeDescr::Kind Kind = TypeDescr::SizedArray; michael@0: michael@0: SizedTypeDescr &elementType() { michael@0: return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as(); michael@0: } michael@0: michael@0: int32_t length() { michael@0: return getReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH).toInt32(); michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * Properties and methods of the `StructType` meta type object. There michael@0: * is no `class_` field because `StructType` is just a native michael@0: * constructor function. michael@0: */ michael@0: class StructMetaTypeDescr : public JSObject michael@0: { michael@0: private: michael@0: static JSObject *create(JSContext *cx, HandleObject structTypeGlobal, michael@0: HandleObject fields); michael@0: michael@0: public: michael@0: // Properties and methods to be installed on StructType.prototype, michael@0: // and hence inherited by all struct type objects: michael@0: static const JSPropertySpec typeObjectProperties[]; michael@0: static const JSFunctionSpec typeObjectMethods[]; michael@0: michael@0: // Properties and methods to be installed on StructType.prototype.prototype, michael@0: // and hence inherited by all struct *typed* objects: michael@0: static const JSPropertySpec typedObjectProperties[]; michael@0: static const JSFunctionSpec typedObjectMethods[]; michael@0: michael@0: // This is the function that gets called when the user michael@0: // does `new StructType(...)`. It produces a struct type object. michael@0: static bool construct(JSContext *cx, unsigned argc, Value *vp); michael@0: }; michael@0: michael@0: class StructTypeDescr : public ComplexTypeDescr michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: michael@0: // Returns the number of fields defined in this struct. michael@0: size_t fieldCount(); michael@0: michael@0: // Set `*out` to the index of the field named `id` and returns true, michael@0: // or return false if no such field exists. michael@0: bool fieldIndex(jsid id, size_t *out); michael@0: michael@0: // Return the name of the field at index `index`. michael@0: JSAtom &fieldName(size_t index); michael@0: michael@0: // Return the type descr of the field at index `index`. michael@0: SizedTypeDescr &fieldDescr(size_t index); michael@0: michael@0: // Return the offset of the field at index `index`. michael@0: int32_t fieldOffset(size_t index); michael@0: }; michael@0: michael@0: typedef Handle HandleStructTypeDescr; michael@0: michael@0: /* michael@0: * This object exists in order to encapsulate the typed object types michael@0: * somewhat, rather than sticking them all into the global object. michael@0: * Eventually it will go away and become a module. michael@0: */ michael@0: class TypedObjectModuleObject : public JSObject { michael@0: public: michael@0: enum Slot { michael@0: ArrayTypePrototype, michael@0: StructTypePrototype, michael@0: SlotCount michael@0: }; michael@0: michael@0: static const Class class_; michael@0: }; michael@0: michael@0: /* michael@0: * Base type for typed objects and handles. Basically any type whose michael@0: * contents consist of typed memory. michael@0: */ michael@0: class TypedObject : public ArrayBufferViewObject michael@0: { michael@0: private: michael@0: static const bool IsTypedObjectClass = true; michael@0: michael@0: template michael@0: static bool obj_getArrayElement(JSContext *cx, michael@0: Handle typedObj, michael@0: Handle typeDescr, michael@0: uint32_t index, michael@0: MutableHandleValue vp); michael@0: michael@0: template michael@0: static bool obj_setArrayElement(JSContext *cx, michael@0: Handle typedObj, michael@0: Handle typeDescr, michael@0: uint32_t index, michael@0: MutableHandleValue vp); michael@0: michael@0: protected: michael@0: static void obj_trace(JSTracer *trace, JSObject *object); michael@0: michael@0: static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, michael@0: HandleId id, MutableHandleObject objp, michael@0: MutableHandleShape propp); michael@0: michael@0: static bool obj_lookupProperty(JSContext *cx, HandleObject obj, michael@0: HandlePropertyName name, michael@0: MutableHandleObject objp, michael@0: MutableHandleShape propp); michael@0: michael@0: static bool obj_lookupElement(JSContext *cx, HandleObject obj, michael@0: uint32_t index, MutableHandleObject objp, michael@0: MutableHandleShape propp); michael@0: michael@0: static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs); michael@0: michael@0: static bool obj_defineProperty(JSContext *cx, HandleObject obj, michael@0: HandlePropertyName name, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs); michael@0: michael@0: static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs); michael@0: michael@0: static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: HandleId id, MutableHandleValue vp); michael@0: michael@0: static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: HandlePropertyName name, MutableHandleValue vp); michael@0: michael@0: static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: uint32_t index, MutableHandleValue vp); michael@0: michael@0: static bool obj_getUnsizedArrayElement(JSContext *cx, HandleObject obj, HandleObject receiver, michael@0: uint32_t index, MutableHandleValue vp); michael@0: michael@0: static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp, bool strict); michael@0: static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, michael@0: MutableHandleValue vp, bool strict); michael@0: static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: MutableHandleValue vp, bool strict); michael@0: michael@0: static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj, michael@0: HandleId id, unsigned *attrsp); michael@0: static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj, michael@0: HandleId id, unsigned *attrsp); michael@0: michael@0: static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, michael@0: bool *succeeded); michael@0: static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: bool *succeeded); michael@0: michael@0: static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, michael@0: MutableHandleValue statep, MutableHandleId idp); michael@0: michael@0: public: michael@0: static size_t offsetOfOwnerSlot(); michael@0: michael@0: // Each typed object contains a void* pointer pointing at the michael@0: // binary data that it represents. (That data may be owned by this michael@0: // object or this object may alias data owned by someone else.) michael@0: // This function returns the offset in bytes within the object michael@0: // where the `void*` pointer can be found. It is intended for use michael@0: // by the JIT. michael@0: static size_t offsetOfDataSlot(); michael@0: michael@0: // Offset of the byte offset slot. michael@0: static size_t offsetOfByteOffsetSlot(); michael@0: michael@0: // Helper for createUnattached() michael@0: static TypedObject *createUnattachedWithClass(JSContext *cx, michael@0: const Class *clasp, michael@0: HandleTypeDescr type, michael@0: int32_t length); michael@0: michael@0: // Creates an unattached typed object or handle (depending on the michael@0: // type parameter T). Note that it is only legal for unattached michael@0: // handles to escape to the end user; for non-handles, the caller michael@0: // should always invoke one of the `attach()` methods below. michael@0: // michael@0: // Arguments: michael@0: // - type: type object for resulting object michael@0: // - length: 0 unless this is an array, otherwise the length michael@0: static TypedObject *createUnattached(JSContext *cx, HandleTypeDescr type, michael@0: int32_t length); michael@0: michael@0: // Creates a typedObj that aliases the memory pointed at by `owner` michael@0: // at the given offset. The typedObj will be a handle iff type is a michael@0: // handle and a typed object otherwise. michael@0: static TypedObject *createDerived(JSContext *cx, michael@0: HandleSizedTypeDescr type, michael@0: Handle typedContents, michael@0: int32_t offset); michael@0: michael@0: // Creates a new typed object whose memory is freshly allocated michael@0: // and initialized with zeroes (or, in the case of references, an michael@0: // appropriate default value). michael@0: static TypedObject *createZeroed(JSContext *cx, michael@0: HandleTypeDescr typeObj, michael@0: int32_t length); michael@0: michael@0: // User-accessible constructor (`new TypeDescriptor(...)`) michael@0: // used for sized types. Note that the callee here is the *type descriptor*, michael@0: // not the typedObj. michael@0: static bool constructSized(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: // As `constructSized`, but for unsized array types. michael@0: static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: // Use this method when `buffer` is the owner of the memory. michael@0: void attach(ArrayBufferObject &buffer, int32_t offset); michael@0: michael@0: // Otherwise, use this to attach to memory referenced by another typedObj. michael@0: void attach(TypedObject &typedObj, int32_t offset); michael@0: michael@0: // Invoked when array buffer is transferred elsewhere michael@0: void neuter(void *newData); michael@0: michael@0: int32_t offset() const { michael@0: return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET).toInt32(); michael@0: } michael@0: michael@0: ArrayBufferObject &owner() const { michael@0: return getReservedSlot(JS_TYPEDOBJ_SLOT_OWNER).toObject().as(); michael@0: } michael@0: michael@0: TypeDescr &typeDescr() const { michael@0: return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as(); michael@0: } michael@0: michael@0: uint8_t *typedMem() const { michael@0: return (uint8_t*) getPrivate(); michael@0: } michael@0: michael@0: int32_t byteLength() const { michael@0: return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH).toInt32(); michael@0: } michael@0: michael@0: int32_t length() const { michael@0: return getReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH).toInt32(); michael@0: } michael@0: michael@0: int32_t size() const { michael@0: switch (typeDescr().kind()) { michael@0: case TypeDescr::Scalar: michael@0: case TypeDescr::X4: michael@0: case TypeDescr::Reference: michael@0: case TypeDescr::Struct: michael@0: case TypeDescr::SizedArray: michael@0: return typeDescr().as().size(); michael@0: michael@0: case TypeDescr::UnsizedArray: { michael@0: SizedTypeDescr &elementType = typeDescr().as().elementType(); michael@0: return elementType.size() * length(); michael@0: } michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("unhandled typerepresentation kind"); michael@0: } michael@0: michael@0: uint8_t *typedMem(size_t offset) const { michael@0: // It seems a bit surprising that one might request an offset michael@0: // == size(), but it can happen when taking the "address of" a michael@0: // 0-sized value. (In other words, we maintain the invariant michael@0: // that `offset + size <= size()` -- this is always checked in michael@0: // the caller's side.) michael@0: JS_ASSERT(offset <= (size_t) size()); michael@0: return typedMem() + offset; michael@0: } michael@0: }; michael@0: michael@0: typedef Handle HandleTypedObject; michael@0: michael@0: class TransparentTypedObject : public TypedObject michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: }; michael@0: michael@0: typedef Handle HandleTransparentTypedObject; michael@0: michael@0: class OpaqueTypedObject : public TypedObject michael@0: { michael@0: public: michael@0: static const Class class_; michael@0: static const JSFunctionSpec handleStaticMethods[]; michael@0: }; michael@0: michael@0: /* michael@0: * Usage: NewOpaqueTypedObject(typeObj) michael@0: * michael@0: * Constructs a new, unattached instance of `Handle`. michael@0: */ michael@0: bool NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: /* michael@0: * Usage: NewDerivedTypedObject(typeObj, owner, offset) michael@0: * michael@0: * Constructs a new, unattached instance of `Handle`. michael@0: */ michael@0: bool NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: /* michael@0: * Usage: AttachTypedObject(typedObj, newDatum, newOffset) michael@0: * michael@0: * Moves `typedObj` to point at the memory referenced by `newDatum` with michael@0: * the offset `newOffset`. michael@0: */ michael@0: bool AttachTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo AttachTypedObjectJitInfo; michael@0: michael@0: /* michael@0: * Usage: SetTypedObjectOffset(typedObj, offset) michael@0: * michael@0: * Changes the offset for `typedObj` within its buffer to `offset`. michael@0: * `typedObj` must already be attached. michael@0: */ michael@0: bool intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp); michael@0: bool SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp); michael@0: extern const JSJitInfo intrinsic_SetTypedObjectOffsetJitInfo; michael@0: michael@0: /* michael@0: * Usage: ObjectIsTypeDescr(obj) michael@0: * michael@0: * True if `obj` is a type object. michael@0: */ michael@0: bool ObjectIsTypeDescr(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo ObjectIsTypeDescrJitInfo; michael@0: michael@0: /* michael@0: * Usage: ObjectIsTypedObject(obj) michael@0: * michael@0: * True if `obj` is a transparent or opaque typed object. michael@0: */ michael@0: bool ObjectIsTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo ObjectIsTypedObjectJitInfo; michael@0: michael@0: /* michael@0: * Usage: ObjectIsOpaqueTypedObject(obj) michael@0: * michael@0: * True if `obj` is an opaque typed object. michael@0: */ michael@0: bool ObjectIsOpaqueTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo ObjectIsOpaqueTypedObjectJitInfo; michael@0: michael@0: /* michael@0: * Usage: ObjectIsTransparentTypedObject(obj) michael@0: * michael@0: * True if `obj` is a transparent typed object. michael@0: */ michael@0: bool ObjectIsTransparentTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo ObjectIsTransparentTypedObjectJitInfo; michael@0: michael@0: /* Predicates on type descriptor objects. In all cases, 'obj' must be a type descriptor. */ michael@0: michael@0: bool TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp); michael@0: extern const JSJitInfo TypeDescrIsSimpleTypeJitInfo; michael@0: michael@0: bool TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp); michael@0: extern const JSJitInfo TypeDescrIsArrayTypeJitInfo; michael@0: michael@0: bool TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp); michael@0: extern const JSJitInfo TypeDescrIsSizedArrayTypeJitInfo; michael@0: michael@0: bool TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp); michael@0: extern const JSJitInfo TypeDescrIsUnsizedArrayTypeJitInfo; michael@0: michael@0: /* michael@0: * Usage: TypedObjectIsAttached(obj) michael@0: * michael@0: * Given a TypedObject `obj`, returns true if `obj` is michael@0: * "attached" (i.e., its data pointer is nullptr). michael@0: */ michael@0: bool TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo TypedObjectIsAttachedJitInfo; michael@0: michael@0: /* michael@0: * Usage: ClampToUint8(v) michael@0: * michael@0: * Same as the C function ClampDoubleToUint8. `v` must be a number. michael@0: */ michael@0: bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo ClampToUint8JitInfo; michael@0: michael@0: /* michael@0: * Usage: Memcpy(targetDatum, targetOffset, michael@0: * sourceDatum, sourceOffset, michael@0: * size) michael@0: * michael@0: * Intrinsic function. Copies size bytes from the data for michael@0: * `sourceDatum` at `sourceOffset` into the data for michael@0: * `targetDatum` at `targetOffset`. michael@0: * michael@0: * Both `sourceDatum` and `targetDatum` must be attached. michael@0: */ michael@0: bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp); michael@0: extern const JSJitInfo MemcpyJitInfo; michael@0: michael@0: /* michael@0: * Usage: GetTypedObjectModule() michael@0: * michael@0: * Returns the global "typed object" module, which provides access michael@0: * to the various builtin type descriptors. These are currently michael@0: * exported as immutable properties so it is safe for self-hosted code michael@0: * to access them; eventually this should be linked into the module michael@0: * system. michael@0: */ michael@0: bool GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: /* michael@0: * Usage: GetFloat32x4TypeDescr() michael@0: * michael@0: * Returns the float32x4 type object. SIMD pseudo-module must have michael@0: * been initialized for this to be safe. michael@0: */ michael@0: bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: /* michael@0: * Usage: GetInt32x4TypeDescr() michael@0: * michael@0: * Returns the int32x4 type object. SIMD pseudo-module must have michael@0: * been initialized for this to be safe. michael@0: */ michael@0: bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp); michael@0: michael@0: /* michael@0: * Usage: Store_int8(targetDatum, targetOffset, value) michael@0: * ... michael@0: * Store_uint8(targetDatum, targetOffset, value) michael@0: * ... michael@0: * Store_float32(targetDatum, targetOffset, value) michael@0: * Store_float64(targetDatum, targetOffset, value) michael@0: * michael@0: * Intrinsic function. Stores `value` into the memory referenced by michael@0: * `targetDatum` at the offset `targetOffset`. michael@0: * michael@0: * Assumes (and asserts) that: michael@0: * - `targetDatum` is attached michael@0: * - `targetOffset` is a valid offset within the bounds of `targetDatum` michael@0: * - `value` is a number michael@0: */ michael@0: #define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \ michael@0: class StoreScalar##T { \ michael@0: public: \ michael@0: static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ michael@0: static const JSJitInfo JitInfo; \ michael@0: }; michael@0: michael@0: /* michael@0: * Usage: Store_Any(targetDatum, targetOffset, value) michael@0: * Store_Object(targetDatum, targetOffset, value) michael@0: * Store_string(targetDatum, targetOffset, value) michael@0: * michael@0: * Intrinsic function. Stores `value` into the memory referenced by michael@0: * `targetDatum` at the offset `targetOffset`. michael@0: * michael@0: * Assumes (and asserts) that: michael@0: * - `targetDatum` is attached michael@0: * - `targetOffset` is a valid offset within the bounds of `targetDatum` michael@0: * - `value` is an object (`Store_Object`) or string (`Store_string`). michael@0: */ michael@0: #define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \ michael@0: class StoreReference##T { \ michael@0: private: \ michael@0: static void store(T* heap, const Value &v); \ michael@0: \ michael@0: public: \ michael@0: static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ michael@0: static const JSJitInfo JitInfo; \ michael@0: }; michael@0: michael@0: /* michael@0: * Usage: LoadScalar(targetDatum, targetOffset, value) michael@0: * michael@0: * Intrinsic function. Loads value (which must be an int32 or uint32) michael@0: * by `scalarTypeRepr` (which must be a type repr obj) and loads the michael@0: * value at the memory for `targetDatum` at offset `targetOffset`. michael@0: * `targetDatum` must be attached. michael@0: */ michael@0: #define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name) \ michael@0: class LoadScalar##T { \ michael@0: public: \ michael@0: static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ michael@0: static const JSJitInfo JitInfo; \ michael@0: }; michael@0: michael@0: /* michael@0: * Usage: LoadReference(targetDatum, targetOffset, value) michael@0: * michael@0: * Intrinsic function. Stores value (which must be an int32 or uint32) michael@0: * by `scalarTypeRepr` (which must be a type repr obj) and stores the michael@0: * value at the memory for `targetDatum` at offset `targetOffset`. michael@0: * `targetDatum` must be attached. michael@0: */ michael@0: #define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \ michael@0: class LoadReference##T { \ michael@0: private: \ michael@0: static void load(T* heap, MutableHandleValue v); \ michael@0: \ michael@0: public: \ michael@0: static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ michael@0: static const JSJitInfo JitInfo; \ michael@0: }; michael@0: michael@0: // I was using templates for this stuff instead of macros, but ran michael@0: // into problems with the Unagi compiler. michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN) michael@0: JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN) michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN) michael@0: JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN) michael@0: michael@0: inline bool michael@0: IsTypedObjectClass(const Class *class_) michael@0: { michael@0: return class_ == &TransparentTypedObject::class_ || michael@0: class_ == &OpaqueTypedObject::class_; michael@0: } michael@0: michael@0: inline bool michael@0: IsSimpleTypeDescrClass(const Class* clasp) michael@0: { michael@0: return clasp == &ScalarTypeDescr::class_ || michael@0: clasp == &ReferenceTypeDescr::class_; michael@0: } michael@0: michael@0: inline bool michael@0: IsComplexTypeDescrClass(const Class* clasp) michael@0: { michael@0: return clasp == &StructTypeDescr::class_ || michael@0: clasp == &SizedArrayTypeDescr::class_ || michael@0: clasp == &X4TypeDescr::class_; michael@0: } michael@0: michael@0: inline bool michael@0: IsSizedTypeDescrClass(const Class* clasp) michael@0: { michael@0: return IsSimpleTypeDescrClass(clasp) || michael@0: IsComplexTypeDescrClass(clasp); michael@0: } michael@0: michael@0: inline bool michael@0: IsTypeDescrClass(const Class* clasp) michael@0: { michael@0: return IsSizedTypeDescrClass(clasp) || michael@0: clasp == &UnsizedArrayTypeDescr::class_; michael@0: } michael@0: michael@0: } // namespace js michael@0: michael@0: JSObject * michael@0: js_InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj); michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return IsSimpleTypeDescrClass(getClass()); michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return IsSizedTypeDescrClass(getClass()); michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return IsComplexTypeDescrClass(getClass()); michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return IsTypeDescrClass(getClass()); michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return IsTypedObjectClass(getClass()); michael@0: } michael@0: michael@0: template<> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return getClass() == &js::SizedArrayTypeDescr::class_; michael@0: } michael@0: michael@0: template<> michael@0: inline bool michael@0: JSObject::is() const michael@0: { michael@0: return getClass() == &js::UnsizedArrayTypeDescr::class_; michael@0: } michael@0: michael@0: inline void michael@0: js::TypedProto::initTypeDescrSlot(TypeDescr &descr) michael@0: { michael@0: initReservedSlot(JS_TYPROTO_SLOT_DESCR, ObjectValue(descr)); michael@0: } michael@0: michael@0: #endif /* builtin_TypedObject_h */