[wasm] Fix serialization after instantiation

The regression comes from attempting to serialize a module with memory
requirements after instantiation - which is what happens in common emscripten
scenarios, where the module is obtained from WebAssembly.instantiate(buffer). We then try and serialize the JSArrayBuffer
representing the instance memory. That operation fails.

Added regression test and also extended the test to cover the other 2
instance-specific values - globals and tables.

Added a discussion on WasmCompiledModule (comments) explaining design decisions.

BUG=chromium:705562

Review-Url: https://codereview.chromium.org/2784453002
Cr-Commit-Position: refs/heads/master@{#44250}
This commit is contained in:
mtrofin 2017-03-29 14:22:57 -07:00 committed by Commit bot
parent 5d990dbfb7
commit f2531acb1e
5 changed files with 418 additions and 134 deletions

View File

@ -94,10 +94,8 @@ class InterpreterHandle {
}
// Set pointer to globals storage.
if (instance->has_globals_buffer()) {
instance_.globals_start =
reinterpret_cast<byte*>(instance->globals_buffer()->backing_store());
}
instance_.globals_start =
debug_info->wasm_instance()->compiled_module()->GetGlobalsStartOrNull();
}
~InterpreterHandle() {

View File

@ -649,77 +649,6 @@ class CompilationHelper {
}
};
static void ResetCompiledModule(Isolate* isolate, WasmInstanceObject* owner,
WasmCompiledModule* compiled_module) {
TRACE("Resetting %d\n", compiled_module->instance_id());
Object* undefined = *isolate->factory()->undefined_value();
Object* fct_obj = compiled_module->ptr_to_code_table();
if (fct_obj != nullptr && fct_obj != undefined) {
uint32_t old_mem_size = compiled_module->mem_size();
uint32_t default_mem_size = compiled_module->default_mem_size();
Object* mem_start = compiled_module->maybe_ptr_to_memory();
// Patch code to update memory references, global references, and function
// table references.
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
CodeSpecialization code_specialization(isolate, &specialization_zone);
if (old_mem_size > 0) {
CHECK_NE(mem_start, undefined);
Address old_mem_address =
static_cast<Address>(JSArrayBuffer::cast(mem_start)->backing_store());
code_specialization.RelocateMemoryReferences(
old_mem_address, old_mem_size, nullptr, default_mem_size);
}
if (owner->has_globals_buffer()) {
Address globals_start =
static_cast<Address>(owner->globals_buffer()->backing_store());
code_specialization.RelocateGlobals(globals_start, nullptr);
}
// Reset function tables.
if (compiled_module->has_function_tables()) {
FixedArray* function_tables = compiled_module->ptr_to_function_tables();
FixedArray* empty_function_tables =
compiled_module->ptr_to_empty_function_tables();
DCHECK_EQ(function_tables->length(), empty_function_tables->length());
for (int i = 0, e = function_tables->length(); i < e; ++i) {
code_specialization.RelocateObject(
handle(function_tables->get(i), isolate),
handle(empty_function_tables->get(i), isolate));
}
compiled_module->set_ptr_to_function_tables(empty_function_tables);
}
FixedArray* functions = FixedArray::cast(fct_obj);
for (int i = compiled_module->num_imported_functions(),
end = functions->length();
i < end; ++i) {
Code* code = Code::cast(functions->get(i));
// Skip lazy compile stubs.
if (code->builtin_index() == Builtins::kWasmCompileLazy) continue;
if (code->kind() != Code::WASM_FUNCTION) {
// From here on, there should only be wrappers for exported functions.
for (; i < end; ++i) {
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION,
Code::cast(functions->get(i))->kind());
}
break;
}
bool changed =
code_specialization.ApplyToWasmCode(code, SKIP_ICACHE_FLUSH);
// TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED
// above.
if (changed) {
Assembler::FlushICache(isolate, code->instruction_start(),
code->instruction_size());
}
}
}
compiled_module->reset_memory();
}
static void MemoryInstanceFinalizer(Isolate* isolate,
WasmInstanceObject* instance) {
DisallowHeapAllocation no_gc;
@ -805,7 +734,7 @@ static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
if (current_template == compiled_module) {
if (next == nullptr) {
ResetCompiledModule(isolate, owner, compiled_module);
WasmCompiledModule::Reset(isolate, compiled_module);
} else {
DCHECK(next->value()->IsFixedArray());
wasm_module->SetEmbedderField(0, next->value());
@ -1268,15 +1197,17 @@ class InstantiationHelper {
thrower_->RangeError("Out of memory: wasm globals");
return {};
}
Address old_globals_start = nullptr;
if (!owner.is_null()) {
DCHECK(owner.ToHandleChecked()->has_globals_buffer());
old_globals_start = static_cast<Address>(
owner.ToHandleChecked()->globals_buffer()->backing_store());
}
Address old_globals_start = compiled_module_->GetGlobalsStartOrNull();
Address new_globals_start =
static_cast<Address>(global_buffer->backing_store());
code_specialization.RelocateGlobals(old_globals_start, new_globals_start);
// The address of the backing buffer for the golbals is in native memory
// and, thus, not moving. We need it saved for
// serialization/deserialization purposes - so that the other end
// understands how to relocate the references. We still need to save the
// JSArrayBuffer on the instance, to keep it all alive.
WasmCompiledModule::SetGlobalsStartAddressFrom(factory, compiled_module_,
global_buffer);
instance->set_globals_buffer(*global_buffer);
}
@ -1312,8 +1243,6 @@ class InstantiationHelper {
//--------------------------------------------------------------------------
// Set up the memory for the new instance.
//--------------------------------------------------------------------------
MaybeHandle<JSArrayBuffer> old_memory;
uint32_t min_mem_pages = module_->min_mem_pages;
(module_->is_wasm() ? isolate_->counters()->wasm_wasm_min_mem_pages_count()
: isolate_->counters()->wasm_asm_min_mem_pages_count())
@ -1362,25 +1291,24 @@ class InstantiationHelper {
// Initialize memory.
//--------------------------------------------------------------------------
if (!memory_.is_null()) {
instance->set_memory_buffer(*memory_);
Address mem_start = static_cast<Address>(memory_->backing_store());
uint32_t mem_size =
static_cast<uint32_t>(memory_->byte_length()->Number());
LoadDataSegments(mem_start, mem_size);
uint32_t old_mem_size = compiled_module_->mem_size();
Address old_mem_start =
compiled_module_->has_memory()
? static_cast<Address>(
compiled_module_->memory()->backing_store())
: nullptr;
Address old_mem_start = compiled_module_->GetEmbeddedMemStartOrNull();
// We might get instantiated again with the same memory. No patching
// needed in this case.
if (old_mem_start != mem_start || old_mem_size != mem_size) {
code_specialization.RelocateMemoryReferences(
old_mem_start, old_mem_size, mem_start, mem_size);
}
compiled_module_->set_memory(memory_);
// Just like with globals, we need to keep both the JSArrayBuffer
// and save the start pointer.
instance->set_memory_buffer(*memory_);
WasmCompiledModule::SetSpecializationMemInfoFrom(
factory, compiled_module_, memory_);
}
//--------------------------------------------------------------------------
@ -2318,11 +2246,12 @@ MaybeHandle<JSArrayBuffer> wasm::GetInstanceMemory(
return MaybeHandle<JSArrayBuffer>();
}
void SetInstanceMemory(Handle<WasmInstanceObject> instance,
JSArrayBuffer* buffer) {
DisallowHeapAllocation no_gc;
instance->set_memory_buffer(buffer);
instance->compiled_module()->set_ptr_to_memory(buffer);
// May GC, because SetSpecializationMemInfoFrom may GC
void SetInstanceMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
Handle<JSArrayBuffer> buffer) {
instance->set_memory_buffer(*buffer);
WasmCompiledModule::SetSpecializationMemInfoFrom(
isolate->factory(), handle(instance->compiled_module()), buffer);
}
int32_t wasm::GetInstanceMemorySize(Isolate* isolate,
@ -2495,14 +2424,14 @@ int32_t wasm::GrowWebAssemblyMemory(Isolate* isolate,
new_buffer = GrowMemoryBuffer(isolate, memory_buffer, pages, max_pages);
if (new_buffer.is_null()) return -1;
DCHECK(!instance_wrapper->has_previous());
SetInstanceMemory(instance, *new_buffer);
SetInstanceMemory(isolate, instance, new_buffer);
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
while (instance_wrapper->has_next()) {
instance_wrapper = instance_wrapper->next_wrapper();
DCHECK(WasmInstanceWrapper::IsWasmInstanceWrapper(*instance_wrapper));
Handle<WasmInstanceObject> instance = instance_wrapper->instance_object();
DCHECK(IsWasmInstance(*instance));
SetInstanceMemory(instance, *new_buffer);
SetInstanceMemory(isolate, instance, new_buffer);
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
}
}
@ -2532,7 +2461,7 @@ int32_t wasm::GrowMemory(Isolate* isolate, Handle<WasmInstanceObject> instance,
Handle<JSArrayBuffer> buffer =
GrowMemoryBuffer(isolate, instance_buffer, pages, max_pages);
if (buffer.is_null()) return -1;
SetInstanceMemory(instance, *buffer);
SetInstanceMemory(isolate, instance, buffer);
UncheckedUpdateInstanceMemory(isolate, instance, old_mem_start, old_size);
DCHECK(old_size % WasmModule::kPageSize == 0);
return (old_size / WasmModule::kPageSize);
@ -3110,7 +3039,7 @@ bool LazyCompilationOrchestrator::CompileFunction(
CodeSpecialization code_specialization(isolate, &specialization_zone);
if (module_env.module->globals_size) {
Address globals_start =
reinterpret_cast<Address>(instance->globals_buffer()->backing_store());
reinterpret_cast<Address>(compiled_module->globals_start());
code_specialization.RelocateGlobals(nullptr, globals_start);
}
if (instance->has_memory_buffer()) {

View File

@ -772,6 +772,102 @@ Handle<WasmCompiledModule> WasmCompiledModule::New(
return compiled_module;
}
Handle<WasmCompiledModule> WasmCompiledModule::Clone(
Isolate* isolate, Handle<WasmCompiledModule> module) {
Handle<WasmCompiledModule> ret = Handle<WasmCompiledModule>::cast(
isolate->factory()->CopyFixedArray(module));
ret->InitId();
ret->reset_weak_owning_instance();
ret->reset_weak_next_instance();
ret->reset_weak_prev_instance();
ret->reset_weak_exported_functions();
if (ret->has_embedded_mem_start()) {
WasmCompiledModule::recreate_embedded_mem_start(ret, isolate->factory(),
ret->embedded_mem_start());
}
if (ret->has_globals_start()) {
WasmCompiledModule::recreate_globals_start(ret, isolate->factory(),
ret->globals_start());
}
if (ret->has_embedded_mem_size()) {
WasmCompiledModule::recreate_embedded_mem_size(ret, isolate->factory(),
ret->embedded_mem_size());
}
return ret;
}
void WasmCompiledModule::Reset(Isolate* isolate,
WasmCompiledModule* compiled_module) {
DisallowHeapAllocation no_gc;
TRACE("Resetting %d\n", compiled_module->instance_id());
Object* undefined = *isolate->factory()->undefined_value();
Object* fct_obj = compiled_module->ptr_to_code_table();
if (fct_obj != nullptr && fct_obj != undefined) {
uint32_t old_mem_size = compiled_module->mem_size();
uint32_t default_mem_size = compiled_module->default_mem_size();
Address old_mem_start = compiled_module->GetEmbeddedMemStartOrNull();
// Patch code to update memory references, global references, and function
// table references.
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
CodeSpecialization code_specialization(isolate, &specialization_zone);
if (old_mem_size > 0 && old_mem_start != nullptr) {
code_specialization.RelocateMemoryReferences(old_mem_start, old_mem_size,
nullptr, default_mem_size);
}
if (compiled_module->has_globals_start()) {
Address globals_start =
reinterpret_cast<Address>(compiled_module->globals_start());
code_specialization.RelocateGlobals(globals_start, nullptr);
compiled_module->set_globals_start(0);
}
// Reset function tables.
if (compiled_module->has_function_tables()) {
FixedArray* function_tables = compiled_module->ptr_to_function_tables();
FixedArray* empty_function_tables =
compiled_module->ptr_to_empty_function_tables();
if (function_tables != empty_function_tables) {
DCHECK_EQ(function_tables->length(), empty_function_tables->length());
for (int i = 0, e = function_tables->length(); i < e; ++i) {
code_specialization.RelocateObject(
handle(function_tables->get(i), isolate),
handle(empty_function_tables->get(i), isolate));
}
compiled_module->set_ptr_to_function_tables(empty_function_tables);
}
}
FixedArray* functions = FixedArray::cast(fct_obj);
for (int i = compiled_module->num_imported_functions(),
end = functions->length();
i < end; ++i) {
Code* code = Code::cast(functions->get(i));
// Skip lazy compile stubs.
if (code->builtin_index() == Builtins::kWasmCompileLazy) continue;
if (code->kind() != Code::WASM_FUNCTION) {
// From here on, there should only be wrappers for exported functions.
for (; i < end; ++i) {
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION,
Code::cast(functions->get(i))->kind());
}
break;
}
bool changed =
code_specialization.ApplyToWasmCode(code, SKIP_ICACHE_FLUSH);
// TODO(wasm): Check if this is faster than passing FLUSH_ICACHE_IF_NEEDED
// above.
if (changed) {
Assembler::FlushICache(isolate, code->instruction_start(),
code->instruction_size());
}
}
}
compiled_module->ResetSpecializationMemInfoIfNeeded();
}
void WasmCompiledModule::InitId() {
#if DEBUG
static uint32_t instance_id_counter = 0;
@ -780,6 +876,45 @@ void WasmCompiledModule::InitId() {
#endif
}
void WasmCompiledModule::ResetSpecializationMemInfoIfNeeded() {
DisallowHeapAllocation no_gc;
if (has_embedded_mem_start()) {
set_embedded_mem_size(0);
set_embedded_mem_start(0);
}
}
void WasmCompiledModule::SetSpecializationMemInfoFrom(
Factory* factory, Handle<WasmCompiledModule> compiled_module,
Handle<JSArrayBuffer> buffer) {
DCHECK(!buffer.is_null());
size_t start_address = reinterpret_cast<size_t>(buffer->backing_store());
uint32_t size = static_cast<uint32_t>(buffer->byte_length()->Number());
if (!compiled_module->has_embedded_mem_start()) {
DCHECK(!compiled_module->has_embedded_mem_size());
WasmCompiledModule::recreate_embedded_mem_start(compiled_module, factory,
start_address);
WasmCompiledModule::recreate_embedded_mem_size(compiled_module, factory,
size);
} else {
compiled_module->set_embedded_mem_start(start_address);
compiled_module->set_embedded_mem_size(size);
}
}
void WasmCompiledModule::SetGlobalsStartAddressFrom(
Factory* factory, Handle<WasmCompiledModule> compiled_module,
Handle<JSArrayBuffer> buffer) {
DCHECK(!buffer.is_null());
size_t start_address = reinterpret_cast<size_t>(buffer->backing_store());
if (!compiled_module->has_globals_start()) {
WasmCompiledModule::recreate_globals_start(compiled_module, factory,
start_address);
} else {
compiled_module->set_globals_start(start_address);
}
}
MaybeHandle<String> WasmCompiledModule::ExtractUtf8StringFromModuleBytes(
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
uint32_t offset, uint32_t size) {
@ -807,13 +942,23 @@ bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
Object* obj = arr->get(kID_##NAME); \
if (!(TYPE_CHECK)) return false; \
} while (false);
// We're OK with undefined, generally, because maybe we don't
// have a value for that item. For example, we may not have a
// memory, or globals.
// We're not OK with the fixed numbers being undefined. We want
// to set once all of them.
#define WCM_CHECK_OBJECT(TYPE, NAME) \
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->Is##TYPE())
#define WCM_CHECK_WASM_OBJECT(TYPE, NAME) \
WCM_CHECK_TYPE(NAME, TYPE::Is##TYPE(obj))
#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT(WeakCell, NAME)
#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) WCM_CHECK_TYPE(NAME, obj->IsSmi())
#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsSmi())
#define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME)
#define WCM_CHECK_SMALL_FIXED_NUMBER(TYPE, NAME) \
WCM_CHECK_TYPE(NAME, obj->IsSmi())
#define WCM_CHECK_LARGE_NUMBER(TYPE, NAME) \
WCM_CHECK_TYPE(NAME, obj->IsUndefined(isolate) || obj->IsMutableHeapNumber())
WCM_PROPERTY_TABLE(WCM_CHECK)
#undef WCM_CHECK
@ -845,11 +990,13 @@ void WasmCompiledModule::ReinitializeAfterDeserialization(
isolate);
DCHECK(!WasmSharedModuleData::IsWasmSharedModuleData(*shared));
WasmSharedModuleData::ReinitializeAfterDeserialization(isolate, shared);
WasmCompiledModule::Reset(isolate, *compiled_module);
DCHECK(WasmSharedModuleData::IsWasmSharedModuleData(*shared));
}
uint32_t WasmCompiledModule::mem_size() const {
return has_memory() ? memory()->byte_length()->Number() : default_mem_size();
DCHECK(has_embedded_mem_size() == has_embedded_mem_start());
return has_embedded_mem_start() ? embedded_mem_size() : default_mem_size();
}
uint32_t WasmCompiledModule::default_mem_size() const {

View File

@ -221,6 +221,30 @@ class WasmSharedModuleData : public FixedArray {
friend class WasmCompiledModule;
};
// This represents the set of wasm compiled functions, together
// with all the information necessary for re-specializing them.
//
// We specialize wasm functions to their instance by embedding:
// - raw interior pointers into the backing store of the array buffer
// used as memory of a particular WebAssembly.Instance object.
// - bounds check limits, computed at compile time, relative to the
// size of the memory.
// - the objects representing the function tables and signature tables
// - raw pointer to the globals buffer.
//
// Even without instantiating, we need values for all of these parameters.
// We need to track these values to be able to create new instances and
// to be able to serialize/deserialize.
// The design decisions for how we track these values is not too immediate,
// and it deserves a summary. The "tricky" ones are: memory, globals, and
// the tables (signature and functions).
// The first 2 (memory & globals) are embedded as raw pointers to native
// buffers. All we need to track them is the start addresses and, in the
// case of memory, the size. We model all of them as HeapNumbers, because
// we need to store size_t values (for addresses), and potentially full
// 32 bit unsigned values for the size. Smis are 31 bits.
// For tables, we need to hold a reference to the JS Heap object, because
// we embed them as objects, and they may move.
class WasmCompiledModule : public FixedArray {
public:
enum Fields { kFieldCount };
@ -267,12 +291,19 @@ class WasmCompiledModule : public FixedArray {
#define WCM_WASM_OBJECT(TYPE, NAME) \
WCM_OBJECT_OR_WEAK(TYPE, NAME, kID_##NAME, TYPE::Is##TYPE(obj))
#define WCM_SMALL_NUMBER(TYPE, NAME) \
#define WCM_SMALL_FIXED_NUMBER(TYPE, NAME) \
TYPE NAME() const { \
return static_cast<TYPE>(Smi::cast(get(kID_##NAME))->value()); \
} \
void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); }
#define WCM_SMALL_NUMBER(TYPE, NAME) \
TYPE NAME() const { \
return static_cast<TYPE>(Smi::cast(get(kID_##NAME))->value()); \
} \
void set_##NAME(TYPE value) { set(kID_##NAME, Smi::FromInt(value)); } \
bool has_##NAME() const { return get(kID_##NAME)->IsSmi(); }
#define WCM_WEAK_LINK(TYPE, NAME) \
WCM_OBJECT_OR_WEAK(WeakCell, weak_##NAME, kID_##NAME, obj->IsWeakCell()); \
\
@ -280,21 +311,44 @@ class WasmCompiledModule : public FixedArray {
return handle(TYPE::cast(weak_##NAME()->value())); \
}
#define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \
MACRO(OBJECT, Context, native_context) \
MACRO(SMALL_NUMBER, uint32_t, num_imported_functions) \
MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, FixedArray, weak_exported_functions) \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, signature_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(OBJECT, JSArrayBuffer, memory) \
MACRO(SMALL_NUMBER, uint32_t, min_mem_pages) \
MACRO(SMALL_NUMBER, uint32_t, max_mem_pages) \
MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
MACRO(WEAK_LINK, JSObject, owning_instance) \
#define WCM_LARGE_NUMBER(TYPE, NAME) \
TYPE NAME() const { \
Object* value = get(kID_##NAME); \
DCHECK(value->IsMutableHeapNumber()); \
return static_cast<TYPE>(HeapNumber::cast(value)->value()); \
} \
\
void set_##NAME(TYPE value) { \
Object* number = get(kID_##NAME); \
DCHECK(number->IsMutableHeapNumber()); \
HeapNumber::cast(number)->set_value(static_cast<double>(value)); \
} \
\
static void recreate_##NAME(Handle<WasmCompiledModule> obj, \
Factory* factory, TYPE init_val) { \
Handle<HeapNumber> number = factory->NewHeapNumber( \
static_cast<double>(init_val), MutableMode::MUTABLE, TENURED); \
obj->set(kID_##NAME, *number); \
} \
bool has_##NAME() const { return get(kID_##NAME)->IsMutableHeapNumber(); }
#define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(WASM_OBJECT, WasmSharedModuleData, shared) \
MACRO(OBJECT, Context, native_context) \
MACRO(SMALL_FIXED_NUMBER, uint32_t, num_imported_functions) \
MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, FixedArray, weak_exported_functions) \
MACRO(OBJECT, FixedArray, function_tables) \
MACRO(OBJECT, FixedArray, signature_tables) \
MACRO(OBJECT, FixedArray, empty_function_tables) \
MACRO(LARGE_NUMBER, size_t, embedded_mem_start) \
MACRO(LARGE_NUMBER, size_t, globals_start) \
MACRO(LARGE_NUMBER, uint32_t, embedded_mem_size) \
MACRO(SMALL_FIXED_NUMBER, uint32_t, min_mem_pages) \
MACRO(SMALL_FIXED_NUMBER, uint32_t, max_mem_pages) \
MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
MACRO(WEAK_LINK, JSObject, owning_instance) \
MACRO(WEAK_LINK, WasmModuleObject, wasm_module)
#if DEBUG
@ -320,20 +374,36 @@ class WasmCompiledModule : public FixedArray {
Handle<WasmSharedModuleData> shared);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
Handle<WasmCompiledModule> module) {
Handle<WasmCompiledModule> ret = Handle<WasmCompiledModule>::cast(
isolate->factory()->CopyFixedArray(module));
ret->InitId();
ret->reset_weak_owning_instance();
ret->reset_weak_next_instance();
ret->reset_weak_prev_instance();
ret->reset_weak_exported_functions();
return ret;
Handle<WasmCompiledModule> module);
static void Reset(Isolate* isolate, WasmCompiledModule* module);
Address GetEmbeddedMemStartOrNull() const {
DisallowHeapAllocation no_gc;
if (has_embedded_mem_start()) {
return reinterpret_cast<Address>(embedded_mem_start());
}
return nullptr;
}
Address GetGlobalsStartOrNull() const {
DisallowHeapAllocation no_gc;
if (has_globals_start()) {
return reinterpret_cast<Address>(globals_start());
}
return nullptr;
}
uint32_t mem_size() const;
uint32_t default_mem_size() const;
void ResetSpecializationMemInfoIfNeeded();
static void SetSpecializationMemInfoFrom(
Factory* factory, Handle<WasmCompiledModule> compiled_module,
Handle<JSArrayBuffer> buffer);
static void SetGlobalsStartAddressFrom(
Factory* factory, Handle<WasmCompiledModule> compiled_module,
Handle<JSArrayBuffer> buffer);
#define DECLARATION(KIND, TYPE, NAME) WCM_##KIND(TYPE, NAME)
WCM_PROPERTY_TABLE(DECLARATION)
#undef DECLARATION

View File

@ -37,16 +37,9 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var buff = %SerializeWasmModule(module);
module = null;
gc();
module = %DeserializeWasmModule(buff, wire_bytes);
var mem_1 = new WebAssembly.Memory({initial: 1});
var view_1 = new Int32Array(mem_1.buffer);
view_1[0] = 42;
var outval_1;
var i1 = new WebAssembly.Instance(module, {"":
{some_value: () => 1,
@ -57,6 +50,28 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(43, i1.exports.main(0));
assertEquals(42, outval_1);
var buff = %SerializeWasmModule(module);
module = null;
gc();
module = %DeserializeWasmModule(buff, wire_bytes);
var mem_2 = new WebAssembly.Memory({initial: 2});
var view_2 = new Int32Array(mem_2.buffer);
view_2[0] = 50;
var outval_2;
var i2 = new WebAssembly.Instance(module, {"":
{some_value: () => 1,
writer: (x) => outval_2 = x ,
memory: mem_2}
});
assertEquals(51, i2.exports.main(0));
assertEquals(50, outval_2);
// The instances don't share memory through deserialization.
assertEquals(43, i1.exports.main(0));
})();
(function DeserializeInvalidObject() {
@ -103,3 +118,128 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var instance3 = new WebAssembly.Instance(clone);
assertFalse(instance3 == undefined);
})();
(function SerializeAfterInstantiationWithMemory() {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("", "memory", 1);
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportFunc();
var wire_bytes = builder.toBuffer()
var compiled_module = new WebAssembly.Module(wire_bytes);
var mem_1 = new WebAssembly.Memory({initial: 1});
var ffi = {"":{memory:mem_1}};
var instance1 = new WebAssembly.Instance(compiled_module, ffi);
var serialized = %SerializeWasmModule(compiled_module);
var clone = %DeserializeWasmModule(serialized, wire_bytes);
assertNotNull(clone);
assertFalse(clone == undefined);
assertFalse(clone == compiled_module);
assertEquals(clone.constructor, compiled_module.constructor);
var instance2 = new WebAssembly.Instance(clone, ffi);
assertFalse(instance2 == undefined);
})();
(function GlobalsArePrivateBetweenClones() {
var builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGetGlobal, 0])
.exportFunc();
builder.addFunction("write", kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprSetGlobal, 0])
.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var i1 = new WebAssembly.Instance(module);
// serialize and replace module
var buff = %SerializeWasmModule(module);
var module_clone = %DeserializeWasmModule(buff, wire_bytes);
var i2 = new WebAssembly.Instance(module_clone);
i1.exports.write(1);
i2.exports.write(2);
assertEquals(1, i1.exports.read());
assertEquals(2, i2.exports.read());
})();
(function SharedTableTest() {
let kTableSize = 3;
var sig_index1;
function MakeTableExportingModule(constant) {
// A module that defines a table and exports it.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_i);
builder.addType(kSig_i_ii);
sig_index1 = builder.addType(kSig_i_v);
var f1 = builder.addFunction("f1", sig_index1)
.addBody([kExprI32Const, constant]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index1, kTableZero]) // --
.exportAs("main");
builder.setFunctionTableLength(kTableSize);
builder.addFunctionTableInit(0, false, [f1.index]);
builder.addExportOfKind("table", kExternalTable, 0);
return new WebAssembly.Module(builder.toBuffer());
}
var m1 = MakeTableExportingModule(11);
// Module {m2} imports the table and adds {f2}.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_ii);
var sig_index2 = builder.addType(kSig_i_v);
var f2 = builder.addFunction("f2", sig_index2)
.addBody([kExprI32Const, 22]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index2, kTableZero]) // --
.exportAs("main");
builder.addImportedTable("z", "table", kTableSize, kTableSize);
builder.addFunctionTableInit(1, false, [f2.index], true);
var m2_bytes = builder.toBuffer();
var m2 = new WebAssembly.Module(m2_bytes);
assertFalse(sig_index1 == sig_index2);
var i1 = new WebAssembly.Instance(m1);
var i2 = new WebAssembly.Instance(m2, {z: {table: i1.exports.table}});
var serialized_m2 = %SerializeWasmModule(m2);
var m2_clone = %DeserializeWasmModule(serialized_m2, m2_bytes);
var m3 = MakeTableExportingModule(33);
var i3 = new WebAssembly.Instance(m3);
var i2_prime = new WebAssembly.Instance(m2_clone,
{z: {table: i3.exports.table}});
assertEquals(11, i1.exports.main(0));
assertEquals(11, i2.exports.main(0));
assertEquals(22, i1.exports.main(1));
assertEquals(22, i2.exports.main(1));
assertEquals(33, i3.exports.main(0));
assertEquals(33, i2_prime.exports.main(0));
assertThrows(() => i1.exports.main(2));
assertThrows(() => i2.exports.main(2));
assertThrows(() => i1.exports.main(3));
assertThrows(() => i2.exports.main(3));
})();