Commit 418b239f authored by titzer's avatar titzer Committed by Commit bot

[wasm] Use a Managed<WasmModule> to hold metadata about modules.

This CL refactors the handling of metadata associated with WebAssembly
modules to reduce the duplicate marshalling of data from the C++ world
to the JavaScript world. It does this by wrapping the C++ WasmModule*
object in a Foreign that is rooted from the on-heap WasmCompiledModule
(which is itself just a FixedArray). Upon serialization, the C++ object
is ignored and the original WASM wire bytes are serialized. Upon
deserialization, the C++ object is reconstituted by reparsing the bytes.

This is motivated by increasing complications in implementing the JS
API, in particular WebAssembly.Table, which must perform signature
canonicalization across instances.

Additionally, this CL implements the proper base + offset initialization
behavior for tables.

R=rossberg@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org,yangguo@chromium.org
BUG=v8:5507, chromium:575167, chromium:657316

Review-Url: https://chromiumcodereview.appspot.com/2424623002
Cr-Commit-Position: refs/heads/master@{#40434}
parent db733efd
...@@ -7204,12 +7204,9 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() { ...@@ -7204,12 +7204,9 @@ WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
i::Handle<i::wasm::WasmCompiledModule> compiled_part = i::Handle<i::wasm::WasmCompiledModule> compiled_part =
i::handle(i::wasm::WasmCompiledModule::cast(obj->GetInternalField(0))); i::handle(i::wasm::WasmCompiledModule::cast(obj->GetInternalField(0)));
i::Handle<i::SeqOneByteString> wire_bytes = compiled_part->module_bytes();
compiled_part->reset_module_bytes();
std::unique_ptr<i::ScriptData> script_data = std::unique_ptr<i::ScriptData> script_data =
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(), i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
compiled_part); compiled_part);
compiled_part->set_module_bytes(wire_bytes);
script_data->ReleaseDataOwnership(); script_data->ReleaseDataOwnership();
size_t size = static_cast<size_t>(script_data->length()); size_t size = static_cast<size_t>(script_data->length());
...@@ -7238,7 +7235,7 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize( ...@@ -7238,7 +7235,7 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize(
i::Handle<i::wasm::WasmCompiledModule> compiled_module = i::Handle<i::wasm::WasmCompiledModule> compiled_module =
handle(i::wasm::WasmCompiledModule::cast(*compiled_part)); handle(i::wasm::WasmCompiledModule::cast(*compiled_part));
return Local<WasmCompiledModule>::Cast( return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(i::wasm::CreateCompiledModuleObject( Utils::ToLocal(i::wasm::CreateWasmModuleObject(
i_isolate, compiled_module, i::wasm::ModuleOrigin::kWasmOrigin))); i_isolate, compiled_module, i::wasm::ModuleOrigin::kWasmOrigin)));
} }
......
...@@ -306,6 +306,44 @@ MaybeHandle<String> Factory::NewStringFromUtf8(Vector<const char> string, ...@@ -306,6 +306,44 @@ MaybeHandle<String> Factory::NewStringFromUtf8(Vector<const char> string,
return result; return result;
} }
MaybeHandle<String> Factory::NewStringFromUtf8SubString(
Handle<SeqOneByteString> str, int begin, int length,
PretenureFlag pretenure) {
// Check for ASCII first since this is the common case.
const char* start = reinterpret_cast<const char*>(str->GetChars() + begin);
int non_ascii_start = String::NonAsciiStart(start, length);
if (non_ascii_start >= length) {
// If the string is ASCII, we can just make a substring.
// TODO(v8): the pretenure flag is ignored in this case.
return NewSubString(str, begin, begin + length);
}
// Non-ASCII and we need to decode.
Access<UnicodeCache::Utf8Decoder> decoder(
isolate()->unicode_cache()->utf8_decoder());
decoder->Reset(start + non_ascii_start, length - non_ascii_start);
int utf16_length = static_cast<int>(decoder->Utf16Length());
DCHECK(utf16_length > 0);
// Allocate string.
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate(), result,
NewRawTwoByteString(non_ascii_start + utf16_length, pretenure), String);
// Reset the decoder, because the original {str} may have moved.
const char* ascii_data =
reinterpret_cast<const char*>(str->GetChars() + begin);
decoder->Reset(ascii_data + non_ascii_start, length - non_ascii_start);
// Copy ASCII portion.
uint16_t* data = result->GetChars();
for (int i = 0; i < non_ascii_start; i++) {
*data++ = *ascii_data++;
}
// Now write the remainder.
decoder->WriteUtf16(data, utf16_length);
return result;
}
MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string, MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
int length, int length,
PretenureFlag pretenure) { PretenureFlag pretenure) {
......
...@@ -183,6 +183,10 @@ class V8_EXPORT_PRIVATE Factory final { ...@@ -183,6 +183,10 @@ class V8_EXPORT_PRIVATE Factory final {
MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8( MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8(
Vector<const char> str, PretenureFlag pretenure = NOT_TENURED); Vector<const char> str, PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8SubString(
Handle<SeqOneByteString> str, int begin, int end,
PretenureFlag pretenure = NOT_TENURED);
MUST_USE_RESULT MaybeHandle<String> NewStringFromTwoByte( MUST_USE_RESULT MaybeHandle<String> NewStringFromTwoByte(
Vector<const uc16> str, PretenureFlag pretenure = NOT_TENURED); Vector<const uc16> str, PretenureFlag pretenure = NOT_TENURED);
......
...@@ -767,7 +767,7 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) { ...@@ -767,7 +767,7 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
if (!maybe_compiled_module.ToHandle(&compiled_module)) { if (!maybe_compiled_module.ToHandle(&compiled_module)) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
return *wasm::CreateCompiledModuleObject( return *wasm::CreateWasmModuleObject(
isolate, Handle<wasm::WasmCompiledModule>::cast(compiled_module), isolate, Handle<wasm::WasmCompiledModule>::cast(compiled_module),
wasm::kWasmOrigin); wasm::kWasmOrigin);
} }
......
...@@ -32,7 +32,16 @@ class Signature : public ZoneObject { ...@@ -32,7 +32,16 @@ class Signature : public ZoneObject {
return reps_[index]; return reps_[index];
} }
const T* raw_data() const { return reps_; } bool Equals(Signature* that) {
if (this == that) return true;
if (this->parameter_count() != that->parameter_count()) return false;
if (this->return_count() != that->return_count()) return false;
size_t size = this->return_count() + this->parameter_count();
for (size_t i = 0; i < size; i++) {
if (this->reps_[i] != that->reps_[i]) return false;
}
return true;
}
// For incrementally building signatures. // For incrementally building signatures.
class Builder { class Builder {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "src/snapshot/deserializer.h" #include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot.h" #include "src/snapshot/snapshot.h"
#include "src/version.h" #include "src/version.h"
#include "src/wasm/wasm-module.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -217,7 +218,9 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize( ...@@ -217,7 +218,9 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
} }
std::unique_ptr<ScriptData> WasmCompiledModuleSerializer::SerializeWasmModule( std::unique_ptr<ScriptData> WasmCompiledModuleSerializer::SerializeWasmModule(
Isolate* isolate, Handle<FixedArray> compiled_module) { Isolate* isolate, Handle<FixedArray> input) {
Handle<wasm::WasmCompiledModule> compiled_module =
Handle<wasm::WasmCompiledModule>::cast(input);
WasmCompiledModuleSerializer wasm_cs(isolate, 0); WasmCompiledModuleSerializer wasm_cs(isolate, 0);
wasm_cs.reference_map()->AddAttachedReference(*isolate->native_context()); wasm_cs.reference_map()->AddAttachedReference(*isolate->native_context());
ScriptData* data = wasm_cs.Serialize(compiled_module); ScriptData* data = wasm_cs.Serialize(compiled_module);
...@@ -247,7 +250,10 @@ MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule( ...@@ -247,7 +250,10 @@ MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule(
MaybeHandle<HeapObject> obj = deserializer.DeserializeObject(isolate); MaybeHandle<HeapObject> obj = deserializer.DeserializeObject(isolate);
if (obj.is_null() || !obj.ToHandleChecked()->IsFixedArray()) return nothing; if (obj.is_null() || !obj.ToHandleChecked()->IsFixedArray()) return nothing;
return Handle<FixedArray>::cast(obj.ToHandleChecked()); Handle<FixedArray> compiled_module =
Handle<FixedArray>::cast(obj.ToHandleChecked());
wasm::WasmCompiledModule::RecreateModuleWrapper(isolate, compiled_module);
return compiled_module;
} }
class Checksum { class Checksum {
......
...@@ -74,7 +74,9 @@ class WasmCompiledModuleSerializer : public CodeSerializer { ...@@ -74,7 +74,9 @@ class WasmCompiledModuleSerializer : public CodeSerializer {
} }
} }
bool ElideObject(Object* obj) override { return obj->IsWeakCell(); }; bool ElideObject(Object* obj) override {
return obj->IsWeakCell() || obj->IsForeign();
};
private: private:
WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash) WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash)
......
...@@ -46,6 +46,7 @@ class Managed : public Foreign { ...@@ -46,6 +46,7 @@ class Managed : public Foreign {
Managed<CppType>** p = Managed<CppType>** p =
reinterpret_cast<Managed<CppType>**>(data.GetParameter()); reinterpret_cast<Managed<CppType>**>(data.GetParameter());
delete (*p)->get(); delete (*p)->get();
(*p)->set_foreign_address(0);
GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
} }
}; };
......
...@@ -1077,10 +1077,9 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end, ...@@ -1077,10 +1077,9 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
} // namespace } // namespace
ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone, ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_start, const byte* module_end, const byte* module_end, bool verify_functions,
bool verify_functions, ModuleOrigin origin) { ModuleOrigin origin) {
size_t decode_memory_start = zone->allocation_size();
HistogramTimerScope wasm_decode_module_time_scope( HistogramTimerScope wasm_decode_module_time_scope(
isolate->counters()->wasm_decode_module_time()); isolate->counters()->wasm_decode_module_time());
size_t size = module_end - module_start; size_t size = module_end - module_start;
...@@ -1089,12 +1088,18 @@ ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone, ...@@ -1089,12 +1088,18 @@ ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
// TODO(bradnelson): Improve histogram handling of size_t. // TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_module_size_bytes()->AddSample( isolate->counters()->wasm_module_size_bytes()->AddSample(
static_cast<int>(size)); static_cast<int>(size));
WasmModule* module = new WasmModule(); // Signatures are stored in zone memory, which have the same lifetime
// as the {module}.
Zone* zone = new Zone(isolate->allocator(), ZONE_NAME);
WasmModule* module = new WasmModule(zone, module_start);
ModuleDecoder decoder(zone, module_start, module_end, origin); ModuleDecoder decoder(zone, module_start, module_end, origin);
ModuleResult result = decoder.DecodeModule(module, verify_functions); ModuleResult result = decoder.DecodeModule(module, verify_functions);
// TODO(bradnelson): Improve histogram handling of size_t. // TODO(bradnelson): Improve histogram handling of size_t.
// TODO(titzer): this isn't accurate, since it doesn't count the data
// allocated on the C++ heap.
// https://bugs.chromium.org/p/chromium/issues/detail?id=657320
isolate->counters()->wasm_decode_module_peak_memory_bytes()->AddSample( isolate->counters()->wasm_decode_module_peak_memory_bytes()->AddSample(
static_cast<int>(zone->allocation_size() - decode_memory_start)); static_cast<int>(zone->allocation_size()));
return result; return result;
} }
......
...@@ -22,7 +22,7 @@ typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets; ...@@ -22,7 +22,7 @@ typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult; typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
// Decodes the bytes of a WASM module between {module_start} and {module_end}. // Decodes the bytes of a WASM module between {module_start} and {module_end}.
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone, V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate,
const byte* module_start, const byte* module_start,
const byte* module_end, const byte* module_end,
bool verify_functions, bool verify_functions,
......
...@@ -104,10 +104,8 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -104,10 +104,8 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
RawBuffer buffer = GetRawBufferSource(args[0], &thrower); RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
if (thrower.error()) return; if (thrower.error()) return;
i::Zone zone(isolate->allocator(), ZONE_NAME); internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
internal::wasm::ModuleResult result = isolate, buffer.start, buffer.end, true, internal::wasm::kWasmOrigin);
internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
true, internal::wasm::kWasmOrigin);
if (result.failed()) { if (result.failed()) {
thrower.CompileFailed("", result); thrower.CompileFailed("", result);
......
This diff is collapsed.
This diff is collapsed.
...@@ -277,15 +277,18 @@ class WasmSerializationTest { ...@@ -277,15 +277,18 @@ class WasmSerializationTest {
testing::SetupIsolateForWasmModule(serialization_isolate); testing::SetupIsolateForWasmModule(serialization_isolate);
ModuleResult decoding_result = ModuleResult decoding_result =
DecodeWasmModule(serialization_isolate, zone(), buffer.begin(), DecodeWasmModule(serialization_isolate, buffer.begin(), buffer.end(),
buffer.end(), false, kWasmOrigin); false, kWasmOrigin);
std::unique_ptr<const WasmModule> module(decoding_result.val);
CHECK(!decoding_result.failed()); CHECK(!decoding_result.failed());
Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(
serialization_isolate, const_cast<WasmModule*>(decoding_result.val));
MaybeHandle<WasmCompiledModule> compiled_module = MaybeHandle<WasmCompiledModule> compiled_module =
module->CompileFunctions(serialization_isolate, &thrower); decoding_result.val->CompileFunctions(serialization_isolate,
module_wrapper, &thrower);
CHECK(!compiled_module.is_null()); CHECK(!compiled_module.is_null());
Handle<JSObject> module_obj = CreateCompiledModuleObject( Handle<JSObject> module_obj = CreateWasmModuleObject(
serialization_isolate, compiled_module.ToHandleChecked(), serialization_isolate, compiled_module.ToHandleChecked(),
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin);
v8::Local<v8::Object> v8_module_obj = v8::Utils::ToLocal(module_obj); v8::Local<v8::Object> v8_module_obj = v8::Utils::ToLocal(module_obj);
...@@ -470,7 +473,7 @@ TEST(TestInterruptLoop) { ...@@ -470,7 +473,7 @@ TEST(TestInterruptLoop) {
ErrorThrower thrower(isolate, "Test"); ErrorThrower thrower(isolate, "Test");
const Handle<JSObject> instance = const Handle<JSObject> instance =
testing::CompileInstantiateWasmModuleForTesting( testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(), isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null()); CHECK(!instance.is_null());
...@@ -544,7 +547,7 @@ TEST(Run_WasmModule_GrowMemOobFixedIndex) { ...@@ -544,7 +547,7 @@ TEST(Run_WasmModule_GrowMemOobFixedIndex) {
ErrorThrower thrower(isolate, "Test"); ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting( Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(), isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null()); CHECK(!instance.is_null());
...@@ -589,7 +592,7 @@ TEST(Run_WasmModule_GrowMemOobVariableIndex) { ...@@ -589,7 +592,7 @@ TEST(Run_WasmModule_GrowMemOobVariableIndex) {
ErrorThrower thrower(isolate, "Test"); ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting( Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(), isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null()); CHECK(!instance.is_null());
......
...@@ -209,16 +209,9 @@ class TestingModule : public ModuleEnv { ...@@ -209,16 +209,9 @@ class TestingModule : public ModuleEnv {
WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context()); WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context());
Handle<Code> ret_code = Handle<Code> ret_code =
compiler::CompileJSToWasmWrapper(isolate_, this, code, index); compiler::CompileJSToWasmWrapper(isolate_, this, code, index);
FunctionSig* funcSig = this->module->functions[index].sig;
Handle<ByteArray> exportedSig = isolate_->factory()->NewByteArray(
static_cast<int>(funcSig->parameter_count() + funcSig->return_count()),
TENURED);
exportedSig->copy_in(0, reinterpret_cast<const byte*>(funcSig->raw_data()),
exportedSig->length());
Handle<JSFunction> ret = WrapExportCodeAsJSFunction( Handle<JSFunction> ret = WrapExportCodeAsJSFunction(
isolate_, ret_code, name, isolate_, ret_code, name, this->module->functions[index].sig,
static_cast<int>(this->module->functions[index].sig->parameter_count()), static_cast<int>(index), module_object);
exportedSig, module_object);
return ret; return ret;
} }
...@@ -226,27 +219,33 @@ class TestingModule : public ModuleEnv { ...@@ -226,27 +219,33 @@ class TestingModule : public ModuleEnv {
instance->function_code[index] = code; instance->function_code[index] = code;
} }
void AddIndirectFunctionTable(uint16_t* functions, uint32_t table_size) { void AddIndirectFunctionTable(uint16_t* function_indexes,
uint32_t table_size) {
module_.function_tables.push_back({table_size, table_size, module_.function_tables.push_back({table_size, table_size,
std::vector<int32_t>(), false, false, std::vector<int32_t>(), false, false,
SignatureMap()}); SignatureMap()});
WasmIndirectFunctionTable& table = module_.function_tables.back(); WasmIndirectFunctionTable& table = module_.function_tables.back();
for (uint32_t i = 0; i < table_size; ++i) { for (uint32_t i = 0; i < table_size; ++i) {
table.values.push_back(functions[i]); table.values.push_back(function_indexes[i]);
table.map.FindOrInsert(module_.functions[functions[i]].sig); table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
} }
Handle<FixedArray> values = BuildFunctionTable( instance->function_tables.push_back(
isolate_, static_cast<int>(module_.function_tables.size() - 1), isolate_->factory()->NewFixedArray(table_size * 2));
&module_);
instance->function_tables.push_back(values);
} }
void PopulateIndirectFunctionTable() { void PopulateIndirectFunctionTable() {
// Initialize the fixed arrays in instance->function_tables.
for (uint32_t i = 0; i < instance->function_tables.size(); i++) { for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
PopulateFunctionTable(instance->function_tables[i], WasmIndirectFunctionTable& table = module_.function_tables[i];
module_.function_tables[i].size, Handle<FixedArray> array = instance->function_tables[i];
&instance->function_code); int table_size = static_cast<int>(table.values.size());
for (int j = 0; j < table_size; j++) {
WasmFunction& function = module_.functions[table.values[j]];
array->set(j, Smi::FromInt(table.map.Find(function.sig)));
array->set(j + table_size,
*instance->function_code[function.func_index]);
}
} }
} }
......
...@@ -23,7 +23,7 @@ uint32_t GetMinModuleMemSize(const WasmModule* module) { ...@@ -23,7 +23,7 @@ uint32_t GetMinModuleMemSize(const WasmModule* module) {
return WasmModule::kPageSize * module->min_mem_pages; return WasmModule::kPageSize * module->min_mem_pages;
} }
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone, const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower, ErrorThrower* thrower,
const byte* module_start, const byte* module_start,
const byte* module_end, const byte* module_end,
...@@ -31,18 +31,19 @@ const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone, ...@@ -31,18 +31,19 @@ const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
// Decode the module, but don't verify function bodies, since we'll // Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway. // be compiling them anyway.
ModuleResult decoding_result = ModuleResult decoding_result =
DecodeWasmModule(isolate, zone, module_start, module_end, false, origin); DecodeWasmModule(isolate, module_start, module_end, false, origin);
std::unique_ptr<const WasmModule> module(decoding_result.val);
if (decoding_result.failed()) { if (decoding_result.failed()) {
// Module verification failed. throw. // Module verification failed. throw.
thrower->CompileError("WASM.compileRun() failed: %s", thrower->CompileError("WASM.compileRun() failed: %s",
decoding_result.error_msg.get()); decoding_result.error_msg.get());
return nullptr;
} }
if (thrower->error()) return nullptr; if (thrower->error()) {
return module.release(); if (decoding_result.val) delete decoding_result.val;
return nullptr;
}
return decoding_result.val;
} }
const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate, const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
...@@ -78,16 +79,16 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate, ...@@ -78,16 +79,16 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
} }
const Handle<JSObject> CompileInstantiateWasmModuleForTesting( const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone, Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_start, const byte* module_end, ModuleOrigin origin) { const byte* module_end, ModuleOrigin origin) {
std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting( const WasmModule* module = DecodeWasmModuleForTesting(
isolate, zone, thrower, module_start, module_end, origin)); isolate, thrower, module_start, module_end, origin);
if (module == nullptr) { if (module == nullptr) {
thrower->CompileError("Wasm module decoding failed"); thrower->CompileError("Wasm module decoding failed");
return Handle<JSObject>::null(); return Handle<JSObject>::null();
} }
return InstantiateModuleForTesting(isolate, thrower, module.get()); return InstantiateModuleForTesting(isolate, thrower, module);
} }
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance, int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
...@@ -102,10 +103,9 @@ int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance, ...@@ -102,10 +103,9 @@ int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start, int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, ModuleOrigin origin) { const byte* module_end, ModuleOrigin origin) {
HandleScope scope(isolate); HandleScope scope(isolate);
Zone zone(isolate->allocator(), ZONE_NAME);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule"); ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
Handle<JSObject> instance = CompileInstantiateWasmModuleForTesting( Handle<JSObject> instance = CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, module_start, module_end, origin); isolate, &thrower, module_start, module_end, origin);
if (instance.is_null()) { if (instance.is_null()) {
return -1; return -1;
} }
......
...@@ -19,7 +19,7 @@ namespace wasm { ...@@ -19,7 +19,7 @@ namespace wasm {
namespace testing { namespace testing {
// Decodes the given encoded module. // Decodes the given encoded module.
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone, const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower, ErrorThrower* thrower,
const byte* module_start, const byte* module_start,
const byte* module_end, const byte* module_end,
...@@ -49,8 +49,8 @@ int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower* thrower, ...@@ -49,8 +49,8 @@ int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower* thrower,
// Compiles WasmModule bytes and return an instance of the compiled module. // Compiles WasmModule bytes and return an instance of the compiled module.
const Handle<JSObject> CompileInstantiateWasmModuleForTesting( const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone, Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_start, const byte* module_end, ModuleOrigin origin); const byte* module_end, ModuleOrigin origin);
// Runs the module instance with arguments. // Runs the module instance with arguments.
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance, int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
......
...@@ -56,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -56,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ErrorThrower interpreter_thrower(i_isolate, "Interpreter"); ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting( std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
i_isolate, &zone, &interpreter_thrower, buffer.begin(), buffer.end(), i_isolate, &interpreter_thrower, buffer.begin(), buffer.end(),
v8::internal::wasm::ModuleOrigin::kWasmOrigin)); v8::internal::wasm::ModuleOrigin::kWasmOrigin));
if (module == nullptr) { if (module == nullptr) {
......
...@@ -57,7 +57,7 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data, ...@@ -57,7 +57,7 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
ErrorThrower thrower(i_isolate, "decoder"); ErrorThrower thrower(i_isolate, "decoder");
std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting( std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
i_isolate, &zone, &thrower, buffer.begin(), buffer.end(), kWasmOrigin)); i_isolate, &thrower, buffer.begin(), buffer.end(), kWasmOrigin));
return 0; return 0;
} }
This diff is collapsed.
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --stress-gc --expose-gc
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function run(f) {
// wrap the creation in a closure so that the only thing returned is
// the module (i.e. the underlying array buffer of WASM wire bytes dies).
var module = (() => {
var builder = new WasmModuleBuilder();
builder.addImport("the_name_of_my_import", kSig_i_i);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0])
.exportAs("main");
print("module");
return new WebAssembly.Module(builder.toBuffer());
})();
gc();
for (var i = 0; i < 10; i++) {
print(" instance " + i);
var instance = new WebAssembly.Instance(module, {the_name_of_my_import: f});
var g = instance.exports.main;
assertEquals("function", typeof g);
for (var j = 0; j < 10; j++) {
assertEquals(f(j), g(j));
}
}
}
(function test() {
for (var i = 0; i < 3; i++) {
run(x => (x + 19));
run(x => (x - 18));
}
})();
...@@ -127,3 +127,111 @@ assertEquals(35, module.exports.main(2, 1)); ...@@ -127,3 +127,111 @@ assertEquals(35, module.exports.main(2, 1));
assertEquals(32, module.exports.main(1, 2)); assertEquals(32, module.exports.main(1, 2));
assertEquals(31, module.exports.main(2, 2)); assertEquals(31, module.exports.main(2, 2));
assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)"); assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
(function ConstBaseTest() {
print("ConstBaseTest...");
function instanceWithTable(base, length) {
var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
var add = builder.addFunction("add", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var sub = builder.addFunction("sub", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0]) // --
.exportAs("main");
builder.setFunctionTableLength(length);
builder.addFunctionTableInit(base, false, [add.index, sub.index, mul.index]);
return builder.instantiate();
}
for (var i = 0; i < 5; i++) {
print(" base = " + i);
var module = instanceWithTable(i, 10);
main = module.exports.main;
for (var j = 0; j < i; j++) {
assertTraps(kTrapFuncSigMismatch, "main(12, " + j + ")");
}
assertEquals(34, main(1, i + 0));
assertEquals(35, main(2, i + 0));
assertEquals(32, main(1, i + 1));
assertEquals(31, main(2, i + 1));
assertEquals(33, main(1, i + 2));
assertEquals(66, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
}
})();
(function GlobalBaseTest() {
print("GlobalBaseTest...");
var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
var add = builder.addFunction("add", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
var sub = builder.addFunction("sub", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0]) // --
.exportAs("main");
builder.setFunctionTableLength(10);
var g = builder.addImportedGlobal("base", undefined, kAstI32);
builder.addFunctionTableInit(g, true, [mul.index, add.index, sub.index]);
var module = new WebAssembly.Module(builder.toBuffer());
for (var i = 0; i < 5; i++) {
print(" base = " + i);
var instance = new WebAssembly.Instance(module, {base: i});
main = instance.exports.main;
for (var j = 0; j < i; j++) {
assertTraps(kTrapFuncSigMismatch, "main(12, " + j + ")");
}
assertEquals(33, main(1, i + 0));
assertEquals(66, main(2, i + 0));
assertEquals(34, main(1, i + 1));
assertEquals(35, main(2, i + 1));
assertEquals(32, main(1, i + 2));
assertEquals(31, main(2, i + 2));
assertTraps(kTrapFuncInvalid, "main(12, 10)");
}
})();
...@@ -132,10 +132,11 @@ class WasmModuleBuilder { ...@@ -132,10 +132,11 @@ class WasmModuleBuilder {
this.exports = []; this.exports = [];
this.globals = []; this.globals = [];
this.functions = []; this.functions = [];
this.table = []; this.function_table = [];
this.function_table_length = 0;
this.function_table_inits = [];
this.segments = []; this.segments = [];
this.explicit = []; this.explicit = [];
this.pad = null;
this.num_imported_funcs = 0; this.num_imported_funcs = 0;
this.num_imported_globals = 0; this.num_imported_globals = 0;
return this; return this;
...@@ -151,11 +152,6 @@ class WasmModuleBuilder { ...@@ -151,11 +152,6 @@ class WasmModuleBuilder {
return this; return this;
} }
addPadFunctionTable(size) {
this.pad = size;
return this;
}
addExplicitSection(bytes) { addExplicitSection(bytes) {
this.explicit.push(bytes); this.explicit.push(bytes);
return this; return this;
...@@ -220,8 +216,21 @@ class WasmModuleBuilder { ...@@ -220,8 +216,21 @@ class WasmModuleBuilder {
this.exports.push({name: name, kind: kExternalMemory, index: 0}); this.exports.push({name: name, kind: kExternalMemory, index: 0});
} }
addFunctionTableInit(base, is_global, array) {
this.function_table_inits.push({base: base, is_global: is_global, array: array});
if (!is_global) {
var length = base + array.length;
if (length > this.function_table_length) this.function_table_length = length;
}
return this;
}
appendToTable(array) { appendToTable(array) {
this.table.push(...array); return this.addFunctionTableInit(this.function_table.length, false, array);
}
setFunctionTableLength(length) {
this.function_table_length = length;
return this; return this;
} }
...@@ -292,15 +301,15 @@ class WasmModuleBuilder { ...@@ -292,15 +301,15 @@ class WasmModuleBuilder {
}); });
} }
// Add table. // Add function_table.
if (wasm.table.length > 0) { if (wasm.function_table_length > 0) {
if (debug) print("emitting table @ " + binary.length); if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kTableSectionCode, section => { binary.emit_section(kTableSectionCode, section => {
section.emit_u8(1); // one table entry section.emit_u8(1); // one table entry
section.emit_u8(kWasmAnyFunctionTypeForm); section.emit_u8(kWasmAnyFunctionTypeForm);
section.emit_u8(1); section.emit_u8(1);
section.emit_u32v(wasm.table.length); section.emit_u32v(wasm.function_table_length);
section.emit_u32v(wasm.table.length); section.emit_u32v(wasm.function_table_length);
}); });
} }
...@@ -394,17 +403,25 @@ class WasmModuleBuilder { ...@@ -394,17 +403,25 @@ class WasmModuleBuilder {
} }
// Add table elements. // Add table elements.
if (wasm.table.length > 0) { if (wasm.function_table_inits.length > 0) {
if (debug) print("emitting table @ " + binary.length); if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kElementSectionCode, section => { binary.emit_section(kElementSectionCode, section => {
section.emit_u8(1); var inits = wasm.function_table_inits;
section.emit_u32v(inits.length);
section.emit_u8(0); // table index section.emit_u8(0); // table index
section.emit_u8(kExprI32Const);
section.emit_u8(0); for (let init of inits) {
section.emit_u8(kExprEnd); if (init.is_global) {
section.emit_u32v(wasm.table.length); section.emit_u8(kExprGetGlobal);
for (let index of wasm.table) { } else {
section.emit_u32v(index); section.emit_u8(kExprI32Const);
}
section.emit_u32v(init.base);
section.emit_u8(kExprEnd);
section.emit_u32v(init.array.length);
for (let index of init.array) {
section.emit_u32v(index);
}
} }
}); });
} }
......
...@@ -143,14 +143,14 @@ class WasmModuleVerifyTest : public TestWithIsolateAndZone { ...@@ -143,14 +143,14 @@ class WasmModuleVerifyTest : public TestWithIsolateAndZone {
auto temp = new byte[total]; auto temp = new byte[total];
memcpy(temp, header, sizeof(header)); memcpy(temp, header, sizeof(header));
memcpy(temp + sizeof(header), module_start, size); memcpy(temp + sizeof(header), module_start, size);
ModuleResult result = DecodeWasmModule(isolate(), zone(), temp, ModuleResult result =
temp + total, false, kWasmOrigin); DecodeWasmModule(isolate(), temp, temp + total, false, kWasmOrigin);
delete[] temp; delete[] temp;
return result; return result;
} }
ModuleResult DecodeModuleNoHeader(const byte* module_start, ModuleResult DecodeModuleNoHeader(const byte* module_start,
const byte* module_end) { const byte* module_end) {
return DecodeWasmModule(isolate(), zone(), module_start, module_end, false, return DecodeWasmModule(isolate(), module_start, module_end, false,
kWasmOrigin); kWasmOrigin);
} }
}; };
......
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