Commit ad9cf534 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Add support for exporting WebAssembly.Table instances.

R=bradnelson@chromium.org, rossberg@chromium.org
BUG=v8:5507

Review-Url: https://codereview.chromium.org/2443353002
Cr-Commit-Position: refs/heads/master@{#40554}
parent 5452f97e
...@@ -425,22 +425,9 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -425,22 +425,9 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::JSFunction> table_ctor( i::Handle<i::FixedArray> fixed_array;
i_isolate->native_context()->wasm_table_constructor()); i::Handle<i::JSObject> table_obj = i::WasmJs::CreateWasmTableObject(
i::Handle<i::JSObject> table_obj = i_isolate, initial, has_maximum.FromJust(), maximum, &fixed_array);
i_isolate->factory()->NewJSObject(table_ctor);
i::Handle<i::FixedArray> fixed_array =
i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value();
for (int i = 0; i < initial; ++i) fixed_array->set(i, null);
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *fixed_array);
table_obj->SetInternalField(
kWasmTableMaximumFieldIndex,
has_maximum.FromJust()
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym());
i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check();
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(Utils::ToLocal(table_obj)); return_value.Set(Utils::ToLocal(table_obj));
} }
...@@ -721,6 +708,28 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject( ...@@ -721,6 +708,28 @@ i::Handle<i::JSObject> i::WasmJs::CreateWasmMemoryObject(
return memory_obj; return memory_obj;
} }
i::Handle<i::JSObject> i::WasmJs::CreateWasmTableObject(
i::Isolate* i_isolate, uint32_t initial, bool has_maximum, uint32_t maximum,
i::Handle<i::FixedArray>* array) {
i::Handle<i::JSFunction> table_ctor(
i_isolate->native_context()->wasm_table_constructor());
i::Handle<i::JSObject> table_obj =
i_isolate->factory()->NewJSObject(table_ctor);
*array = i_isolate->factory()->NewFixedArray(initial);
i::Object* null = i_isolate->heap()->null_value();
// TODO(titzer): consider moving FixedArray to size_t.
for (int i = 0; i < static_cast<int>(initial); ++i) (*array)->set(i, null);
table_obj->SetInternalField(kWasmTableArrayFieldIndex, *(*array));
table_obj->SetInternalField(
kWasmTableMaximumFieldIndex,
has_maximum
? static_cast<i::Object*>(i::Smi::FromInt(maximum))
: static_cast<i::Object*>(i_isolate->heap()->undefined_value()));
i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym());
i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check();
return table_obj;
}
// TODO(titzer): we use the API to create the function template because the // TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here. // internal guts are too ugly to replicate here.
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
......
...@@ -28,6 +28,12 @@ class WasmJs { ...@@ -28,6 +28,12 @@ class WasmJs {
Handle<JSArrayBuffer> buffer, Handle<JSArrayBuffer> buffer,
bool has_maximum, int maximum); bool has_maximum, int maximum);
static Handle<JSObject> CreateWasmTableObject(Isolate* isolate,
uint32_t initial,
bool has_maximum,
uint32_t maximum,
Handle<FixedArray>* array);
static bool IsWasmMemoryObject(Isolate* isolate, Handle<Object> value); static bool IsWasmMemoryObject(Isolate* isolate, Handle<Object> value);
static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate, static Handle<JSArrayBuffer> GetWasmMemoryArrayBuffer(Isolate* isolate,
......
...@@ -1037,6 +1037,8 @@ class WasmInstanceBuilder { ...@@ -1037,6 +1037,8 @@ class WasmInstanceBuilder {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
int function_table_count = int function_table_count =
static_cast<int>(module_->function_tables.size()); static_cast<int>(module_->function_tables.size());
std::vector<InitializedTable> inited_tables;
inited_tables.reserve(module_->function_tables.size());
if (function_table_count > 0) { if (function_table_count > 0) {
Handle<FixedArray> old_function_tables = Handle<FixedArray> old_function_tables =
compiled_module_->function_tables(); compiled_module_->function_tables();
...@@ -1044,8 +1046,18 @@ class WasmInstanceBuilder { ...@@ -1044,8 +1046,18 @@ class WasmInstanceBuilder {
factory->NewFixedArray(function_table_count); factory->NewFixedArray(function_table_count);
for (int index = 0; index < function_table_count; ++index) { for (int index = 0; index < function_table_count; ++index) {
WasmIndirectFunctionTable& table = module_->function_tables[index]; WasmIndirectFunctionTable& table = module_->function_tables[index];
uint32_t size = table.max_size > 0 ? table.max_size : table.size; uint32_t table_size = table.size;
Handle<FixedArray> new_table = factory->NewFixedArray(size * 2); Handle<FixedArray> new_table = factory->NewFixedArray(table_size * 2);
inited_tables.push_back({Handle<JSObject>::null(),
Handle<FixedArray>::null(), new_table,
std::vector<WasmFunction*>()});
InitializedTable& init_table = inited_tables.back();
if (table.exported) {
init_table.new_entries.insert(init_table.new_entries.begin(),
table_size, nullptr);
}
for (int i = 0; i < new_table->length(); ++i) { for (int i = 0; i < new_table->length(); ++i) {
static const int kInvalidSigIndex = -1; static const int kInvalidSigIndex = -1;
// Fill the table with invalid signature indexes so that uninitialized // Fill the table with invalid signature indexes so that uninitialized
...@@ -1054,17 +1066,20 @@ class WasmInstanceBuilder { ...@@ -1054,17 +1066,20 @@ class WasmInstanceBuilder {
} }
for (auto table_init : module_->table_inits) { for (auto table_init : module_->table_inits) {
uint32_t base = EvalUint32InitExpr(globals, table_init.offset); uint32_t base = EvalUint32InitExpr(globals, table_init.offset);
uint32_t table_size = static_cast<uint32_t>(new_table->length());
if (base > table_size || if (base > table_size ||
(base + table_init.entries.size() > table_size)) { (base + table_init.entries.size() > table_size)) {
thrower_->CompileError("table initializer is out of bounds"); thrower_->CompileError("table initializer is out of bounds");
continue; continue;
} }
for (size_t i = 0; i < table_init.entries.size(); ++i) { for (size_t i = 0; i < table_init.entries.size(); ++i) {
FunctionSig* sig = module_->functions[table_init.entries[i]].sig; WasmFunction* func = &module_->functions[table_init.entries[i]];
if (table.exported) {
init_table.new_entries[i + base] = func;
}
FunctionSig* sig = func->sig;
int32_t sig_index = table.map.Find(sig); int32_t sig_index = table.map.Find(sig);
new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index)); new_table->set(static_cast<int>(i + base), Smi::FromInt(sig_index));
new_table->set(static_cast<int>(i + base + size), new_table->set(static_cast<int>(i + base + table_size),
code_table->get(table_init.entries[i])); code_table->get(table_init.entries[i]));
} }
} }
...@@ -1086,7 +1101,7 @@ class WasmInstanceBuilder { ...@@ -1086,7 +1101,7 @@ class WasmInstanceBuilder {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Set up the exports object for the new instance. // Set up the exports object for the new instance.
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
ProcessExports(globals, code_table, instance); ProcessExports(globals, inited_tables, code_table, instance);
if (num_imported_functions > 0 || !owner.is_null()) { if (num_imported_functions > 0 || !owner.is_null()) {
// If the code was cloned, or new imports were compiled, patch. // If the code was cloned, or new imports were compiled, patch.
...@@ -1174,6 +1189,14 @@ class WasmInstanceBuilder { ...@@ -1174,6 +1189,14 @@ class WasmInstanceBuilder {
} }
private: private:
// Represents the initialized state of a table.
struct InitializedTable {
Handle<JSObject> table_object; // WebAssembly.Table instance
Handle<FixedArray> js_functions; // JSFunctions exported
Handle<FixedArray> dispatch_table; // internal (code, sig) pairs
std::vector<WasmFunction*> new_entries; // overwriting entries
};
Isolate* isolate_; Isolate* isolate_;
WasmModule* module_; WasmModule* module_;
ErrorThrower* thrower_; ErrorThrower* thrower_;
...@@ -1495,10 +1518,27 @@ class WasmInstanceBuilder { ...@@ -1495,10 +1518,27 @@ class WasmInstanceBuilder {
// Process the exports, creating wrappers for functions, tables, memories, // Process the exports, creating wrappers for functions, tables, memories,
// and globals. // and globals.
void ProcessExports(MaybeHandle<JSArrayBuffer> globals, void ProcessExports(MaybeHandle<JSArrayBuffer> globals,
std::vector<InitializedTable>& inited_tables,
Handle<FixedArray> code_table, Handle<FixedArray> code_table,
Handle<JSObject> instance) { Handle<JSObject> instance) {
if (module_->export_table.size() == 0) return; if (module_->export_table.size() == 0) return;
// Allocate a table to cache the exported JSFunctions if needed.
bool has_exported_functions = module_->num_exported_functions > 0;
if (!has_exported_functions) {
for (auto table : module_->function_tables) {
if (table.exported) {
has_exported_functions = true;
break;
}
}
}
Handle<FixedArray> exported_functions =
has_exported_functions
? isolate_->factory()->NewFixedArray(
static_cast<int>(module_->functions.size()))
: Handle<FixedArray>::null();
Handle<JSObject> exports_object = instance; Handle<JSObject> exports_object = instance;
if (module_->origin == kWasmOrigin) { if (module_->origin == kWasmOrigin) {
// Create the "exports" object. // Create the "exports" object.
...@@ -1526,17 +1566,36 @@ class WasmInstanceBuilder { ...@@ -1526,17 +1566,36 @@ class WasmInstanceBuilder {
WasmFunction& function = module_->functions[exp.index]; WasmFunction& function = module_->functions[exp.index];
int export_index = int export_index =
static_cast<int>(module_->functions.size() + func_index); static_cast<int>(module_->functions.size() + func_index);
Handle<Code> export_code = Handle<Object> value(exported_functions->get(exp.index), isolate_);
code_table->GetValueChecked<Code>(isolate_, export_index); Handle<JSFunction> js_function;
desc.set_value(WrapExportCodeAsJSFunction( if (value->IsJSFunction()) {
isolate_, export_code, name, function.sig, func_index, instance)); // There already is a JSFunction for this WASM function.
js_function = Handle<JSFunction>::cast(value);
} else {
// Wrap the exported code as a JSFunction.
Handle<Code> export_code =
code_table->GetValueChecked<Code>(isolate_, export_index);
js_function =
WrapExportCodeAsJSFunction(isolate_, export_code, name,
function.sig, func_index, instance);
exported_functions->set(exp.index, *js_function);
}
desc.set_value(js_function);
func_index++; func_index++;
break; break;
} }
case kExternalTable: case kExternalTable: {
// TODO(titzer): create a WebAssembly.Table instance. InitializedTable& init_table = inited_tables[exp.index];
// TODO(titzer): should it have the same identity as an import? WasmIndirectFunctionTable& table =
module_->function_tables[exp.index];
if (init_table.table_object.is_null()) {
init_table.table_object = WasmJs::CreateWasmTableObject(
isolate_, table.size, table.max_size != 0, table.max_size,
&init_table.js_functions);
}
desc.set_value(init_table.table_object);
break; break;
}
case kExternalMemory: { case kExternalMemory: {
// Export the memory as a WebAssembly.Memory object. // Export the memory as a WebAssembly.Memory object.
Handle<Object> memory_object( Handle<Object> memory_object(
...@@ -1588,6 +1647,47 @@ class WasmInstanceBuilder { ...@@ -1588,6 +1647,47 @@ class WasmInstanceBuilder {
return; return;
} }
} }
// Fill out the contents of WebAssembly.Table instances.
if (!exported_functions.is_null()) {
// TODO(titzer): We compile JS->WASM wrappers for any function that is a
// member of an exported indirect table that is not itself exported. This
// should be done at compile time and cached instead.
WasmInstance temp_instance(module_);
temp_instance.context = isolate_->native_context();
temp_instance.mem_size = 0;
temp_instance.mem_start = nullptr;
temp_instance.globals_start = nullptr;
ModuleEnv module_env;
module_env.module = module_;
module_env.instance = &temp_instance;
module_env.origin = module_->origin;
// Fill the exported tables with JSFunctions.
for (auto inited_table : inited_tables) {
if (inited_table.js_functions.is_null()) continue;
for (int i = 0; i < static_cast<int>(inited_table.new_entries.size());
i++) {
if (inited_table.new_entries[i] == nullptr) continue;
int func_index =
static_cast<int>(inited_table.new_entries[i]->func_index);
if (!exported_functions->get(func_index)->IsJSFunction()) {
// No JSFunction entry yet exists for this function. Create one.
Handle<Code> wasm_code(Code::cast(code_table->get(func_index)),
isolate_);
Handle<Code> wrapper_code = compiler::CompileJSToWasmWrapper(
isolate_, &module_env, wasm_code, func_index);
Handle<JSFunction> js_function = WrapExportCodeAsJSFunction(
isolate_, wrapper_code, isolate_->factory()->empty_string(),
module_->functions[func_index].sig, func_index, instance);
exported_functions->set(func_index, *js_function);
}
inited_table.js_functions->set(i,
exported_functions->get(func_index));
}
}
}
} }
}; };
......
...@@ -8,6 +8,7 @@ load("test/mjsunit/wasm/wasm-constants.js"); ...@@ -8,6 +8,7 @@ load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
(function testExportedMain() { (function testExportedMain() {
print("TestExportedMain...");
var kReturnValue = 88; var kReturnValue = 88;
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
...@@ -28,6 +29,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -28,6 +29,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
})(); })();
(function testExportedTwice() { (function testExportedTwice() {
print("TestExportedTwice...");
var kReturnValue = 99; var kReturnValue = 99;
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
...@@ -49,10 +51,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -49,10 +51,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(kReturnValue, module.exports.foo()); assertEquals(kReturnValue, module.exports.foo());
assertEquals(kReturnValue, module.exports.blah()); assertEquals(kReturnValue, module.exports.blah());
assertSame(module.exports.blah, module.exports.foo);
})(); })();
(function testNumericName() { (function testNumericName() {
print("TestNumericName...");
var kReturnValue = 93; var kReturnValue = 93;
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
...@@ -74,6 +78,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -74,6 +78,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
})(); })();
(function testExportNameClash() { (function testExportNameClash() {
print("TestExportNameClash...");
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
builder.addFunction("one", kSig_v_v).addBody([kExprNop]).exportAs("main"); builder.addFunction("one", kSig_v_v).addBody([kExprNop]).exportAs("main");
...@@ -87,3 +92,23 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -87,3 +92,23 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertContains("Duplicate export", e.toString()); assertContains("Duplicate export", e.toString());
} }
})(); })();
(function testExportMultipleIdentity() {
print("TestExportMultipleIdentity...");
var builder = new WasmModuleBuilder();
builder.addFunction("one", kSig_v_v).addBody([kExprNop])
.exportAs("a")
.exportAs("b")
.exportAs("c");
let instance = builder.instantiate();
let e = instance.exports;
assertEquals("function", typeof e.a);
assertEquals("function", typeof e.b);
assertEquals("function", typeof e.c);
assertSame(e.a, e.b);
assertSame(e.a, e.c);
assertEquals("a", e.a.name);
})();
...@@ -73,7 +73,7 @@ module = (function () { ...@@ -73,7 +73,7 @@ module = (function () {
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallIndirect, sig_i_ii kExprCallIndirect, sig_i_ii
]) ])
.exportFunc() .exportFunc();
builder.appendToTable([mul.index, add.index, popcnt.index, main.index]); builder.appendToTable([mul.index, add.index, popcnt.index, main.index]);
return builder.instantiate({mul: function(a, b) { return a * b | 0; }}); return builder.instantiate({mul: function(a, b) { return a * b | 0; }});
...@@ -85,10 +85,7 @@ assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)"); ...@@ -85,10 +85,7 @@ assertTraps(kTrapFuncSigMismatch, "module.exports.main(2, 12, 33)");
assertTraps(kTrapFuncSigMismatch, "module.exports.main(3, 12, 33)"); assertTraps(kTrapFuncSigMismatch, "module.exports.main(3, 12, 33)");
assertTraps(kTrapFuncInvalid, "module.exports.main(4, 12, 33)"); assertTraps(kTrapFuncInvalid, "module.exports.main(4, 12, 33)");
function AddFunctions(builder) {
module = (function () {
var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii) var mul = builder.addFunction("mul", kSig_i_ii)
.addBody([ .addBody([
kExprGetLocal, 0, // -- kExprGetLocal, 0, // --
...@@ -107,6 +104,14 @@ module = (function () { ...@@ -107,6 +104,14 @@ module = (function () {
kExprGetLocal, 1, // -- kExprGetLocal, 1, // --
kExprI32Sub // -- kExprI32Sub // --
]); ]);
return {mul: mul, add: add, sub: sub};
}
module = (function () {
var builder = new WasmModuleBuilder();
var f = AddFunctions(builder);
builder.addFunction("main", kSig_i_ii) builder.addFunction("main", kSig_i_ii)
.addBody([ .addBody([
kExprI32Const, 33, // -- kExprI32Const, 33, // --
...@@ -115,7 +120,7 @@ module = (function () { ...@@ -115,7 +120,7 @@ module = (function () {
kExprCallIndirect, 0]) // -- kExprCallIndirect, 0]) // --
.exportAs("main"); .exportAs("main");
builder.appendToTable([mul.index, add.index, sub.index]); builder.appendToTable([f.mul.index, f.add.index, f.sub.index]);
return builder.instantiate(); return builder.instantiate();
})(); })();
...@@ -133,24 +138,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)"); ...@@ -133,24 +138,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
function instanceWithTable(base, length) { function instanceWithTable(base, length) {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii) var f = AddFunctions(builder);
.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) builder.addFunction("main", kSig_i_ii)
.addBody([ .addBody([
kExprI32Const, 33, // -- kExprI32Const, 33, // --
...@@ -160,7 +148,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)"); ...@@ -160,7 +148,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
.exportAs("main"); .exportAs("main");
builder.setFunctionTableLength(length); builder.setFunctionTableLength(length);
builder.addFunctionTableInit(base, false, [add.index, sub.index, mul.index]); builder.addFunctionTableInit(base, false, [f.add.index, f.sub.index, f.mul.index]);
return builder.instantiate(); return builder.instantiate();
} }
...@@ -187,24 +175,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)"); ...@@ -187,24 +175,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
var mul = builder.addFunction("mul", kSig_i_ii) var f = AddFunctions(builder);
.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) builder.addFunction("main", kSig_i_ii)
.addBody([ .addBody([
kExprI32Const, 33, // -- kExprI32Const, 33, // --
...@@ -215,7 +186,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)"); ...@@ -215,7 +186,7 @@ assertTraps(kTrapFuncInvalid, "module.exports.main(12, 3)");
builder.setFunctionTableLength(10); builder.setFunctionTableLength(10);
var g = builder.addImportedGlobal("base", undefined, kAstI32); var g = builder.addImportedGlobal("base", undefined, kAstI32);
builder.addFunctionTableInit(g, true, [mul.index, add.index, sub.index]); builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index, f.sub.index]);
var module = new WebAssembly.Module(builder.toBuffer()); var module = new WebAssembly.Module(builder.toBuffer());
......
// 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
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function AddFunctions(builder) {
let sig_index = builder.addType(kSig_i_ii);
let mul = builder.addFunction("mul", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
let add = builder.addFunction("add", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
let sub = builder.addFunction("sub", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
return {mul: mul, add: add, sub: sub};
}
(function ExportedTableTest() {
print("ExportedTableTest...");
let builder = new WasmModuleBuilder();
let d = builder.addImport("js_div", kSig_i_ii);
let f = AddFunctions(builder);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0]) // --
.exportAs("main");
f.add.exportAs("blarg");
builder.setFunctionTableLength(10);
let g = builder.addImportedGlobal("base", undefined, kAstI32);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addExportOfKind("table", kExternalTable, 0);
let module = new WebAssembly.Module(builder.toBuffer());
function js_div(a, b) { return (a / b) | 0; }
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let instance = new WebAssembly.Instance(module, {base: i, js_div: js_div});
main = instance.exports.main;
let table = instance.exports.table;
assertTrue(table instanceof WebAssembly.Table);
assertEquals(10, table.length);
for (let j = 0; j < i; j++) {
assertSame(null, table.get(j));
}
let mul = table.get(i+0);
let add = table.get(i+1);
let sub = table.get(i+2);
print(" mul=" + mul);
print(" add=" + add);
print(" sub=" + sub);
assertEquals("function", typeof mul);
assertEquals("function", typeof add);
assertEquals("function", typeof sub);
assertEquals(2, mul.length);
assertEquals(2, add.length);
assertEquals(2, sub.length);
assertEquals("blarg", add.name);
let exp_div = table.get(i+3);
assertEquals("function", typeof exp_div);
print(" js_div=" + exp_div);
// Should have a new, wrapped version of the import.
assertFalse(js_div == exp_div);
for (let j = i + 4; j < 10; j++) {
assertSame(null, table.get(j));
}
assertEquals(-33, mul(-11, 3));
assertEquals(4444444, add(3333333, 1111111));
assertEquals(-9999, sub(1, 10000));
assertEquals(-44, exp_div(-88.1, 2));
}
})();
...@@ -120,7 +120,8 @@ class WasmGlobalBuilder { ...@@ -120,7 +120,8 @@ class WasmGlobalBuilder {
} }
exportAs(name) { exportAs(name) {
this.module.exports.push({name: name, kind: kExternalGlobal, index: this.index}); this.module.exports.push({name: name, kind: kExternalGlobal,
index: this.index});
return this; return this;
} }
} }
...@@ -197,7 +198,8 @@ class WasmModuleBuilder { ...@@ -197,7 +198,8 @@ class WasmModuleBuilder {
} }
addImportedMemory(module, name, initial = 0, maximum) { addImportedMemory(module, name, initial = 0, maximum) {
let o = {module: module, name: name, kind: kExternalMemory, initial: initial, maximum: maximum}; let o = {module: module, name: name, kind: kExternalMemory,
initial: initial, maximum: maximum};
this.imports.push(o); this.imports.push(o);
return this; return this;
} }
...@@ -207,6 +209,11 @@ class WasmModuleBuilder { ...@@ -207,6 +209,11 @@ class WasmModuleBuilder {
return this; return this;
} }
addExportOfKind(name, kind, index) {
this.exports.push({name: name, kind: kind, index: index});
return this;
}
addDataSegment(addr, data, is_global = false) { addDataSegment(addr, data, is_global = false) {
this.segments.push({addr: addr, data: data, is_global: is_global}); this.segments.push({addr: addr, data: data, is_global: is_global});
return this.segments.length - 1; return this.segments.length - 1;
...@@ -217,10 +224,13 @@ class WasmModuleBuilder { ...@@ -217,10 +224,13 @@ class WasmModuleBuilder {
} }
addFunctionTableInit(base, is_global, array) { addFunctionTableInit(base, is_global, array) {
this.function_table_inits.push({base: base, is_global: is_global, array: array}); this.function_table_inits.push({base: base, is_global: is_global,
array: array});
if (!is_global) { if (!is_global) {
var length = base + array.length; var length = base + array.length;
if (length > this.function_table_length) this.function_table_length = length; if (length > this.function_table_length) {
this.function_table_length = length;
}
} }
return this; return this;
} }
......
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