Bug 789589 - Implement JS_NewDataView, r=Waldo

--HG--
extra : commitid : b5iH1lEe6N
extra : rebase_source : 81579a6e32a33acb08850dd7b8d6d3b900e02b3b
This commit is contained in:
Steve Fink 2015-07-24 13:05:01 -07:00
parent 8586708ac6
commit 713936e41b
3 changed files with 85 additions and 29 deletions

View File

@ -86,23 +86,10 @@ BEGIN_TEST(testArrayBufferView_type)
static JSObject*
CreateDataView(JSContext* cx)
{
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
if (!global)
JS::Rooted<JSObject*> buffer(cx, JS_NewArrayBuffer(cx, 8));
if (!buffer)
return nullptr;
static const char code[] = "new DataView(new ArrayBuffer(8))";
JS::Rooted<JS::Value> val(cx);
JS::CompileOptions opts(cx);
if (!JS::Evaluate(cx, opts.setFileAndLine(__FILE__, __LINE__),
code, strlen(code), &val))
return nullptr;
JS::Rooted<JSObject*> dv(cx, &val.toObject());
if (!JS_IsDataViewObject(dv))
return nullptr;
return dv;
return JS_NewDataView(cx, buffer, 0, 8);
}
template<JSObject * CreateTypedArray(JSContext* cx, uint32_t length),
@ -141,6 +128,42 @@ bool TestViewType(JSContext* cx)
CHECK(len == ExpectedLength);
}
JS::CompartmentOptions options;
JS::RootedObject otherGlobal(cx, JS_NewGlobalObject(cx, basicGlobalClass(), nullptr, JS::DontFireOnNewGlobalHook, options));
CHECK(otherGlobal);
JS::Rooted<JSObject*> buffer(cx);
{
AutoCompartment ac(cx, otherGlobal);
buffer = JS_NewArrayBuffer(cx, 8);
CHECK(buffer);
CHECK(buffer->as<ArrayBufferObject>().byteLength() == 8);
}
CHECK(buffer->compartment() == otherGlobal->compartment());
CHECK(JS_WrapObject(cx, &buffer));
CHECK(buffer->compartment() == global->compartment());
JS::Rooted<JSObject*> dataview(cx, JS_NewDataView(cx, buffer, 4, 4));
CHECK(dataview);
CHECK(dataview->is<ProxyObject>());
JS::Rooted<JS::Value> val(cx);
val = ObjectValue(*dataview);
CHECK(JS_SetProperty(cx, global, "view", val));
EVAL("view.buffer", &val);
CHECK(val.toObject().is<ProxyObject>());
CHECK(dataview->compartment() == global->compartment());
JS::Rooted<JSObject*> otherView(cx, js::UncheckedUnwrap(dataview));
CHECK(otherView->compartment() == otherGlobal->compartment());
JS::Rooted<JSObject*> otherBuffer(cx, js::UncheckedUnwrap(&val.toObject()));
CHECK(otherBuffer->compartment() == otherGlobal->compartment());
EVAL("Object.getPrototypeOf(view) === DataView.prototype", &val);
CHECK(val.toBoolean() == true);
return true;
}

View File

@ -2127,6 +2127,16 @@ JS_IsNeuteredArrayBufferObject(JSObject* obj);
JS_FRIEND_API(bool)
JS_IsDataViewObject(JSObject* obj);
/*
* Create a new DataView using the given ArrayBuffer for storage. The given
* buffer must be an ArrayBuffer (or a cross-compartment wrapper of an
* ArrayBuffer), and the offset and length must fit within the bounds of the
* arrayBuffer. Currently, nullptr will be returned and an exception will be
* thrown if these conditions do not hold, but do not depend on that behavior.
*/
JS_FRIEND_API(JSObject*)
JS_NewDataView(JSContext* cx, JS::HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength);
/*
* Return the byte offset of a data view into its array buffer. |obj| must be a
* DataView.

View File

@ -481,8 +481,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
public:
static JSObject*
fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
RootedObject proto(cx, nullptr);
return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, proto);
return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
}
static JSObject*
@ -969,17 +968,11 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
{
MOZ_ASSERT(byteOffset <= INT32_MAX);
MOZ_ASSERT(byteLength <= INT32_MAX);
MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
RootedObject proto(cx, protoArg);
RootedObject obj(cx);
// This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t.
if (byteOffset + byteLength > arrayBuffer->byteLength()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return nullptr;
}
NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
obj = NewBuiltinClassInstance(cx, &class_, newKind);
if (!obj)
@ -1002,12 +995,17 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
}
}
// Caller should have established these preconditions, and no
// (non-self-hosted) JS code has had an opportunity to run so nothing can
// have invalidated them.
MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
DataViewObject& dvobj = obj->as<DataViewObject>();
dvobj.setFixedSlot(TypedArrayLayout::BYTEOFFSET_SLOT, Int32Value(byteOffset));
dvobj.setFixedSlot(TypedArrayLayout::LENGTH_SLOT, Int32Value(byteLength));
dvobj.setFixedSlot(TypedArrayLayout::BUFFER_SLOT, ObjectValue(*arrayBuffer));
dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
// Include a barrier if the data view's data pointer is in the nursery, as
// is done for typed arrays.
@ -1033,9 +1031,8 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
}
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
uint32_t bufferLength = buffer->byteLength();
uint32_t byteOffset = 0;
uint32_t byteLength = bufferLength;
uint32_t byteLength = buffer->byteLength();
if (args.length() > 1) {
if (!ToUint32(cx, args[1], &byteOffset))
@ -1055,6 +1052,8 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
return false;
}
} else {
uint32_t bufferLength = buffer->byteLength();
if (byteOffset > bufferLength) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
@ -1069,7 +1068,7 @@ DataViewObject::construct(JSContext* cx, JSObject* bufobj, const CallArgs& args,
MOZ_ASSERT(byteOffset <= INT32_MAX);
MOZ_ASSERT(byteLength <= INT32_MAX);
if (byteOffset + byteLength > bufferLength) {
if (byteOffset + byteLength > buffer->byteLength()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
return false;
}
@ -2348,3 +2347,27 @@ JS_GetDataViewByteLength(JSObject* obj)
return 0;
return obj->as<DataViewObject>().byteLength();
}
JS_FRIEND_API(JSObject*)
JS_NewDataView(JSContext* cx, HandleObject arrayBuffer, uint32_t byteOffset, int32_t byteLength)
{
ConstructArgs cargs(cx);
if (!cargs.init(3))
return nullptr;
RootedObject constructor(cx);
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewObject::class_);
if (!GetBuiltinConstructor(cx, key, &constructor))
return nullptr;
cargs[0].setObject(*arrayBuffer);
cargs[1].setNumber(byteOffset);
cargs[2].setInt32(byteLength);
RootedValue fun(cx, ObjectValue(*constructor));
RootedValue rval(cx);
if (!Construct(cx, fun, cargs, fun, &rval))
return nullptr;
MOZ_ASSERT(rval.isObject());
return &rval.toObject();
}