Commit 073c5d2d authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

[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: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarKim-Anh Tran <kimanh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66877}
parent dbda6c3d
...@@ -313,6 +313,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -313,6 +313,8 @@ class ModuleDecoderImpl : public Decoder {
error(start_, "end is less than start"); error(start_, "end is less than start");
end_ = start_; end_ = start_;
} }
module_start_ = module_start;
module_end_ = module_end;
} }
void onFirstError() override { void onFirstError() override {
...@@ -933,7 +935,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -933,7 +935,7 @@ class ModuleDecoderImpl : public Decoder {
&module_->functions[index + module_->num_imported_functions]; &module_->functions[index + module_->num_imported_functions];
function->code = {offset, length}; function->code = {offset, length};
if (verify_functions) { if (verify_functions) {
ModuleWireBytes bytes(start_, end_); ModuleWireBytes bytes(module_start_, module_end_);
VerifyFunctionBody(module_->signature_zone->allocator(), VerifyFunctionBody(module_->signature_zone->allocator(),
index + module_->num_imported_functions, bytes, index + module_->num_imported_functions, bytes,
module_.get(), function); module_.get(), function);
...@@ -1296,6 +1298,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1296,6 +1298,8 @@ class ModuleDecoderImpl : public Decoder {
private: private:
const WasmFeatures enabled_features_; const WasmFeatures enabled_features_;
std::shared_ptr<WasmModule> module_; std::shared_ptr<WasmModule> module_;
const byte* module_start_;
const byte* module_end_;
Counters* counters_ = nullptr; Counters* counters_ = nullptr;
// The type section is the first section in a module. // The type section is the first section in a module.
uint8_t next_ordered_section_ = kFirstSectionInModule; uint8_t next_ordered_section_ = kFirstSectionInModule;
...@@ -2208,38 +2212,52 @@ bool FindNameSection(Decoder* decoder) { ...@@ -2208,38 +2212,52 @@ bool FindNameSection(Decoder* decoder) {
} // namespace } // namespace
void DecodeFunctionNames(const byte* module_start, const byte* module_end, 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_NOT_NULL(names);
DCHECK(names->empty()); DCHECK(names->empty());
Decoder decoder(module_start, module_end); 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()) { uint32_t name_payload_len = decoder.consume_u32v("name payload length");
uint8_t name_type = decoder.consume_u8("name type"); if (!decoder.checkAvailable(name_payload_len)) break;
if (name_type & 0x80) break; // no varuint7
uint32_t name_payload_len = decoder.consume_u32v("name payload length"); if (name_type != NameSectionKindCode::kFunction) {
if (!decoder.checkAvailable(name_payload_len)) break; decoder.consume_bytes(name_payload_len, "name subsection payload");
continue;
if (name_type != NameSectionKindCode::kFunction) { }
decoder.consume_bytes(name_payload_len, "name subsection payload"); uint32_t functions_count = decoder.consume_u32v("functions count");
continue;
}
uint32_t functions_count = decoder.consume_u32v("functions count");
for (; decoder.ok() && functions_count > 0; --functions_count) { for (; decoder.ok() && functions_count > 0; --functions_count) {
uint32_t function_index = decoder.consume_u32v("function index"); uint32_t function_index = decoder.consume_u32v("function index");
WireBytesRef name = consume_string(&decoder, false, "function name"); WireBytesRef name = consume_string(&decoder, false, "function name");
// Be lenient with errors in the name section: Ignore non-UTF8 names. You // Be lenient with errors in the name section: Ignore non-UTF8 names.
// can even assign to the same function multiple times (last valid one // You can even assign to the same function multiple times (last valid
// wins). // one wins).
if (decoder.ok() && validate_utf8(&decoder, name)) { if (decoder.ok() && validate_utf8(&decoder, name)) {
names->insert(std::make_pair(function_index, 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;
}
}
} }
LocalNames DecodeLocalNames(Vector<const uint8_t> module_bytes) { LocalNames DecodeLocalNames(Vector<const uint8_t> module_bytes) {
......
...@@ -160,11 +160,13 @@ V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections( ...@@ -160,11 +160,13 @@ V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
// function. // function.
AsmJsOffsetsResult DecodeAsmJsOffsets(Vector<const uint8_t> encoded_offsets); AsmJsOffsetsResult DecodeAsmJsOffsets(Vector<const uint8_t> encoded_offsets);
// Decode the function names from the name section. // Decode the function names from the name section and also look at export
// Returns the result as an unordered map. Only names with valid utf8 encoding // table. Returns the result as an unordered map. Only names with valid utf8
// are stored and conflicts are resolved by choosing the last name read. // encoding are stored and conflicts are resolved by choosing the last name
// read.
void DecodeFunctionNames(const byte* module_start, const byte* module_end, 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. // Decode the local names assignment from the name section.
// The result will be empty if no name section is present. On encountering an // The result will be empty if no name section is present. On encountering an
......
...@@ -195,8 +195,8 @@ void WasmCode::LogCode(Isolate* isolate) const { ...@@ -195,8 +195,8 @@ void WasmCode::LogCode(Isolate* isolate) const {
if (IsAnonymous()) return; if (IsAnonymous()) return;
ModuleWireBytes wire_bytes(native_module()->wire_bytes()); ModuleWireBytes wire_bytes(native_module()->wire_bytes());
WireBytesRef name_ref = WireBytesRef name_ref = native_module()->module()->function_names.Lookup(
native_module()->module()->function_names.Lookup(wire_bytes, index()); wire_bytes, index(), VectorOf(native_module()->module()->export_table));
WasmName name = wire_bytes.GetNameOrNull(name_ref); WasmName name = wire_bytes.GetNameOrNull(name_ref);
const std::string& source_map_url = native_module()->module()->source_map_url; const std::string& source_map_url = native_module()->module()->source_map_url;
......
...@@ -162,8 +162,8 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module, ...@@ -162,8 +162,8 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module,
const ModuleWireBytes& bytes, const ModuleWireBytes& bytes,
ErrorThrower* thrower) { ErrorThrower* thrower) {
for (const WasmFunction& F : raw_module->functions) { for (const WasmFunction& F : raw_module->functions) {
WireBytesRef name_ref = WireBytesRef name_ref = raw_module->function_names.Lookup(
raw_module->function_names.Lookup(bytes, F.func_index); bytes, F.func_index, VectorOf(raw_module->export_table));
std::string name(bytes.start() + name_ref.offset(), std::string name(bytes.start() + name_ref.offset(),
bytes.start() + name_ref.end_offset()); bytes.start() + name_ref.end_offset());
if (F.exported && name == "wasm_format") { if (F.exported && name == "wasm_format") {
......
...@@ -30,13 +30,14 @@ namespace wasm { ...@@ -30,13 +30,14 @@ namespace wasm {
// static // static
const uint32_t WasmElemSegment::kNullIndex; const uint32_t WasmElemSegment::kNullIndex;
WireBytesRef DecodedFunctionNames::Lookup(const ModuleWireBytes& wire_bytes, WireBytesRef DecodedFunctionNames::Lookup(
uint32_t function_index) const { const ModuleWireBytes& wire_bytes, uint32_t function_index,
Vector<const WasmExport> export_table) const {
base::MutexGuard lock(&mutex_); base::MutexGuard lock(&mutex_);
if (!function_names_) { if (!function_names_) {
function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>()); function_names_.reset(new std::unordered_map<uint32_t, WireBytesRef>());
DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(), DecodeFunctionNames(wire_bytes.start(), wire_bytes.end(),
function_names_.get()); function_names_.get(), export_table);
} }
auto it = function_names_->find(function_index); auto it = function_names_->find(function_index);
if (it == function_names_->end()) return WireBytesRef(); if (it == function_names_->end()) return WireBytesRef();
...@@ -176,8 +177,8 @@ WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const { ...@@ -176,8 +177,8 @@ WasmName ModuleWireBytes::GetNameOrNull(WireBytesRef ref) const {
// Get a string stored in the module bytes representing a function name. // Get a string stored in the module bytes representing a function name.
WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function, WasmName ModuleWireBytes::GetNameOrNull(const WasmFunction* function,
const WasmModule* module) const { const WasmModule* module) const {
return GetNameOrNull( return GetNameOrNull(module->function_names.Lookup(
module->function_names.Lookup(*this, function->func_index)); *this, function->func_index, VectorOf(module->export_table)));
} }
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) { std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name) {
......
...@@ -194,7 +194,8 @@ struct ModuleWireBytes; ...@@ -194,7 +194,8 @@ struct ModuleWireBytes;
class V8_EXPORT_PRIVATE DecodedFunctionNames { class V8_EXPORT_PRIVATE DecodedFunctionNames {
public: public:
WireBytesRef Lookup(const ModuleWireBytes& wire_bytes, 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); void AddForTesting(int function_index, WireBytesRef name);
private: private:
......
...@@ -243,7 +243,7 @@ MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull( ...@@ -243,7 +243,7 @@ MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull(
DCHECK_LT(func_index, module_object->module()->functions.size()); DCHECK_LT(func_index, module_object->module()->functions.size());
wasm::WireBytesRef name = module_object->module()->function_names.Lookup( wasm::WireBytesRef name = module_object->module()->function_names.Lookup(
wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()), wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()),
func_index); func_index, VectorOf(module_object->module()->export_table));
if (!name.is_set()) return {}; if (!name.is_set()) return {};
return ExtractUtf8StringFromModuleBytes(isolate, module_object, name, return ExtractUtf8StringFromModuleBytes(isolate, module_object, name,
kNoInternalize); kNoInternalize);
...@@ -267,8 +267,8 @@ Vector<const uint8_t> WasmModuleObject::GetRawFunctionName( ...@@ -267,8 +267,8 @@ Vector<const uint8_t> WasmModuleObject::GetRawFunctionName(
uint32_t func_index) { uint32_t func_index) {
DCHECK_GT(module()->functions.size(), func_index); DCHECK_GT(module()->functions.size(), func_index);
wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes()); wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes());
wasm::WireBytesRef name_ref = wasm::WireBytesRef name_ref = module()->function_names.Lookup(
module()->function_names.Lookup(wire_bytes, func_index); wire_bytes, func_index, VectorOf(module()->export_table));
wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref); wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref);
return Vector<const uint8_t>::cast(name); return Vector<const uint8_t>::cast(name);
} }
......
...@@ -4,7 +4,7 @@ Paused on 'debugger;' ...@@ -4,7 +4,7 @@ Paused on 'debugger;'
Number of frames: 5 Number of frames: 5
- [0] call_debugger - [0] call_debugger
- [1] func1 - [1] func1
- [2] func2 - [2] main
- [3] testFunction - [3] testFunction
- [4] - [4]
Finished! Finished!
wasm-function[0]:0x22: RuntimeError: unreachable wasm-function[0]:0x22: RuntimeError: unreachable
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 at *%(basename)s:{NUMBER}:31
wasm-function[0]:0x22: RuntimeError: unreachable wasm-function[0]:0x22: RuntimeError: unreachable
RuntimeError: unreachable RuntimeError: unreachable
at <anonymous>:wasm-function[0]:0x22 at main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:31 at *%(basename)s:{NUMBER}:31
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}:27 at *%(basename)s:{NUMBER}:27
at test/mjsunit/mjsunit.js:* at test/mjsunit/mjsunit.js:*
RuntimeError: unreachable 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/message/wasm-module-name-async.js:{NUMBER}:27
at test/mjsunit/mjsunit.js:* at test/mjsunit/mjsunit.js:*
RuntimeError: unreachable RuntimeError: unreachable
at <anonymous>:wasm-function[0]:0x22 at main (<anonymous>:wasm-function[0]:0x22)
at *%(basename)s:{NUMBER}:27 at *%(basename)s:{NUMBER}:27
at test/mjsunit/mjsunit.js:* at test/mjsunit/mjsunit.js:*
RuntimeError: unreachable 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/message/wasm-no-name-async.js:{NUMBER}:27
at test/mjsunit/mjsunit.js:* at test/mjsunit/mjsunit.js:*
...@@ -28,6 +28,6 @@ kExprEnd, // @21 ...@@ -28,6 +28,6 @@ kExprEnd, // @21
builder.addExport('main', 0); builder.addExport('main', 0);
assertThrows( assertThrows(
() => {builder.toModule()}, WebAssembly.CompileError, () => {builder.toModule()}, WebAssembly.CompileError,
'WebAssembly.Module(): Compiling function #0 failed: type error in ' + 'WebAssembly.Module(): Compiling function #0:\"main\" failed: type ' +
'merge[0] (expected <bot>, got i32) @+57'); 'error in merge[0] (expected <bot>, got i32) @+57');
})(); })();
...@@ -119,7 +119,7 @@ Error.prepareStackTrace = function(error, frames) { ...@@ -119,7 +119,7 @@ Error.prepareStackTrace = function(error, frames) {
assertContains("out of bounds", e.message); assertContains("out of bounds", e.message);
verifyStack(e.stack, [ verifyStack(e.stack, [
// isWasm function line pos file offset funcIndex // 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], [ true, "call_mem_out_of_bounds", 0, 1, null, '0x97', 4],
[ false, "testWasmMemOutOfBounds", 116, 0, "stack.js"], [ false, "testWasmMemOutOfBounds", 116, 0, "stack.js"],
[ false, null, 128, 0, "stack.js"] [ false, null, 128, 0, "stack.js"]
......
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