Bug 898342 - Implement movable handles for Typed Objects r=till

This commit is contained in:
Nicholas D. Matsakis 2013-09-11 16:24:23 -04:00
parent 4eefe958c7
commit e548c599ce
16 changed files with 1404 additions and 439 deletions

View File

@ -223,7 +223,7 @@ StructTypeRepresentation::init(JSContext *cx,
uint32_t totalSize = 0;
for (size_t i = 0; i < ids.length(); i++) {
TypeRepresentation *fieldTypeRepr = fromOwnerObject(typeReprOwners[i]);
TypeRepresentation *fieldTypeRepr = fromOwnerObject(*typeReprOwners[i]);
uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
if (alignedSize < totalSize) {
@ -412,7 +412,7 @@ TypeRepresentation::mark(JSTracer *trace)
/*static*/ void
TypeRepresentation::obj_trace(JSTracer *trace, JSObject *object)
{
fromOwnerObject(object)->traceFields(trace);
fromOwnerObject(*object)->traceFields(trace);
}
void
@ -457,7 +457,7 @@ ArrayTypeRepresentation::traceArrayFields(JSTracer *trace)
TypeRepresentation::obj_finalize(js::FreeOp *fop, JSObject *object)
{
JSCompartment *comp = object->compartment();
TypeRepresentation *typeRepr = fromOwnerObject(object);
TypeRepresentation *typeRepr = fromOwnerObject(*object);
comp->typeReprs.remove(typeRepr);
js_free(typeRepr);
}
@ -573,16 +573,16 @@ StructTypeRepresentation::fieldNamed(jsid id) const
}
/*static*/ bool
TypeRepresentation::isTypeRepresentationOwnerObject(JSObject *obj)
TypeRepresentation::isOwnerObject(JSObject &obj)
{
return obj->getClass() == &class_;
return obj.getClass() == &class_;
}
/*static*/ TypeRepresentation *
TypeRepresentation::fromOwnerObject(JSObject *obj)
TypeRepresentation::fromOwnerObject(JSObject &obj)
{
JS_ASSERT(obj->getClass() == &class_);
return (TypeRepresentation*) obj->getPrivate();
JS_ASSERT(obj.getClass() == &class_);
return (TypeRepresentation*) obj.getPrivate();
}

View File

@ -123,8 +123,8 @@ class TypeRepresentation {
// buffer, for use in error messages and the like.
bool appendString(JSContext *cx, StringBuffer &buffer);
static bool isTypeRepresentationOwnerObject(JSObject *obj);
static TypeRepresentation *fromOwnerObject(JSObject *obj);
static bool isOwnerObject(JSObject &obj);
static TypeRepresentation *fromOwnerObject(JSObject &obj);
bool isScalar() const {
return kind() == Scalar;

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,88 @@
#include "builtin/TypedObjectConstants.h"
#include "builtin/TypeRepresentation.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_InitTypedObjectClass()`).
*
* - 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 datums, objects, and handles:
*
* A typed object is an instance of a type object. A handle is a
* relocatable pointer that points into other typed objects. Both of them
* are basically represented the same way, though they have distinct
* js::Class entries. They are both subtypes of `TypedDatum`.
*
* Both typed objects and handles 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
* TypedDatum.
*
* Typed datums may be attached or unattached. An unattached typed
* datum has no memory associated with it; it is basically a null
* pointer. This can only happen when a new handle is created, since
* typed object instances are always associated with memory at the
* point of creation.
*
* When a new typed object instance is created, fresh memory is
* allocated and set as that typed object's private field. The object
* is then considered the *owner* of that memory: when the object is
* collected, its finalizer will free the memory. The fact that an
* object `o` owns its memory is indicated by setting its reserved
* slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other
* words).
*
* Later, *derived* typed objects can be created, typically via an
* access like `o.f` where `f` is some complex (non-scalar) type, but
* also explicitly via Handle objects. In those cases, the memory
* pointer of the derived object is set to alias the owner's memory
* pointer, and the owner slot for the derived object is set to the
* owner object, thus ensuring that the owner is not collected while
* the derived object is alive. We always maintain the invariant that
* JS_TYPEDOBJ_SLOT_OWNER is the true owner of the memory, meaning
* that there is a shallow tree. This prevents an access pattern like
* `a.b.c.d` from keeping all the intermediate objects alive.
*/
namespace js {
/*
@ -79,7 +161,8 @@ class StructType : public JSObject
private:
static JSObject *create(JSContext *cx, HandleObject structTypeGlobal,
HandleObject fields);
/**
/*
* Sets up structType slots based on calculated memory size
* and alignment and stores fieldmap as well.
*/
@ -110,16 +193,16 @@ class StructType : public JSObject
HandleValue from, uint8_t *mem);
};
/* Binary data objects and handles */
class BinaryBlock
/*
* Base type for typed objects and handles. Basically any type whose
* contents consist of typed memory.
*/
class TypedDatum : public JSObject
{
private:
// Creates a binary data object of the given type and class, but with
// a nullptr memory pointer. Caller must use setPrivate() to set the
// memory pointer properly.
static JSObject *createNull(JSContext *cx, HandleObject type,
HandleValue owner);
static const bool IsTypedDatumClass = true;
protected:
static void obj_finalize(js::FreeOp *op, JSObject *obj);
static void obj_trace(JSTracer *trace, JSObject *object);
@ -195,50 +278,152 @@ class BinaryBlock
MutableHandleValue statep, MutableHandleId idp);
public:
static const Class class_;
// Returns the offset in bytes within the object where the `void*`
// pointer can be found.
static size_t dataOffset();
static bool isBlock(HandleObject val);
static uint8_t *mem(HandleObject val);
static TypedDatum *createUnattachedWithClass(JSContext *cx,
const Class *clasp,
HandleObject type);
// 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
template<class T>
static T *createUnattached(JSContext *cx, HandleObject type);
// Creates a datum that aliases the memory pointed at by `owner`
// at the given offset. The datum will be a handle iff type is a
// handle and a typed object otherwise.
static TypedDatum *createDerived(JSContext *cx,
HandleObject type,
HandleObject typedContents,
size_t offset);
// If `this` is the owner of the memory, use this.
void attach(void *mem);
// Otherwise, use this to attach to memory referenced by another datum.
void attach(JSObject &datum, uint32_t offset);
};
class TypedObject : public TypedDatum
{
public:
static const Class class_;
// creates zeroed memory of size of type
static JSObject *createZeroed(JSContext *cx, HandleObject type);
// creates a block that aliases the memory owned by `owner` at the
// given offset
static JSObject *createDerived(JSContext *cx, HandleObject type,
HandleObject owner, size_t offset);
// user-accessible constructor (`new TypeDescriptor(...)`)
static bool construct(JSContext *cx, unsigned argc, Value *vp);
};
class TypedHandle : public TypedDatum
{
public:
static const Class class_;
static const JSFunctionSpec handleStaticMethods[];
};
// Usage: ClampToUint8(v)
//
// Same as the C function ClampDoubleToUint8. `v` must be a number.
/*
* Usage: NewTypedHandle(typeObj)
*
* Constructs a new, unattached instance of `Handle`.
*/
bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
/*
* Usage: NewDerivedTypedDatum(typeObj, owner, offset)
*
* Constructs a new, unattached instance of `Handle`.
*/
bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp);
/*
* Usage: AttachHandle(handle, newOwner, newOffset)
*
* Moves `handle` to point at the memory owned by `newOwner` with
* the offset `newOffset`.
*/
bool AttachHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo AttachHandleJitInfo;
/*
* Usage: ObjectIsTypeObject(obj)
*
* True if `obj` is a type object.
*/
bool ObjectIsTypeObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ObjectIsTypeObjectJitInfo;
/*
* Usage: ObjectIsTypeRepresentation(obj)
*
* True if `obj` is a type representation object.
*/
bool ObjectIsTypeRepresentation(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ObjectIsTypeRepresentationJitInfo;
/*
* Usage: ObjectIsTypedHandle(obj)
*
* True if `obj` is a handle.
*/
bool ObjectIsTypedHandle(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ObjectIsTypedHandleJitInfo;
/*
* Usage: ObjectIsTypedObject(obj)
*
* True if `obj` is a typed object.
*/
bool ObjectIsTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ObjectIsTypedObjectJitInfo;
/*
* Usage: IsAttached(obj)
*
* Given a TypedDatum `obj`, returns true if `obj` is
* "attached" (i.e., its data pointer is NULL).
*/
bool IsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo IsAttachedJitInfo;
/*
* Usage: ClampToUint8(v)
*
* Same as the C function ClampDoubleToUint8. `v` must be a number.
*/
bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo ClampToUint8JitInfo;
// Usage: Memcpy(targetTypedObj, targetOffset,
// sourceTypedObj, sourceOffset,
// size)
//
// Intrinsic function. Copies size bytes from the data for
// `sourceTypedObj` at `sourceOffset` into the data for
// `targetTypedObj` at `targetOffset`.
/*
* Usage: Memcpy(targetDatum, targetOffset,
* sourceDatum, sourceOffset,
* size)
*
* Intrinsic function. Copies size bytes from the data for
* `sourceDatum` at `sourceOffset` into the data for
* `targetDatum` at `targetOffset`.
*
* Both `sourceDatum` and `targetDatum` must be attached.
*/
bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo MemcpyJitInfo;
// Usage: StoreScalar(targetTypedObj, 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 `targetTypedObj` at offset `targetOffset`.
/*
* Usage: StoreScalar(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_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \
class StoreScalar##T { \
public: \
@ -246,12 +431,14 @@ class StoreScalar##T { \
static const JSJitInfo JitInfo; \
};
// Usage: LoadScalar(targetTypedObj, 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 `targetTypedObj` at offset `targetOffset`.
// `targetTypedObj` must be attached.
/*
* 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: \

View File

@ -10,8 +10,10 @@
// Typed object slots
#define TYPED_TYPE_OBJ(obj) \
UnsafeGetReservedSlot(obj, JS_TYPEDOBJ_SLOT_TYPE_OBJ)
#define DATUM_TYPE_OBJ(obj) \
UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_OBJ)
#define DATUM_OWNER(obj) \
UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER)
// Type repr slots
@ -29,9 +31,9 @@
#define HAS_PROPERTY(obj, prop) \
callFunction(std_Object_hasOwnProperty, obj, prop)
function TYPED_TYPE_REPR(obj) {
function DATUM_TYPE_REPR(obj) {
// Eventually this will be a slot on typed objects
return TYPE_TYPE_REPR(TYPED_TYPE_OBJ(obj));
return TYPE_TYPE_REPR(DATUM_TYPE_OBJ(obj));
}
///////////////////////////////////////////////////////////////////////////
@ -41,8 +43,8 @@ function TYPED_TYPE_REPR(obj) {
// pointer into typed object memory. They pull together:
// - typeRepr: the internal type representation
// - typeObj: the user-visible type object
// - owner: the owner object that contains the allocated block of memory
// - offset: an offset into that owner object
// - datum: the typed object that contains the allocated block of memory
// - offset: an offset into that typed object
//
// They are basically equivalent to a typed object, except that they
// offer lots of internal unsafe methods and are not native objects.
@ -54,15 +56,22 @@ function TYPED_TYPE_REPR(obj) {
// they mutate the receiver in place, because it makes for prettier
// code.
function TypedObjectPointer(typeRepr, typeObj, owner, offset) {
function TypedObjectPointer(typeRepr, typeObj, datum, offset) {
this.typeRepr = typeRepr;
this.typeObj = typeObj;
this.owner = owner;
this.datum = datum;
this.offset = offset;
}
MakeConstructible(TypedObjectPointer, {});
TypedObjectPointer.fromTypedDatum = function(typed) {
return new TypedObjectPointer(DATUM_TYPE_REPR(typed),
DATUM_TYPE_OBJ(typed),
typed,
0);
}
#ifdef DEBUG
TypedObjectPointer.prototype.toString = function() {
return "Ptr(" + this.typeObj.toSource() + " @ " + this.offset + ")";
@ -71,13 +80,13 @@ TypedObjectPointer.prototype.toString = function() {
TypedObjectPointer.prototype.copy = function() {
return new TypedObjectPointer(this.typeRepr, this.typeObj,
this.owner, this.offset);
this.datum, this.offset);
};
TypedObjectPointer.prototype.reset = function(inPtr) {
this.typeRepr = inPtr.typeRepr;
this.typeObj = inPtr.typeObj;
this.owner = inPtr.owner;
this.datum = inPtr.datum;
this.offset = inPtr.offset;
return this;
};
@ -161,6 +170,60 @@ TypedObjectPointer.prototype.moveToField = function(propName) {
return this;
}
///////////////////////////////////////////////////////////////////////////
// Getting values
//
// The methods in this section read from the memory pointed at
// by `this` and produce JS values. This process is called *reification*
// in the spec.
// Reifies the value referenced by the pointer, meaning that it
// returns a new object pointing at the value. If the value is
// a scalar, it will return a JS number, but otherwise the reified
// result will be a typed object or handle, depending on the type
// of the ptr's datum.
TypedObjectPointer.prototype.get = function() {
assert(ObjectIsAttached(this.datum), "get() called with unattached datum");
if (REPR_KIND(this.typeRepr) == JS_TYPEREPR_SCALAR_KIND)
return this.getScalar();
return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
}
TypedObjectPointer.prototype.getScalar = function() {
var type = REPR_TYPE(this.typeRepr);
switch (type) {
case JS_SCALARTYPEREPR_INT8:
return Load_int8(this.datum, this.offset);
case JS_SCALARTYPEREPR_UINT8:
case JS_SCALARTYPEREPR_UINT8_CLAMPED:
return Load_uint8(this.datum, this.offset);
case JS_SCALARTYPEREPR_INT16:
return Load_int16(this.datum, this.offset);
case JS_SCALARTYPEREPR_UINT16:
return Load_uint16(this.datum, this.offset);
case JS_SCALARTYPEREPR_INT32:
return Load_int32(this.datum, this.offset);
case JS_SCALARTYPEREPR_UINT32:
return Load_uint32(this.datum, this.offset);
case JS_SCALARTYPEREPR_FLOAT32:
return Load_float32(this.datum, this.offset);
case JS_SCALARTYPEREPR_FLOAT64:
return Load_float64(this.datum, this.offset);
}
assert(false, "Unhandled scalar type: " + type);
}
///////////////////////////////////////////////////////////////////////////
// Setting values
//
@ -170,15 +233,20 @@ TypedObjectPointer.prototype.moveToField = function(propName) {
// to `typeRepr` as needed. This is the most general entry point and
// works for any type.
TypedObjectPointer.prototype.set = function(fromValue) {
assert(ObjectIsAttached(this.datum), "set() called with unattached datum");
var typeRepr = this.typeRepr;
// Fast path: `fromValue` is a typed object with same type
// representation as the destination. In that case, we can just do a
// memcpy.
if (IsObject(fromValue) && HaveSameClass(fromValue, this.owner)) {
if (TYPED_TYPE_REPR(fromValue) === typeRepr) {
if (IsObject(fromValue) && HaveSameClass(fromValue, this.datum)) {
if (DATUM_TYPE_REPR(fromValue) === typeRepr) {
if (!ObjectIsAttached(fromValue))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var size = REPR_SIZE(typeRepr);
Memcpy(this.owner, this.offset, fromValue, 0, size);
Memcpy(this.datum, this.offset, fromValue, 0, size);
return;
}
}
@ -233,38 +301,38 @@ TypedObjectPointer.prototype.setScalar = function(fromValue) {
var type = REPR_TYPE(this.typeRepr);
switch (type) {
case JS_SCALARTYPEREPR_INT8:
return Store_int8(this.owner, this.offset,
return Store_int8(this.datum, this.offset,
TO_INT32(fromValue) & 0xFF);
case JS_SCALARTYPEREPR_UINT8:
return Store_uint8(this.owner, this.offset,
return Store_uint8(this.datum, this.offset,
TO_UINT32(fromValue) & 0xFF);
case JS_SCALARTYPEREPR_UINT8_CLAMPED:
var v = ClampToUint8(+fromValue);
return Store_int8(this.owner, this.offset, v);
return Store_int8(this.datum, this.offset, v);
case JS_SCALARTYPEREPR_INT16:
return Store_int16(this.owner, this.offset,
return Store_int16(this.datum, this.offset,
TO_INT32(fromValue) & 0xFFFF);
case JS_SCALARTYPEREPR_UINT16:
return Store_uint16(this.owner, this.offset,
return Store_uint16(this.datum, this.offset,
TO_UINT32(fromValue) & 0xFFFF);
case JS_SCALARTYPEREPR_INT32:
return Store_int32(this.owner, this.offset,
return Store_int32(this.datum, this.offset,
TO_INT32(fromValue));
case JS_SCALARTYPEREPR_UINT32:
return Store_uint32(this.owner, this.offset,
return Store_uint32(this.datum, this.offset,
TO_UINT32(fromValue));
case JS_SCALARTYPEREPR_FLOAT32:
return Store_float32(this.owner, this.offset, +fromValue);
return Store_float32(this.datum, this.offset, +fromValue);
case JS_SCALARTYPEREPR_FLOAT64:
return Store_float64(this.owner, this.offset, +fromValue);
return Store_float64(this.datum, this.offset, +fromValue);
}
assert(false, "Unhandled scalar type: " + type);
@ -278,26 +346,54 @@ TypedObjectPointer.prototype.setScalar = function(fromValue) {
// Wrapper for use from C++ code.
function ConvertAndCopyTo(destTypeRepr,
destTypeObj,
destTypedObj,
destDatum,
destOffset,
fromValue)
{
assert(IsObject(destTypeRepr) && ObjectIsTypeRepresentation(destTypeRepr),
"ConvertAndCopyTo: not type repr");
assert(IsObject(destTypeObj) && ObjectIsTypeObject(destTypeObj),
"ConvertAndCopyTo: not type obj");
assert(IsObject(destDatum) && ObjectIsTypedDatum(destDatum),
"ConvertAndCopyTo: not type datum");
if (!ObjectIsAttached(destDatum))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var ptr = new TypedObjectPointer(destTypeRepr, destTypeObj,
destTypedObj, destOffset);
destDatum, destOffset);
ptr.set(fromValue);
}
// Wrapper for use from C++ code.
function Reify(sourceTypeRepr,
sourceTypeObj,
sourceDatum,
sourceOffset) {
assert(IsObject(sourceTypeRepr) && ObjectIsTypeRepresentation(sourceTypeRepr),
"Reify: not type repr");
assert(IsObject(sourceTypeObj) && ObjectIsTypeObject(sourceTypeObj),
"Reify: not type obj");
assert(IsObject(sourceDatum) && ObjectIsTypedDatum(sourceDatum),
"Reify: not type datum");
if (!ObjectIsAttached(sourceDatum))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var ptr = new TypedObjectPointer(sourceTypeRepr, sourceTypeObj,
sourceDatum, sourceOffset);
return ptr.get();
}
function FillTypedArrayWithValue(destArray, fromValue) {
var typeRepr = TYPED_TYPE_REPR(destArray);
var typeRepr = DATUM_TYPE_REPR(destArray);
var length = REPR_LENGTH(typeRepr);
if (length === 0)
return;
// Use convert and copy to to produce the first element:
var ptr = new TypedObjectPointer(typeRepr,
TYPED_TYPE_OBJ(destArray),
destArray,
0);
var ptr = TypedObjectPointer.fromTypedDatum(destArray);
ptr.moveToElem(0);
ptr.set(fromValue);
@ -308,4 +404,96 @@ function FillTypedArrayWithValue(destArray, fromValue) {
Memcpy(destArray, offset, destArray, 0, elementSize);
}
///////////////////////////////////////////////////////////////////////////
// Handles
//
// Note: these methods are directly invokable by users and so must be
// defensive.
// This is the `handle([obj, [...path]])` method on type objects.
// User exposed!
//
// FIXME bug 929656 -- label algorithms with steps from the spec
function HandleCreate(obj, ...path) {
if (!ObjectIsTypeObject(this))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "handle", "value");
var handle = NewTypedHandle(this);
if (obj !== undefined)
HandleMoveInternal(handle, obj, path)
return handle;
}
// Handle.move: user exposed!
// FIXME bug 929656 -- label algorithms with steps from the spec
function HandleMove(handle, obj, ...path) {
if (!ObjectIsTypedHandle(handle))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
HandleMoveInternal(handle, obj, path);
}
function HandleMoveInternal(handle, obj, path) {
assert(ObjectIsTypedHandle(handle),
"HandleMoveInternal: not typed handle");
if (!IsObject(obj) || !ObjectIsTypedDatum(obj))
ThrowError(JSMSG_INCOMPATIBLE_PROTO);
var ptr = TypedObjectPointer.fromTypedDatum(obj);
for (var i = 0; i < path.length; i++)
ptr.moveTo(path[i]);
// Check that the new destination is equivalent to the handle type.
if (ptr.typeRepr !== DATUM_TYPE_REPR(handle))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE);
AttachHandle(handle, ptr.datum, ptr.offset)
}
// Handle.get: user exposed!
// FIXME bug 929656 -- label algorithms with steps from the spec
function HandleGet(handle) {
if (!IsObject(handle) || !ObjectIsTypedHandle(handle))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
if (!ObjectIsAttached(handle))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var ptr = TypedObjectPointer.fromTypedDatum(handle);
return ptr.get();
}
// Handle.set: user exposed!
// FIXME bug 929656 -- label algorithms with steps from the spec
function HandleSet(handle, value) {
if (!IsObject(handle) || !ObjectIsTypedHandle(handle))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Handle", "set", typeof value);
if (!ObjectIsAttached(handle))
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
var ptr = TypedObjectPointer.fromTypedDatum(handle);
ptr.set(value);
}
// Handle.isHandle: user exposed!
// FIXME bug 929656 -- label algorithms with steps from the spec
function HandleTest(obj) {
return IsObject(obj) && ObjectIsTypedHandle(obj);
}
///////////////////////////////////////////////////////////////////////////
// Miscellaneous
function ObjectIsTypedDatum(obj) {
return ObjectIsTypedObject(obj) || ObjectIsTypedHandle(obj);
}
function ObjectIsAttached(obj) {
assert(ObjectIsTypedDatum(obj),
"ObjectIsAttached() invoked on invalid obj");
return DATUM_OWNER(obj) != null;
}

View File

@ -77,10 +77,10 @@
#define JS_SCALARTYPEREPR_UINT8_CLAMPED 8
///////////////////////////////////////////////////////////////////////////
// Slots for typed objects
// Slots for typed objects (actually, any TypedContents objects)
#define JS_TYPEDOBJ_SLOT_TYPE_OBJ 0 // Type object for a given typed object
#define JS_TYPEDOBJ_SLOT_OWNER 1 // Owner of data (if null, this is owner)
#define JS_TYPEDOBJ_SLOTS 2 // Number of slots for typed objs
#define JS_DATUM_SLOT_TYPE_OBJ 0 // Type object for a given typed object
#define JS_DATUM_SLOT_OWNER 1 // Owner of data (if null, this is owner)
#define JS_DATUM_SLOTS 2 // Number of slots for typed objs
#endif

View File

@ -3659,7 +3659,7 @@ CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
{
Register obj = ToRegister(lir->object());
Register out = ToRegister(lir->output());
masm.loadPtr(Address(obj, BinaryBlock::dataOffset()), out);
masm.loadPtr(Address(obj, TypedObject::dataOffset()), out);
return true;
}

View File

@ -9559,7 +9559,7 @@ IonBuilder::loadTypedObjectType(MDefinition *typedObj)
return typedObj->toNewDerivedTypedObject()->type();
MInstruction *load = MLoadFixedSlot::New(typedObj,
JS_TYPEDOBJ_SLOT_TYPE_OBJ);
JS_DATUM_SLOT_TYPE_OBJ);
current->add(load);
return load;
}

View File

@ -897,7 +897,7 @@ InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t
JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
HandleObject owner, int32_t offset)
{
return BinaryBlock::createDerived(cx, type, owner, offset);
return TypedObject::createDerived(cx, type, owner, offset);
}

View File

@ -417,3 +417,6 @@ MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 363, 0, JSEXN_RANGEERR, "too many functio
MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE, 364, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPED_OBJECT, 365, 0, JSEXN_ERR, "Expected a typed object")
MSG_DEF(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, 366, 1, JSEXN_TYPEERR, "No such property: {0}")
MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 367, 2, JSEXN_TYPEERR, "argument {0} invalid: expected {1}")
MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 368, 0, JSEXN_TYPEERR, "handle unattached")
MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE, 369, 0, JSEXN_TYPEERR, "handle moved to destination of incorrect type")

View File

@ -0,0 +1,60 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 898342;
var summary = 'Handles';
var T = TypedObject;
function runTests() {
var Point = new T.ArrayType(T.float32, 3);
var Line = new T.StructType({from: Point, to: Point});
var Lines = new T.ArrayType(Line, 3);
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
var handle = Lines.handle(lines);
var handle0 = Line.handle(lines, 0);
var handle2 = Line.handle(lines, 2);
// Reads from handles should see the correct data:
assertEq(handle[0].from[0], 1);
assertEq(handle0.from[0], 1);
assertEq(handle2.from[0], 13);
// Writes to handles should modify the original:
handle2.from[0] = 22;
assertEq(lines[2].from[0], 22);
// Reads from handles should see the updated data:
assertEq(handle[0].from[0], 1);
assertEq(handle0.from[0], 1);
assertEq(handle2.from[0], 22);
// isHandle, when called on nonsense, returns false:
assertEq(T.Handle.isHandle(22), false);
assertEq(T.Handle.isHandle({}), false);
// Derived handles should remain handles:
assertEq(T.Handle.isHandle(lines), false);
assertEq(T.Handle.isHandle(lines[0]), false);
assertEq(T.Handle.isHandle(lines[0].from), false);
assertEq(T.Handle.isHandle(handle), true);
assertEq(T.Handle.isHandle(handle[0]), true);
assertEq(T.Handle.isHandle(handle[0].from), true);
reportCompare(true, true);
print("Tests complete");
}
runTests();

View File

@ -0,0 +1,60 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 898342;
var summary = 'Handle Move';
var T = TypedObject;
var Point = new T.ArrayType(T.float32, 3);
var Line = new T.StructType({from: Point, to: Point});
var Lines = new T.ArrayType(Line, 3);
function runTests() {
function testHandleGetSetWithScalarType() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
var handle = T.float32.handle(lines, 0, "to", 1);
assertEq(T.Handle.get(handle), 5);
T.Handle.set(handle, 22);
assertEq(T.Handle.get(handle), 22);
assertEq(lines[0].to[1], 22);
}
testHandleGetSetWithScalarType();
function testHandleGetSetWithComplexType() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
var handle = Point.handle(lines, 0, "to");
T.Handle.set(handle, [22, 23, 24]);
assertEq(handle[0], 22);
assertEq(handle[1], 23);
assertEq(handle[2], 24);
assertEq(T.Handle.get(handle)[0], 22);
assertEq(T.Handle.get(handle)[1], 23);
assertEq(T.Handle.get(handle)[2], 24);
}
testHandleGetSetWithComplexType();
reportCompare(true, true);
print("Tests complete");
}
runTests();

View File

@ -0,0 +1,157 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 898342;
var summary = 'Handle Move';
var T = TypedObject;
var Point = new T.ArrayType(T.float32, 3);
var Line = new T.StructType({from: Point, to: Point});
var Lines = new T.ArrayType(Line, 3);
function runTests() {
function testHandleToPoint() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
function allPoints(lines, func) {
var handle = Point.handle();
for (var i = 0; i < lines.length; i++) {
T.Handle.move(handle, lines, i, "from");
func(handle);
T.Handle.move(handle, lines, i, "to");
func(handle);
}
}
// Iterate over all ponts and mutate them in place:
allPoints(lines, function(p) {
p[0] += 100;
p[1] += 200;
p[2] += 300;
});
// Spot check the results
assertEq(lines[0].from[0], 101);
assertEq(lines[1].to[1], 211);
assertEq(lines[2].to[2], 318);
}
testHandleToPoint();
function testHandleToFloat() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
function allPoints(lines, func) {
var handle = T.float32.handle();
for (var i = 0; i < lines.length; i++) {
T.Handle.move(handle, lines, i, "from", 0);
func(handle);
T.Handle.move(handle, lines, i, "from", 1);
func(handle);
T.Handle.move(handle, lines, i, "from", 2);
func(handle);
T.Handle.move(handle, lines, i, "to", 0);
func(handle);
T.Handle.move(handle, lines, i, "to", 1);
func(handle);
T.Handle.move(handle, lines, i, "to", 2);
func(handle);
}
}
// Iterate over all ponts and mutate them in place:
allPoints(lines, function(p) {
T.Handle.set(p, T.Handle.get(p) + 100);
});
// Spot check the results
assertEq(lines[0].from[0], 101);
assertEq(lines[1].to[1], 111);
assertEq(lines[2].to[2], 118);
}
testHandleToFloat();
function testHandleToEquivalentType() {
var Point2 = new T.ArrayType(T.float32, 3);
assertEq(Point.equivalent(Point2), true);
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
var handle = Point2.handle(lines, 0, "to");
assertEq(handle[0], 4);
assertEq(handle[1], 5);
assertEq(handle[2], 6);
}
testHandleToEquivalentType();
function testHandleMoveToIllegalType() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
// Moving a handle to a value of incorrect type should report an error:
assertThrowsInstanceOf(function() {
Line.handle(lines);
}, TypeError, "handle moved to destination of incorrect type");
assertThrowsInstanceOf(function() {
var h = Line.handle();
T.Handle.move(h, lines);
}, TypeError, "handle moved to destination of incorrect type");
assertThrowsInstanceOf(function() {
var h = T.float32.handle();
T.Handle.move(h, lines, 0);
}, TypeError, "handle moved to destination of incorrect type");
}
testHandleMoveToIllegalType();
function testHandleMoveToIllegalProperty() {
var lines = new Lines([
{from: [1, 2, 3], to: [4, 5, 6]},
{from: [7, 8, 9], to: [10, 11, 12]},
{from: [13, 14, 15], to: [16, 17, 18]}
]);
assertThrowsInstanceOf(function() {
var h = Point.handle();
T.Handle.move(h, lines, 0, "foo");
}, TypeError, "No such property: foo");
assertThrowsInstanceOf(function() {
var h = Point.handle();
T.Handle.move(h, lines, 22, "to");
}, TypeError, "No such property: 22");
}
testHandleMoveToIllegalProperty();
reportCompare(true, true);
print("Tests complete");
}
runTests();

View File

@ -0,0 +1,45 @@
// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 898342;
var summary = 'Unattached handles';
var T = TypedObject;
function runTests() {
var Line = new T.StructType({from: T.uint8, to: T.uint8});
var Lines = new T.ArrayType(Line, 3);
// Create unattached handle to array, struct:
var handle = Lines.handle();
var handle0 = Line.handle();
// Accessing properties throws:
assertThrowsInstanceOf(function() handle[0], TypeError,
"Unattached handle did not yield error");
assertThrowsInstanceOf(function() handle0.from, TypeError,
"Unattached handle did not yield error");
// Handle.get() throws:
assertThrowsInstanceOf(function() T.Handle.get(handle), TypeError,
"Unattached handle did not yield error");
assertThrowsInstanceOf(function() T.Handle.get(handle0), TypeError,
"Unattached handle did not yield error");
// Handle.set() throws:
assertThrowsInstanceOf(function() T.Handle.set(handle, [{},{},{}]), TypeError,
"Unattached handle did not yield error");
assertThrowsInstanceOf(function() T.Handle.set(handle0, {}), TypeError,
"Unattached handle did not yield error");
reportCompare(true, true);
print("Tests complete");
}
runTests();

View File

@ -74,6 +74,7 @@
macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
macro(global, global, "global") \
macro(Handle, Handle, "Handle") \
macro(has, has, "has") \
macro(hasOwn, hasOwn, "hasOwn") \
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
@ -132,6 +133,7 @@
macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
macro(proto, proto, "__proto__") \
macro(prototype, prototype, "prototype") \
macro(Reify, Reify, "Reify") \
macro(return, return_, "return") \
macro(sensitivity, sensitivity, "sensitivity") \
macro(set, set, "set") \

View File

@ -628,6 +628,30 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
// See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewTypedHandle",
js::NewTypedHandle,
1, 0),
JS_FN("NewDerivedTypedDatum",
js::NewDerivedTypedDatum,
3, 0),
JS_FNINFO("AttachHandle",
JSNativeThreadSafeWrapper<js::AttachHandle>,
&js::AttachHandleJitInfo, 5, 0),
JS_FNINFO("ObjectIsTypeObject",
JSNativeThreadSafeWrapper<js::ObjectIsTypeObject>,
&js::ObjectIsTypeObjectJitInfo, 5, 0),
JS_FNINFO("ObjectIsTypeRepresentation",
JSNativeThreadSafeWrapper<js::ObjectIsTypeRepresentation>,
&js::ObjectIsTypeRepresentationJitInfo, 5, 0),
JS_FNINFO("ObjectIsTypedObject",
JSNativeThreadSafeWrapper<js::ObjectIsTypedObject>,
&js::ObjectIsTypedObjectJitInfo, 5, 0),
JS_FNINFO("ObjectIsTypedHandle",
JSNativeThreadSafeWrapper<js::ObjectIsTypedHandle>,
&js::ObjectIsTypedHandleJitInfo, 5, 0),
JS_FN("NewHandle",
js::NewTypedHandle,
1, 0),
JS_FNINFO("ClampToUint8",
JSNativeThreadSafeWrapper<js::ClampToUint8>,
&js::ClampToUint8JitInfo, 1, 0),