mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 987508 - Create array buffers lazily for small typed arrays, r=sfink.
This commit is contained in:
parent
ded5e5b4b0
commit
c273031592
@ -197,7 +197,7 @@ StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
|
||||
new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
|
||||
for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
|
||||
JS::Rooted<JSObject*> arrayBuffer(aJSContext,
|
||||
JS_GetArrayBufferViewBuffer(aJSArrays[i]));
|
||||
JS_GetArrayBufferViewBuffer(aJSContext, aJSArrays[i]));
|
||||
uint8_t* stolenData = arrayBuffer
|
||||
? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer)
|
||||
: nullptr;
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
#include "vm/ObjectImpl-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace gc;
|
||||
using namespace mozilla;
|
||||
@ -365,6 +367,15 @@ GetObjectAllocKindForCopy(JSRuntime *rt, JSObject *obj)
|
||||
if (obj->is<JSFunction>())
|
||||
return obj->as<JSFunction>().getAllocKind();
|
||||
|
||||
/*
|
||||
* Typed arrays in the nursery may have a lazily allocated buffer, make
|
||||
* sure there is room for the array's fixed data when moving the array.
|
||||
*/
|
||||
if (obj->is<TypedArrayObject>() && !obj->as<TypedArrayObject>().buffer()) {
|
||||
size_t nbytes = obj->as<TypedArrayObject>().byteLength();
|
||||
return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
|
||||
}
|
||||
|
||||
AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots());
|
||||
JS_ASSERT(!IsBackgroundFinalized(kind));
|
||||
JS_ASSERT(CanBeFinalizedInBackground(kind, obj->getClass()));
|
||||
@ -561,6 +572,9 @@ js::Nursery::moveObjectToTenured(JSObject *dst, JSObject *src, AllocKind dstKind
|
||||
tenuredSize += moveSlotsToTenured(dst, src, dstKind);
|
||||
tenuredSize += moveElementsToTenured(dst, src, dstKind);
|
||||
|
||||
if (src->is<TypedArrayObject>())
|
||||
dst->setPrivate(dst->fixedData(TypedArrayObject::FIXED_DATA_START));
|
||||
|
||||
/* The shape's list head may point into the old object. */
|
||||
if (&src->shape_ == dst->shape_->listp)
|
||||
dst->shape_->listp = &dst->shape_;
|
||||
|
@ -109,7 +109,7 @@ TestArrayFromBuffer(JSContext *cx)
|
||||
CHECK_EQUAL(JS_GetTypedArrayLength(array), elts);
|
||||
CHECK_EQUAL(JS_GetTypedArrayByteOffset(array), 0);
|
||||
CHECK_EQUAL(JS_GetTypedArrayByteLength(array), nbytes);
|
||||
CHECK_EQUAL(JS_GetArrayBufferViewBuffer(array), (JSObject*) buffer);
|
||||
CHECK_EQUAL(JS_GetArrayBufferViewBuffer(cx, array), (JSObject*) buffer);
|
||||
|
||||
Element *data;
|
||||
CHECK(data = GetData(array));
|
||||
|
@ -1432,7 +1432,7 @@ JS_GetArrayBufferViewData(JSObject *obj);
|
||||
* object that would return true for JS_IsArrayBufferViewObject().
|
||||
*/
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
JS_GetArrayBufferViewBuffer(JSObject *obj);
|
||||
JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Set an ArrayBuffer's length to 0 and neuter all of its views.
|
||||
|
@ -1253,17 +1253,12 @@ NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::
|
||||
if (!NewObjectMetadata(cx, &metadata))
|
||||
return nullptr;
|
||||
|
||||
// Normally, the number of fixed slots given an object is the maximum
|
||||
// permitted for its size class. For array buffers we only use enough to
|
||||
// cover the class reservd slots, so that the remaining space in the
|
||||
// object's allocation is available for the buffer's data.
|
||||
size_t nfixed;
|
||||
if (clasp == &ArrayBufferObject::class_) {
|
||||
JS_STATIC_ASSERT(ArrayBufferObject::RESERVED_SLOTS == 4);
|
||||
nfixed = ArrayBufferObject::RESERVED_SLOTS;
|
||||
} else {
|
||||
nfixed = GetGCKindSlots(kind, clasp);
|
||||
}
|
||||
// For objects which can have fixed data following the object, only use
|
||||
// enough fixed slots to cover the number of reserved slots in the object,
|
||||
// regardless of the allocation kind specified.
|
||||
size_t nfixed = ClassCanHaveFixedData(clasp)
|
||||
? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
|
||||
: GetGCKindSlots(kind, clasp);
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(),
|
||||
parent, metadata, nfixed));
|
||||
|
@ -475,7 +475,8 @@ JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto,
|
||||
return SetClassAndProto(cx, obj, obj->getClass(), proto, succeeded);
|
||||
}
|
||||
|
||||
inline bool JSObject::isVarObj()
|
||||
inline bool
|
||||
JSObject::isVarObj()
|
||||
{
|
||||
if (is<js::DebugScopeObject>())
|
||||
return as<js::DebugScopeObject>().scope().isVarObj();
|
||||
@ -495,7 +496,7 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
|
||||
JS_ASSERT(shape && type);
|
||||
JS_ASSERT(type->clasp() == shape->getObjectClass());
|
||||
JS_ASSERT(type->clasp() != &js::ArrayObject::class_);
|
||||
JS_ASSERT_IF(type->clasp() != &js::ArrayBufferObject::class_,
|
||||
JS_ASSERT_IF(!ClassCanHaveFixedData(type->clasp()),
|
||||
js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots());
|
||||
JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind));
|
||||
JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap);
|
||||
|
@ -631,7 +631,7 @@ ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullpt
|
||||
if (data) {
|
||||
obj->initialize(nbytes, data, OwnsData);
|
||||
} else {
|
||||
void *data = &obj->fixedSlots()[reservedSlots];
|
||||
void *data = obj->fixedData(reservedSlots);
|
||||
memset(data, 0, nbytes);
|
||||
obj->initialize(nbytes, data, DoesntOwnData);
|
||||
}
|
||||
@ -929,6 +929,17 @@ ArrayBufferViewObject::neuter(void *newData)
|
||||
as<TypedObject>().neuter(newData);
|
||||
}
|
||||
|
||||
/* static */ ArrayBufferObject *
|
||||
ArrayBufferViewObject::bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> thisObject)
|
||||
{
|
||||
if (thisObject->is<TypedArrayObject>()) {
|
||||
Rooted<TypedArrayObject *> typedArray(cx, &thisObject->as<TypedArrayObject>());
|
||||
if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
|
||||
return nullptr;
|
||||
}
|
||||
return &thisObject->getFixedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>();
|
||||
}
|
||||
|
||||
/* JS Friend API */
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
@ -1047,12 +1058,13 @@ JS_GetArrayBufferViewData(JSObject *obj)
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
JS_GetArrayBufferViewBuffer(JSObject *obj)
|
||||
JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
obj = CheckedUnwrap(obj);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
return obj->as<ArrayBufferViewObject>().bufferObject();
|
||||
Rooted<ArrayBufferViewObject *> viewObject(cx, &obj->as<ArrayBufferViewObject>());
|
||||
return ArrayBufferViewObject::bufferObject(cx, viewObject);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32_t)
|
||||
|
@ -240,9 +240,7 @@ class ArrayBufferViewObject : public JSObject
|
||||
static const size_t NEXT_VIEW_SLOT = JS_TYPEDOBJ_SLOT_NEXT_VIEW;
|
||||
|
||||
public:
|
||||
JSObject *bufferObject() const {
|
||||
return &getFixedSlot(BUFFER_SLOT).toObject();
|
||||
}
|
||||
static ArrayBufferObject *bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> obj);
|
||||
|
||||
ArrayBufferViewObject *nextView() const {
|
||||
return static_cast<ArrayBufferViewObject*>(getFixedSlot(NEXT_VIEW_SLOT).toPrivate());
|
||||
|
@ -13,9 +13,12 @@
|
||||
#include "jsproxy.h"
|
||||
|
||||
#include "vm/ProxyObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* static */ inline bool
|
||||
js::ObjectImpl::isExtensible(ExclusiveContext *cx, js::Handle<ObjectImpl*> obj, bool *extensible)
|
||||
ObjectImpl::isExtensible(ExclusiveContext *cx, Handle<ObjectImpl*> obj, bool *extensible)
|
||||
{
|
||||
if (obj->asObjectPtr()->is<ProxyObject>()) {
|
||||
if (!cx->shouldBeJSContext())
|
||||
@ -29,4 +32,24 @@ js::ObjectImpl::isExtensible(ExclusiveContext *cx, js::Handle<ObjectImpl*> obj,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
ClassCanHaveFixedData(const Class *clasp)
|
||||
{
|
||||
// Normally, the number of fixed slots given an object is the maximum
|
||||
// permitted for its size class. For array buffers and typed arrays we only
|
||||
// use enough to cover the class reserved slots, so that the remaining
|
||||
// space in the object's allocation is available for the buffer's data.
|
||||
return clasp == &ArrayBufferObject::class_ || IsTypedArrayClass(clasp);
|
||||
}
|
||||
|
||||
inline void *
|
||||
ObjectImpl::fixedData(size_t nslots) const
|
||||
{
|
||||
JS_ASSERT(ClassCanHaveFixedData(getClass()));
|
||||
JS_ASSERT(nslots == numFixedSlots() + (hasPrivate() ? 1 : 0));
|
||||
return &fixedSlots()[nslots];
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* vm_ObjectImpl_inl_h */
|
||||
|
@ -94,13 +94,12 @@ ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
|
||||
unsigned attrs, HandleValue value, bool setterIsStrict);
|
||||
|
||||
/*
|
||||
* Elements header used for all objects other than non-native objects (except
|
||||
* for ArrayBufferObjects!!!) and typed arrays. The elements component of such
|
||||
* objects offers an efficient representation for all or some of the indexed
|
||||
* properties of the object, using a flat array of Values rather than a shape
|
||||
* hierarchy stored in the object's slots. This structure is immediately
|
||||
* followed by an array of elements, with the elements member in an object
|
||||
* pointing to the beginning of that array (the end of this structure).
|
||||
* Elements header used for all objects. The elements component of such objects
|
||||
* offers an efficient representation for all or some of the indexed properties
|
||||
* of the object, using a flat array of Values rather than a shape hierarchy
|
||||
* stored in the object's slots. This structure is immediately followed by an
|
||||
* array of elements, with the elements member in an object pointing to the
|
||||
* beginning of that array (the end of this structure).
|
||||
* See below for usage of this structure.
|
||||
*
|
||||
* The sets of properties represented by an object's elements and slots
|
||||
@ -149,14 +148,14 @@ ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
|
||||
* value less than or equal to both the object's length and the object's
|
||||
* capacity.
|
||||
*
|
||||
* With inference enabled, there is flexibility in exactly the value the
|
||||
* initialized length must hold, e.g. if an array has length 5, capacity 10,
|
||||
* completely empty, it is valid for the initialized length to be any value
|
||||
* between zero and 5, as long as the in memory values below the initialized
|
||||
* length have been initialized with a hole value. However, in such cases we
|
||||
* want to keep the initialized length as small as possible: if the object is
|
||||
* known to have no hole values below its initialized length, then it is
|
||||
* "packed" and can be accessed much faster by JIT code.
|
||||
* There is flexibility in exactly the value the initialized length must hold,
|
||||
* e.g. if an array has length 5, capacity 10, completely empty, it is valid
|
||||
* for the initialized length to be any value between zero and 5, as long as
|
||||
* the in memory values below the initialized length have been initialized with
|
||||
* a hole value. However, in such cases we want to keep the initialized length
|
||||
* as small as possible: if the object is known to have no hole values below
|
||||
* its initialized length, then it is "packed" and can be accessed much faster
|
||||
* by JIT code.
|
||||
*
|
||||
* Elements do not track property creation order, so enumerating the elements
|
||||
* of an object does not necessarily visit indexes in the order they were
|
||||
@ -288,7 +287,7 @@ IsObjectValueInCompartment(js::Value v, JSCompartment *comp);
|
||||
* variable-sized array of values for inline storage, which may be used by
|
||||
* either properties of native objects (fixed slots), by elements (fixed
|
||||
* elements), or by other data for certain kinds of objects, such as
|
||||
* ArrayBufferObjects.
|
||||
* ArrayBufferObjects and TypedArrayObjects.
|
||||
*
|
||||
* Two native objects with the same shape are guaranteed to have the same
|
||||
* number of fixed slots.
|
||||
@ -827,6 +826,13 @@ class ObjectImpl : public gc::BarrieredCell<ObjectImpl>
|
||||
return elements == emptyObjectElements;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to the unused data in the object's allocation immediately
|
||||
* following this object, for use with objects which allocate a larger size
|
||||
* class than they need and store non-elements data inline.
|
||||
*/
|
||||
inline void *fixedData(size_t nslots) const;
|
||||
|
||||
/* GC support. */
|
||||
static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
|
||||
|
||||
|
@ -813,6 +813,10 @@ bool
|
||||
JSStructuredCloneWriter::writeTypedArray(HandleObject obj)
|
||||
{
|
||||
Rooted<TypedArrayObject*> tarr(context(), &obj->as<TypedArrayObject>());
|
||||
|
||||
if (!TypedArrayObject::ensureHasBuffer(context(), tarr))
|
||||
return false;
|
||||
|
||||
if (!out.writePair(SCTAG_TYPED_ARRAY_OBJECT, tarr->length()))
|
||||
return false;
|
||||
uint64_t type = tarr->type();
|
||||
|
@ -106,6 +106,25 @@ TypedArrayObject::sharedBuffer() const
|
||||
return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray)
|
||||
{
|
||||
if (tarray->buffer())
|
||||
return true;
|
||||
|
||||
Rooted<ArrayBufferObject *> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength()));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
buffer->addView(tarray);
|
||||
|
||||
memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength());
|
||||
InitArrayBufferViewDataPointer(tarray, buffer, 0);
|
||||
|
||||
tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ int
|
||||
TypedArrayObject::lengthOffset()
|
||||
{
|
||||
@ -187,13 +206,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
return &TypedArrayObject::protoClasses[ArrayTypeID()];
|
||||
}
|
||||
|
||||
static inline const Class *fastClass()
|
||||
static inline const Class *instanceClass()
|
||||
{
|
||||
return &TypedArrayObject::classes[ArrayTypeID()];
|
||||
}
|
||||
|
||||
static bool is(HandleValue v) {
|
||||
return v.isObject() && v.toObject().hasClass(fastClass());
|
||||
return v.isObject() && v.toObject().hasClass(instanceClass());
|
||||
}
|
||||
|
||||
static void
|
||||
@ -222,11 +241,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
}
|
||||
|
||||
static TypedArrayObject *
|
||||
makeProtoInstance(JSContext *cx, HandleObject proto)
|
||||
makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind)
|
||||
{
|
||||
JS_ASSERT(proto);
|
||||
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, fastClass()));
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
@ -239,19 +258,19 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
}
|
||||
|
||||
static TypedArrayObject *
|
||||
makeTypedInstance(JSContext *cx, uint32_t len)
|
||||
makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind)
|
||||
{
|
||||
if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) {
|
||||
return &NewBuiltinClassInstance(cx, fastClass(),
|
||||
return &NewBuiltinClassInstance(cx, instanceClass(), allocKind,
|
||||
SingletonObject)->as<TypedArrayObject>();
|
||||
}
|
||||
|
||||
jsbytecode *pc;
|
||||
RootedScript script(cx, cx->currentScript(&pc));
|
||||
NewObjectKind newKind = script
|
||||
? UseNewTypeForInitializer(script, pc, fastClass())
|
||||
? UseNewTypeForInitializer(script, pc, instanceClass())
|
||||
: GenericObject;
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, fastClass(), newKind));
|
||||
RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
@ -264,56 +283,61 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
makeInstance(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len,
|
||||
makeInstance(JSContext *cx, Handle<ArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len,
|
||||
HandleObject proto)
|
||||
{
|
||||
JS_ASSERT_IF(!buffer, byteOffset == 0);
|
||||
|
||||
gc::AllocKind allocKind = buffer
|
||||
? GetGCObjectKind(instanceClass())
|
||||
: AllocKindForLazyBuffer(len * sizeof(NativeType));
|
||||
|
||||
Rooted<TypedArrayObject*> obj(cx);
|
||||
if (proto)
|
||||
obj = makeProtoInstance(cx, proto);
|
||||
obj = makeProtoInstance(cx, proto, allocKind);
|
||||
else
|
||||
obj = makeTypedInstance(cx, len);
|
||||
obj = makeTypedInstance(cx, len, allocKind);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
JS_ASSERT_IF(obj->isTenured(),
|
||||
obj->tenuredGetAllocKind() == gc::FINALIZE_OBJECT8_BACKGROUND);
|
||||
|
||||
obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID()));
|
||||
obj->setSlot(BUFFER_SLOT, ObjectValue(*bufobj));
|
||||
obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer));
|
||||
|
||||
Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj));
|
||||
if (buffer) {
|
||||
InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
|
||||
} else {
|
||||
void *data = obj->fixedData(FIXED_DATA_START);
|
||||
obj->initPrivate(data);
|
||||
memset(data, 0, len * sizeof(NativeType));
|
||||
}
|
||||
|
||||
InitArrayBufferViewDataPointer(obj, buffer, byteOffset);
|
||||
obj->setSlot(LENGTH_SLOT, Int32Value(len));
|
||||
obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
||||
obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType)));
|
||||
obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr));
|
||||
|
||||
js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(),
|
||||
obj->getProto(), obj->getParent(), obj->getMetadata(),
|
||||
gc::FINALIZE_OBJECT8_BACKGROUND);
|
||||
if (!empty)
|
||||
return nullptr;
|
||||
obj->setLastPropertyInfallible(empty);
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t bufferByteLength = buffer->byteLength();
|
||||
uint32_t arrayByteLength = obj->byteLength();
|
||||
uint32_t arrayByteOffset = obj->byteOffset();
|
||||
JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData());
|
||||
JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
|
||||
JS_ASSERT(arrayByteOffset <= bufferByteLength);
|
||||
if (buffer) {
|
||||
uint32_t arrayByteLength = obj->byteLength();
|
||||
uint32_t arrayByteOffset = obj->byteOffset();
|
||||
uint32_t bufferByteLength = buffer->byteLength();
|
||||
JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData());
|
||||
JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
|
||||
JS_ASSERT(arrayByteOffset <= bufferByteLength);
|
||||
}
|
||||
|
||||
// Verify that the private slot is at the expected place
|
||||
JS_ASSERT(obj->numFixedSlots() == DATA_SLOT);
|
||||
#endif
|
||||
|
||||
buffer->addView(obj);
|
||||
if (buffer)
|
||||
buffer->addView(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
makeInstance(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len)
|
||||
makeInstance(JSContext *cx, Handle<ArrayBufferObject *> bufobj, uint32_t byteOffset, uint32_t len)
|
||||
{
|
||||
RootedObject nullproto(cx, nullptr);
|
||||
return makeInstance(cx, bufobj, byteOffset, len, nullproto);
|
||||
@ -328,7 +352,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
static bool
|
||||
class_constructor(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
/* N.B. this is a constructor for protoClass, not fastClass! */
|
||||
/* N.B. this is a constructor for protoClass, not instanceClass! */
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JSObject *obj = create(cx, args);
|
||||
if (!obj)
|
||||
@ -396,7 +420,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
}
|
||||
|
||||
static bool IsThisClass(HandleValue v) {
|
||||
return v.isObject() && v.toObject().hasClass(fastClass());
|
||||
return v.isObject() && v.toObject().hasClass(instanceClass());
|
||||
}
|
||||
|
||||
template<Value ValueGetter(TypedArrayObject *tarr)>
|
||||
@ -420,16 +444,36 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
ThisTypedArrayObject::GetterImpl<ValueGetter> >(cx, args);
|
||||
}
|
||||
|
||||
// Define an accessor for a read-only property that invokes a native getter
|
||||
template<Value ValueGetter(TypedArrayObject *tarr)>
|
||||
static bool
|
||||
DefineGetter(JSContext *cx, PropertyName *name, HandleObject proto)
|
||||
BufferGetterImpl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
JS_ASSERT(IsThisClass(args.thisv()));
|
||||
Rooted<TypedArrayObject *> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
|
||||
if (!ensureHasBuffer(cx, tarray))
|
||||
return false;
|
||||
args.rval().set(bufferValue(tarray));
|
||||
return true;
|
||||
}
|
||||
|
||||
// BufferGetter is a function that lazily constructs the array buffer for a
|
||||
// typed array before fetching it.
|
||||
static bool
|
||||
BufferGetter(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
|
||||
ThisTypedArrayObject::BufferGetterImpl>(cx, args);
|
||||
}
|
||||
|
||||
// Define an accessor for a read-only property that invokes a native getter
|
||||
static bool
|
||||
DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native)
|
||||
{
|
||||
RootedId id(cx, NameToId(name));
|
||||
unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
|
||||
|
||||
Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
|
||||
JSObject *getter = NewFunction(cx, NullPtr(), Getter<ValueGetter>, 0,
|
||||
JSObject *getter = NewFunction(cx, NullPtr(), native, 0,
|
||||
JSFunction::NATIVE_FUN, global, NullPtr());
|
||||
if (!getter)
|
||||
return false;
|
||||
@ -442,16 +486,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
static
|
||||
bool defineGetters(JSContext *cx, HandleObject proto)
|
||||
{
|
||||
if (!DefineGetter<lengthValue>(cx, cx->names().length, proto))
|
||||
if (!DefineGetter(cx, proto, cx->names().length, Getter<lengthValue>))
|
||||
return false;
|
||||
|
||||
if (!DefineGetter<bufferValue>(cx, cx->names().buffer, proto))
|
||||
if (!DefineGetter(cx, proto, cx->names().buffer, BufferGetter))
|
||||
return false;
|
||||
|
||||
if (!DefineGetter<byteLengthValue>(cx, cx->names().byteLength, proto))
|
||||
if (!DefineGetter(cx, proto, cx->names().byteLength, Getter<byteLengthValue>))
|
||||
return false;
|
||||
|
||||
if (!DefineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto))
|
||||
if (!DefineGetter(cx, proto, cx->names().byteOffset, Getter<byteOffsetValue>))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -671,7 +715,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
*/
|
||||
|
||||
Rooted<JSObject*> proto(cx);
|
||||
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(fastClass()), &proto))
|
||||
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto))
|
||||
return nullptr;
|
||||
|
||||
InvokeArgs args(cx);
|
||||
@ -695,17 +739,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
return nullptr; // must be arrayBuffer
|
||||
}
|
||||
|
||||
ArrayBufferObject &buffer = AsArrayBuffer(bufobj);
|
||||
Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj));
|
||||
|
||||
if (byteOffset > buffer.byteLength() || byteOffset % sizeof(NativeType) != 0) {
|
||||
if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return nullptr; // invalid byteOffset
|
||||
}
|
||||
|
||||
uint32_t len;
|
||||
if (lengthInt == -1) {
|
||||
len = (buffer.byteLength() - byteOffset) / sizeof(NativeType);
|
||||
if (len * sizeof(NativeType) != buffer.byteLength() - byteOffset) {
|
||||
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
||||
if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
|
||||
@ -721,19 +765,41 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType)
|
||||
}
|
||||
|
||||
if (arrayByteLength + byteOffset > buffer.byteLength()) {
|
||||
if (arrayByteLength + byteOffset > buffer->byteLength()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return nullptr; // byteOffset + len is too big for the arraybuffer
|
||||
}
|
||||
|
||||
return makeInstance(cx, bufobj, byteOffset, len, proto);
|
||||
return makeInstance(cx, buffer, byteOffset, len, proto);
|
||||
}
|
||||
|
||||
static bool
|
||||
maybeCreateArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<ArrayBufferObject *> buffer)
|
||||
{
|
||||
// Make sure that array elements evenly divide into the inline buffer's
|
||||
// size, for the test below.
|
||||
JS_STATIC_ASSERT((INLINE_BUFFER_LIMIT / sizeof(NativeType)) * sizeof(NativeType) == INLINE_BUFFER_LIMIT);
|
||||
|
||||
if (nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) {
|
||||
// The array's data can be inline, and the buffer created lazily.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nelements >= INT32_MAX / sizeof(NativeType)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_NEED_DIET, "size and count");
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer.set(ArrayBufferObject::create(cx, nelements * sizeof(NativeType)));
|
||||
return !!buffer;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
fromLength(JSContext *cx, uint32_t nelements)
|
||||
{
|
||||
RootedObject buffer(cx, createBufferWithSizeAndCount(cx, nelements));
|
||||
if (!buffer)
|
||||
Rooted<ArrayBufferObject *> buffer(cx);
|
||||
if (!maybeCreateArrayBuffer(cx, nelements, &buffer))
|
||||
return nullptr;
|
||||
return makeInstance(cx, buffer, 0, nelements);
|
||||
}
|
||||
@ -748,11 +814,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RootedObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
|
||||
if (!bufobj)
|
||||
Rooted<ArrayBufferObject *> buffer(cx);
|
||||
if (!maybeCreateArrayBuffer(cx, len, &buffer))
|
||||
return nullptr;
|
||||
|
||||
RootedObject obj(cx, makeInstance(cx, bufobj, 0, len));
|
||||
RootedObject obj(cx, makeInstance(cx, buffer, 0, len));
|
||||
if (!obj || !copyFromArray(cx, obj, other, len))
|
||||
return nullptr;
|
||||
return obj;
|
||||
@ -781,7 +847,10 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
JS_ASSERT(begin <= tarray->length());
|
||||
JS_ASSERT(end <= tarray->length());
|
||||
|
||||
RootedObject bufobj(cx, tarray->buffer());
|
||||
if (!ensureHasBuffer(cx, tarray))
|
||||
return nullptr;
|
||||
|
||||
Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer());
|
||||
JS_ASSERT(bufobj);
|
||||
|
||||
JS_ASSERT(begin <= end);
|
||||
@ -1065,20 +1134,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||
js_free(srcbuf);
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
createBufferWithSizeAndCount(JSContext *cx, uint32_t count)
|
||||
{
|
||||
size_t size = sizeof(NativeType);
|
||||
if (size != 0 && count >= INT32_MAX / size) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_NEED_DIET, "size and count");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t bytelen = size * count;
|
||||
return ArrayBufferObject::create(cx, bytelen);
|
||||
}
|
||||
};
|
||||
|
||||
class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> {
|
||||
|
@ -40,6 +40,22 @@ class TypedArrayObject : public ArrayBufferViewObject
|
||||
static const Class classes[ScalarTypeDescr::TYPE_MAX];
|
||||
static const Class protoClasses[ScalarTypeDescr::TYPE_MAX];
|
||||
|
||||
static const size_t FIXED_DATA_START = DATA_SLOT + 1;
|
||||
|
||||
// For typed arrays which can store their data inline, the array buffer
|
||||
// object is created lazily.
|
||||
static const uint32_t INLINE_BUFFER_LIMIT =
|
||||
(JSObject::MAX_FIXED_SLOTS - FIXED_DATA_START) * sizeof(Value);
|
||||
|
||||
static gc::AllocKind
|
||||
AllocKindForLazyBuffer(size_t nbytes)
|
||||
{
|
||||
JS_ASSERT(nbytes <= INLINE_BUFFER_LIMIT);
|
||||
int dataSlots = (nbytes - 1) / sizeof(Value) + 1;
|
||||
JS_ASSERT(int(nbytes) <= dataSlots * int(sizeof(Value)));
|
||||
return gc::GetGCObjectKind(FIXED_DATA_START + dataSlots);
|
||||
}
|
||||
|
||||
static Value bufferValue(TypedArrayObject *tarr) {
|
||||
return tarr->getFixedSlot(BUFFER_SLOT);
|
||||
}
|
||||
@ -53,11 +69,16 @@ class TypedArrayObject : public ArrayBufferViewObject
|
||||
return tarr->getFixedSlot(LENGTH_SLOT);
|
||||
}
|
||||
|
||||
static bool
|
||||
ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray);
|
||||
|
||||
ArrayBufferObject *sharedBuffer() const;
|
||||
ArrayBufferObject *buffer() const {
|
||||
JSObject &obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObject();
|
||||
if (obj.is<ArrayBufferObject>())
|
||||
return &obj.as<ArrayBufferObject>();
|
||||
JSObject *obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
if (obj->is<ArrayBufferObject>())
|
||||
return &obj->as<ArrayBufferObject>();
|
||||
return sharedBuffer();
|
||||
}
|
||||
uint32_t byteOffset() const {
|
||||
@ -228,6 +249,10 @@ class DataViewObject : public ArrayBufferViewObject
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value bufferValue(DataViewObject *view) {
|
||||
return view->getReservedSlot(BUFFER_SLOT);
|
||||
}
|
||||
|
||||
uint32_t byteOffset() const {
|
||||
return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
|
||||
}
|
||||
@ -236,16 +261,8 @@ class DataViewObject : public ArrayBufferViewObject
|
||||
return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32();
|
||||
}
|
||||
|
||||
bool hasBuffer() const {
|
||||
return getReservedSlot(BUFFER_SLOT).isObject();
|
||||
}
|
||||
|
||||
ArrayBufferObject &arrayBuffer() const {
|
||||
return getReservedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>();
|
||||
}
|
||||
|
||||
static Value bufferValue(DataViewObject *view) {
|
||||
return view->hasBuffer() ? ObjectValue(view->arrayBuffer()) : UndefinedValue();
|
||||
return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>();
|
||||
}
|
||||
|
||||
void *dataPointer() const {
|
||||
|
Loading…
Reference in New Issue
Block a user