diff --git a/js/src/jsclass.h b/js/src/jsclass.h index 3dd132de2e5..731d0e9c066 100644 --- a/js/src/jsclass.h +++ b/js/src/jsclass.h @@ -395,7 +395,8 @@ Valueify(const JSClass *c) * value of objects. */ enum ESClassValue { - ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp + ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, + ESClass_RegExp, ESClass_ArrayBuffer }; /* diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index dddd57ad24d..2e392fe25e0 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -1595,6 +1595,7 @@ ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx) case ESClass_String: return obj.isString(); case ESClass_Boolean: return obj.isBoolean(); case ESClass_RegExp: return obj.isRegExp(); + case ESClass_ArrayBuffer: return obj.isArrayBuffer(); } JS_NOT_REACHED("bad classValue"); return false; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 7eeb96ac580..d8bb741088d 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -785,6 +785,22 @@ ArrayBufferObject::obj_typeOf(JSContext *cx, JSObject *obj) return JSTYPE_OBJECT; } +/* + * ArrayBufferViews of various sorts + */ + +static JSObject * +GetProtoForClass(JSContext *cx, Class *clasp) +{ + // Pass in the proto from this compartment + GlobalObject *parent = GetCurrentGlobal(cx); + Root parentRoot(cx, &parent); + JSObject *proto; + if (!FindProto(cx, clasp, parentRoot, &proto)) + return NULL; + return proto; +} + /* * TypedArray * @@ -1068,12 +1084,6 @@ template class TypedArrayTemplate : public TypedArray { - template - friend JSObject *NewTypedArrayFromArray(JSContext *cx, JSObject *other); - - template - friend JSObject *NewArray(JSContext *cx, uint32_t nelements); - public: typedef NativeType ThisType; typedef TypedArrayTemplate ThisTypeArray; @@ -1440,7 +1450,8 @@ class TypedArrayTemplate } static JSObject * - createTypedArray(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len) + makeInstance(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len, + HandleObject proto) { RootedVarObject obj(cx, NewBuiltinClassInstance(cx, protoClass())); if (!obj) @@ -1451,14 +1462,19 @@ class TypedArrayTemplate JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8); #endif - /* - * Specialize the type of the object on the current scripted location, - * and mark the type as definitely a typed array. - */ - JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(protoClass()); - types::TypeObject *type = types::GetTypeCallerInitObject(cx, key); - if (!type) - return NULL; + types::TypeObject *type; + if (proto) { + type = proto->getNewType(cx); + } else { + /* + * Specialize the type of the object on the current scripted location, + * and mark the type as definitely a typed array. + */ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(protoClass()); + type = types::GetTypeCallerInitObject(cx, key); + if (!type) + return NULL; + } obj->setType(type); obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID())); @@ -1516,6 +1532,18 @@ class TypedArrayTemplate return true; } + static JSBool + fromBuffer(JSContext *cx, unsigned argc, Value *vp) + { + CallArgs args = CallArgsFromVp(argc, vp); + JSObject *obj = fromBuffer(cx, RootedVarObject(cx, &args[0].toObject()), + args[1].toInt32(), args[2].toInt32(), RootedVarObject(cx, &args[3].toObject())); + if (!obj) + return false; + vp->setObject(*obj); + return true; + } + static JSObject * create(JSContext *cx, unsigned argc, Value *argv) { @@ -1523,40 +1551,29 @@ class TypedArrayTemplate /* () or (number) */ uint32_t len = 0; - if (argc == 0 || ValueIsLength(cx, argv[0], &len)) { - RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len)); - if (!bufobj) - return NULL; - - return createTypedArray(cx, bufobj, 0, len); - } + if (argc == 0 || ValueIsLength(cx, argv[0], &len)) + return fromLength(cx, len); /* (not an object) */ if (!argv[0].isObject()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TYPED_ARRAY_BAD_ARGS); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); return NULL; } RootedVarObject dataObj(cx, &argv[0].toObject()); - /* (typedArray) */ - if (dataObj->isTypedArray()) { - JSObject *otherTypedArray = getTypedArray(dataObj); - JS_ASSERT(otherTypedArray); + /* + * (typedArray) + * (type[] array) + * + * Otherwise create a new typed array and copy elements 0..len-1 + * properties from the object, treating it as some sort of array. + * Note that offset and length will be ignored + */ + if (!UnwrapObject(dataObj)->isArrayBuffer()) + return fromArray(cx, dataObj); - uint32_t len = getLength(otherTypedArray); - RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len)); - if (!bufobj) - return NULL; - - JSObject *obj = createTypedArray(cx, bufobj, 0, len); - if (!obj || !copyFromTypedArray(cx, obj, otherTypedArray, 0)) - return NULL; - return obj; - } - - /* (obj, byteOffset, length). */ + /* (ArrayBuffer, [byteOffset, [length]]) */ int32_t byteOffset = -1; int32_t length = -1; @@ -1580,8 +1597,7 @@ class TypedArrayTemplate } } - /* (obj, byteOffset, length) */ - return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length); + return fromBuffer(cx, dataObj, byteOffset, length, RootedVarObject(cx)); } /* subarray(start[, end]) */ @@ -1693,9 +1709,61 @@ class TypedArrayTemplate public: static JSObject * - createTypedArrayWithBuffer(JSContext *cx, HandleObject bufobj, - int32_t byteOffsetInt, int32_t lengthInt) + fromBuffer(JSContext *cx, HandleObject bufobj, int32_t byteOffsetInt, int32_t lengthInt, + HandleObject proto) { + if (!ObjectClassIs(*bufobj, ESClass_ArrayBuffer, cx)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); + return NULL; // must be arrayBuffer + } + + JS_ASSERT(bufobj->isArrayBuffer() || bufobj->isProxy()); + if (bufobj->isProxy()) { + /* + * Normally, NonGenericMethodGuard handles the case of transparent + * wrappers. However, we have a peculiar situation: we want to + * construct the new typed array in the compartment of the buffer, + * so that the typed array can point directly at their buffer's + * data without crossing compartment boundaries. So we use the + * machinery underlying NonGenericMethodGuard directly to proxy the + * native call. We will end up with a wrapper in the origin + * compartment for a view in the target compartment referencing the + * ArrayBuffer in that same compartment. + */ + JSObject *wrapped = UnwrapObjectChecked(cx, bufobj); + if (!wrapped) + return NULL; + if (wrapped->isArrayBuffer()) { + /* + * And for even more fun, the new view's prototype should be + * set to the origin compartment's prototype object, not the + * target's (specifically, the actual view in the target + * compartment will use as its prototype a wrapper around the + * origin compartment's view.prototype object) + */ + JSObject *proto = GetProtoForClass(cx, fastClass()); + if (!proto) + return NULL; + Value argv[] = { UndefinedValue(), + MagicValue(JS_IS_CONSTRUCTING), + ObjectValue(*bufobj), + Int32Value(byteOffsetInt), + Int32Value(lengthInt), + ObjectValue(*proto) }; + uint32_t argc = sizeof(argv) / sizeof(argv[0]) - 2; + + CallArgs args = CallArgsFromVp(argc, argv); + if (!Proxy::nativeCall(cx, bufobj, &ArrayBufferClass, fromBuffer, args)) + return NULL; + return &args.rval().toObject(); + } + } + + if (!bufobj->isArrayBuffer()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); + return NULL; // must be arrayBuffer + } + uint32_t boffset = (byteOffsetInt == -1) ? 0 : uint32_t(byteOffsetInt); ArrayBufferObject &buffer = bufobj->asArrayBuffer(); @@ -1728,11 +1796,20 @@ class TypedArrayTemplate return NULL; // boffset + len is too big for the arraybuffer } - return createTypedArray(cx, bufobj, boffset, len); + return makeInstance(cx, bufobj, boffset, len, proto); } static JSObject * - createTypedArrayFromArray(JSContext *cx, HandleObject other) + fromLength(JSContext *cx, int32_t nelements) + { + RootedVarObject buffer(cx, createBufferWithSizeAndCount(cx, nelements)); + if (!buffer) + return NULL; + return makeInstance(cx, buffer, 0, nelements, RootedVarObject(cx, NULL)); + } + + static JSObject * + fromArray(JSContext *cx, HandleObject other) { uint32_t len; if (!js_GetLengthProperty(cx, other, &len)) @@ -1742,33 +1819,12 @@ class TypedArrayTemplate if (!bufobj) return NULL; - RootedVarObject obj(cx, createTypedArray(cx, bufobj, 0, len)); + RootedVarObject obj(cx, makeInstance(cx, bufobj, 0, len, RootedVarObject(cx))); if (!obj || !copyFromArray(cx, obj, other, len)) return NULL; return obj; } - /* - * Note: the offset and length arguments are ignored if an array is passed in. - */ - static JSObject * - createTypedArrayWithOffsetLength(JSContext *cx, HandleObject other, - int32_t byteOffsetInt, int32_t lengthInt) - { - JS_ASSERT(!other->isTypedArray()); - - if (other->isArrayBuffer()) { - /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */ - return createTypedArrayWithBuffer(cx, other, byteOffsetInt, lengthInt); - } - - /* - * Otherwise create a new typed array and copy len properties from - * the object. - */ - return createTypedArrayFromArray(cx, other); - } - static const NativeType getIndex(JSObject *obj, uint32_t index) { @@ -1801,7 +1857,7 @@ class TypedArrayTemplate JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= getByteOffset(tarray)); uint32_t byteOffset = getByteOffset(tarray) + begin * sizeof(NativeType); - return createTypedArray(cx, bufobj, byteOffset, length); + return makeInstance(cx, bufobj, byteOffset, length, RootedVarObject(cx)); } protected: @@ -2172,14 +2228,8 @@ TypedArrayTemplate::copyIndexToValue(JSContext *cx, JSObject *tarray, ui } JSBool -DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) +DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, JSObject *proto) { - 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); @@ -2227,7 +2277,7 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) return false; } - JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer); + JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); if (!obj) return false; args.rval().setObject(*obj); @@ -2264,6 +2314,51 @@ DataViewObject::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value return true; } +JSBool +DataViewObject::constructWithProto(JSContext *cx, unsigned argc, Value *vp) +{ + // Pop the proto argument off the end + CallArgs args = CallArgsFromVp(argc, vp); + JSObject &proto = args[args.length() - 1].toObject(); + + // And now mimic class_constructor for everything else, but pass in the proto + args = CallArgsFromVp(argc - 1, vp); + JSObject *bufobj; + if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj)) + return false; + return construct(cx, bufobj, args, &proto); +} + +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->isWrapper() && UnwrapObject(bufobj)->isArrayBuffer()) { + JSObject *proto = GetProtoForClass(cx, &DataViewClass); + if (!proto) + return false; + + Vector argv(cx); + argv.resize(argc + 2 + 1); + memcpy(argv.begin(), args.base(), sizeof(Value) * (argc + 2)); + argv[argc + 2].setObject(*proto); + argv[0].setUndefined(); // We want to use a different callee (avoid an assertion) + + CallArgs proxyArgs = CallArgsFromVp(argc + 1, argv.begin()); + if (!Proxy::nativeCall(cx, bufobj, &DataViewClass, constructWithProto, proxyArgs)) + return false; + args.rval() = proxyArgs.rval(); + return true; + } + + return construct(cx, bufobj, args, NULL); +} + JSBool DataViewObject::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp) { @@ -2831,48 +2926,26 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \ JS_FS_END \ } -template -static inline JSObject * -NewArray(JSContext *cx, uint32_t nelements) -{ - RootedVarObject buffer(cx, TypedArrayTemplate::createBufferWithSizeAndCount(cx, nelements)); - if (!buffer) - return NULL; - return TypedArrayTemplate::createTypedArray(cx, buffer, 0, nelements); -} - -template -static inline JSObject * -NewArrayWithBuffer(JSContext *cx, HandleObject arrayBuffer, int32_t byteoffset, int32_t intLength) -{ - if (!arrayBuffer->isArrayBuffer()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS); - return NULL; // must be arrayBuffer - } - - return TypedArrayTemplate::createTypedArrayWithBuffer(cx, arrayBuffer, - byteoffset, intLength); -} - #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \ { \ MOZ_ASSERT(nelements <= INT32_MAX); \ - return NewArray(cx, nelements); \ + return TypedArrayTemplate::fromLength(cx, nelements); \ } \ JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, JSObject *other) \ { \ - return TypedArrayTemplate::createTypedArrayFromArray(cx, RootedVarObject(cx, other)); \ + return TypedArrayTemplate::fromArray(cx, RootedVarObject(cx, other)); \ } \ JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \ JSObject *arrayBuffer, uint32_t byteoffset, int32_t length) \ { \ MOZ_ASSERT(byteoffset <= INT32_MAX); \ - return NewArrayWithBuffer(cx, RootedVarObject(cx, arrayBuffer), byteoffset, length); \ + return TypedArrayTemplate::fromBuffer(cx, RootedVarObject(cx, arrayBuffer), byteoffset, length, RootedVarObject(cx)); \ } \ JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj, JSContext *cx) \ { \ - obj = UnwrapObject(obj); \ + if (!(obj = UnwrapObjectChecked(cx, obj))) \ + return false; \ Class *clasp = obj->getClass(); \ return (clasp == &TypedArray::classes[TypedArrayTemplate::ArrayTypeID()]); \ } @@ -3192,38 +3265,66 @@ js_InitTypedArrayClasses(JSContext *cx, JSObject *obj) /* JS Friend API */ +// The typed array friend API defines a number of accessor functions that want +// to unwrap an argument, but in certain rare cases may not have a cx available +// and so pass in NULL instead. Use UnwrapObjectChecked when possible. +static JSObject * +CheckedUnwrap(JSContext *cx, JSObject *obj) +{ + if (!cx) + return UnwrapObject(obj); + MOZ_ASSERT(!cx->isExceptionPending()); + obj = UnwrapObjectChecked(cx, obj); + MOZ_ASSERT(obj); + return obj; +} + JS_FRIEND_API(JSBool) JS_IsArrayBufferObject(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + MOZ_ASSERT(!cx->isExceptionPending()); + if (!(obj = UnwrapObjectChecked(cx, obj))) { + cx->clearPendingException(); + return false; + } return obj->isArrayBuffer(); } JS_FRIEND_API(JSBool) JS_IsTypedArrayObject(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + MOZ_ASSERT(!cx->isExceptionPending()); + if (!(obj = UnwrapObjectChecked(cx, obj))) { + cx->clearPendingException(); + return false; + } return obj->isTypedArray(); } JS_FRIEND_API(JSBool) JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + MOZ_ASSERT(!cx->isExceptionPending()); + if (!(obj = UnwrapObjectChecked(cx, obj))) { + cx->clearPendingException(); + return false; + } return obj->isTypedArray() || obj->isDataView(); } JS_FRIEND_API(uint32_t) JS_GetArrayBufferByteLength(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; return obj->asArrayBuffer().byteLength(); } JS_FRIEND_API(uint8_t *) JS_GetArrayBufferData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; return obj->asArrayBuffer().dataPointer(); } @@ -3237,7 +3338,8 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) JS_FRIEND_API(uint32_t) JS_GetTypedArrayLength(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; JS_ASSERT(obj->isTypedArray()); return obj->getSlot(TypedArray::FIELD_LENGTH).toInt32(); } @@ -3245,7 +3347,8 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint32_t) JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; JS_ASSERT(obj->isTypedArray()); return obj->getSlot(TypedArray::FIELD_BYTEOFFSET).toInt32(); } @@ -3253,7 +3356,8 @@ JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint32_t) JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; JS_ASSERT(obj->isTypedArray()); return obj->getSlot(TypedArray::FIELD_BYTELENGTH).toInt32(); } @@ -3261,7 +3365,8 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx) JS_FRIEND_API(JSArrayBufferViewType) JS_GetTypedArrayType(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return ArrayBufferView::TYPE_MAX; JS_ASSERT(obj->isTypedArray()); return static_cast(obj->getSlot(TypedArray::FIELD_TYPE).toInt32()); } @@ -3269,7 +3374,8 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *cx) JS_FRIEND_API(int8_t *) JS_GetInt8ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT8); return static_cast(TypedArray::getDataOffset(obj)); @@ -3278,7 +3384,8 @@ JS_GetInt8ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint8_t *) JS_GetUint8ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT8); return static_cast(TypedArray::getDataOffset(obj)); @@ -3287,7 +3394,8 @@ JS_GetUint8ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint8_t *) JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT8_CLAMPED); return static_cast(TypedArray::getDataOffset(obj)); @@ -3296,7 +3404,8 @@ JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(int16_t *) JS_GetInt16ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT16); return static_cast(TypedArray::getDataOffset(obj)); @@ -3305,7 +3414,8 @@ JS_GetInt16ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint16_t *) JS_GetUint16ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT16); return static_cast(TypedArray::getDataOffset(obj)); @@ -3314,7 +3424,8 @@ JS_GetUint16ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(int32_t *) JS_GetInt32ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT32); return static_cast(TypedArray::getDataOffset(obj)); @@ -3323,7 +3434,8 @@ JS_GetInt32ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint32_t *) JS_GetUint32ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT32); return static_cast(TypedArray::getDataOffset(obj)); @@ -3332,7 +3444,8 @@ JS_GetUint32ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(float *) JS_GetFloat32ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_FLOAT32); return static_cast(TypedArray::getDataOffset(obj)); @@ -3341,7 +3454,8 @@ JS_GetFloat32ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(double *) JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray()); JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_FLOAT64); return static_cast(TypedArray::getDataOffset(obj)); @@ -3350,7 +3464,8 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx) JS_FRIEND_API(JSBool) JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return false; *isDataView = obj->isDataView(); return true; } @@ -3358,14 +3473,16 @@ JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView) JS_FRIEND_API(uint32_t) JS_GetDataViewByteOffset(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; return obj->asDataView().byteOffset(); } JS_FRIEND_API(void *) JS_GetDataViewData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isDataView()); return obj->asDataView().dataPointer(); } @@ -3373,7 +3490,8 @@ JS_GetDataViewData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint32_t) JS_GetDataViewByteLength(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; JS_ASSERT(obj->isDataView()); return obj->asDataView().byteLength(); } @@ -3381,7 +3499,8 @@ JS_GetDataViewByteLength(JSObject *obj, JSContext *cx) JS_FRIEND_API(void *) JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return NULL; JS_ASSERT(obj->isTypedArray() || obj->isDataView()); return obj->isDataView() ? obj->asDataView().dataPointer() : TypedArray::getDataOffset(obj); } @@ -3389,7 +3508,8 @@ JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx) JS_FRIEND_API(uint32_t) JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx) { - obj = UnwrapObject(obj); + if (!(obj = CheckedUnwrap(cx, obj))) + return 0; JS_ASSERT(obj->isTypedArray() || obj->isDataView()); return obj->isDataView() ? obj->asDataView().byteLength() : TypedArray::getByteLength(obj); } diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index a628abbd9f4..9ed2e5433c8 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -330,9 +330,12 @@ class DataViewObject : public JSObject static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp); static JSBool class_constructor(JSContext *cx, unsigned argc, Value *vp); + static JSBool constructWithProto(JSContext *cx, unsigned argc, Value *vp); + static JSBool construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, JSObject *proto); static inline DataViewObject * - create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, Handle arrayBuffer); + create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, + Handle arrayBuffer, JSObject *proto); static JSBool fun_getInt8(JSContext *cx, unsigned argc, Value *vp); static JSBool fun_getUint8(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h index 7ca415e393f..b2e0af36a5a 100644 --- a/js/src/jstypedarrayinlines.h +++ b/js/src/jstypedarrayinlines.h @@ -129,7 +129,7 @@ TypedArray::getDataOffset(JSObject *obj) { inline DataViewObject * DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, - Handle arrayBuffer) + Handle arrayBuffer, JSObject *proto) { JS_ASSERT(byteOffset <= INT32_MAX); JS_ASSERT(byteLength <= INT32_MAX); @@ -138,6 +138,21 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, if (!obj) return NULL; + types::TypeObject *type; + if (proto) { + type = proto->getNewType(cx); + } else { + /* + * Specialize the type of the object on the current scripted location, + * and mark the type as definitely a data view + */ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewClass); + type = types::GetTypeCallerInitObject(cx, key); + if (!type) + return NULL; + } + obj->setType(type); + JS_ASSERT(arrayBuffer->isArrayBuffer()); DataViewObject &dvobj = obj->asDataView(); diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index a4275e6a901..554a499c7c3 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -793,7 +793,7 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *cla JS_ASSERT_IF(!srcArgs.calleev().isUndefined(), srcArgs.callee().toFunction()->native() == native || srcArgs.callee().toFunction()->native() == js_generic_native_method_dispatcher); - JS_ASSERT(&srcArgs.thisv().toObject() == wrapper); + JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || &srcArgs.thisv().toObject() == wrapper); JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper()); JSObject *wrapped = wrappedObject(wrapper); diff --git a/js/src/tests/js1_8_5/extensions/dataview.js b/js/src/tests/js1_8_5/extensions/dataview.js index 8ae1b43ad5b..bc4dc42a4f8 100644 --- a/js/src/tests/js1_8_5/extensions/dataview.js +++ b/js/src/tests/js1_8_5/extensions/dataview.js @@ -1626,6 +1626,11 @@ function test() { buffer = Object.create(buffer1); checkThrow(function () new DataView(buffer), TypeError); + // view of proxy for buffer + av = new DataView(alien_buffer); + assertEq(av.getUint8(4), 100); + assertEq(Object.getPrototypeOf(av), DataView.prototype); + reportCompare(0, 0, 'done.'); exitFunc ('test'); } diff --git a/js/src/tests/js1_8_5/extensions/typedarray.js b/js/src/tests/js1_8_5/extensions/typedarray.js index 300c26248aa..39ce5e1620b 100644 --- a/js/src/tests/js1_8_5/extensions/typedarray.js +++ b/js/src/tests/js1_8_5/extensions/typedarray.js @@ -424,6 +424,33 @@ function test() // use a loop to invoke the TM for (var i = 0; i < b.length; i++) check(function() b[90] == 5) + + // Protos and proxies, oh my! + var alien = newGlobal('new-compartment'); + + var alien_view = alien.eval('view = new Uint8Array(7)'); + var alien_buffer = alien.eval('buffer = view.buffer'); + + // when creating a view of a buffer in a different compartment, the view + // itself should be created in the other compartment and wrapped for use in + // this compartment. (There should never be a compartment boundary between + // an ArrayBufferView and its ArrayBuffer.) + var view = new Int8Array(alien_buffer); + + // First make sure they're looking at the same data + alien_view[3] = 77; + check(function () view[3] == 77); + + // Now check that the proxy setup is as expected + check(function () isProxy(alien_view)); + check(function () isProxy(alien_buffer)); + check(function () isProxy(view)); // the real test + + // typed array protos should be equal + simple = new Int8Array(12); + check(function () Object.getPrototypeOf(view) == Object.getPrototypeOf(simple)); + check(function () Object.getPrototypeOf(view) == Int8Array.prototype); + print ("done"); reportCompare(0, TestFailCount, "typed array tests");