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() {
i::Handle<i::wasm::WasmCompiledModule> compiled_part =
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 =
i::WasmCompiledModuleSerializer::SerializeWasmModule(obj->GetIsolate(),
compiled_part);
compiled_part->set_module_bytes(wire_bytes);
script_data->ReleaseDataOwnership();
size_t size = static_cast<size_t>(script_data->length());
......@@ -7238,7 +7235,7 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Deserialize(
i::Handle<i::wasm::WasmCompiledModule> compiled_module =
handle(i::wasm::WasmCompiledModule::cast(*compiled_part));
return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(i::wasm::CreateCompiledModuleObject(
Utils::ToLocal(i::wasm::CreateWasmModuleObject(
i_isolate, compiled_module, i::wasm::ModuleOrigin::kWasmOrigin)));
}
......
......@@ -306,6 +306,44 @@ MaybeHandle<String> Factory::NewStringFromUtf8(Vector<const char> string,
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,
int length,
PretenureFlag pretenure) {
......
......@@ -183,6 +183,10 @@ class V8_EXPORT_PRIVATE Factory final {
MUST_USE_RESULT MaybeHandle<String> NewStringFromUtf8(
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(
Vector<const uc16> str, PretenureFlag pretenure = NOT_TENURED);
......
......@@ -767,7 +767,7 @@ RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
if (!maybe_compiled_module.ToHandle(&compiled_module)) {
return isolate->heap()->undefined_value();
}
return *wasm::CreateCompiledModuleObject(
return *wasm::CreateWasmModuleObject(
isolate, Handle<wasm::WasmCompiledModule>::cast(compiled_module),
wasm::kWasmOrigin);
}
......
......@@ -32,7 +32,16 @@ class Signature : public ZoneObject {
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.
class Builder {
......
......@@ -12,6 +12,7 @@
#include "src/snapshot/deserializer.h"
#include "src/snapshot/snapshot.h"
#include "src/version.h"
#include "src/wasm/wasm-module.h"
namespace v8 {
namespace internal {
......@@ -217,7 +218,9 @@ MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
}
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);
wasm_cs.reference_map()->AddAttachedReference(*isolate->native_context());
ScriptData* data = wasm_cs.Serialize(compiled_module);
......@@ -247,7 +250,10 @@ MaybeHandle<FixedArray> WasmCompiledModuleSerializer::DeserializeWasmModule(
MaybeHandle<HeapObject> obj = deserializer.DeserializeObject(isolate);
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 {
......
......@@ -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:
WasmCompiledModuleSerializer(Isolate* isolate, uint32_t source_hash)
......
......@@ -46,6 +46,7 @@ class Managed : public Foreign {
Managed<CppType>** p =
reinterpret_cast<Managed<CppType>**>(data.GetParameter());
delete (*p)->get();
(*p)->set_foreign_address(0);
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
}
};
......
......@@ -1077,10 +1077,9 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
} // namespace
ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
const byte* module_start, const byte* module_end,
bool verify_functions, ModuleOrigin origin) {
size_t decode_memory_start = zone->allocation_size();
ModuleResult DecodeWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, bool verify_functions,
ModuleOrigin origin) {
HistogramTimerScope wasm_decode_module_time_scope(
isolate->counters()->wasm_decode_module_time());
size_t size = module_end - module_start;
......@@ -1089,12 +1088,18 @@ ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
// TODO(bradnelson): Improve histogram handling of size_t.
isolate->counters()->wasm_module_size_bytes()->AddSample(
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);
ModuleResult result = decoder.DecodeModule(module, verify_functions);
// 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(
static_cast<int>(zone->allocation_size() - decode_memory_start));
static_cast<int>(zone->allocation_size()));
return result;
}
......
......@@ -22,7 +22,7 @@ typedef std::vector<std::vector<std::pair<int, int>>> AsmJsOffsets;
typedef Result<AsmJsOffsets> AsmJsOffsetsResult;
// 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_end,
bool verify_functions,
......
......@@ -104,10 +104,8 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
RawBuffer buffer = GetRawBufferSource(args[0], &thrower);
if (thrower.error()) return;
i::Zone zone(isolate->allocator(), ZONE_NAME);
internal::wasm::ModuleResult result =
internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
true, internal::wasm::kWasmOrigin);
internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
isolate, buffer.start, buffer.end, true, internal::wasm::kWasmOrigin);
if (result.failed()) {
thrower.CompileFailed("", result);
......
This diff is collapsed.
This diff is collapsed.
......@@ -277,15 +277,18 @@ class WasmSerializationTest {
testing::SetupIsolateForWasmModule(serialization_isolate);
ModuleResult decoding_result =
DecodeWasmModule(serialization_isolate, zone(), buffer.begin(),
buffer.end(), false, kWasmOrigin);
std::unique_ptr<const WasmModule> module(decoding_result.val);
DecodeWasmModule(serialization_isolate, buffer.begin(), buffer.end(),
false, kWasmOrigin);
CHECK(!decoding_result.failed());
Handle<WasmModuleWrapper> module_wrapper = WasmModuleWrapper::New(
serialization_isolate, const_cast<WasmModule*>(decoding_result.val));
MaybeHandle<WasmCompiledModule> compiled_module =
module->CompileFunctions(serialization_isolate, &thrower);
decoding_result.val->CompileFunctions(serialization_isolate,
module_wrapper, &thrower);
CHECK(!compiled_module.is_null());
Handle<JSObject> module_obj = CreateCompiledModuleObject(
Handle<JSObject> module_obj = CreateWasmModuleObject(
serialization_isolate, compiled_module.ToHandleChecked(),
ModuleOrigin::kWasmOrigin);
v8::Local<v8::Object> v8_module_obj = v8::Utils::ToLocal(module_obj);
......@@ -470,7 +473,7 @@ TEST(TestInterruptLoop) {
ErrorThrower thrower(isolate, "Test");
const Handle<JSObject> instance =
testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......@@ -544,7 +547,7 @@ TEST(Run_WasmModule_GrowMemOobFixedIndex) {
ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......@@ -589,7 +592,7 @@ TEST(Run_WasmModule_GrowMemOobVariableIndex) {
ErrorThrower thrower(isolate, "Test");
Handle<JSObject> instance = testing::CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, buffer.begin(), buffer.end(),
isolate, &thrower, buffer.begin(), buffer.end(),
ModuleOrigin::kWasmOrigin);
CHECK(!instance.is_null());
......
......@@ -209,16 +209,9 @@ class TestingModule : public ModuleEnv {
WasmJs::InstallWasmMapsIfNeeded(isolate_, isolate_->native_context());
Handle<Code> ret_code =
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(
isolate_, ret_code, name,
static_cast<int>(this->module->functions[index].sig->parameter_count()),
exportedSig, module_object);
isolate_, ret_code, name, this->module->functions[index].sig,
static_cast<int>(index), module_object);
return ret;
}
......@@ -226,27 +219,33 @@ class TestingModule : public ModuleEnv {
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,
std::vector<int32_t>(), false, false,
SignatureMap()});
WasmIndirectFunctionTable& table = module_.function_tables.back();
for (uint32_t i = 0; i < table_size; ++i) {
table.values.push_back(functions[i]);
table.map.FindOrInsert(module_.functions[functions[i]].sig);
table.values.push_back(function_indexes[i]);
table.map.FindOrInsert(module_.functions[function_indexes[i]].sig);
}
Handle<FixedArray> values = BuildFunctionTable(
isolate_, static_cast<int>(module_.function_tables.size() - 1),
&module_);
instance->function_tables.push_back(values);
instance->function_tables.push_back(
isolate_->factory()->NewFixedArray(table_size * 2));
}
void PopulateIndirectFunctionTable() {
// Initialize the fixed arrays in instance->function_tables.
for (uint32_t i = 0; i < instance->function_tables.size(); i++) {
PopulateFunctionTable(instance->function_tables[i],
module_.function_tables[i].size,
&instance->function_code);
WasmIndirectFunctionTable& table = module_.function_tables[i];
Handle<FixedArray> array = instance->function_tables[i];
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) {
return WasmModule::kPageSize * module->min_mem_pages;
}
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower,
const byte* module_start,
const byte* module_end,
......@@ -31,18 +31,19 @@ const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
// Decode the module, but don't verify function bodies, since we'll
// be compiling them anyway.
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()) {
// Module verification failed. throw.
thrower->CompileError("WASM.compileRun() failed: %s",
decoding_result.error_msg.get());
return nullptr;
}
if (thrower->error()) return nullptr;
return module.release();
if (thrower->error()) {
if (decoding_result.val) delete decoding_result.val;
return nullptr;
}
return decoding_result.val;
}
const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
......@@ -78,16 +79,16 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
}
const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone,
const byte* module_start, const byte* module_end, ModuleOrigin origin) {
std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting(
isolate, zone, thrower, module_start, module_end, origin));
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin) {
const WasmModule* module = DecodeWasmModuleForTesting(
isolate, thrower, module_start, module_end, origin);
if (module == nullptr) {
thrower->CompileError("Wasm module decoding failed");
return Handle<JSObject>::null();
}
return InstantiateModuleForTesting(isolate, thrower, module.get());
return InstantiateModuleForTesting(isolate, thrower, module);
}
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,
const byte* module_end, ModuleOrigin origin) {
HandleScope scope(isolate);
Zone zone(isolate->allocator(), ZONE_NAME);
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
Handle<JSObject> instance = CompileInstantiateWasmModuleForTesting(
isolate, &thrower, &zone, module_start, module_end, origin);
isolate, &thrower, module_start, module_end, origin);
if (instance.is_null()) {
return -1;
}
......
......@@ -19,7 +19,7 @@ namespace wasm {
namespace testing {
// Decodes the given encoded module.
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate, Zone* zone,
const WasmModule* DecodeWasmModuleForTesting(Isolate* isolate,
ErrorThrower* thrower,
const byte* module_start,
const byte* module_end,
......@@ -49,8 +49,8 @@ int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower* thrower,
// Compiles WasmModule bytes and return an instance of the compiled module.
const Handle<JSObject> CompileInstantiateWasmModuleForTesting(
Isolate* isolate, ErrorThrower* thrower, Zone* zone,
const byte* module_start, const byte* module_end, ModuleOrigin origin);
Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
const byte* module_end, ModuleOrigin origin);
// Runs the module instance with arguments.
int32_t RunWasmModuleForTesting(Isolate* isolate, Handle<JSObject> instance,
......
......@@ -56,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
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));
if (module == nullptr) {
......
......@@ -57,7 +57,7 @@ int fuzz_wasm_section(WasmSectionCode section, const uint8_t* data,
ErrorThrower thrower(i_isolate, "decoder");
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;
}
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));
assertEquals(32, module.exports.main(1, 2));
assertEquals(31, module.exports.main(2, 2));
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 {
this.exports = [];
this.globals = [];
this.functions = [];
this.table = [];
this.function_table = [];
this.function_table_length = 0;
this.function_table_inits = [];
this.segments = [];
this.explicit = [];
this.pad = null;
this.num_imported_funcs = 0;
this.num_imported_globals = 0;
return this;
......@@ -151,11 +152,6 @@ class WasmModuleBuilder {
return this;
}
addPadFunctionTable(size) {
this.pad = size;
return this;
}
addExplicitSection(bytes) {
this.explicit.push(bytes);
return this;
......@@ -220,8 +216,21 @@ class WasmModuleBuilder {
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) {
this.table.push(...array);
return this.addFunctionTableInit(this.function_table.length, false, array);
}
setFunctionTableLength(length) {
this.function_table_length = length;
return this;
}
......@@ -292,15 +301,15 @@ class WasmModuleBuilder {
});
}
// Add table.
if (wasm.table.length > 0) {
// Add function_table.
if (wasm.function_table_length > 0) {
if (debug) print("emitting table @ " + binary.length);
binary.emit_section(kTableSectionCode, section => {
section.emit_u8(1); // one table entry
section.emit_u8(kWasmAnyFunctionTypeForm);
section.emit_u8(1);
section.emit_u32v(wasm.table.length);
section.emit_u32v(wasm.table.length);
section.emit_u32v(wasm.function_table_length);
section.emit_u32v(wasm.function_table_length);
});
}
......@@ -394,17 +403,25 @@ class WasmModuleBuilder {
}
// Add table elements.
if (wasm.table.length > 0) {
if (wasm.function_table_inits.length > 0) {
if (debug) print("emitting table @ " + binary.length);
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(kExprI32Const);
section.emit_u8(0);
section.emit_u8(kExprEnd);
section.emit_u32v(wasm.table.length);
for (let index of wasm.table) {
section.emit_u32v(index);
for (let init of inits) {
if (init.is_global) {
section.emit_u8(kExprGetGlobal);
} else {
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 {
auto temp = new byte[total];
memcpy(temp, header, sizeof(header));
memcpy(temp + sizeof(header), module_start, size);
ModuleResult result = DecodeWasmModule(isolate(), zone(), temp,
temp + total, false, kWasmOrigin);
ModuleResult result =
DecodeWasmModule(isolate(), temp, temp + total, false, kWasmOrigin);
delete[] temp;
return result;
}
ModuleResult DecodeModuleNoHeader(const byte* module_start,
const byte* module_end) {
return DecodeWasmModule(isolate(), zone(), module_start, module_end, false,
return DecodeWasmModule(isolate(), module_start, module_end, false,
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