| Index: test/unittests/value-serializer-unittest.cc
|
| diff --git a/test/unittests/value-serializer-unittest.cc b/test/unittests/value-serializer-unittest.cc
|
| index c0037efb01709e9c401992d89292422494bf18c1..9f49c6498af92304c13cef6577306a4962111946 100644
|
| --- a/test/unittests/value-serializer-unittest.cc
|
| +++ b/test/unittests/value-serializer-unittest.cc
|
| @@ -74,6 +74,9 @@ class ValueSerializerTest : public TestWithIsolate {
|
| return deserialization_context_;
|
| }
|
|
|
| + bool ExpectInlineWasm() const { return expect_inline_wasm_; }
|
| + void SetExpectInlineWasm(bool value) { expect_inline_wasm_ = value; }
|
| +
|
| // Overridden in more specific fixtures.
|
| virtual ValueSerializer::Delegate* GetSerializerDelegate() { return nullptr; }
|
| virtual void BeforeEncode(ValueSerializer*) {}
|
| @@ -172,6 +175,7 @@ class ValueSerializerTest : public TestWithIsolate {
|
| static_cast<int>(data.size()),
|
| GetDeserializerDelegate());
|
| deserializer.SetSupportsLegacyWireFormat(true);
|
| + deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
| BeforeDecode(&deserializer);
|
| ASSERT_TRUE(deserializer.ReadHeader(context).FromMaybe(false));
|
| Local<Value> result;
|
| @@ -196,6 +200,7 @@ class ValueSerializerTest : public TestWithIsolate {
|
| static_cast<int>(data.size()),
|
| GetDeserializerDelegate());
|
| deserializer.SetSupportsLegacyWireFormat(true);
|
| + deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
| BeforeDecode(&deserializer);
|
| ASSERT_TRUE(deserializer.ReadHeader(context).FromMaybe(false));
|
| ASSERT_EQ(0u, deserializer.GetWireFormatVersion());
|
| @@ -219,6 +224,7 @@ class ValueSerializerTest : public TestWithIsolate {
|
| static_cast<int>(data.size()),
|
| GetDeserializerDelegate());
|
| deserializer.SetSupportsLegacyWireFormat(true);
|
| + deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
| BeforeDecode(&deserializer);
|
| Maybe<bool> header_result = deserializer.ReadHeader(context);
|
| if (header_result.IsNothing()) {
|
| @@ -275,6 +281,7 @@ class ValueSerializerTest : public TestWithIsolate {
|
| Local<Context> deserialization_context_;
|
| Local<FunctionTemplate> host_object_constructor_template_;
|
| i::Isolate* isolate_;
|
| + bool expect_inline_wasm_ = false;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(ValueSerializerTest);
|
| };
|
| @@ -2594,7 +2601,44 @@ TEST_F(ValueSerializerTestWithHostArrayBufferView, RoundTripUint8ArrayInput) {
|
| // mostly checks that the logic to embed it in structured clone serialization
|
| // works correctly.
|
|
|
| +// A simple module which exports an "increment" function.
|
| +// Copied from test/mjsunit/wasm/incrementer.wasm.
|
| +const unsigned char kIncrementerWasm[] = {
|
| + 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127,
|
| + 3, 2, 1, 0, 7, 13, 1, 9, 105, 110, 99, 114, 101, 109, 101, 110,
|
| + 116, 0, 0, 10, 9, 1, 7, 0, 32, 0, 65, 1, 106, 11,
|
| +};
|
| +
|
| class ValueSerializerTestWithWasm : public ValueSerializerTest {
|
| + public:
|
| + static const char* kUnsupportedSerialization;
|
| +
|
| + ValueSerializerTestWithWasm()
|
| + : serialize_delegate_(&transfer_modules_),
|
| + deserialize_delegate_(&transfer_modules_) {}
|
| +
|
| + void Reset() {
|
| + current_serializer_delegate_ = nullptr;
|
| + transfer_modules_.clear();
|
| + SetExpectInlineWasm(false);
|
| + }
|
| +
|
| + void EnableTransferSerialization() {
|
| + current_serializer_delegate_ = &serialize_delegate_;
|
| + }
|
| +
|
| + void EnableTransferDeserialization() {
|
| + current_deserializer_delegate_ = &deserialize_delegate_;
|
| + }
|
| +
|
| + void EnableThrowingSerializer() {
|
| + current_serializer_delegate_ = &throwing_serializer_;
|
| + }
|
| +
|
| + void EnableDefaultDeserializer() {
|
| + current_deserializer_delegate_ = &default_deserializer_;
|
| + }
|
| +
|
| protected:
|
| static void SetUpTestCase() {
|
| g_saved_flag = i::FLAG_expose_wasm;
|
| @@ -2608,32 +2652,243 @@ class ValueSerializerTestWithWasm : public ValueSerializerTest {
|
| g_saved_flag = false;
|
| }
|
|
|
| + class ThrowingSerializer : public ValueSerializer::Delegate {
|
| + public:
|
| + Maybe<uint32_t> GetWasmModuleTransferId(
|
| + Isolate* isolate, Local<WasmCompiledModule> module) override {
|
| + isolate->ThrowException(Exception::Error(
|
| + String::NewFromOneByte(
|
| + isolate,
|
| + reinterpret_cast<const uint8_t*>(kUnsupportedSerialization),
|
| + NewStringType::kNormal)
|
| + .ToLocalChecked()));
|
| + return Nothing<uint32_t>();
|
| + }
|
| +
|
| + void ThrowDataCloneError(Local<String> message) override { UNREACHABLE(); }
|
| + };
|
| +
|
| + class SerializeToTransfer : public ValueSerializer::Delegate {
|
| + public:
|
| + SerializeToTransfer(
|
| + std::vector<WasmCompiledModule::TransferrableModule>* modules)
|
| + : modules_(modules) {}
|
| + Maybe<uint32_t> GetWasmModuleTransferId(
|
| + Isolate* isolate, Local<WasmCompiledModule> module) override {
|
| + modules_->push_back(module->GetTransferrableModule());
|
| + return Just(static_cast<uint32_t>(modules_->size()) - 1);
|
| + }
|
| +
|
| + void ThrowDataCloneError(Local<String> message) override { UNREACHABLE(); }
|
| +
|
| + private:
|
| + std::vector<WasmCompiledModule::TransferrableModule>* modules_;
|
| + };
|
| +
|
| + class DeserializeFromTransfer : public ValueDeserializer::Delegate {
|
| + public:
|
| + DeserializeFromTransfer(
|
| + std::vector<WasmCompiledModule::TransferrableModule>* modules)
|
| + : modules_(modules) {}
|
| +
|
| + MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(Isolate* isolate,
|
| + uint32_t id) override {
|
| + return WasmCompiledModule::FromTransferrableModule(isolate,
|
| + modules_->at(id));
|
| + }
|
| +
|
| + private:
|
| + std::vector<WasmCompiledModule::TransferrableModule>* modules_;
|
| + };
|
| +
|
| + ValueSerializer::Delegate* GetSerializerDelegate() override {
|
| + return current_serializer_delegate_;
|
| + }
|
| +
|
| + ValueDeserializer::Delegate* GetDeserializerDelegate() override {
|
| + return current_deserializer_delegate_;
|
| + }
|
| +
|
| + Local<WasmCompiledModule> MakeWasm() {
|
| + return WasmCompiledModule::DeserializeOrCompile(
|
| + isolate(), {nullptr, 0},
|
| + {kIncrementerWasm, sizeof(kIncrementerWasm)})
|
| + .ToLocalChecked();
|
| + }
|
| +
|
| + void ExpectPass() {
|
| + RoundTripTest(
|
| + [this]() { return MakeWasm(); },
|
| + [this](Local<Value> value) {
|
| + ASSERT_TRUE(value->IsWebAssemblyCompiledModule());
|
| + EXPECT_TRUE(EvaluateScriptForResultBool(
|
| + "new WebAssembly.Instance(result).exports.increment(8) === 9"));
|
| + });
|
| + }
|
| +
|
| + void ExpectFail() {
|
| + EncodeTest(
|
| + [this]() { return MakeWasm(); },
|
| + [this](const std::vector<uint8_t>& data) { InvalidDecodeTest(data); });
|
| + }
|
| +
|
| + Local<Value> GetComplexObjectWithDuplicate() {
|
| + Local<Value> wasm_module = MakeWasm();
|
| + serialization_context()
|
| + ->Global()
|
| + ->CreateDataProperty(serialization_context(),
|
| + StringFromUtf8("wasm_module"), wasm_module)
|
| + .FromMaybe(false);
|
| + Local<Script> script =
|
| + Script::Compile(
|
| + serialization_context(),
|
| + StringFromUtf8("({mod1: wasm_module, num: 2, mod2: wasm_module})"))
|
| + .ToLocalChecked();
|
| + return script->Run(serialization_context()).ToLocalChecked();
|
| + }
|
| +
|
| + void VerifyComplexObject(Local<Value> value) {
|
| + ASSERT_TRUE(value->IsObject());
|
| + EXPECT_TRUE(EvaluateScriptForResultBool(
|
| + "result.mod1 instanceof WebAssembly.Module"));
|
| + EXPECT_TRUE(EvaluateScriptForResultBool(
|
| + "result.mod2 instanceof WebAssembly.Module"));
|
| + EXPECT_TRUE(EvaluateScriptForResultBool("result.num === 2"));
|
| + }
|
| +
|
| + Local<Value> GetComplexObjectWithMany() {
|
| + Local<Value> wasm_module1 = MakeWasm();
|
| + Local<Value> wasm_module2 = MakeWasm();
|
| + serialization_context()
|
| + ->Global()
|
| + ->CreateDataProperty(serialization_context(),
|
| + StringFromUtf8("wasm_module1"), wasm_module1)
|
| + .FromMaybe(false);
|
| + serialization_context()
|
| + ->Global()
|
| + ->CreateDataProperty(serialization_context(),
|
| + StringFromUtf8("wasm_module2"), wasm_module2)
|
| + .FromMaybe(false);
|
| + Local<Script> script =
|
| + Script::Compile(
|
| + serialization_context(),
|
| + StringFromUtf8(
|
| + "({mod1: wasm_module1, num: 2, mod2: wasm_module2})"))
|
| + .ToLocalChecked();
|
| + return script->Run(serialization_context()).ToLocalChecked();
|
| + }
|
| +
|
| private:
|
| static bool g_saved_flag;
|
| + std::vector<WasmCompiledModule::TransferrableModule> transfer_modules_;
|
| + SerializeToTransfer serialize_delegate_;
|
| + DeserializeFromTransfer deserialize_delegate_;
|
| + ValueSerializer::Delegate* current_serializer_delegate_ = nullptr;
|
| + ValueDeserializer::Delegate* current_deserializer_delegate_ = nullptr;
|
| + ThrowingSerializer throwing_serializer_;
|
| + ValueDeserializer::Delegate default_deserializer_;
|
| };
|
|
|
| bool ValueSerializerTestWithWasm::g_saved_flag = false;
|
| +const char* ValueSerializerTestWithWasm::kUnsupportedSerialization =
|
| + "Wasm Serialization Not Supported";
|
| +
|
| +// The default implementation of the serialization
|
| +// delegate throws when trying to serialize wasm. The
|
| +// embedder must decide serialization policy.
|
| +TEST_F(ValueSerializerTestWithWasm, DefaultSerializationDelegate) {
|
| + EnableThrowingSerializer();
|
| + InvalidEncodeTest(
|
| + [this]() { return MakeWasm(); },
|
| + [](Local<Message> message) {
|
| + size_t msg_len = static_cast<size_t>(message->Get()->Length());
|
| + std::unique_ptr<char[]> buff(new char[msg_len + 1]);
|
| + message->Get()->WriteOneByte(reinterpret_cast<uint8_t*>(buff.get()));
|
| + // the message ends with the custom error string
|
| + size_t custom_msg_len = strlen(kUnsupportedSerialization);
|
| + ASSERT_GE(msg_len, custom_msg_len);
|
| + size_t start_pos = msg_len - custom_msg_len;
|
| + ASSERT_EQ(strcmp(&buff.get()[start_pos], kUnsupportedSerialization), 0);
|
| + });
|
| +}
|
|
|
| -// A simple module which exports an "increment" function.
|
| -// Copied from test/mjsunit/wasm/incrementer.wasm.
|
| -const unsigned char kIncrementerWasm[] = {
|
| - 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127,
|
| - 3, 2, 1, 0, 7, 13, 1, 9, 105, 110, 99, 114, 101, 109, 101, 110,
|
| - 116, 0, 0, 10, 9, 1, 7, 0, 32, 0, 65, 1, 106, 11,
|
| -};
|
| +// The default deserializer throws if wasm transfer is attempted
|
| +TEST_F(ValueSerializerTestWithWasm, DefaultDeserializationDelegate) {
|
| + EnableTransferSerialization();
|
| + EnableDefaultDeserializer();
|
| + EncodeTest(
|
| + [this]() { return MakeWasm(); },
|
| + [this](const std::vector<uint8_t>& data) { InvalidDecodeTest(data); });
|
| +}
|
|
|
| -TEST_F(ValueSerializerTestWithWasm, RoundTripWasmModule) {
|
| +// We only want to allow deserialization through
|
| +// transferred modules - which requres both serializer
|
| +// and deserializer to understand that - or through
|
| +// explicitly allowing inlined data, which requires
|
| +// deserializer opt-in (we default the serializer to
|
| +// inlined data because we don't trust that data on the
|
| +// receiving end anyway).
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, RoundtripWasmTransfer) {
|
| + EnableTransferSerialization();
|
| + EnableTransferDeserialization();
|
| + ExpectPass();
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, RountripWasmInline) {
|
| + SetExpectInlineWasm(true);
|
| + ExpectPass();
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, CannotDeserializeWasmInlineData) {
|
| + ExpectFail();
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, CannotTransferWasmWhenExpectingInline) {
|
| + EnableTransferSerialization();
|
| + SetExpectInlineWasm(true);
|
| + ExpectFail();
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, ComplexObjectDuplicateTransfer) {
|
| + EnableTransferSerialization();
|
| + EnableTransferDeserialization();
|
| RoundTripTest(
|
| - [this]() {
|
| - return WasmCompiledModule::DeserializeOrCompile(
|
| - isolate(), {nullptr, 0},
|
| - {kIncrementerWasm, sizeof(kIncrementerWasm)})
|
| - .ToLocalChecked();
|
| - },
|
| + [this]() { return GetComplexObjectWithDuplicate(); },
|
| [this](Local<Value> value) {
|
| - ASSERT_TRUE(value->IsWebAssemblyCompiledModule());
|
| - EXPECT_TRUE(EvaluateScriptForResultBool(
|
| - "new WebAssembly.Instance(result).exports.increment(8) === 9"));
|
| + VerifyComplexObject(value);
|
| + EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 === result.mod2"));
|
| + });
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, ComplexObjectDuplicateInline) {
|
| + SetExpectInlineWasm(true);
|
| + RoundTripTest(
|
| + [this]() { return GetComplexObjectWithDuplicate(); },
|
| + [this](Local<Value> value) {
|
| + VerifyComplexObject(value);
|
| + EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 === result.mod2"));
|
| + });
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, ComplexObjectWithManyTransfer) {
|
| + EnableTransferSerialization();
|
| + EnableTransferDeserialization();
|
| + RoundTripTest(
|
| + [this]() { return GetComplexObjectWithMany(); },
|
| + [this](Local<Value> value) {
|
| + VerifyComplexObject(value);
|
| + EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 != result.mod2"));
|
| + });
|
| +}
|
| +
|
| +TEST_F(ValueSerializerTestWithWasm, ComplexObjectWithManyInline) {
|
| + SetExpectInlineWasm(true);
|
| + RoundTripTest(
|
| + [this]() { return GetComplexObjectWithMany(); },
|
| + [this](Local<Value> value) {
|
| + VerifyComplexObject(value);
|
| + EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 != result.mod2"));
|
| });
|
| }
|
|
|
|
|