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")); |
}); |
} |