Bug 741039 - Modify TypedArrays and ArrayBuffers to comply with the WebIDL spec. r=Waldo

WebIDL 4.4.6 says Float32Array.prototype is not a platform object and should throw TypeErrors when any of the Float32Array attributes is accessed. This patch makes obj->isTypedArray only succeed for actual TypedArray instances, and adds TypeErrors when special properties are retrieved from the prototypes.

This patch also eliminates some lingering uses of TypedArray::getTypedArray and makes it and getArrayBuffer static within jstypedarray.cpp. (Both are used for objects whose prototypes are typed arrays or array buffers.)

--HG--
extra : rebase_source : 93032b5c8dc98c4e18eec4fa438f6a9a4173487d
This commit is contained in:
Steve Fink 2012-04-23 15:13:02 -07:00
parent 3250e70d87
commit 7aee790f0d
10 changed files with 200 additions and 188 deletions

View File

@ -1741,7 +1741,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
} }
#define CLASP(name) (&name##Class) #define CLASP(name) (&name##Class)
#define TYPED_ARRAY_CLASP(type) (&TypedArray::fastClasses[TypedArray::type]) #define TYPED_ARRAY_CLASP(type) (&TypedArray::classes[TypedArray::type])
#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL
#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL
#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) #define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name)

View File

@ -434,9 +434,8 @@ JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v)
} }
bool bool
JSStructuredCloneWriter::writeTypedArray(JSObject *obj) JSStructuredCloneWriter::writeTypedArray(JSObject *arr)
{ {
JSObject *arr = TypedArray::getTypedArray(obj);
if (!out.writePair(ArrayTypeToTag(TypedArray::getType(arr)), TypedArray::getLength(arr))) if (!out.writePair(ArrayTypeToTag(TypedArray::getType(arr)), TypedArray::getLength(arr)))
return false; return false;
@ -750,27 +749,26 @@ JSStructuredCloneReader::readTypedArray(uint32_t tag, uint32_t nelems, Value *vp
return false; return false;
vp->setObject(*obj); vp->setObject(*obj);
JSObject *arr = TypedArray::getTypedArray(obj); JS_ASSERT(TypedArray::getLength(obj) == nelems);
JS_ASSERT(TypedArray::getLength(arr) == nelems);
switch (tag) { switch (tag) {
case SCTAG_TYPED_ARRAY_INT8: case SCTAG_TYPED_ARRAY_INT8:
return in.readArray((uint8_t *) JS_GetInt8ArrayData(arr, context()), nelems); return in.readArray((uint8_t*) JS_GetInt8ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_UINT8: case SCTAG_TYPED_ARRAY_UINT8:
return in.readArray((uint8_t *) JS_GetUint8ArrayData(arr, context()), nelems); return in.readArray(JS_GetUint8ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_INT16: case SCTAG_TYPED_ARRAY_INT16:
return in.readArray((uint16_t *) JS_GetInt16ArrayData(arr, context()), nelems); return in.readArray((uint16_t*) JS_GetInt16ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_UINT16: case SCTAG_TYPED_ARRAY_UINT16:
return in.readArray((uint16_t *) JS_GetUint16ArrayData(arr, context()), nelems); return in.readArray(JS_GetUint16ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_INT32: case SCTAG_TYPED_ARRAY_INT32:
return in.readArray((uint32_t *) JS_GetInt32ArrayData(arr, context()), nelems); return in.readArray((uint32_t*) JS_GetInt32ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_UINT32: case SCTAG_TYPED_ARRAY_UINT32:
return in.readArray((uint32_t *) JS_GetUint32ArrayData(arr, context()), nelems); return in.readArray(JS_GetUint32ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_FLOAT32: case SCTAG_TYPED_ARRAY_FLOAT32:
return in.readArray((uint32_t *) JS_GetFloat32ArrayData(arr, context()), nelems); return in.readArray((uint32_t*) JS_GetFloat32ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_FLOAT64: case SCTAG_TYPED_ARRAY_FLOAT64:
return in.readArray((uint64_t *) JS_GetFloat64ArrayData(arr, context()), nelems); return in.readArray((uint64_t*) JS_GetFloat64ArrayData(obj, context()), nelems);
case SCTAG_TYPED_ARRAY_UINT8_CLAMPED: case SCTAG_TYPED_ARRAY_UINT8_CLAMPED:
return in.readArray((uint8_t *) JS_GetUint8ClampedArrayData(arr, context()), nelems); return in.readArray(JS_GetUint8ClampedArrayData(obj, context()), nelems);
default: default:
JS_NOT_REACHED("unknown TypedArray type"); JS_NOT_REACHED("unknown TypedArray type");
return false; return false;

View File

@ -233,8 +233,7 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp
} }
if (obj->isTypedArray()) { if (obj->isTypedArray()) {
JSObject *tarray = TypedArray::getTypedArray(obj); *vp = Int32Value(TypedArray::getLength(obj));
*vp = Int32Value(TypedArray::getLength(tarray));
return true; return true;
} }
} }

View File

@ -811,7 +811,7 @@ inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); }
inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); } inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); }
inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); } inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }
inline bool JSObject::isString() const { return hasClass(&js::StringClass); } inline bool JSObject::isString() const { return hasClass(&js::StringClass); }
inline bool JSObject::isTypedArray() const { return IsFastTypedArrayClass(getClass()); } inline bool JSObject::isTypedArray() const { return IsTypedArrayClass(getClass()); }
inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); } inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); }
inline bool JSObject::isWith() const { return hasClass(&js::WithClass); } inline bool JSObject::isWith() const { return hasClass(&js::WithClass); }
inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); } inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); }

View File

@ -138,29 +138,27 @@ ToClampedIndex(JSContext *cx, const Value &v, int32_t length, int32_t *out)
*/ */
/** /**
* Walks up the prototype chain to find the actual ArrayBuffer data. * Walks up the prototype chain to find the actual ArrayBuffer data, if any.
* This MAY return NULL. Callers should always use isArrayBuffer()
* first.
*/ */
JSObject * static ArrayBufferObject *
ArrayBufferObject::getArrayBuffer(JSObject *obj) getArrayBuffer(JSObject *obj)
{ {
while (obj && !obj->isArrayBuffer()) while (obj && !obj->isArrayBuffer())
obj = obj->getProto(); obj = obj->getProto();
return obj; return obj ? &obj->asArrayBuffer() : NULL;
} }
JSBool JSBool
ArrayBufferObject::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) ArrayBufferObject::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
JSObject *bufobj = getArrayBuffer(obj); ArrayBufferObject *buffer = getArrayBuffer(obj);
if (!bufobj) { if (!buffer) {
vp->setInt32(0); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
return true; JSMSG_INCOMPATIBLE_PROTO, "ArrayBuffer", "byteLength", "object");
return false;
} }
ArrayBufferObject &arrayBuffer = bufobj->asArrayBuffer(); vp->setInt32(int32_t(buffer->byteLength()));
vp->setInt32(int32_t(arrayBuffer.byteLength()));
return true; return true;
} }
@ -505,7 +503,15 @@ ArrayBufferObject::obj_getProperty(JSContext *cx, JSObject *obj_,
RootedVarObject obj(cx, obj_), receiver(cx, receiver_); RootedVarObject obj(cx, obj_), receiver(cx, receiver_);
RootedVarPropertyName name(cx, name_); RootedVarPropertyName name(cx, name_);
obj = getArrayBuffer(obj); if (!(obj = getArrayBuffer(obj))) {
JSAutoByteString bs(cx, name);
if (!bs)
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INCOMPATIBLE_PROTO, "ArrayBuffer", bs.ptr(), "object");
return false;
}
if (name == cx->runtime->atomState.byteLengthAtom) { if (name == cx->runtime->atomState.byteLengthAtom) {
vp->setInt32(obj->asArrayBuffer().byteLength()); vp->setInt32(obj->asArrayBuffer().byteLength());
return true; return true;
@ -787,12 +793,15 @@ ArrayBufferObject::obj_typeOf(JSContext *cx, JSObject *obj)
* the subclasses. * the subclasses.
*/ */
JSObject * static JSObject *
TypedArray::getTypedArray(JSObject *obj) getTypedArray(JSObject *obj)
{ {
while (!obj->isTypedArray()) MOZ_ASSERT(obj);
obj = obj->getProto(); do {
if (obj->isTypedArray())
return obj; return obj;
} while ((obj = obj->getProto()));
return NULL;
} }
inline bool inline bool
@ -808,75 +817,60 @@ TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, uint32_t *ip)
return false; return false;
} }
typedef Value (* TypedArrayPropertyGetter)(JSObject *tarray);
template <TypedArrayPropertyGetter Get>
class TypedArrayGetter {
public:
static inline bool get(JSContext *cx, JSObject *obj, jsid id, Value *vp) {
do {
if (obj->isTypedArray()) {
JSObject *tarray = TypedArray::getTypedArray(obj);
if (tarray)
*vp = Get(tarray);
return true;
}
} while ((obj = obj->getProto()) != NULL);
return true;
}
};
/* /*
* For now (until slots directly hold data) * For now (until slots directly hold data)
* slots data element points to the JSObject representing the ArrayBuffer. * slots data element points to the JSObject representing the ArrayBuffer.
*/ */
inline Value
getBufferValue(JSObject *tarray)
{
JSObject *buffer = TypedArray::getBuffer(tarray);
return ObjectValue(*buffer);
}
JSBool JSBool
TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp) TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
return TypedArrayGetter<getBufferValue>::get(cx, obj, id, vp); if (!(obj = getTypedArray(obj))) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INCOMPATIBLE_PROTO, "TypedArray", "buffer", "object");
return false;
} }
inline Value JS_SET_RVAL(cx, vp, ObjectValue(*TypedArray::getBuffer(obj)));
getByteOffsetValue(JSObject *tarray) return true;
{
return Int32Value(TypedArray::getByteOffset(tarray));
} }
JSBool JSBool
TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp) TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
return TypedArrayGetter<getByteOffsetValue>::get(cx, obj, id, vp); if (!(obj = getTypedArray(obj))) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INCOMPATIBLE_PROTO, "TypedArray", "byteOffset", "object");
return false;
} }
inline Value JS_SET_RVAL(cx, vp, Int32Value(TypedArray::getByteOffset(obj)));
getByteLengthValue(JSObject *tarray) return true;
{
return Int32Value(TypedArray::getByteLength(tarray));
} }
JSBool JSBool
TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
return TypedArrayGetter<getByteLengthValue>::get(cx, obj, id, vp); if (!(obj = getTypedArray(obj))) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INCOMPATIBLE_PROTO, "TypedArray", "byteLength", "object");
return false;
} }
inline Value JS_SET_RVAL(cx, vp, Int32Value(TypedArray::getByteLength(obj)));
getLengthValue(JSObject *tarray) return true;
{
return Int32Value(TypedArray::getLength(tarray));
} }
JSBool JSBool
TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
return TypedArrayGetter<getLengthValue>::get(cx, obj, id, vp); if (!(obj = getTypedArray(obj))) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INCOMPATIBLE_PROTO, "TypedArray", "length", "object");
return false;
}
JS_SET_RVAL(cx, vp, Int32Value(TypedArray::getLength(obj)));
return true;
} }
JSBool JSBool
@ -1090,7 +1084,7 @@ class TypedArrayTemplate
static inline Class *fastClass() static inline Class *fastClass()
{ {
return &TypedArray::fastClasses[ArrayTypeID()]; return &TypedArray::classes[ArrayTypeID()];
} }
static void static void
@ -1354,7 +1348,7 @@ class TypedArrayTemplate
static JSBool static JSBool
obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict) obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
{ {
JSObject *tarray = TypedArray::getTypedArray(obj); JSObject *tarray = getTypedArray(obj);
JS_ASSERT(tarray); JS_ASSERT(tarray);
if (index < getLength(tarray)) { if (index < getLength(tarray)) {
@ -1627,56 +1621,52 @@ class TypedArrayTemplate
if (!tarray) if (!tarray)
return true; return true;
// these are the default values
int32_t off = 0;
if (args.length() > 1) {
if (!ToInt32(cx, args[1], &off))
return false;
if (off < 0 || uint32_t(off) > getLength(tarray)) {
// the given offset is bogus
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
}
uint32_t offset(off);
// first arg must be either a typed array or a JS array // first arg must be either a typed array or a JS array
if (args.length() == 0 || !args[0].isObject()) { if (args.length() == 0 || !args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
JSMSG_TYPED_ARRAY_BAD_ARGS); return false;
}
int32_t offset = 0;
if (args.length() > 1) {
if (!ToInt32(cx, args[1], &offset))
return false;
if (offset < 0 || uint32_t(offset) > getLength(tarray)) {
// the given offset is bogus
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
return false;
}
}
if (!args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false; return false;
} }
RootedVarObject arg0(cx, args[0].toObjectOrNull()); RootedVarObject arg0(cx, args[0].toObjectOrNull());
if (arg0->isTypedArray()) { RootedVarObject src(getTypedArray(arg0));
JSObject *src = TypedArray::getTypedArray(arg0); if (src) {
if (!src || if (getLength(src) > getLength(tarray) - offset) {
getLength(src) > getLength(tarray) - offset) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_TYPED_ARRAY_BAD_ARGS);
return false; return false;
} }
if (!copyFromTypedArray(cx, obj, src, offset)) if (!copyFromTypedArray(cx, obj, src, offset))
return false; return false;
} else { } else {
src = arg0;
uint32_t len; uint32_t len;
if (!js_GetLengthProperty(cx, arg0, &len)) if (!js_GetLengthProperty(cx, src, &len))
return false; return false;
// avoid overflow; we know that offset <= length // avoid overflow; we know that offset <= length
if (len > getLength(tarray) - offset) { if (len > getLength(tarray) - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
JSMSG_TYPED_ARRAY_BAD_ARGS);
return false; return false;
} }
if (!copyFromArray(cx, obj, arg0, len, offset)) if (!copyFromArray(cx, obj, src, len, offset))
return false; return false;
} }
@ -2145,10 +2135,7 @@ TypedArrayTemplate<float>::copyIndexToValue(JSContext *cx, JSObject *tarray, uin
* This could be removed for platforms/compilers known to convert a 32-bit * This could be removed for platforms/compilers known to convert a 32-bit
* non-canonical nan to a 64-bit canonical nan. * non-canonical nan to a 64-bit canonical nan.
*/ */
if (JS_UNLIKELY(MOZ_DOUBLE_IS_NaN(dval))) vp->setDouble(JS_CANONICALIZE_NAN(dval));
dval = js_NaN;
vp->setDouble(dval);
} }
template<> template<>
@ -2335,7 +2322,7 @@ NewArrayWithBuffer(JSContext *cx, HandleObject arrayBuffer, int32_t byteoffset,
{ \ { \
obj = UnwrapObject(obj); \ obj = UnwrapObject(obj); \
Class *clasp = obj->getClass(); \ Class *clasp = obj->getClass(); \
return (clasp == &TypedArray::fastClasses[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \ return (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \
} }
IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t) IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
@ -2350,7 +2337,7 @@ IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
#define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray) \ #define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray) \
{ \ { \
#_typedArray, \ #_typedArray "Prototype", \
JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
JSCLASS_HAS_PRIVATE | \ JSCLASS_HAS_PRIVATE | \
JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \
@ -2475,7 +2462,7 @@ IMPL_TYPED_ARRAY_STATICS(Float32Array);
IMPL_TYPED_ARRAY_STATICS(Float64Array); IMPL_TYPED_ARRAY_STATICS(Float64Array);
IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray); IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray);
Class TypedArray::fastClasses[TYPE_MAX] = { Class TypedArray::classes[TYPE_MAX] = {
IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array), IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array),
IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array), IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array),
IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array), IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array),
@ -2554,27 +2541,6 @@ js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
return InitArrayBufferClass(cx, global); return InitArrayBufferClass(cx, global);
} }
bool
js::IsFastTypedArrayClass(const Class *clasp)
{
return &TypedArray::fastClasses[0] <= clasp &&
clasp < &TypedArray::fastClasses[TypedArray::TYPE_MAX];
}
static inline bool
IsSlowTypedArrayClass(const Class *clasp)
{
return &TypedArray::protoClasses[0] <= clasp &&
clasp < &TypedArray::protoClasses[TypedArray::TYPE_MAX];
}
bool
js::IsFastOrSlowTypedArray(JSObject *obj)
{
Class *clasp = obj->getClass();
return IsFastTypedArrayClass(clasp) || IsSlowTypedArrayClass(clasp);
}
/* JS Friend API */ /* JS Friend API */
JS_FRIEND_API(JSBool) JS_FRIEND_API(JSBool)

View File

@ -161,9 +161,6 @@ class ArrayBufferObject : public JSObject
static JSType static JSType
obj_typeOf(JSContext *cx, JSObject *obj); obj_typeOf(JSContext *cx, JSObject *obj);
static JSObject *
getArrayBuffer(JSObject *obj);
bool bool
allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL); allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents = NULL);
@ -218,7 +215,7 @@ struct TypedArray {
}; };
// and MUST NOT be used to construct new objects. // and MUST NOT be used to construct new objects.
static Class fastClasses[TYPE_MAX]; static Class classes[TYPE_MAX];
// These are the proto/original classes, used // These are the proto/original classes, used
// fo constructing new objects // fo constructing new objects
@ -226,8 +223,6 @@ struct TypedArray {
static JSPropertySpec jsprops[]; static JSPropertySpec jsprops[];
static JSObject *getTypedArray(JSObject *obj);
static JSBool prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
@ -292,14 +287,31 @@ struct TypedArray {
static int dataOffset(); static int dataOffset();
}; };
bool inline bool
IsFastOrSlowTypedArray(JSObject *obj); IsTypedArrayClass(const Class *clasp)
{
return &TypedArray::classes[0] <= clasp &&
clasp < &TypedArray::classes[TypedArray::TYPE_MAX];
}
bool inline bool
IsFastOrSlowTypedArrayClass(const Class *clasp); IsTypedArrayProtoClass(const Class *clasp)
{
return &TypedArray::protoClasses[0] <= clasp &&
clasp < &TypedArray::protoClasses[TypedArray::TYPE_MAX];
}
bool inline bool
IsFastTypedArrayClass(const Class *clasp); IsTypedArray(JSObject *obj)
{
return IsTypedArrayClass(obj->getClass());
}
inline bool
IsTypedArrayProto(JSObject *obj)
{
return IsTypedArrayProtoClass(obj->getClass());
}
} // namespace js } // namespace js

View File

@ -83,37 +83,37 @@ ClampIntForUint8Array(int32_t x)
inline uint32_t inline uint32_t
TypedArray::getLength(JSObject *obj) { TypedArray::getLength(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_LENGTH).toInt32(); return obj->getFixedSlot(FIELD_LENGTH).toInt32();
} }
inline uint32_t inline uint32_t
TypedArray::getByteOffset(JSObject *obj) { TypedArray::getByteOffset(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_BYTEOFFSET).toInt32(); return obj->getFixedSlot(FIELD_BYTEOFFSET).toInt32();
} }
inline uint32_t inline uint32_t
TypedArray::getByteLength(JSObject *obj) { TypedArray::getByteLength(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_BYTELENGTH).toInt32(); return obj->getFixedSlot(FIELD_BYTELENGTH).toInt32();
} }
inline uint32_t inline uint32_t
TypedArray::getType(JSObject *obj) { TypedArray::getType(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_TYPE).toInt32(); return obj->getFixedSlot(FIELD_TYPE).toInt32();
} }
inline ArrayBufferObject * inline ArrayBufferObject *
TypedArray::getBuffer(JSObject *obj) { TypedArray::getBuffer(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return &obj->getFixedSlot(FIELD_BUFFER).toObject().asArrayBuffer(); return &obj->getFixedSlot(FIELD_BUFFER).toObject().asArrayBuffer();
} }
inline void * inline void *
TypedArray::getDataOffset(JSObject *obj) { TypedArray::getDataOffset(JSObject *obj) {
JS_ASSERT(IsFastOrSlowTypedArray(obj)); JS_ASSERT(obj->isTypedArray());
return (void *)obj->getPrivate(NUM_FIXED_SLOTS); return (void *)obj->getPrivate(NUM_FIXED_SLOTS);
} }

View File

@ -2319,17 +2319,16 @@ GetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid i
? Int32Key::FromConstant(v.toInt32()) ? Int32Key::FromConstant(v.toInt32())
: Int32Key::FromRegister(idRemat.dataReg()); : Int32Key::FromRegister(idRemat.dataReg());
JSObject *tarray = js::TypedArray::getTypedArray(obj);
if (!masm.supportsFloatingPoint() && if (!masm.supportsFloatingPoint() &&
(TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32 || (TypedArray::getType(obj) == TypedArray::TYPE_FLOAT32 ||
TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT64 || TypedArray::getType(obj) == TypedArray::TYPE_FLOAT64 ||
TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT32)) TypedArray::getType(obj) == TypedArray::TYPE_UINT32))
{ {
return disable(f, "fpu not supported"); return disable(f, "fpu not supported");
} }
MaybeRegisterID tempReg; MaybeRegisterID tempReg;
masm.loadFromTypedArray(TypedArray::getType(tarray), objReg, key, typeReg, objReg, tempReg); masm.loadFromTypedArray(TypedArray::getType(obj), objReg, key, typeReg, objReg, tempReg);
Jump done = masm.jump(); Jump done = masm.jump();
@ -2633,18 +2632,17 @@ SetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, int32_t key)
// Load the array's packed data vector. // Load the array's packed data vector.
masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg); masm.loadPtr(Address(objReg, TypedArray::dataOffset()), objReg);
JSObject *tarray = js::TypedArray::getTypedArray(obj);
if (!masm.supportsFloatingPoint() && if (!masm.supportsFloatingPoint() &&
(TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32 || (TypedArray::getType(obj) == TypedArray::TYPE_FLOAT32 ||
TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT64)) TypedArray::getType(obj) == TypedArray::TYPE_FLOAT64))
{ {
return disable(f, "fpu not supported"); return disable(f, "fpu not supported");
} }
int shift = js::TypedArray::slotWidth(obj); int shift = TypedArray::slotWidth(obj);
if (hasConstantKey) { if (hasConstantKey) {
Address addr(objReg, keyValue * shift); Address addr(objReg, keyValue * shift);
if (!StoreToTypedArray(cx, masm, tarray, addr, vr, volatileMask)) if (!StoreToTypedArray(cx, masm, obj, addr, vr, volatileMask))
return error(cx); return error(cx);
} else { } else {
Assembler::Scale scale = Assembler::TimesOne; Assembler::Scale scale = Assembler::TimesOne;
@ -2660,7 +2658,7 @@ SetElementIC::attachTypedArray(VMFrame &f, JSObject *obj, int32_t key)
break; break;
} }
BaseIndex addr(objReg, keyReg, scale); BaseIndex addr(objReg, keyReg, scale);
if (!StoreToTypedArray(cx, masm, tarray, addr, vr, volatileMask)) if (!StoreToTypedArray(cx, masm, obj, addr, vr, volatileMask))
return error(cx); return error(cx);
} }

View File

@ -3409,17 +3409,16 @@ Deserialize(JSContext *cx, unsigned argc, jsval *vp)
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize"); JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
return false; return false;
} }
JSObject *array = TypedArray::getTypedArray(obj); if ((TypedArray::getByteLength(obj) & 7) != 0) {
if ((TypedArray::getByteLength(array) & 7) != 0) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize"); JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "deserialize");
return false; return false;
} }
if ((uintptr_t(TypedArray::getDataOffset(array)) & 7) != 0) { if ((uintptr_t(TypedArray::getDataOffset(obj)) & 7) != 0) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT); JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_BAD_ALIGNMENT);
return false; return false;
} }
if (!JS_ReadStructuredClone(cx, (uint64_t *) TypedArray::getDataOffset(array), TypedArray::getByteLength(array), if (!JS_ReadStructuredClone(cx, (uint64_t *) TypedArray::getDataOffset(obj), TypedArray::getByteLength(obj),
JS_STRUCTURED_CLONE_VERSION, v.address(), NULL, NULL)) { JS_STRUCTURED_CLONE_VERSION, v.address(), NULL, NULL)) {
return false; return false;
} }

View File

@ -28,7 +28,7 @@ function test()
var TODO = 1; var TODO = 1;
function check(fun, todo) { function check(fun, msg, todo) {
var thrown = null; var thrown = null;
var success = false; var success = false;
try { try {
@ -60,6 +60,8 @@ function test()
var ex = new Error; var ex = new Error;
print ("=== FAILED ==="); print ("=== FAILED ===");
if (msg)
print (msg);
print (ex.stack); print (ex.stack);
if (thrown) { if (thrown) {
print (" threw exception:"); print (" threw exception:");
@ -69,18 +71,29 @@ function test()
} }
} }
function checkThrows(fun, todo) { function checkThrows(fun, type, todo) {
let thrown = false; var thrown = false;
try { try {
fun(); fun();
} catch (x) { } catch (x) {
thrown = true; thrown = x;
} }
check(function() thrown, todo); if (typeof(type) !== 'undefined')
if (thrown) {
check(function () thrown instanceof type,
"expected " + type.name + " but saw " + thrown,
todo);
} else {
check(function () thrown, "expected " + type.name + " but no exception thrown", todo);
}
else
check(function () thrown, undefined, todo);
} }
check(function() ArrayBuffer.prototype.byteLength == 0); function checkThrowsTODO(fun, type) {
checkThrows(fun, type, true);
}
var buf, buf2; var buf, buf2;
@ -101,9 +114,9 @@ function test()
var zerobuf2 = new ArrayBuffer(); var zerobuf2 = new ArrayBuffer();
check(function() zerobuf2.byteLength == 0); check(function() zerobuf2.byteLength == 0);
checkThrows(function() new ArrayBuffer(-100)); checkThrows(function() new ArrayBuffer(-100), RangeError);
// this is using js_ValueToECMAUInt32, which is giving 0 for "abc" // this is using js_ValueToECMAUInt32, which is giving 0 for "abc"
checkThrows(function() new ArrayBuffer("abc"), TODO); checkThrowsTODO(function() new ArrayBuffer("abc"), TypeError);
var zeroarray = new Int32Array(0); var zeroarray = new Int32Array(0);
check(function() zeroarray.length == 0); check(function() zeroarray.length == 0);
@ -187,15 +200,15 @@ function test()
check(function() a[1] == 0xbb); check(function() a[1] == 0xbb);
// not sure if this is supposed to throw or to treat "foo"" as 0. // not sure if this is supposed to throw or to treat "foo"" as 0.
checkThrows(function() new Int32Array([0xaa, "foo", 0xbb]), TODO); checkThrowsTODO(function() new Int32Array([0xaa, "foo", 0xbb]), Error);
checkThrows(function() new Int32Array(-100)); checkThrows(function() new Int32Array(-100));
a = new Uint8Array(3); a = new Uint8Array(3);
// XXX these are ignored now and return undefined // XXX these are ignored now and return undefined
//checkThrows(function() a[5000] = 0); //checkThrows(function() a[5000] = 0, RangeError);
//checkThrows(function() a["hello"] = 0); //checkThrows(function() a["hello"] = 0, TypeError);
//checkThrows(function() a[-10] = 0); //checkThrows(function() a[-10] = 0, RangeError);
check(function() (a[0] = "10") && (a[0] == 10)); check(function() (a[0] = "10") && (a[0] == 10));
// check Uint8ClampedArray, which is an extension to this extension // check Uint8ClampedArray, which is an extension to this extension
@ -265,8 +278,8 @@ function test()
checkThrows(function() a.set([1,2,3], 2147483647)); checkThrows(function() a.set([1,2,3], 2147483647));
a.set(ArrayBuffer.prototype); a.set(ArrayBuffer.prototype);
a.set(Int16Array.prototype); checkThrows(function () a.set(Int16Array.prototype), TypeError);
a.set(Int32Array.prototype); checkThrows(function () a.set(Int32Array.prototype), TypeError);
a.set([1,2,3]); a.set([1,2,3]);
a.set([4,5,6], 3); a.set([4,5,6], 3);
@ -317,16 +330,43 @@ function test()
a = new Uint8Array(0x100); a = new Uint8Array(0x100);
checkThrows(function() Uint32Array.prototype.subarray.apply(a, [0, 0x100])); checkThrows(function() Uint32Array.prototype.subarray.apply(a, [0, 0x100]));
// The prototypes are objects that don't have a length property, so they act // webidl section 4.4.6, getter bullet point 2.2: prototypes are not
// like empty arrays. // platform objects, and calling the getter of any attribute defined on the
check(function() new Int32Array(ArrayBuffer.prototype).length == 0); // interface should throw a TypeError according to
check(function() new Int32Array(Int32Array.prototype).length == 0); checkThrows(function() ArrayBuffer.prototype.byteLength, TypeError);
check(function() new Int32Array(Float64Array.prototype).length == 0); checkThrows(function() Int32Array.prototype.length, TypeError);
checkThrows(function() Int32Array.prototype.byteLength, TypeError);
checkThrows(function() Int32Array.prototype.byteOffset, TypeError);
checkThrows(function() Float64Array.prototype.length, TypeError);
checkThrows(function() Float64Array.prototype.byteLength, TypeError);
checkThrows(function() Float64Array.prototype.byteOffset, TypeError);
// ArrayBuffer, Int32Array and Float64Array are native functions and have a .length // webidl 4.4.6: a readonly attribute's setter is undefined. From
// checkThrows(function() new Int32Array(ArrayBuffer)); // observation, that seems to mean it silently does nothing, and returns
// checkThrows(function() new Int32Array(Int32Array)); // the value that you tried to set it to.
// checkThrows(function() new Int32Array(Float64Array)); check(function() Int32Array.prototype.length = true);
check(function() Float64Array.prototype.length = true);
check(function() Int32Array.prototype.byteLength = true);
check(function() Float64Array.prototype.byteLength = true);
check(function() Int32Array.prototype.byteOffset = true);
check(function() Float64Array.prototype.byteOffset = true);
// ArrayBuffer, Int32Array and Float64Array are native functions and have a
// .length, so none of these should throw:
check(function() (new Int32Array(ArrayBuffer)).length >= 0);
check(function() (new Int32Array(Int32Array)).length >= 0);
check(function() (new Int32Array(Float64Array)).length >= 0);
// webidl 4.4.6, under getters: "The value of the Function objects
// 'length' property is the Number value 0"
//
// Except this fails in getOwnPropertyDescriptor, I think because
// Int32Array.prototype does not provide a lookup hook, and the fallback
// case ends up calling the getter. Which seems odd to me, but much of this
// stuff baffles me. It does seem strange that there's no way to do
// getOwnPropertyDescriptor on any of these attributes.
//
//check(Object.getOwnPropertyDescriptor(Int32Array.prototype, 'byteOffset')['get'].length == 0);
check(function() Int32Array.BYTES_PER_ELEMENT == 4); check(function() Int32Array.BYTES_PER_ELEMENT == 4);
check(function() (new Int32Array(4)).BYTES_PER_ELEMENT == 4); check(function() (new Int32Array(4)).BYTES_PER_ELEMENT == 4);