Commit 917ef616 authored by mtrofin's avatar mtrofin Committed by Commit bot

[wasm] Support recompilation if deserialization fails.

One step closer to the informally-agreed upon specification
that structured cloning will always succeed, meaning, if
we fail to deserialize (e.g. because version mismatch in
serialized format and v8 version), we recompile.

As part of this work, the deserializer will need to become
more resilient to invalid input data, and fail graciously
rather than CHECK-ing. This CL addresses some of that,
sufficient to unblock the current serialization tests.
Subsequent CLs will add more testing and the appropriate
fixes.

BUG=639090

Review-Url: https://codereview.chromium.org/2395793003
Cr-Commit-Position: refs/heads/master@{#40058}
parent 3aeaf497
......@@ -3902,13 +3902,27 @@ class V8_EXPORT Proxy : public Object {
class V8_EXPORT WasmCompiledModule : public Object {
public:
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> SerializedModule;
// Get the uncompiled bytes that were used to compile this module.
Local<String> GetUncompiledBytes();
// Serialize the compiled module. The serialized data does not include the
// uncompiled bytes.
SerializedModule Serialize();
// TODO(mtrofin): Back-compat. Move to private once change lands in Chrome.
// The resulting wasm setup won't have its uncompiled bytes available.
static MaybeLocal<WasmCompiledModule> Deserialize(
Isolate* isolate, const SerializedModule& serialized_data);
// If possible, deserialize the module, otherwise compile it from the provided
// uncompiled bytes.
static MaybeLocal<WasmCompiledModule> DeserializeOrCompile(
Isolate* isolate, const SerializedModule& serialized_data,
Local<String> uncompiled_bytes);
V8_INLINE static WasmCompiledModule* Cast(Value* obj);
private:
static MaybeLocal<WasmCompiledModule> Compile(Isolate* isolate,
Local<String> bytes);
WasmCompiledModule();
static void CheckCast(Value* obj);
};
......
......@@ -7184,15 +7184,28 @@ MaybeLocal<Proxy> Proxy::New(Local<Context> context, Local<Object> local_target,
RETURN_ESCAPED(result);
}
Local<String> WasmCompiledModule::GetUncompiledBytes() {
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
i::Handle<i::wasm::WasmCompiledModule> compiled_part =
i::handle(i::wasm::WasmCompiledModule::cast(obj->GetInternalField(0)));
return Local<String>::Cast(Utils::ToLocal(compiled_part->module_bytes()));
}
WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
i::Handle<i::FixedArray> compiled_part =
i::handle(i::FixedArray::cast(obj->GetInternalField(0)));
i::Handle<i::wasm::WasmCompiledModule> compiled_part =
i::handle(i::wasm::WasmCompiledModule::cast(obj->GetInternalField(0)));
i::Handle<i::String> uncompiled_bytes = compiled_part->module_bytes();
compiled_part->reset_module_bytes();
std::unique_ptr<i::ScriptData> script_data =
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
compiled_part);
compiled_part->set_module_bytes(uncompiled_bytes);
script_data->ReleaseDataOwnership();
size_t size = static_cast<size_t>(script_data->length());
return {std::unique_ptr<const uint8_t[]>(script_data->data()), size};
}
......@@ -7209,9 +7222,35 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize(
if (!maybe_compiled_part.ToHandle(&compiled_part)) {
return MaybeLocal<WasmCompiledModule>();
}
i::Handle<i::wasm::WasmCompiledModule> compiled_module =
handle(i::wasm::WasmCompiledModule::cast(*compiled_part));
return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(i::wasm::CreateCompiledModuleObject(
i_isolate, compiled_part, i::wasm::ModuleOrigin::kWasmOrigin)));
i_isolate, compiled_module, i::wasm::ModuleOrigin::kWasmOrigin)));
}
MaybeLocal<WasmCompiledModule> WasmCompiledModule::DeserializeOrCompile(
Isolate* isolate,
const WasmCompiledModule::SerializedModule& serialized_data,
Local<String> uncompiled_bytes) {
MaybeLocal<WasmCompiledModule> ret = Deserialize(isolate, serialized_data);
if (!ret.IsEmpty()) return ret;
return Compile(isolate, uncompiled_bytes);
}
MaybeLocal<WasmCompiledModule> WasmCompiledModule::Compile(
Isolate* isolate, Local<String> bytes) {
i::Handle<i::String> module_bytes = Utils::OpenHandle(*bytes);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::wasm::ErrorThrower thrower(i_isolate, "WasmCompiledModule::Deserialize()");
i::SeqOneByteString* data = i::SeqOneByteString::cast(*module_bytes);
i::MaybeHandle<i::JSObject> maybe_compiled =
i::wasm::CreateModuleObjectFromBytes(
i_isolate, data->GetChars(), data->GetChars() + data->length(),
&thrower, i::wasm::ModuleOrigin::kWasmOrigin);
if (maybe_compiled.is_null()) return MaybeLocal<WasmCompiledModule>();
return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(maybe_compiled.ToHandleChecked()));
}
// static
......
......@@ -340,6 +340,7 @@ SerializedCodeData::SerializedCodeData(const List<byte>* payload,
SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
Isolate* isolate, uint32_t expected_source_hash) const {
if (this->size_ < kHeaderSize) return INVALID_HEADER;
uint32_t magic_number = GetMagicNumber();
if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
......
......@@ -92,7 +92,8 @@ class SerializedCodeData : public SerializedData {
SOURCE_MISMATCH = 3,
CPU_FEATURES_MISMATCH = 4,
FLAGS_MISMATCH = 5,
CHECKSUM_MISMATCH = 6
CHECKSUM_MISMATCH = 6,
INVALID_HEADER = 7
};
// Used when consuming.
......
......@@ -197,6 +197,8 @@ TEST(Run_WasmModule_Serialization) {
Isolate* isolate = CcTest::InitIsolateOnce();
ErrorThrower thrower(isolate, "");
uint8_t* bytes = nullptr;
int buffer_size = -1;
v8::WasmCompiledModule::SerializedModule data;
{
HandleScope scope(isolate);
......@@ -217,6 +219,11 @@ TEST(Run_WasmModule_Serialization) {
v8::Local<v8::WasmCompiledModule> v8_compiled_module =
v8_module_obj.As<v8::WasmCompiledModule>();
v8::Local<v8::String> uncompiled_bytes =
v8_compiled_module->GetUncompiledBytes();
buffer_size = uncompiled_bytes->Length();
bytes = zone.NewArray<uint8_t>(buffer_size);
uncompiled_bytes->WriteOneByte(bytes);
data = v8_compiled_module->Serialize();
}
......@@ -224,32 +231,44 @@ TEST(Run_WasmModule_Serialization) {
create_params.array_buffer_allocator =
CcTest::InitIsolateOnce()->array_buffer_allocator();
v8::Isolate* v8_isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(v8_isolate);
v8::HandleScope new_scope(v8_isolate);
v8::Local<v8::Context> new_ctx = v8::Context::New(v8_isolate);
new_ctx->Enter();
isolate = reinterpret_cast<Isolate*>(v8_isolate);
testing::SetupIsolateForWasmModule(isolate);
v8::MaybeLocal<v8::WasmCompiledModule> deserialized =
v8::WasmCompiledModule::Deserialize(v8_isolate, data);
v8::Local<v8::WasmCompiledModule> compiled_module;
CHECK(deserialized.ToLocal(&compiled_module));
Handle<JSObject> module_object =
Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module));
Handle<JSObject> instance =
WasmModule::Instantiate(isolate, &thrower, module_object,
Handle<JSReceiver>::null(),
Handle<JSArrayBuffer>::null())
.ToHandleChecked();
Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(41), isolate)};
int32_t result = testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, kFunctionName, 1, params,
ModuleOrigin::kWasmOrigin);
CHECK(result == 42);
new_ctx->Exit();
for (int i = 0; i < 2; ++i) {
v8::Isolate* v8_isolate = v8::Isolate::New(create_params);
if (i == 1) {
// Mess with the serialized data to force recompilation.
data.first.reset();
data.second = 0;
}
{
v8::Isolate::Scope isolate_scope(v8_isolate);
v8::HandleScope new_scope(v8_isolate);
v8::Local<v8::Context> new_ctx = v8::Context::New(v8_isolate);
new_ctx->Enter();
isolate = reinterpret_cast<Isolate*>(v8_isolate);
testing::SetupIsolateForWasmModule(isolate);
Vector<const uint8_t> raw(bytes, buffer_size);
v8::MaybeLocal<v8::WasmCompiledModule> deserialized =
v8::WasmCompiledModule::DeserializeOrCompile(
v8_isolate, data,
v8::Utils::ToLocal(isolate->factory()
->NewStringFromOneByte(raw)
.ToHandleChecked()));
v8::Local<v8::WasmCompiledModule> compiled_module;
CHECK(deserialized.ToLocal(&compiled_module));
Handle<JSObject> module_object =
Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module));
Handle<JSObject> instance =
WasmModule::Instantiate(isolate, &thrower, module_object,
Handle<JSReceiver>::null(),
Handle<JSArrayBuffer>::null())
.ToHandleChecked();
Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(41), isolate)};
int32_t result = testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, kFunctionName, 1, params,
ModuleOrigin::kWasmOrigin);
CHECK(result == 42);
new_ctx->Exit();
}
v8_isolate->Dispose();
}
}
......
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