Bug 575688 - Implement DataView from Typed Arrays spec. r=Waldo

--HG--
extra : rebase_source : b351000320c910c905be386cf71a9933cfe01a0f
This commit is contained in:
Steve Fink 2012-03-25 19:14:27 -07:00
parent 7c9f81a176
commit 0aa674568a
12 changed files with 2502 additions and 35 deletions

View File

@ -147,7 +147,7 @@ MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}")
MSG_DEF(JSMSG_TOO_MANY_FUN_APPLY_ARGS, 61, 0, JSEXN_RANGEERR, "arguments array passed to Function.prototype.apply is too large")
MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression")
MSG_DEF(JSMSG_TOO_BIG_TO_ENCODE, 63, 0, JSEXN_INTERNALERR, "data are to big to encode")
MSG_DEF(JSMSG_UNUSED64, 64, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE, 64, 1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
MSG_DEF(JSMSG_UNUSED65, 65, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_UNUSED66, 66, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_UNUSED67, 67, 0, JSEXN_NONE, "")

View File

@ -1862,6 +1862,7 @@ static JSStdName standard_class_names[] = {
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Float64Array), TYPED_ARRAY_CLASP(TYPE_FLOAT64)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray),
TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)},
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(DataView), &DataViewClass},
{js_InitWeakMapClass, EAGER_ATOM_AND_CLASP(WeakMap)},
{js_InitProxyClass, EAGER_ATOM_AND_CLASP(Proxy)},

View File

@ -3733,7 +3733,7 @@ struct JSClass {
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 10)
#define JSCLASS_CACHED_PROTO_WIDTH 6
#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH)
#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT)
#define JSCLASS_HAS_CACHED_PROTO(key) (uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT)
#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \
(((clasp)->flags \
>> JSCLASS_CACHED_PROTO_SHIFT) \

View File

@ -973,6 +973,16 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes);
extern JS_FRIEND_API(JSBool)
JS_IsTypedArrayObject(JSObject *obj, JSContext *cx);
/*
* Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
* return false if a security wrapper is encountered that denies the
* unwrapping. If this test or one of the more specific tests succeeds, then it
* is safe to call the various ArrayBufferView accessor JSAPI calls defined
* below. cx MUST be non-NULL and valid.
*/
extern JS_FRIEND_API(JSBool)
JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx);
/*
* Test for specific typed array types (ArrayBufferView subtypes)
*/
@ -1124,4 +1134,46 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx);
extern JS_FRIEND_API(void *)
JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx);
/*
* Check whether obj supports JS_GetDataView* APIs. Note that this may fail and
* throw an exception if a security wrapper is encountered that denies the
* operation.
*/
JS_FRIEND_API(JSBool)
JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView);
/*
* Return the byte offset of a data view into its array buffer. |obj| must be a
* DataView.
*
* |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
* it would pass such a test: it is a data view or a wrapper of a data view,
* and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
* unable to assert when unwrapping should be disallowed.
*/
JS_FRIEND_API(uint32_t)
JS_GetDataViewByteOffset(JSObject *obj, JSContext *cx);
/*
* Return the byte length of a data view.
*
* |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
* it would pass such a test: it is a data view or a wrapper of a data view,
* and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
* unable to assert when unwrapping should be disallowed.
*/
JS_FRIEND_API(uint32_t)
JS_GetDataViewByteLength(JSObject *obj, JSContext *cx);
/*
* Return a pointer to the beginning of the data referenced by a DataView.
*
* |obj| must have passed a JS_IsDataViewObject test, or somehow be known that
* it would pass such a test: it is a data view or a wrapper of a data view,
* and the unwrapping will succeed. If cx is NULL, then DEBUG builds may be
* unable to assert when unwrapping should be disallowed.
*/
JS_FRIEND_API(void *)
JS_GetDataViewData(JSObject *obj, JSContext *cx);
#endif /* jsfriendapi_h___ */

View File

@ -1663,9 +1663,7 @@ GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, Value *vp
NewPropertyDescriptorObject(cx, &desc, vp);
}
}
static bool
bool
GetFirstArgumentAsObject(JSContext *cx, unsigned argc, Value *vp, const char *method, JSObject **objp)
{
if (argc == 0) {
@ -1689,6 +1687,8 @@ GetFirstArgumentAsObject(JSContext *cx, unsigned argc, Value *vp, const char *me
return true;
}
} /* namespace js */
static JSBool
obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -233,6 +233,7 @@ extern Class ArrayBufferClass;
extern Class BlockClass;
extern Class BooleanClass;
extern Class CallableObjectClass;
extern Class DataViewClass;
extern Class DateClass;
extern Class ErrorClass;
extern Class ElementIteratorClass;
@ -259,6 +260,7 @@ class ArrayBufferObject;
class BlockObject;
class BooleanObject;
class ClonedBlockObject;
class DataViewObject;
class DeclEnvObject;
class ElementIteratorObject;
class GlobalObject;
@ -915,6 +917,7 @@ struct JSObject : public js::ObjectImpl
/* Direct subtypes of JSObject: */
inline bool isArguments() const;
inline bool isArrayBuffer() const;
inline bool isDataView() const;
inline bool isDate() const;
inline bool isElementIterator() const;
inline bool isError() const;
@ -967,6 +970,7 @@ struct JSObject : public js::ObjectImpl
inline js::BooleanObject &asBoolean();
inline js::CallObject &asCall();
inline js::ClonedBlockObject &asClonedBlock();
inline js::DataViewObject &asDataView();
inline js::DeclEnvObject &asDeclEnv();
inline js::GlobalObject &asGlobal();
inline js::NestedScopeObject &asNestedScope();
@ -1427,6 +1431,9 @@ InformalValueTypeName(const Value &v);
inline void
DestroyIdArray(FreeOp *fop, JSIdArray *ida);
extern bool
GetFirstArgumentAsObject(JSContext *cx, unsigned argc, Value *vp, const char *method, JSObject **objp);
/* Helpers for throwing. These always return false. */
extern bool
Throw(JSContext *cx, jsid id, unsigned errorNumber);

View File

@ -790,6 +790,7 @@ inline bool JSObject::isBlock() const { return hasClass(&js::BlockClass); }
inline bool JSObject::isBoolean() const { return hasClass(&js::BooleanClass); }
inline bool JSObject::isCall() const { return hasClass(&js::CallClass); }
inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); }
inline bool JSObject::isDataView() const { return hasClass(&js::DataViewClass); }
inline bool JSObject::isDate() const { return hasClass(&js::DateClass); }
inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); }
inline bool JSObject::isElementIterator() const { return hasClass(&js::ElementIteratorClass); }

View File

@ -94,6 +94,7 @@ JS_PROTO(AnyName, 35, js_InitNullClass)
JS_PROTO(WeakMap, 36, js_InitWeakMapClass)
JS_PROTO(Map, 37, js_InitMapClass)
JS_PROTO(Set, 38, js_InitSetClass)
JS_PROTO(DataView, 39, js_InitTypedArrayClasses)
#undef XML_INIT
#undef NAMESPACE_INIT

View File

@ -40,8 +40,6 @@
#include <string.h>
#include "mozilla/FloatingPoint.h"
#include "mozilla/Util.h"
#include "jstypes.h"
#include "jsutil.h"
#include "jshash.h"
@ -51,6 +49,7 @@
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jscpucfg.h"
#include "jsversion.h"
#include "jsgc.h"
#include "jsinterp.h"
@ -60,6 +59,7 @@
#include "jstypedarray.h"
#include "gc/Marking.h"
#include "mozilla/Util.h"
#include "vm/GlobalObject.h"
#include "vm/NumericConversions.h"
@ -817,6 +817,13 @@ TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, uint32_t *ip)
return false;
}
bool
js::IsDataView(JSObject* obj)
{
JS_ASSERT(obj);
return obj->isDataView();
}
/*
* For now (until slots directly hold data)
* slots data element points to the JSObject representing the ArrayBuffer.
@ -1190,7 +1197,32 @@ class TypedArrayTemplate
}
static bool
setElementTail(JSContext *cx, HandleObject tarray, uint32_t index, Value *vp, JSBool strict)
toDoubleForTypedArray(JSContext *cx, Value *vp, double *d)
{
if (vp->isDouble()) {
*d = vp->toDouble();
} else if (vp->isNull()) {
*d = 0.0;
} else if (vp->isPrimitive()) {
JS_ASSERT(vp->isString() || vp->isUndefined() || vp->isBoolean());
if (vp->isString()) {
if (!ToNumber(cx, *vp, d))
return false;
} else if (vp->isUndefined()) {
*d = js_NaN;
} else {
*d = double(vp->toBoolean());
}
} else {
// non-primitive assignments become NaN or 0 (for float/int arrays)
*d = js_NaN;
}
return true;
}
static bool
setElementTail(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp, JSBool strict)
{
JS_ASSERT(tarray);
JS_ASSERT(index < getLength(tarray));
@ -1201,23 +1233,8 @@ class TypedArrayTemplate
}
double d;
if (vp->isDouble()) {
d = vp->toDouble();
} else if (vp->isNull()) {
d = 0.0;
} else if (vp->isPrimitive()) {
JS_ASSERT(vp->isString() || vp->isUndefined() || vp->isBoolean());
if (vp->isString()) {
JS_ALWAYS_TRUE(ToNumber(cx, *vp, &d));
} else if (vp->isUndefined()) {
d = js_NaN;
} else {
d = double(vp->toBoolean());
}
} else {
// non-primitive assignments become NaN or 0 (for float/int arrays)
d = js_NaN;
}
if (!toDoubleForTypedArray(cx, vp, &d))
return false;
// If the array is an integer array, we only handle up to
// 32-bit ints from this point on. if we want to handle
@ -1645,7 +1662,7 @@ class TypedArrayTemplate
}
RootedVarObject arg0(cx, args[0].toObjectOrNull());
RootedVarObject src(getTypedArray(arg0));
RootedVarObject src(cx, getTypedArray(arg0));
if (src) {
if (getLength(src) > getLength(tarray) - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
@ -2151,10 +2168,552 @@ TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, JSObject *tarray, ui
* confuse the engine into interpreting a double-typed jsval as an
* object-typed jsval.
*/
if (JS_UNLIKELY(MOZ_DOUBLE_IS_NaN(val)))
val = js_NaN;
vp->setDouble(JS_CANONICALIZE_NAN(val));
}
vp->setDouble(val);
JSBool
DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *bufobj;
if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj))
return false;
if (!bufobj->isArrayBuffer()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE,
"DataView", "ArrayBuffer", bufobj->getClass()->name);
return false;
}
RootedVar<ArrayBufferObject*> buffer(cx, &bufobj->asArrayBuffer());
uint32_t bufferLength = buffer->byteLength();
uint32_t byteOffset = 0;
uint32_t byteLength = bufferLength;
if (args.length() > 1) {
if (!ToUint32(cx, args[1], &byteOffset))
return false;
if (byteOffset > INT32_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return false;
}
if (args.length() > 2) {
if (!ToUint32(cx, args[2], &byteLength))
return false;
if (byteLength > INT32_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
return false;
}
} else {
if (byteOffset > bufferLength) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return false;
}
byteLength = bufferLength - byteOffset;
}
}
/* The sum of these cannot overflow a uint32_t */
JS_ASSERT(byteOffset <= INT32_MAX);
JS_ASSERT(byteLength <= INT32_MAX);
if (byteOffset + byteLength > bufferLength) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return false;
}
JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer);
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
JSBool
DataViewObject::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isDataView());
DataViewObject &view = obj->asDataView();
if (view.hasBuffer())
vp->setObject(view.arrayBuffer());
else
vp->setUndefined();
return true;
}
JSBool
DataViewObject::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isDataView());
DataViewObject &view = obj->asDataView();
vp->setInt32(view.byteOffset());
return true;
}
JSBool
DataViewObject::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isDataView());
DataViewObject &view = obj->asDataView();
vp->setInt32(view.byteLength());
return true;
}
bool
DataViewObject::getDataPointer(JSContext *cx, CallArgs args, size_t typeSize, uint8_t **data)
{
uint32_t offset;
JS_ASSERT(args.length() > 0);
if (!ToUint32(cx, args[0], &offset))
return false;
if (offset > UINT32_MAX - typeSize || offset + typeSize > byteLength()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return false;
}
*data = static_cast<uint8_t*>(dataPointer()) + offset;
return true;
}
static inline bool
needToSwapBytes(bool littleEndian)
{
#if IS_LITTLE_ENDIAN
return !littleEndian;
#else
return littleEndian;
#endif
}
static inline uint8_t
swapBytes(uint8_t x)
{
return x;
}
static inline uint16_t
swapBytes(uint16_t x)
{
return ((x & 0xff) << 8) | (x >> 8);
}
static inline uint32_t
swapBytes(uint32_t x)
{
return ((x & 0xff) << 24) |
((x & 0xff00) << 8) |
((x & 0xff0000) >> 8) |
((x & 0xff000000) >> 24);
}
static inline uint64_t
swapBytes(uint64_t x)
{
uint32_t a = x & UINT32_MAX;
uint32_t b = x >> 32;
return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
}
template <typename DataType> struct DataToRepType { typedef DataType result; };
template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
template <> struct DataToRepType<int16_t> { typedef uint16_t result; };
template <> struct DataToRepType<uint16_t> { typedef uint16_t result; };
template <> struct DataToRepType<int32_t> { typedef uint32_t result; };
template <> struct DataToRepType<uint32_t> { typedef uint32_t result; };
template <> struct DataToRepType<float> { typedef uint32_t result; };
template <> struct DataToRepType<double> { typedef uint64_t result; };
template <typename DataType>
struct DataViewIO
{
typedef typename DataToRepType<DataType>::result ReadWriteType;
static void fromBuffer(DataType *dest, const uint8_t *unalignedBuffer, bool wantSwap)
{
JS_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(JS_ALIGN_OF_POINTER, sizeof(DataType)) - 1)) == 0);
memcpy((void *) dest, unalignedBuffer, sizeof(ReadWriteType));
if (wantSwap) {
ReadWriteType *rwDest = reinterpret_cast<ReadWriteType *>(dest);
*rwDest = swapBytes(*rwDest);
}
}
static void toBuffer(uint8_t *unalignedBuffer, const DataType *src, bool wantSwap)
{
JS_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(JS_ALIGN_OF_POINTER, sizeof(DataType)) - 1)) == 0);
ReadWriteType temp = *reinterpret_cast<const ReadWriteType *>(src);
if (wantSwap)
temp = swapBytes(temp);
memcpy(unalignedBuffer, (void *) &temp, sizeof(ReadWriteType));
}
};
template<typename NativeType>
bool
DataViewObject::read(JSContext *cx, CallArgs &args, NativeType *val, const char *method)
{
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_MORE_ARGS_NEEDED, method, "0", "s");
return false;
}
uint8_t *data;
if (!getDataPointer(cx, args, sizeof(NativeType), &data))
return false;
bool fromLittleEndian = args.length() >= 2 && js_ValueToBoolean(args[1]);
DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian));
return true;
}
template <typename NativeType>
static inline bool
WebIDLCast(JSContext *cx, const Value &value, NativeType *out)
{
int32_t temp;
if (!ToInt32(cx, value, &temp))
return false;
// Technically, the behavior of assigning an out of range value to a signed
// variable is undefined. In practice, compilers seem to do what we want
// without issuing any warnings.
*out = static_cast<NativeType>(temp);
return true;
}
template <>
inline bool
WebIDLCast<float>(JSContext *cx, const Value &value, float *out)
{
double temp;
if (!ToNumber(cx, value, &temp))
return false;
*out = static_cast<float>(temp);
return true;
}
template <>
inline bool
WebIDLCast<double>(JSContext *cx, const Value &value, double *out)
{
return ToNumber(cx, value, out);
}
template<typename NativeType>
bool
DataViewObject::write(JSContext *cx, CallArgs &args, const char *method)
{
if (args.length() < 2) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_MORE_ARGS_NEEDED, method, "1", "");
return false;
}
uint8_t *data;
if (!getDataPointer(cx, args, sizeof(NativeType), &data))
return false;
NativeType value;
if (!WebIDLCast(cx, args[1], &value))
return false;
bool toLittleEndian = args.length() >= 3 && js_ValueToBoolean(args[2]);
DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian));
return true;
}
inline JSObject *
NonGenericProtoSearchingMethodGuard(JSContext *cx, CallArgs args, Native native, Class *clasp, bool *ok)
{
const Value &thisv = args.thisv();
if (thisv.isObject()) {
JSObject *obj = thisv.toObjectOrNull();
while (obj) {
if (obj->getClass() == clasp) {
*ok = true; /* quell gcc overwarning */
return obj;
}
obj = obj->getProto();
}
}
*ok = HandleNonGenericMethodClassMismatch(cx, args, native, clasp);
return NULL;
}
JSBool
DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getInt8, &DataViewClass, &ok);
if (!obj)
return ok;
int8_t val;
if (!obj->asDataView().read(cx, args, &val, "getInt8"))
return false;
args.rval().setInt32(val);
return true;
}
JSBool
DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getUint8, &DataViewClass, &ok);
if (!obj)
return ok;
uint8_t val;
if (!obj->asDataView().read(cx, args, &val, "getUint8"))
return false;
args.rval().setInt32(val);
return true;
}
JSBool
DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getInt16, &DataViewClass, &ok);
if (!obj)
return ok;
int16_t val;
if (!obj->asDataView().read(cx, args, &val, "getInt16"))
return false;
args.rval().setInt32(val);
return true;
}
JSBool
DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getUint16, &DataViewClass, &ok);
if (!obj)
return ok;
uint16_t val;
if (!obj->asDataView().read(cx, args, &val, "getUint16"))
return false;
args.rval().setInt32(val);
return true;
}
JSBool
DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getInt32, &DataViewClass, &ok);
if (!obj)
return ok;
int32_t val;
if (!obj->asDataView().read(cx, args, &val, "getInt32"))
return false;
args.rval().setInt32(val);
return true;
}
JSBool
DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getUint32, &DataViewClass, &ok);
if (!obj)
return ok;
uint32_t val;
if (!obj->asDataView().read(cx, args, &val, "getUint32"))
return false;
args.rval().setNumber(val);
return true;
}
JSBool
DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getFloat32, &DataViewClass, &ok);
if (!obj)
return ok;
float val;
if (!obj->asDataView().read(cx, args, &val, "getFloat32"))
return false;
args.rval().setDouble(JS_CANONICALIZE_NAN(val));
return true;
}
JSBool
DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_getFloat64, &DataViewClass, &ok);
if (!obj)
return ok;
double val;
if (!obj->asDataView().read(cx, args, &val, "getFloat64"))
return false;
args.rval().setDouble(JS_CANONICALIZE_NAN(val));
return true;
}
JSBool
DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setInt8, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<int8_t>(cx, args, "setInt8"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setUint8, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<uint8_t>(cx, args, "setUint8"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setInt16, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<int16_t>(cx, args, "setInt16"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setUint16, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<uint16_t>(cx, args, "setUint16"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setInt32, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<int32_t>(cx, args, "setInt32"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setUint32, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<uint32_t>(cx, args, "setUint32"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setFloat32, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<float>(cx, args, "setFloat32"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool ok;
JSObject *obj = NonGenericProtoSearchingMethodGuard(cx, args, fun_setFloat64, &DataViewClass, &ok);
if (!obj)
return ok;
if (!obj->asDataView().write<double>(cx, args, "setFloat64"))
return false;
args.rval().setUndefined();
return true;
}
/***
@ -2511,6 +3070,93 @@ InitArrayBufferClass(JSContext *cx, Handle<GlobalObject*> global)
return arrayBufferProto;
}
Class js::DataViewClass = {
"DataView",
JSCLASS_HAS_PRIVATE |
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_DataView),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
NULL, /* trace */
JS_NULL_CLASS_EXT,
JS_NULL_OBJECT_OPS
};
JSPropertySpec DataViewObject::jsprops[] = {
{ "byteLength",
-1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
DataViewObject::prop_getByteLength, JS_StrictPropertyStub },
{ "byteOffset",
-1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
DataViewObject::prop_getByteOffset, JS_StrictPropertyStub },
{ "buffer",
-1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
DataViewObject::prop_getBuffer, JS_StrictPropertyStub },
{0,0,0,0,0}
};
JSFunctionSpec DataViewObject::jsfuncs[] = {
JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0),
JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0),
JS_FN("getInt16", DataViewObject::fun_getInt16, 2,0),
JS_FN("getUint16", DataViewObject::fun_getUint16, 2,0),
JS_FN("getInt32", DataViewObject::fun_getInt32, 2,0),
JS_FN("getUint32", DataViewObject::fun_getUint32, 2,0),
JS_FN("getFloat32", DataViewObject::fun_getFloat32, 2,0),
JS_FN("getFloat64", DataViewObject::fun_getFloat64, 2,0),
JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0),
JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0),
JS_FN("setInt16", DataViewObject::fun_setInt16, 3,0),
JS_FN("setUint16", DataViewObject::fun_setUint16, 3,0),
JS_FN("setInt32", DataViewObject::fun_setInt32, 3,0),
JS_FN("setUint32", DataViewObject::fun_setUint32, 3,0),
JS_FN("setFloat32", DataViewObject::fun_setFloat32, 3,0),
JS_FN("setFloat64", DataViewObject::fun_setFloat64, 3,0),
JS_FS_END
};
JSObject *
DataViewObject::initClass(JSContext *cx, GlobalObject *global)
{
JSObject *proto = global->createBlankPrototype(cx, &DataViewClass);
if (!proto)
return NULL;
DataViewObject &dvobj = proto->asDataView();
dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(0));
dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(0));
dvobj.setFixedSlot(BUFFER_SLOT, JSVAL_VOID);
dvobj.setPrivate(NULL);
JSFunction *ctor =
global->createConstructor(cx, DataViewObject::class_constructor,
CLASS_ATOM(cx, DataView), 3);
if (!ctor)
return NULL;
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return NULL;
if (!DefinePropertiesAndBrand(cx, proto, DataViewObject::jsprops, DataViewObject::jsfuncs))
return NULL;
if (!DefineConstructorAndPrototype(cx, global, JSProto_DataView, ctor, proto))
return NULL;
return proto;
}
JSObject *
js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
{
@ -2533,7 +3179,8 @@ js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
!InitTypedArrayClass<Uint32Array>(cx, global) ||
!InitTypedArrayClass<Float32Array>(cx, global) ||
!InitTypedArrayClass<Float64Array>(cx, global) ||
!InitTypedArrayClass<Uint8ClampedArray>(cx, global))
!InitTypedArrayClass<Uint8ClampedArray>(cx, global) ||
!DataViewObject::initClass(cx, global))
{
return NULL;
}
@ -2561,7 +3208,7 @@ JS_FRIEND_API(JSBool)
JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
return obj->isTypedArray();
return obj->isTypedArray() || obj->isDataView();
}
JS_FRIEND_API(uint32_t)
@ -2698,18 +3345,49 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx)
return static_cast<double *>(TypedArray::getDataOffset(obj));
}
JS_FRIEND_API(JSBool)
JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView)
{
obj = UnwrapObject(obj);
*isDataView = obj->isDataView();
return true;
}
JS_FRIEND_API(uint32_t)
JS_GetDataViewByteOffset(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
return obj->asDataView().byteOffset();
}
JS_FRIEND_API(void *)
JS_GetDataViewData(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isDataView());
return obj->asDataView().dataPointer();
}
JS_FRIEND_API(uint32_t)
JS_GetDataViewByteLength(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isDataView());
return obj->asDataView().byteLength();
}
JS_FRIEND_API(void *)
JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isTypedArray());
return TypedArray::getDataOffset(obj);
JS_ASSERT(obj->isTypedArray() || obj->isDataView());
return obj->isDataView() ? obj->asDataView().dataPointer() : TypedArray::getDataOffset(obj);
}
JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx)
{
obj = UnwrapObject(obj);
JS_ASSERT(obj->isTypedArray());
return obj->getSlot(TypedArray::FIELD_BYTELENGTH).toInt32();
JS_ASSERT(obj->isTypedArray() || obj->isDataView());
return obj->isDataView() ? obj->asDataView().byteLength() : TypedArray::getByteLength(obj);
}

View File

@ -42,6 +42,7 @@
#include "jsapi.h"
#include "jsclass.h"
#include "jsobj.h"
#include "gc/Barrier.h"
@ -313,6 +314,59 @@ IsTypedArrayProto(JSObject *obj)
return IsTypedArrayProtoClass(obj->getClass());
}
class DataViewObject : public JSObject
{
static const size_t BYTEOFFSET_SLOT = 0;
static const size_t BYTELENGTH_SLOT = 1;
static const size_t BUFFER_SLOT = 2;
public:
static const size_t RESERVED_SLOTS = 3;
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_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
static JSBool class_constructor(JSContext *cx, unsigned argc, Value *vp);
static inline DataViewObject *
create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, Handle<ArrayBufferObject*> arrayBuffer);
static JSBool fun_getInt8(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getUint8(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getInt16(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getUint16(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getInt32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getUint32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getFloat32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_getFloat64(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setInt8(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setUint8(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setInt16(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setUint16(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setInt32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setUint32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setFloat32(JSContext *cx, unsigned argc, Value *vp);
static JSBool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp);
inline uint32_t byteLength();
inline uint32_t byteOffset();
inline JSObject & arrayBuffer();
inline void *dataPointer();
inline bool hasBuffer() const;
static JSObject *initClass(JSContext *cx, GlobalObject *global);
bool getDataPointer(JSContext *cx, CallArgs args, size_t typeSize, uint8_t **data);
template<typename NativeType>
bool read(JSContext *cx, CallArgs &args, NativeType *val, const char *method);
template<typename NativeType>
bool write(JSContext *cx, CallArgs &args, const char *method);
private:
static JSPropertySpec jsprops[];
static JSFunctionSpec jsfuncs[];
};
bool
IsDataView(JSObject *obj);
} // namespace js
#endif /* jstypedarray_h */

View File

@ -42,6 +42,9 @@
#include "jsapi.h"
#include "jsobj.h"
#include "jstypedarray.h"
#include "jsobjinlines.h"
inline uint32_t
js::ArrayBufferObject::byteLength() const
@ -63,6 +66,13 @@ JSObject::asArrayBuffer()
return *static_cast<js::ArrayBufferObject *>(this);
}
inline js::DataViewObject &
JSObject::asDataView()
{
JS_ASSERT(isDataView());
return *static_cast<js::DataViewObject *>(this);
}
namespace js {
inline bool
@ -117,6 +127,69 @@ TypedArray::getDataOffset(JSObject *obj) {
return (void *)obj->getPrivate(NUM_FIXED_SLOTS);
}
inline DataViewObject *
DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
Handle<ArrayBufferObject*> arrayBuffer)
{
JS_ASSERT(byteOffset <= INT32_MAX);
JS_ASSERT(byteLength <= INT32_MAX);
RootedVarObject obj(cx, NewBuiltinClassInstance(cx, &DataViewClass));
if (!obj)
return NULL;
JS_ASSERT(arrayBuffer->isArrayBuffer());
DataViewObject &dvobj = obj->asDataView();
dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength));
dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer));
dvobj.setPrivate(arrayBuffer->dataPointer() + byteOffset);
JS_ASSERT(dvobj.numFixedSlots() == RESERVED_SLOTS);
return &dvobj;
}
inline uint32_t
DataViewObject::byteLength()
{
JS_ASSERT(isDataView());
int32_t length = getReservedSlot(BYTELENGTH_SLOT).toInt32();
JS_ASSERT(length >= 0);
return static_cast<uint32_t>(length);
}
inline uint32_t
DataViewObject::byteOffset()
{
JS_ASSERT(isDataView());
int32_t offset = getReservedSlot(BYTEOFFSET_SLOT).toInt32();
JS_ASSERT(offset >= 0);
return static_cast<uint32_t>(offset);
}
inline void *
DataViewObject::dataPointer()
{
JS_ASSERT(isDataView());
return getPrivate();
}
inline JSObject &
DataViewObject::arrayBuffer()
{
JS_ASSERT(isDataView());
return getReservedSlot(BUFFER_SLOT).toObject();
}
inline bool
DataViewObject::hasBuffer() const
{
JS_ASSERT(isDataView());
return getReservedSlot(BUFFER_SLOT).isObject();
}
} /* namespace js */
#endif /* jstypedarrayinlines_h */

File diff suppressed because it is too large Load Diff