[wasm] WAT-compatible naming for exported function

For exported functions that do not have a name yet, we use the field
name (see <name> of WasmExport) of the first export entry.

Doc: https://docs.google.com/document/d/1XoXWONLBgZWQ9dhtoMpQPvD0fnnWA50OorsuSXfME3g/edit#heading=h.6yuhg1v2w3q4
Bug: v8:10242
Change-Id: Icfa55fd50e5d1c4cf10581b7d322112e9f113388
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2112684
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66877}
This commit is contained in:
Z Nguyen-Huu 2020-03-26 09:38:25 -07:00 committed by Commit Bot
parent dbda6c3d4f
commit 073c5d2dd6
16 changed files with 73 additions and 51 deletions

View File

@ -313,6 +313,8 @@ class ModuleDecoderImpl : public Decoder {
error(start_, "end is less than start");
end_ = start_;
}
module_start_ = module_start;
module_end_ = module_end;
}
void onFirstError() override {
@ -933,7 +935,7 @@ class ModuleDecoderImpl : public Decoder {
&module_->functions[index + module_->num_imported_functions];
function->code = {offset, length};
if (verify_functions) {
ModuleWireBytes bytes(start_, end_);
ModuleWireBytes bytes(module_start_, module_end_);
VerifyFunctionBody(module_->signature_zone->allocator(),
index + module_->num_imported_functions, bytes,
module_.get(), function);
@ -1296,6 +1298,8 @@ class ModuleDecoderImpl : public Decoder {
private:
const WasmFeatures enabled_features_;
std::shared_ptr<WasmModule> module_;
const byte* module_start_;
const byte* module_end_;
Counters* counters_ = nullptr;
// The type section is the first section in a module.
uint8_t next_ordered_section_ = kFirstSectionInModule;
@ -2208,36 +2212,50 @@ bool FindNameSection(Decoder* decoder) {
} // namespace
void DecodeFunctionNames(const byte* module_start, const byte* module_end,
std::unordered_map<uint32_t, WireBytesRef>* names) {
std::unordered_map<uint32_t, WireBytesRef>* names,
const Vector<const WasmExport> export_table) {
DCHECK_NOT_NULL(names);
DCHECK(names->empty());
Decoder decoder(module_start, module_end);
if (!FindNameSection(&decoder)) return;
if (FindNameSection(&decoder)) {
while (decoder.ok() && decoder.more()) {
uint8_t name_type = decoder.consume_u8("name type");
if (name_type & 0x80) break; // no varuint7
while (decoder.ok() && decoder.more()) {
uint8_t name_type = decoder.consume_u8("name type");
if (name_type & 0x80) break; // no varuint7
uint32_t name_payload_len = decoder.consume_u32v("name payload length");
if (!decoder.checkAvailable(name_payload_len)) break;
uint32_t name_payload_len = decoder.consume_u32v("name payload length");
if (!decoder.checkAvailable(name_payload_len)) break;
if (name_type != NameSectionKindCode::kFunction) {
decoder.consume_bytes(name_payload_len, "name subsection payload");
continue;
}
uint32_t functions_count = decoder.consume_u32v("functions count");
for (; decoder.ok() && functions_count > 0; --functions_count) {
uint32_t function_index = decoder.consume_u32v("function index");
WireBytesRef name = consume_string(&decoder, false, "function name");
// Be lenient with errors in the name section: Ignore non-UTF8 names. You
// can even assign to the same function multiple times (last valid one
// wins).
if (decoder.ok() && validate_utf8(&decoder, name)) {
names->insert(std::make_pair(function_index, name));
if (name_type != NameSectionKindCode::kFunction) {
decoder.consume_bytes(name_payload_len, "name subsection payload");
continue;
}
uint32_t functions_count = decoder.consume_u32v("functions count");
for (; decoder.ok() && functions_count > 0; --functions_count) {
uint32_t function_index = decoder.consume_u32v("function index");
WireBytesRef name = consume_string(&decoder, false, "function name");
// Be lenient with errors in the name section: Ignore non-UTF8 names.
// You can even assign to the same function multiple times (last valid
// one wins).
if (decoder.ok() && validate_utf8(&decoder, name)) {
names->insert(std::make_pair(function_index, name));
}
}
}
}
// Extract from export table.
for (const WasmExport& exp : export_table) {
switch (exp.kind) {
case kExternalFunction:
if (names->count(exp.index) == 0) {
names->insert(std::make_pair(exp.index, exp.name));
}
break;
default:
break;
}
}
}

View File

@ -160,11 +160,13 @@ V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
// function.
AsmJsOffsetsResult DecodeAsmJsOffsets(Vector<const uint8_t> encoded_offsets);
// Decode the function names from the name section.
// Returns the result as an unordered map. Only names with valid utf8 encoding
// are stored and conflicts are resolved by choosing the last name read.
// Decode the function names from the name section and also look at export
// table. Returns the result as an unordered map. Only names with valid utf8
// encoding are stored and conflicts are resolved by choosing the last name
// read.
void DecodeFunctionNames(const byte* module_start, const byte* module_end,
std::unordered_map<uint32_t, WireBytesRef>* names);
std::unordered_map<uint32_t, WireBytesRef>* names,
const Vector<const WasmExport> export_table);
// Decode the local names assignment from the name section.
// The result will be empty if no name section is present. On encountering an

View File

@ -195,8 +195,8 @@ void WasmCode::LogCode(Isolate* isolate) const {
if (IsAnonymous()) return;
ModuleWireBytes wire_bytes(native_module()->wire_bytes());
WireBytesRef name_ref =
native_module()->module()->function_names.Lookup(wire_bytes, index());
WireBytesRef name_ref = native_module()->module()->function_names.Lookup(
wire_bytes, index(), VectorOf(native_module()->module()->export_table));
WasmName name = wire_bytes.GetNameOrNull(name_ref);
const std::string& source_map_url = native_module()->module()->source_map_url;

View File

@ -162,8 +162,8 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module,
const ModuleWireBytes& bytes,
ErrorThrower* thrower) {
for (const WasmFunction& F : raw_module->functions) {
WireBytesRef name_ref =
raw_module->function_names.Lookup(bytes, F.func_index);
WireBytesRef name_ref = raw_module->function_names.Lookup(
bytes, F.func_index, VectorOf(raw_module->export_table));
std::string name(bytes.start() + name_ref.offset(),
bytes.start() + name_ref.end_offset());
if (F.exported && name == "wasm_format") {

View File

@ -30,13 +30,14 @@ namespace wasm {
// static
const uint32_t WasmElemSegment::kNullIndex;
WireBytesRef DecodedFunctionNames::Lookup(const ModuleWireBytes& wire_bytes,
uint32_t function_index) const {
WireBytesRef DecodedFunctionNames::Lookup(
const ModuleWireBytes& wire_bytes, uint32_t function_index,
Vector<const WasmExport> export_table) const {
base::MutexGuard lock(&mutex_);
if (!function_names_) {
function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>());
DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(),
function_names_.get());
function_names_.get(), export_table);
}
auto it = function_names_->find(function_index);
if (it == function_names_->end()) return WireBytesRef();
@ -176,8 +177,8 @@ WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const {
// Get a string stored in the module bytes representing a function name.
WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function,
const WasmModule* module) const {
return GetNameOrNull(
module->function_names.Lookup(*this, function->func_index));
return GetNameOrNull(module->function_names.Lookup(
*this, function->func_index, VectorOf(module->export_table)));
}
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {

View File

@ -194,7 +194,8 @@ struct ModuleWireBytes;
class V8_EXPORT_PRIVATE DecodedFunctionNames {
public:
WireBytesRef Lookup(const ModuleWireBytes& wire_bytes,
uint32_t function_index) const;
uint32_t function_index,
Vector<const WasmExport> export_table) const;
void AddForTesting(int function_index, WireBytesRef name);
private:

View File

@ -243,7 +243,7 @@ MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull(
DCHECK_LT(func_index, module_object->module()->functions.size());
wasm::WireBytesRef name = module_object->module()->function_names.Lookup(
wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()),
func_index);
func_index, VectorOf(module_object->module()->export_table));
if (!name.is_set()) return {};
return ExtractUtf8StringFromModuleBytes(isolate, module_object, name,
kNoInternalize);
@ -267,8 +267,8 @@ Vector<const uint8_t> WasmModuleObject::GetRawFunctionName(
uint32_t func_index) {
DCHECK_GT(module()->functions.size(), func_index);
wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes());
wasm::WireBytesRef name_ref =
module()->function_names.Lookup(wire_bytes, func_index);
wasm::WireBytesRef name_ref = module()->function_names.Lookup(
wire_bytes, func_index, VectorOf(module()->export_table));
wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref);
return Vector<const uint8_t>::cast(name);
}

View File

@ -4,7 +4,7 @@ Paused on 'debugger;'
Number of frames: 5
- [0] call_debugger
- [1] func1
- [2] func2
- [2] main
- [3] testFunction
- [4]
Finished!

View File

@ -1,4 +1,4 @@
wasm-function[0]:0x22: RuntimeError: unreachable
RuntimeError: unreachable
at test-module (<anonymous>:wasm-function[0]:0x22)
at test-module.main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:31

View File

@ -1,4 +1,4 @@
wasm-function[0]:0x22: RuntimeError: unreachable
RuntimeError: unreachable
at <anonymous>:wasm-function[0]:0x22
at main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:31

View File

@ -1,4 +1,4 @@
RuntimeError: unreachable
at test-module (<anonymous>:wasm-function[0]:0x22)
at test-module.main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:27
at test/mjsunit/mjsunit.js:*

View File

@ -1,4 +1,4 @@
RuntimeError: unreachable
at test-module (<anonymous>:wasm-function[0]:0x22)
at test-module.main (<anonymous>:wasm-function[0]:0x22)
at test/message/wasm-module-name-async.js:{NUMBER}:27
at test/mjsunit/mjsunit.js:*

View File

@ -1,4 +1,4 @@
RuntimeError: unreachable
at <anonymous>:wasm-function[0]:0x22
at main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:27
at test/mjsunit/mjsunit.js:*

View File

@ -1,4 +1,4 @@
RuntimeError: unreachable
at <anonymous>:wasm-function[0]:0x22
at main (<anonymous>:wasm-function[0]:0x22)
at test/message/wasm-no-name-async.js:{NUMBER}:27
at test/mjsunit/mjsunit.js:*

View File

@ -28,6 +28,6 @@ kExprEnd, // @21
builder.addExport('main', 0);
assertThrows(
() => {builder.toModule()}, WebAssembly.CompileError,
'WebAssembly.Module(): Compiling function #0 failed: type error in ' +
'merge[0] (expected <bot>, got i32) @+57');
'WebAssembly.Module(): Compiling function #0:\"main\" failed: type ' +
'error in merge[0] (expected <bot>, got i32) @+57');
})();

View File

@ -119,7 +119,7 @@ Error.prepareStackTrace = function(error, frames) {
assertContains("out of bounds", e.message);
verifyStack(e.stack, [
// isWasm function line pos file offset funcIndex
[ true, null, 0, 3, null, '0x91', 3],
[ true, "mem_out_of_bounds", 0, 3, null, '0x91', 3],
[ true, "call_mem_out_of_bounds", 0, 1, null, '0x97', 4],
[ false, "testWasmMemOutOfBounds", 116, 0, "stack.js"],
[ false, null, 128, 0, "stack.js"]