Revert of [wasm] Transferrable modules (patchset #13 id:280001 of https://codereview.chromium.org/2748473004/ )

Reason for revert:
Breaks layout tests:
https://build.chromium.org/p/client.v8.fyi/builders/V8-Blink%20Linux%2064/builds/14312

See https://github.com/v8/v8/wiki/Blink-layout-tests

Original issue's description:
> [wasm] Transferrable modules
>
> We want to restrict structured cloning in Chrome to:
> - postMessage senders and receivers that are co-located
> in the same process
> - indexedDB (just https).
>
> For context, on the Chrome side, we will achieve the postMessage part
> by using a mechanism similar to transferrables: the
> SerializedScriptValue will have a list of wasm modules, separate from
> the serialized data stream; and this list won't be copied cross
> process boundaries. The IDB part is achieved by explicitly opting in
> reading/writing to the serialization stream. To block attack vectors
> in IPC cases, the default for deserialization will be to expect data
> in the wasm transfers list.
>
> This change is the V8 side necessary to enabling this design. We
> introduce TransferrableModule, an opaque datatype exposed to the
> embedder. Internally, TransferrableModules are just serialized data,
> because we don't have a better mechanism, at the moment, for
> de-contextualizing/re-contextualizing wasm modules (wrt Isolate and
> Context).
>
> The chrome defaults will be implemented in the
> serialization/deserialization delegates on that side. For the v8 side
> of things, in the absence of a serialization delegate, the V8
> serializer will write to serialization stream. In the absence of a
> deserialization delegate, the deserializer won't work. This asymmetry
> is intentional - it communicates to the embedder the need to make a
> policy decision, otherwise wasm serialization/deserialization won't
> work "out of the box".
>
> BUG=v8:6079
>
> Review-Url: https://codereview.chromium.org/2748473004
> Cr-Commit-Position: refs/heads/master@{#43955}
> Committed: 99743ad460

TBR=jbroman@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:6079

Review-Url: https://codereview.chromium.org/2762163002
Cr-Commit-Position: refs/heads/master@{#43981}
This commit is contained in:
machenbach 2017-03-21 06:54:14 -07:00 committed by Commit bot
parent 6deb226705
commit e538b70e1a
6 changed files with 24 additions and 464 deletions

View File

@ -108,7 +108,6 @@ class Private;
class Uint32;
class Utils;
class Value;
class WasmCompiledModule;
template <class T> class Local;
template <class T>
class MaybeLocal;
@ -1710,8 +1709,6 @@ class V8_EXPORT ValueSerializer {
virtual Maybe<uint32_t> GetSharedArrayBufferId(
Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer);
virtual Maybe<uint32_t> GetWasmModuleTransferId(
Isolate* isolate, Local<WasmCompiledModule> module);
/*
* Allocates memory for the buffer of at least the size provided. The actual
* size (which may be greater or equal) is written to |actual_size|. If no
@ -1822,13 +1819,6 @@ class V8_EXPORT ValueDeserializer {
* MaybeLocal<Object>() returned.
*/
virtual MaybeLocal<Object> ReadHostObject(Isolate* isolate);
/*
* Get a WasmCompiledModule given a transfer_id previously provided
* by ValueSerializer::GetWasmModuleTransferId
*/
virtual MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
Isolate* isolate, uint32_t transfer_id);
};
ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size);
@ -1871,11 +1861,6 @@ class V8_EXPORT ValueDeserializer {
*/
void SetSupportsLegacyWireFormat(bool supports_legacy_wire_format);
/*
* Expect inline wasm in the data stream (rather than in-memory transfer)
*/
void SetExpectInlineWasm(bool allow_inline_wasm);
/*
* Reads the underlying wire format version. Likely mostly to be useful to
* legacy code reading old wire format versions. Must be called after
@ -3918,37 +3903,6 @@ class V8_EXPORT WasmCompiledModule : public Object {
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> SerializedModule;
// A buffer that is owned by the caller.
typedef std::pair<const uint8_t*, size_t> CallerOwnedBuffer;
// An opaque, native heap object for transferring wasm modules. It
// supports move semantics, and does not support copy semantics.
class TransferrableModule final {
public:
TransferrableModule(TransferrableModule&& src) = default;
TransferrableModule(const TransferrableModule& src) = delete;
TransferrableModule& operator=(TransferrableModule&& src) = default;
TransferrableModule& operator=(const TransferrableModule& src) = delete;
private:
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer;
friend class WasmCompiledModule;
TransferrableModule(OwnedBuffer&& code, OwnedBuffer&& bytes)
: compiled_code(std::move(code)), wire_bytes(std::move(bytes)) {}
OwnedBuffer compiled_code = {nullptr, 0};
OwnedBuffer wire_bytes = {nullptr, 0};
};
// Get an in-memory, non-persistable, and context-independent (meaning,
// suitable for transfer to another Isolate and Context) representation
// of this wasm compiled module.
TransferrableModule GetTransferrableModule();
// Efficiently re-create a WasmCompiledModule, without recompiling, from
// a TransferrableModule.
static MaybeLocal<WasmCompiledModule> FromTransferrableModule(
Isolate* isolate, const TransferrableModule&);
// Get the wasm-encoded bytes that were used to compile this module.
Local<String> GetWasmWireBytes();
@ -3970,11 +3924,6 @@ class V8_EXPORT WasmCompiledModule : public Object {
static MaybeLocal<WasmCompiledModule> Compile(Isolate* isolate,
const uint8_t* start,
size_t length);
static CallerOwnedBuffer AsCallerOwned(
const TransferrableModule::OwnedBuffer& buff) {
return {buff.first.get(), buff.second};
}
WasmCompiledModule();
static void CheckCast(Value* obj);
};

View File

@ -3125,11 +3125,6 @@ Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
return Nothing<uint32_t>();
}
Maybe<uint32_t> ValueSerializer::Delegate::GetWasmModuleTransferId(
Isolate* v8_isolate, Local<WasmCompiledModule> module) {
return Nothing<uint32_t>();
}
void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
size_t size,
size_t* actual_size) {
@ -3218,15 +3213,6 @@ MaybeLocal<Object> ValueDeserializer::Delegate::ReadHostObject(
return MaybeLocal<Object>();
}
MaybeLocal<WasmCompiledModule> ValueDeserializer::Delegate::GetWasmModuleFromId(
Isolate* v8_isolate, uint32_t id) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->ScheduleThrow(*isolate->factory()->NewError(
isolate->error_function(),
i::MessageTemplate::kDataCloneDeserializationError));
return MaybeLocal<WasmCompiledModule>();
}
struct ValueDeserializer::PrivateData {
PrivateData(i::Isolate* i, i::Vector<const uint8_t> data, Delegate* delegate)
: isolate(i), deserializer(i, data, delegate) {}
@ -3289,10 +3275,6 @@ void ValueDeserializer::SetSupportsLegacyWireFormat(
private_->supports_legacy_wire_format = supports_legacy_wire_format;
}
void ValueDeserializer::SetExpectInlineWasm(bool expect_inline_wasm) {
private_->deserializer.set_expect_inline_wasm(expect_inline_wasm);
}
uint32_t ValueDeserializer::GetWireFormatVersion() const {
CHECK(!private_->has_aborted);
return private_->deserializer.GetWireFormatVersion();
@ -7524,36 +7506,6 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() {
return Local<String>::Cast(Utils::ToLocal(wire_bytes));
}
// Currently, wasm modules are bound, both to Isolate and to
// the Context they were created in. The currently-supported means to
// decontextualize and then re-contextualize a module is via
// serialization/deserialization.
WasmCompiledModule::TransferrableModule
WasmCompiledModule::GetTransferrableModule() {
i::DisallowHeapAllocation no_gc;
WasmCompiledModule::SerializedModule compiled_part = Serialize();
Local<String> wire_bytes = GetWasmWireBytes();
size_t wire_size = static_cast<size_t>(wire_bytes->Length());
uint8_t* bytes = new uint8_t[wire_size];
wire_bytes->WriteOneByte(bytes, 0, wire_bytes->Length());
return TransferrableModule(
std::move(compiled_part),
std::make_pair(
std::unique_ptr<const uint8_t[]>(const_cast<const uint8_t*>(bytes)),
wire_size));
}
MaybeLocal<WasmCompiledModule> WasmCompiledModule::FromTransferrableModule(
Isolate* isolate,
const WasmCompiledModule::TransferrableModule& transferrable_module) {
MaybeLocal<WasmCompiledModule> ret =
Deserialize(isolate, AsCallerOwned(transferrable_module.compiled_code),
AsCallerOwned(transferrable_module.wire_bytes));
return ret;
}
WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));

View File

@ -126,8 +126,6 @@ enum class SerializationTag : uint8_t {
// wasmWireByteLength:uint32_t, then raw data
// compiledDataLength:uint32_t, then raw data
kWasmModule = 'W',
// A wasm module object transfer. next value is its index.
kWasmModuleTransfer = 'w',
// The delegate is responsible for processing all following data.
// This "escapes" to whatever wire format the delegate chooses.
kHostObject = '\\',
@ -805,19 +803,6 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
}
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
if (delegate_ != nullptr) {
Maybe<uint32_t> transfer_id = delegate_->GetWasmModuleTransferId(
reinterpret_cast<v8::Isolate*>(isolate_),
v8::Local<v8::WasmCompiledModule>::Cast(Utils::ToLocal(object)));
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
uint32_t id = 0;
if (transfer_id.To(&id)) {
WriteTag(SerializationTag::kWasmModuleTransfer);
WriteVarint<uint32_t>(id);
return Just(true);
}
}
Handle<WasmCompiledModule> compiled_part(
WasmCompiledModule::cast(object->GetEmbedderField(0)), isolate_);
WasmEncodingTag encoding_tag = WasmEncodingTag::kRawBytes;
@ -1165,8 +1150,6 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
}
case SerializationTag::kWasmModule:
return ReadWasmModule();
case SerializationTag::kWasmModuleTransfer:
return ReadWasmModuleTransfer();
case SerializationTag::kHostObject:
return ReadHostObject();
default:
@ -1612,32 +1595,8 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
return typed_array;
}
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModuleTransfer() {
if (FLAG_wasm_disable_structured_cloning || expect_inline_wasm()) {
return MaybeHandle<JSObject>();
}
uint32_t transfer_id = 0;
Local<Value> module_value;
if (!ReadVarint<uint32_t>().To(&transfer_id) || delegate_ == nullptr ||
!delegate_
->GetWasmModuleFromId(reinterpret_cast<v8::Isolate*>(isolate_),
transfer_id)
.ToLocal(&module_value)) {
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
return MaybeHandle<JSObject>();
}
uint32_t id = next_id_++;
Handle<JSObject> module =
Handle<JSObject>::cast(Utils::OpenHandle(*module_value));
AddObjectWithID(id, module);
return module;
}
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
if (FLAG_wasm_disable_structured_cloning || !expect_inline_wasm()) {
return MaybeHandle<JSObject>();
}
if (FLAG_wasm_disable_structured_cloning) return MaybeHandle<JSObject>();
Vector<const uint8_t> encoding_tag;
if (!ReadRawBytes(sizeof(WasmEncodingTag)).To(&encoding_tag) ||
@ -1666,22 +1625,21 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
// Try to deserialize the compiled module first.
ScriptData script_data(compiled_bytes.start(), compiled_bytes.length());
Handle<FixedArray> compiled_part;
MaybeHandle<JSObject> result;
if (WasmCompiledModuleSerializer::DeserializeWasmModule(
isolate_, &script_data, wire_bytes)
.ToHandle(&compiled_part)) {
result = WasmModuleObject::New(
return WasmModuleObject::New(
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
} else {
}
// If that fails, recompile.
MaybeHandle<JSObject> result;
{
wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
result = wasm::SyncCompile(isolate_, &thrower,
wasm::ModuleWireBytes(wire_bytes));
}
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
uint32_t id = next_id_++;
if (!result.is_null()) {
AddObjectWithID(id, result.ToHandleChecked());
}
return result;
}

View File

@ -31,7 +31,6 @@ class JSValue;
class Object;
class Oddball;
class Smi;
class WasmModuleObject;
enum class SerializationTag : uint8_t;
@ -219,9 +218,6 @@ class ValueDeserializer {
bool ReadUint64(uint64_t* value) WARN_UNUSED_RESULT;
bool ReadDouble(double* value) WARN_UNUSED_RESULT;
bool ReadRawBytes(size_t length, const void** data) WARN_UNUSED_RESULT;
void set_expect_inline_wasm(bool expect_inline_wasm) {
expect_inline_wasm_ = expect_inline_wasm;
}
private:
// Reading the wire format.
@ -234,7 +230,6 @@ class ValueDeserializer {
Maybe<T> ReadZigZag() WARN_UNUSED_RESULT;
Maybe<double> ReadDouble() WARN_UNUSED_RESULT;
Maybe<Vector<const uint8_t>> ReadRawBytes(int size) WARN_UNUSED_RESULT;
bool expect_inline_wasm() const { return expect_inline_wasm_; }
// Reads a string if it matches the one provided.
// Returns true if this was the case. Otherwise, nothing is consumed.
@ -268,7 +263,6 @@ class ValueDeserializer {
MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView(
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModule() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadWasmModuleTransfer() WARN_UNUSED_RESULT;
MaybeHandle<JSObject> ReadHostObject() WARN_UNUSED_RESULT;
/*
@ -291,7 +285,6 @@ class ValueDeserializer {
PretenureFlag pretenure_;
uint32_t version_ = 0;
uint32_t next_id_ = 0;
bool expect_inline_wasm_ = false;
// Always global handles.
Handle<FixedArray> id_map_;

View File

@ -469,43 +469,6 @@ TEST(BlockWasmCodeGenAtDeserialization) {
Cleanup();
}
TEST(TransferrableWasmModules) {
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
ZoneBuffer buffer(&zone);
WasmSerializationTest::BuildWireBytes(&zone, &buffer);
Isolate* from_isolate = CcTest::InitIsolateOnce();
ErrorThrower thrower(from_isolate, "");
std::vector<v8::WasmCompiledModule::TransferrableModule> store;
{
HandleScope scope(from_isolate);
testing::SetupIsolateForWasmModule(from_isolate);
MaybeHandle<WasmModuleObject> module_object = SyncCompile(
from_isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
v8::Local<v8::WasmCompiledModule> v8_module =
v8::Local<v8::WasmCompiledModule>::Cast(v8::Utils::ToLocal(
Handle<JSObject>::cast(module_object.ToHandleChecked())));
store.push_back(v8_module->GetTransferrableModule());
}
{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
from_isolate->array_buffer_allocator();
v8::Isolate* to_isolate = v8::Isolate::New(create_params);
v8::HandleScope new_scope(to_isolate);
v8::Local<v8::Context> deserialization_context =
v8::Context::New(to_isolate);
deserialization_context->Enter();
v8::MaybeLocal<v8::WasmCompiledModule> mod =
v8::WasmCompiledModule::FromTransferrableModule(to_isolate, store[0]);
CHECK(!mod.IsEmpty());
}
}
TEST(MemorySize) {
{
// Initial memory size is 16, see wasm-module-builder.cc

View File

@ -74,9 +74,6 @@ 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*) {}
@ -175,7 +172,6 @@ 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;
@ -200,7 +196,6 @@ 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());
@ -224,7 +219,6 @@ 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()) {
@ -281,7 +275,6 @@ 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);
};
@ -2601,44 +2594,7 @@ 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;
@ -2652,243 +2608,32 @@ 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); });
}
// 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();
TEST_F(ValueSerializerTestWithWasm, RoundTripWasmModule) {
RoundTripTest(
[this]() { return GetComplexObjectWithDuplicate(); },
[this]() {
return WasmCompiledModule::DeserializeOrCompile(
isolate(), {nullptr, 0},
{kIncrementerWasm, sizeof(kIncrementerWasm)})
.ToLocalChecked();
},
[this](Local<Value> value) {
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"));
ASSERT_TRUE(value->IsWebAssemblyCompiledModule());
EXPECT_TRUE(EvaluateScriptForResultBool(
"new WebAssembly.Instance(result).exports.increment(8) === 9"));
});
}