Commit 3b15d950 authored by addaleax's avatar addaleax Committed by Commit bot

ValueSerializer: Add SetTreatArrayBufferViewsAsHostObjects() flag

Add `ValueSerializer::SetTreatArrayBufferViewsAsHostObjects()` which
instructs the `ValueSerializer` to treat ArrayBufferView objects as
host objects.

BUG=v8:5926

Review-Url: https://codereview.chromium.org/2696133007
Cr-Commit-Position: refs/heads/master@{#43281}
parent d91a7be5
......@@ -1799,6 +1799,15 @@ class V8_EXPORT ValueSerializer {
uint32_t transfer_id,
Local<SharedArrayBuffer> shared_array_buffer));
/*
* Indicate whether to treat ArrayBufferView objects as host objects,
* i.e. pass them to Delegate::WriteHostObject. This should not be
* called when no Delegate was passed.
*
* The default is not to treat ArrayBufferViews as host objects.
*/
void SetTreatArrayBufferViewsAsHostObjects(bool mode);
/*
* Write raw data in various common formats to the buffer.
* Note that integer types are written in base-128 varint format, not with a
......
......@@ -3158,6 +3158,10 @@ ValueSerializer::~ValueSerializer() { delete private_; }
void ValueSerializer::WriteHeader() { private_->serializer.WriteHeader(); }
void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) {
private_->serializer.SetTreatArrayBufferViewsAsHostObjects(mode);
}
Maybe<bool> ValueSerializer::WriteValue(Local<Context> context,
Local<Value> value) {
PREPARE_FOR_EXECUTION_PRIMITIVE(context, ValueSerializer, WriteValue, bool);
......
......@@ -170,6 +170,10 @@ void ValueSerializer::WriteHeader() {
WriteVarint(kLatestVersion);
}
void ValueSerializer::SetTreatArrayBufferViewsAsHostObjects(bool mode) {
treat_array_buffer_views_as_host_objects_ = mode;
}
void ValueSerializer::WriteTag(SerializationTag tag) {
uint8_t raw_tag = static_cast<uint8_t>(tag);
WriteRawBytes(&raw_tag, sizeof(raw_tag));
......@@ -318,7 +322,7 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
// TODO(jbroman): It may be possible to avoid materializing a typed
// array's buffer here.
Handle<JSArrayBufferView> view = Handle<JSArrayBufferView>::cast(object);
if (!id_map_.Find(view)) {
if (!id_map_.Find(view) && !treat_array_buffer_views_as_host_objects_) {
Handle<JSArrayBuffer> buffer(
view->IsJSTypedArray()
? Handle<JSTypedArray>::cast(view)->GetBuffer()
......@@ -768,6 +772,9 @@ Maybe<bool> ValueSerializer::WriteJSArrayBuffer(
}
Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
if (treat_array_buffer_views_as_host_objects_) {
return WriteHostObject(handle(view, isolate_));
}
WriteTag(SerializationTag::kArrayBufferView);
ArrayBufferViewTag tag = ArrayBufferViewTag::kInt8Array;
if (view->IsJSTypedArray()) {
......
......@@ -84,6 +84,15 @@ class ValueSerializer {
void WriteRawBytes(const void* source, size_t length);
void WriteDouble(double value);
/*
* Indicate whether to treat ArrayBufferView objects as host objects,
* i.e. pass them to Delegate::WriteHostObject. This should not be
* called when no Delegate was passed.
*
* The default is not to treat ArrayBufferViews as host objects.
*/
void SetTreatArrayBufferViewsAsHostObjects(bool mode);
private:
// Managing allocations of the internal buffer.
Maybe<bool> ExpandBuffer(size_t required_capacity);
......@@ -138,6 +147,7 @@ class ValueSerializer {
Isolate* const isolate_;
v8::ValueSerializer::Delegate* const delegate_;
bool treat_array_buffer_views_as_host_objects_ = false;
uint8_t* buffer_ = nullptr;
size_t buffer_size_ = 0;
size_t buffer_capacity_ = 0;
......
......@@ -263,6 +263,13 @@ class ValueSerializerTest : public TestWithIsolate {
.ToLocalChecked();
}
Local<Object> NewDummyUint8Array() {
static uint8_t data[] = {4, 5, 6};
Local<ArrayBuffer> ab =
ArrayBuffer::New(isolate(), static_cast<void*>(data), sizeof(data));
return Uint8Array::New(ab, 0, sizeof(data));
}
private:
Local<Context> serialization_context_;
Local<Context> deserialization_context_;
......@@ -2537,6 +2544,38 @@ TEST_F(ValueSerializerTestWithHostObject, RoundTripSameObject) {
});
}
class ValueSerializerTestWithHostArrayBufferView
: public ValueSerializerTestWithHostObject {
protected:
void BeforeEncode(ValueSerializer* serializer) override {
ValueSerializerTestWithHostObject::BeforeEncode(serializer);
serializer_->SetTreatArrayBufferViewsAsHostObjects(true);
}
};
TEST_F(ValueSerializerTestWithHostArrayBufferView, RoundTripUint8ArrayInput) {
EXPECT_CALL(serializer_delegate_, WriteHostObject(isolate(), _))
.WillOnce(Invoke([this](Isolate*, Local<Object> object) {
EXPECT_TRUE(object->IsUint8Array());
WriteExampleHostObjectTag();
return Just(true);
}));
EXPECT_CALL(deserializer_delegate_, ReadHostObject(isolate()))
.WillOnce(Invoke([this](Isolate*) {
EXPECT_TRUE(ReadExampleHostObjectTag());
return NewDummyUint8Array();
}));
RoundTripTest(
"({ a: new Uint8Array([1, 2, 3]), get b() { return this.a; }})",
[this](Local<Value> value) {
EXPECT_TRUE(
EvaluateScriptForResultBool("result.a instanceof Uint8Array"));
EXPECT_TRUE(
EvaluateScriptForResultBool("result.a.toString() === '4,5,6'"));
EXPECT_TRUE(EvaluateScriptForResultBool("result.a === result.b"));
});
}
// It's expected that WebAssembly has more exhaustive tests elsewhere; this
// mostly checks that the logic to embed it in structured clone serialization
// works correctly.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment