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

[wasm] Support a two-level namespace for imports.

R=binji@chromium.org,dschuff@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1780483002

Cr-Commit-Position: refs/heads/master@{#34600}
parent 3f8af30e
......@@ -2323,7 +2323,9 @@ Handle<JSFunction> CompileJSToWasmWrapper(
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Handle<JSFunction> function,
wasm::FunctionSig* sig, const char* name) {
wasm::FunctionSig* sig,
const char* module_cstr,
const char* function_cstr) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -2391,7 +2393,7 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
}
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, "wasm-to-js", 0,
name);
module_cstr);
}
return code;
}
......
......@@ -40,7 +40,9 @@ Handle<Code> CompileWasmFunction(wasm::ErrorThrower& thrower, Isolate* isolate,
// Wraps a JS function, producing a code object that can be called from WASM.
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Handle<JSFunction> function,
wasm::FunctionSig* sig, const char* name);
wasm::FunctionSig* sig,
const char* module_name,
const char* function_name);
// Wraps a given wasm code object, producing a JSFunction that can be called
// from JavaScript.
......
......@@ -251,7 +251,11 @@ class ModuleDecoder : public Decoder {
} else {
import->sig = module->signatures[import->sig_index];
}
const byte* pos = pc_;
import->module_name_offset = consume_string("import module name");
if (import->module_name_offset == 0) {
error(pos, "import module name cannot be NULL");
}
import->function_name_offset =
consume_string("import function name");
}
......
......@@ -278,29 +278,65 @@ WasmModule::WasmModule()
start_function_index(-1),
origin(kWasmOrigin) {}
static MaybeHandle<JSFunction> LookupFunction(ErrorThrower& thrower,
Handle<JSObject> ffi,
uint32_t index,
Handle<String> name,
const char* cstr) {
if (!ffi.is_null()) {
MaybeHandle<Object> result = Object::GetProperty(ffi, name);
if (!result.is_null()) {
Handle<Object> obj = result.ToHandleChecked();
if (obj->IsJSFunction()) {
return Handle<JSFunction>::cast(obj);
} else {
thrower.Error("FFI function #%d:%s is not a JSFunction.", index, cstr);
return MaybeHandle<JSFunction>();
}
} else {
thrower.Error("FFI function #%d:%s not found.", index, cstr);
return MaybeHandle<JSFunction>();
static MaybeHandle<JSFunction> ReportFFIError(ErrorThrower& thrower,
const char* error, uint32_t index,
const char* module_cstr,
const char* function_cstr) {
if (function_cstr) {
thrower.Error("Import #%d module=\"%s\" function=\"%s\" error: %s", index,
module_cstr, function_cstr, error);
} else {
thrower.Error("Import #%d module=\"%s\" error: %s", index, module_cstr,
error);
}
thrower.Error("Import ");
return MaybeHandle<JSFunction>();
}
static MaybeHandle<JSFunction> LookupFunction(
ErrorThrower& thrower, Factory* factory, Handle<JSObject> ffi,
uint32_t index, const char* module_cstr, const char* function_cstr) {
if (ffi.is_null()) {
return ReportFFIError(thrower, "FFI is not an object", index, module_cstr,
function_cstr);
}
// Look up the module first.
Handle<String> name = factory->InternalizeUtf8String(module_cstr);
MaybeHandle<Object> result = Object::GetProperty(ffi, name);
if (result.is_null()) {
return ReportFFIError(thrower, "module not found", index, module_cstr,
function_cstr);
}
Handle<Object> module = result.ToHandleChecked();
if (!module->IsJSReceiver()) {
return ReportFFIError(thrower, "module is not an object or function", index,
module_cstr, function_cstr);
}
Handle<Object> function;
if (function_cstr) {
// Look up the function in the module.
Handle<String> name = factory->InternalizeUtf8String(function_cstr);
MaybeHandle<Object> result = Object::GetProperty(module, name);
if (result.is_null()) {
return ReportFFIError(thrower, "function not found", index, module_cstr,
function_cstr);
}
function = result.ToHandleChecked();
} else {
thrower.Error("FFI table is not an object.");
return MaybeHandle<JSFunction>();
// No function specified. Use the "default export".
function = module;
}
if (!function->IsJSFunction()) {
return ReportFFIError(thrower, "not a function", index, module_cstr,
function_cstr);
}
return Handle<JSFunction>::cast(function);
}
// Instantiates a wasm module as a JSObject.
......@@ -375,13 +411,14 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
if (import_table.size() > 0) {
instance.import_code.reserve(import_table.size());
for (const WasmImport& import : import_table) {
const char* cstr = GetName(import.function_name_offset);
Handle<String> name = factory->InternalizeUtf8String(cstr);
MaybeHandle<JSFunction> function =
LookupFunction(thrower, ffi, index, name, cstr);
const char* module_cstr = GetNameOrNull(import.module_name_offset);
const char* function_cstr = GetNameOrNull(import.function_name_offset);
MaybeHandle<JSFunction> function = LookupFunction(
thrower, factory, ffi, index, module_cstr, function_cstr);
if (function.is_null()) return MaybeHandle<JSObject>();
Handle<Code> code = compiler::CompileWasmToJSWrapper(
isolate, &module_env, function.ToHandleChecked(), import.sig, cstr);
isolate, &module_env, function.ToHandleChecked(), import.sig,
module_cstr, function_cstr);
instance.import_code.push_back(code);
index++;
}
......@@ -404,10 +441,11 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
if (func.external) {
// Lookup external function in FFI object.
MaybeHandle<JSFunction> function =
LookupFunction(thrower, ffi, index, name, cstr);
LookupFunction(thrower, factory, ffi, index, cstr, nullptr);
if (function.is_null()) return MaybeHandle<JSObject>();
code = compiler::CompileWasmToJSWrapper(
isolate, &module_env, function.ToHandleChecked(), func.sig, cstr);
code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
function.ToHandleChecked(),
func.sig, cstr, nullptr);
} else {
// Compile the function.
code = compiler::CompileWasmFunction(thrower, isolate, &module_env, func);
......
......@@ -141,6 +141,13 @@ struct WasmModule {
return reinterpret_cast<const char*>(module_start + offset);
}
// Get a pointer to a string stored in the module bytes representing a name.
const char* GetNameOrNull(uint32_t offset) const {
if (offset == 0) return nullptr; // no name.
CHECK(BoundsCheck(offset, offset + 1));
return reinterpret_cast<const char*>(module_start + offset);
}
// Checks the given offset range is contained within the module bytes.
bool BoundsCheck(uint32_t start, uint32_t end) const {
size_t size = module_end - module_start;
......
......@@ -57,7 +57,7 @@ uint32_t AddJsFunction(TestingModule* module, FunctionSig* sig,
uint32_t index = static_cast<uint32_t>(module->module->functions.size() - 1);
Isolate* isolate = CcTest::InitIsolateOnce();
Handle<Code> code =
CompileWasmToJSWrapper(isolate, module, jsfunc, sig, "test");
CompileWasmToJSWrapper(isolate, module, jsfunc, sig, "test", nullptr);
module->instance->function_code[index] = code;
return index;
}
......
......@@ -138,3 +138,16 @@ var debug = false;
var instance = Wasm.instantiateModule(array2);
assertEquals(17, instance.exports.blarg());
})();
(function ImportTestTwoLevel() {
var module = new WasmModuleBuilder();
var index = module.addImportWithModule("mod", "print", [kAstStmt, kAstI32]);
module.addFunction("foo", [kAstStmt])
.addBody([kExprCallImport, index, kExprI8Const, 19])
.exportAs("main");
var buffer = module.toBuffer(debug);
var instance = Wasm.instantiateModule(buffer, {mod: {print: print}});
print("should print 19! ");
instance.exports.main();
})();
......@@ -78,10 +78,16 @@ WasmModuleBuilder.prototype.addFunction = function(name, sig) {
return func;
}
WasmModuleBuilder.prototype.addImportWithModule = function(module, name, sig) {
var sig_index = (typeof sig) == "number" ? sig : this.addSignature(sig);
this.imports.push({module: module, name: name, sig_index: sig_index});
return this.imports.length - 1;
}
WasmModuleBuilder.prototype.addImport = function(name, sig) {
var sig_index = (typeof sig) == "number" ? sig : this.addSignature(sig);
this.imports.push({name: name, sig_index: sig_index});
return this.imports.length - 1;
var sig_index = (typeof sig) == "number" ? sig : this.addSignature(sig);
this.imports.push({module: name, name: undefined, sig_index: sig_index});
return this.imports.length - 1;
}
WasmModuleBuilder.prototype.addDataSegment = function(addr, data, init) {
......@@ -172,8 +178,12 @@ WasmModuleBuilder.prototype.toArray = function(debug) {
emit_varint(bytes, this.imports.length);
for (imp of this.imports) {
emit_u16(bytes, imp.sig_index);
emit_string(bytes, "");
emit_string(bytes, imp.name);
emit_string(bytes, imp.module);
if (imp.name == undefined) {
emit_u32(bytes, 0);
} else {
emit_string(bytes, imp.name);
}
}
}
......
......@@ -388,7 +388,6 @@ TEST_F(WasmModuleVerifyTest, OneFunctionImported) {
if (result.val) delete result.val;
}
TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody) {
static const byte kCodeStartOffset = 19;
static const byte kCodeEndOffset = kCodeStartOffset + 1;
......@@ -1095,6 +1094,20 @@ TEST_F(WasmModuleVerifyTest, ImportTable_one_sig) {
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_invalid_module) {
static const byte data[] = {
kDeclSignatures,
1,
VOID_VOID_SIG,
kDeclImportTable,
1, // --
SIG_INDEX(0), // sig index
NAME_OFFSET(0), // module name
NAME_OFFSET(1) // function name
};
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, ImportTable_off_end) {
static const byte data[] = {
kDeclSignatures, 1, VOID_VOID_SIG, kDeclImportTable, 1,
......
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