Commit 6c8ed9cf authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

Fix WebAssembly.Memory deserialization in more complex objects

The wasm memory deserialization didn't properly increment the object id, so
wouldn't work properly if the memory object (or its contained
SharedArrayBuffer) where included multiple times in the object.

Bug: v8:6895
Change-Id: I5c4c25bad2ec6152883c5a7321038aba1950480a
Reviewed-on: https://chromium-review.googlesource.com/721630Reviewed-by: 's avatarDeepti Gandluri <gdeepti@chromium.org>
Commit-Queue: Ben Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48767}
parent 06ff9e97
...@@ -1735,28 +1735,34 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() { ...@@ -1735,28 +1735,34 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
return result; return result;
} }
MaybeHandle<JSObject> ValueDeserializer::ReadWasmMemory() { MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() {
uint32_t id = next_id_++;
if (!FLAG_experimental_wasm_threads) { if (!FLAG_experimental_wasm_threads) {
return MaybeHandle<JSObject>(); return MaybeHandle<WasmMemoryObject>();
} }
int32_t maximum_pages; int32_t maximum_pages;
if (!ReadZigZag<int32_t>().To(&maximum_pages)) { if (!ReadZigZag<int32_t>().To(&maximum_pages)) {
return MaybeHandle<JSObject>(); return MaybeHandle<WasmMemoryObject>();
} }
SerializationTag tag; SerializationTag tag;
if (!ReadTag().To(&tag) || tag != SerializationTag::kSharedArrayBuffer) { if (!ReadTag().To(&tag) || tag != SerializationTag::kSharedArrayBuffer) {
return MaybeHandle<JSObject>(); return MaybeHandle<WasmMemoryObject>();
} }
const bool is_shared = true; const bool is_shared = true;
Handle<JSArrayBuffer> buffer; Handle<JSArrayBuffer> buffer;
if (!ReadTransferredJSArrayBuffer(is_shared).ToHandle(&buffer)) { if (!ReadTransferredJSArrayBuffer(is_shared).ToHandle(&buffer)) {
return MaybeHandle<JSObject>(); return MaybeHandle<WasmMemoryObject>();
} }
return WasmMemoryObject::New(isolate_, buffer, maximum_pages); Handle<WasmMemoryObject> result =
WasmMemoryObject::New(isolate_, buffer, maximum_pages);
AddObjectWithID(id, result);
return result;
} }
MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() { MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() {
......
...@@ -273,7 +273,7 @@ class ValueDeserializer { ...@@ -273,7 +273,7 @@ class ValueDeserializer {
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT; Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModule() WARN_UNUSED_RESULT; MaybeHandle<JSObject> ReadWasmModule() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModuleTransfer() WARN_UNUSED_RESULT; MaybeHandle<JSObject> ReadWasmModuleTransfer() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmMemory() WARN_UNUSED_RESULT; MaybeHandle<WasmMemoryObject> ReadWasmMemory() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadHostObject() WARN_UNUSED_RESULT; MaybeHandle<JSObject> ReadHostObject() WARN_UNUSED_RESULT;
/* /*
......
...@@ -158,9 +158,8 @@ class WasmMemoryObject : public JSObject { ...@@ -158,9 +158,8 @@ class WasmMemoryObject : public JSObject {
uint32_t current_pages(); uint32_t current_pages();
inline bool has_maximum_pages(); inline bool has_maximum_pages();
static Handle<WasmMemoryObject> New(Isolate* isolate, V8_EXPORT_PRIVATE static Handle<WasmMemoryObject> New(
Handle<JSArrayBuffer> buffer, Isolate* isolate, Handle<JSArrayBuffer> buffer, int32_t maximum);
int32_t maximum);
static int32_t Grow(Isolate*, Handle<WasmMemoryObject>, uint32_t pages); static int32_t Grow(Isolate*, Handle<WasmMemoryObject>, uint32_t pages);
static void SetupNewBufferWithSameBackingStore( static void SetupNewBufferWithSameBackingStore(
......
...@@ -11,30 +11,59 @@ ...@@ -11,30 +11,59 @@
assertThrows(() => worker.postMessage(memory), Error); assertThrows(() => worker.postMessage(memory), Error);
})(); })();
// Can't use assert in a worker.
let workerHelpers =
`function assertTrue(value, msg) {
if (!value) {
postMessage("Error: " + msg);
throw new Error("Exit"); // To stop testing.
}
}
function assertIsWasmMemory(memory, expectedSize) {
assertTrue(memory instanceof WebAssembly.Memory,
"object is not a WebAssembly.Memory");
assertTrue(memory.buffer instanceof SharedArrayBuffer,
"object.buffer is not a SharedArrayBuffer");
assertTrue(memory.buffer.byteLength == expectedSize,
"object.buffer.byteLength is not " + expectedSize + " bytes");
}
`;
(function TestPostMessageSharedMemory() { (function TestPostMessageSharedMemory() {
let workerScript = let workerScript = workerHelpers +
`onmessage = function(memory) { `onmessage = function(memory) {
// Can't use assert in a worker. assertIsWasmMemory(memory, 65536);
if (!(memory instanceof WebAssembly.Memory)) {
postMessage("Error: memory is not a WebAssembly.Memory");
return;
}
if (!(memory.buffer instanceof SharedArrayBuffer)) {
postMessage("Error: memory.buffer is not a SharedArrayBuffer");
return;
}
if (memory.buffer.byteLength != 65536) {
postMessage("Error: memory.buffer.byteLength is not 1 page");
return;
}
postMessage("OK"); postMessage("OK");
};`; };`;
let worker = new Worker(workerScript); let worker = new Worker(workerScript);
let memory = new WebAssembly.Memory({initial: 1, maximum: 2, shared: true}); let memory = new WebAssembly.Memory({initial: 1, maximum: 2, shared: true});
worker.postMessage(memory); worker.postMessage(memory);
assertEquals("OK", worker.getMessage()); assertEquals("OK", worker.getMessage());
worker.terminate(); worker.terminate();
})(); })();
(function TestPostMessageComplexObjectWithSharedMemory() {
let workerScript = workerHelpers +
`onmessage = function(obj) {
assertIsWasmMemory(obj.memories[0], 65536);
assertIsWasmMemory(obj.memories[1], 65536);
assertTrue(obj.buffer instanceof SharedArrayBuffer,
"buffer is not a SharedArrayBuffer");
assertTrue(obj.memories[0] === obj.memories[1], "memories aren't equal");
assertTrue(obj.memories[0].buffer === obj.buffer,
"buffers aren't equal");
assertTrue(obj.foo === 1, "foo is not 1");
postMessage("OK");
};`;
let worker = new Worker(workerScript);
let memory = new WebAssembly.Memory({initial: 1, maximum: 2, shared: true});
let obj = {memories: [memory, memory], buffer: memory.buffer, foo: 1};
worker.postMessage(obj);
assertEquals("OK", worker.getMessage());
worker.terminate();
})();
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "src/api.h" #include "src/api.h"
#include "src/base/build_config.h" #include "src/base/build_config.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/wasm/wasm-objects.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -2233,21 +2234,20 @@ TEST_F(ValueSerializerTest, DecodeInvalidDataView) { ...@@ -2233,21 +2234,20 @@ TEST_F(ValueSerializerTest, DecodeInvalidDataView) {
class ValueSerializerTestWithSharedArrayBufferTransfer class ValueSerializerTestWithSharedArrayBufferTransfer
: public ValueSerializerTest { : public ValueSerializerTest {
protected: protected:
static const size_t kTestByteLength = 4;
ValueSerializerTestWithSharedArrayBufferTransfer() ValueSerializerTestWithSharedArrayBufferTransfer()
: serializer_delegate_(this) { : serializer_delegate_(this) {}
const uint8_t data[kTestByteLength] = {0x00, 0x01, 0x80, 0xff};
memcpy(data_, data, kTestByteLength); void InitializeData(const std::vector<uint8_t>& data) {
data_ = data;
{ {
Context::Scope scope(serialization_context()); Context::Scope scope(serialization_context());
input_buffer_ = input_buffer_ =
SharedArrayBuffer::New(isolate(), &data_, kTestByteLength); SharedArrayBuffer::New(isolate(), data_.data(), data_.size());
} }
{ {
Context::Scope scope(deserialization_context()); Context::Scope scope(deserialization_context());
output_buffer_ = output_buffer_ =
SharedArrayBuffer::New(isolate(), &data_, kTestByteLength); SharedArrayBuffer::New(isolate(), data_.data(), data_.size());
} }
} }
...@@ -2305,7 +2305,7 @@ class ValueSerializerTestWithSharedArrayBufferTransfer ...@@ -2305,7 +2305,7 @@ class ValueSerializerTestWithSharedArrayBufferTransfer
private: private:
static bool flag_was_enabled_; static bool flag_was_enabled_;
uint8_t data_[kTestByteLength]; std::vector<uint8_t> data_;
Local<SharedArrayBuffer> input_buffer_; Local<SharedArrayBuffer> input_buffer_;
Local<SharedArrayBuffer> output_buffer_; Local<SharedArrayBuffer> output_buffer_;
}; };
...@@ -2315,6 +2315,8 @@ bool ValueSerializerTestWithSharedArrayBufferTransfer::flag_was_enabled_ = ...@@ -2315,6 +2315,8 @@ bool ValueSerializerTestWithSharedArrayBufferTransfer::flag_was_enabled_ =
TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer, TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer,
RoundTripSharedArrayBufferTransfer) { RoundTripSharedArrayBufferTransfer) {
InitializeData({0x00, 0x01, 0x80, 0xff});
EXPECT_CALL(serializer_delegate_, EXPECT_CALL(serializer_delegate_,
GetSharedArrayBufferId(isolate(), input_buffer())) GetSharedArrayBufferId(isolate(), input_buffer()))
.WillRepeatedly(Return(Just(0U))); .WillRepeatedly(Return(Just(0U)));
...@@ -2350,6 +2352,40 @@ TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer, ...@@ -2350,6 +2352,40 @@ TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer,
}); });
} }
TEST_F(ValueSerializerTestWithSharedArrayBufferTransfer,
RoundTripWebAssemblyMemory) {
bool flag_was_enabled = i::FLAG_experimental_wasm_threads;
i::FLAG_experimental_wasm_threads = true;
std::vector<uint8_t> data = {0x00, 0x01, 0x80, 0xff};
data.resize(65536);
InitializeData(data);
EXPECT_CALL(serializer_delegate_,
GetSharedArrayBufferId(isolate(), input_buffer()))
.WillRepeatedly(Return(Just(0U)));
RoundTripTest(
[this]() -> Local<Value> {
const int32_t kMaxPages = 1;
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate());
i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(*input_buffer());
return Utils::Convert<i::WasmMemoryObject, Value>(
i::WasmMemoryObject::New(i_isolate, obj, kMaxPages));
},
[this](Local<Value> value) {
EXPECT_TRUE(EvaluateScriptForResultBool(
"result instanceof WebAssembly.Memory"));
EXPECT_TRUE(
EvaluateScriptForResultBool("result.buffer.byteLength === 65536"));
EXPECT_TRUE(
EvaluateScriptForResultBool("new Uint8Array(result.buffer, 0, "
"4).toString() === '0,1,128,255'"));
});
i::FLAG_experimental_wasm_threads = flag_was_enabled;
}
TEST_F(ValueSerializerTest, UnsupportedHostObject) { TEST_F(ValueSerializerTest, UnsupportedHostObject) {
InvalidEncodeTest("new ExampleHostObject()"); InvalidEncodeTest("new ExampleHostObject()");
InvalidEncodeTest("({ a: new ExampleHostObject() })"); InvalidEncodeTest("({ a: new ExampleHostObject() })");
......
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