Commit 5d9fa102 authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Provide better stack traces for asm.js code

For the asm.js to WASM pipeline, the current stack traces only show
low-level WASM information.
This CL maps this back to asm.js source positions.
It does so by attaching the asm.js source Script to the compiled WASM
module, and emitting a delta-encoded table which maps from WASM byte
offsets to positions within that Script. As asm.js code does not throw
exceptions, we only store a mapping for call instructions.

The new AsmJsWasmStackFrame implementation inherits from
WasmStackFrame, but contains the logic to provide the source script and
the position inside of it.
What is still missing is the JSFunction object returned by
CallSite.getFunction(). We currently return null.

R=jgruber@chromium.org, titzer@chromium.org
BUG=v8:4203

Review-Url: https://codereview.chromium.org/2404253002
Cr-Commit-Position: refs/heads/master@{#40205}
parent e7a00891
...@@ -7239,9 +7239,10 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Compile(Isolate* isolate, ...@@ -7239,9 +7239,10 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Compile(Isolate* isolate,
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::wasm::ErrorThrower thrower(i_isolate, "WasmCompiledModule::Deserialize()"); i::wasm::ErrorThrower thrower(i_isolate, "WasmCompiledModule::Deserialize()");
i::MaybeHandle<i::JSObject> maybe_compiled = i::MaybeHandle<i::JSObject> maybe_compiled =
i::wasm::CreateModuleObjectFromBytes(i_isolate, start, start + length, i::wasm::CreateModuleObjectFromBytes(
&thrower, i_isolate, start, start + length, &thrower,
i::wasm::ModuleOrigin::kWasmOrigin); i::wasm::ModuleOrigin::kWasmOrigin, i::Handle<i::Script>::null(),
nullptr, nullptr);
if (maybe_compiled.is_null()) return MaybeLocal<WasmCompiledModule>(); if (maybe_compiled.is_null()) return MaybeLocal<WasmCompiledModule>();
return Local<WasmCompiledModule>::Cast( return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(maybe_compiled.ToHandleChecked())); Utils::ToLocal(maybe_compiled.ToHandleChecked()));
......
...@@ -162,11 +162,14 @@ MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) { ...@@ -162,11 +162,14 @@ MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(), v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
info->literal(), &typer); info->literal(), &typer);
i::Handle<i::FixedArray> foreign_globals; i::Handle<i::FixedArray> foreign_globals;
auto module = builder.Run(&foreign_globals); auto asm_wasm_result = builder.Run(&foreign_globals);
wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
i::MaybeHandle<i::JSObject> compiled = wasm::CreateModuleObjectFromBytes( i::MaybeHandle<i::JSObject> compiled = wasm::CreateModuleObjectFromBytes(
info->isolate(), module->begin(), module->end(), &thrower, info->isolate(), module->begin(), module->end(), &thrower,
internal::wasm::kAsmJsOrigin); internal::wasm::kAsmJsOrigin, info->script(), asm_offsets->begin(),
asm_offsets->end());
DCHECK(!compiled.is_null()); DCHECK(!compiled.is_null());
wasm::AsmTyper::StdlibSet uses = typer.StdlibUses(); wasm::AsmTyper::StdlibSet uses = typer.StdlibUses();
......
...@@ -1359,11 +1359,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> { ...@@ -1359,11 +1359,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
uint32_t index = imported_function_table_.LookupOrInsertImport( uint32_t index = imported_function_table_.LookupOrInsertImport(
vp->var(), sig.Build()); vp->var(), sig.Build());
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->AddAsmWasmOffset(expr->position());
current_function_builder_->Emit(kExprCallFunction); current_function_builder_->Emit(kExprCallFunction);
current_function_builder_->EmitVarInt(index); current_function_builder_->EmitVarInt(index);
} else { } else {
WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var()); WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var());
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->AddAsmWasmOffset(expr->position());
current_function_builder_->Emit(kExprCallFunction); current_function_builder_->Emit(kExprCallFunction);
current_function_builder_->EmitDirectCallIndex( current_function_builder_->EmitDirectCallIndex(
function->func_index()); function->func_index());
...@@ -1389,6 +1391,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> { ...@@ -1389,6 +1391,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
VisitCallArgs(expr); VisitCallArgs(expr);
current_function_builder_->EmitGetLocal(tmp.index()); current_function_builder_->EmitGetLocal(tmp.index());
current_function_builder_->AddAsmWasmOffset(expr->position());
current_function_builder_->Emit(kExprCallIndirect); current_function_builder_->Emit(kExprCallIndirect);
current_function_builder_->EmitVarInt(indices->signature_index); current_function_builder_->EmitVarInt(indices->signature_index);
returns_value = returns_value =
...@@ -1870,13 +1873,16 @@ AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone, ...@@ -1870,13 +1873,16 @@ AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
// TODO(aseemgarg): probably should take zone (to write wasm to) as input so // TODO(aseemgarg): probably should take zone (to write wasm to) as input so
// that zone in constructor may be thrown away once wasm module is written. // that zone in constructor may be thrown away once wasm module is written.
ZoneBuffer* AsmWasmBuilder::Run(i::Handle<i::FixedArray>* foreign_args) { AsmWasmBuilder::Result AsmWasmBuilder::Run(
i::Handle<i::FixedArray>* foreign_args) {
AsmWasmBuilderImpl impl(isolate_, zone_, literal_, typer_); AsmWasmBuilderImpl impl(isolate_, zone_, literal_, typer_);
impl.Build(); impl.Build();
*foreign_args = impl.GetForeignArgs(); *foreign_args = impl.GetForeignArgs();
ZoneBuffer* buffer = new (zone_) ZoneBuffer(zone_); ZoneBuffer* module_buffer = new (zone_) ZoneBuffer(zone_);
impl.builder_->WriteTo(*buffer); impl.builder_->WriteTo(*module_buffer);
return buffer; ZoneBuffer* asm_offsets_buffer = new (zone_) ZoneBuffer(zone_);
impl.builder_->WriteAsmJsOffsetTable(*asm_offsets_buffer);
return {module_buffer, asm_offsets_buffer};
} }
const char* AsmWasmBuilder::foreign_init_name = "__foreign_init__"; const char* AsmWasmBuilder::foreign_init_name = "__foreign_init__";
......
...@@ -20,9 +20,14 @@ namespace wasm { ...@@ -20,9 +20,14 @@ namespace wasm {
class AsmWasmBuilder { class AsmWasmBuilder {
public: public:
struct Result {
ZoneBuffer* module_bytes;
ZoneBuffer* asm_offset_table;
};
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root, explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
AsmTyper* typer); AsmTyper* typer);
ZoneBuffer* Run(Handle<FixedArray>* foreign_args); Result Run(Handle<FixedArray>* foreign_args);
static const char* foreign_init_name; static const char* foreign_init_name;
static const char* single_function_name; static const char* single_function_name;
......
...@@ -520,9 +520,13 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object, ...@@ -520,9 +520,13 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
DCHECK(wasm::IsWasmObject(*wasm_object) || DCHECK(wasm::IsWasmObject(*wasm_object) ||
wasm_object->IsUndefined(this)); wasm_object->IsUndefined(this));
elements = FrameArray::AppendWasmFrame( int flags = wasm::WasmIsAsmJs(*wasm_object, this)
elements, wasm_object, wasm_function_index, abstract_code, offset, ? FrameArray::kIsAsmJsWasmFrame
FrameArray::kIsWasmFrame); : FrameArray::kIsWasmFrame;
elements = FrameArray::AppendWasmFrame(elements, wasm_object,
wasm_function_index,
abstract_code, offset, flags);
} break; } break;
default: default:
......
...@@ -211,14 +211,6 @@ Handle<Object> JSStackFrame::GetFunctionName() { ...@@ -211,14 +211,6 @@ Handle<Object> JSStackFrame::GetFunctionName() {
return isolate_->factory()->null_value(); return isolate_->factory()->null_value();
} }
Handle<Object> JSStackFrame::GetScriptNameOrSourceUrl() {
if (!HasScript()) return isolate_->factory()->null_value();
Handle<Script> script = GetScript();
Object* source_url = script->source_url();
return (source_url->IsString()) ? handle(source_url, isolate_)
: handle(script->name(), isolate_);
}
namespace { namespace {
bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name, bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
...@@ -238,8 +230,19 @@ bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name, ...@@ -238,8 +230,19 @@ bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
return false; return false;
} }
Handle<Object> ScriptNameOrSourceUrl(Handle<Script> script, Isolate* isolate) {
Object* name_or_url = script->source_url();
if (!name_or_url->IsString()) name_or_url = script->name();
return handle(name_or_url, isolate);
}
} // namespace } // namespace
Handle<Object> JSStackFrame::GetScriptNameOrSourceUrl() {
if (!HasScript()) return isolate_->factory()->null_value();
return ScriptNameOrSourceUrl(GetScript(), isolate_);
}
Handle<Object> JSStackFrame::GetMethodName() { Handle<Object> JSStackFrame::GetMethodName() {
if (receiver_->IsNull(isolate_) || receiver_->IsUndefined(isolate_)) { if (receiver_->IsNull(isolate_) || receiver_->IsUndefined(isolate_)) {
return isolate_->factory()->null_value(); return isolate_->factory()->null_value();
...@@ -455,7 +458,7 @@ bool IsNonEmptyString(Handle<Object> object) { ...@@ -455,7 +458,7 @@ bool IsNonEmptyString(Handle<Object> object) {
return (object->IsString() && String::cast(*object)->length() > 0); return (object->IsString() && String::cast(*object)->length() > 0);
} }
void AppendFileLocation(Isolate* isolate, JSStackFrame* call_site, void AppendFileLocation(Isolate* isolate, StackFrameBase* call_site,
IncrementalStringBuilder* builder) { IncrementalStringBuilder* builder) {
if (call_site->IsNative()) { if (call_site->IsNative()) {
builder->AppendCString("native"); builder->AppendCString("native");
...@@ -617,7 +620,8 @@ Handle<Script> JSStackFrame::GetScript() const { ...@@ -617,7 +620,8 @@ Handle<Script> JSStackFrame::GetScript() const {
void WasmStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array, void WasmStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
int frame_ix) { int frame_ix) {
DCHECK(array->IsWasmFrame(frame_ix)); // This function is called for both wasm and asm.js->wasm frames.
DCHECK(array->IsWasmFrame(frame_ix) || array->IsAsmJsWasmFrame(frame_ix));
isolate_ = isolate; isolate_ = isolate;
wasm_obj_ = handle(array->WasmObject(frame_ix), isolate); wasm_obj_ = handle(array->WasmObject(frame_ix), isolate);
wasm_func_index_ = array->WasmFunctionIndex(frame_ix)->value(); wasm_func_index_ = array->WasmFunctionIndex(frame_ix)->value();
...@@ -667,6 +671,72 @@ Handle<Object> WasmStackFrame::Null() const { ...@@ -667,6 +671,72 @@ Handle<Object> WasmStackFrame::Null() const {
return isolate_->factory()->null_value(); return isolate_->factory()->null_value();
} }
Handle<Object> AsmJsWasmStackFrame::GetReceiver() const {
return isolate_->global_proxy();
}
Handle<Object> AsmJsWasmStackFrame::GetFunction() const {
// TODO(clemensh): Return lazily created JSFunction.
return Null();
}
Handle<Object> AsmJsWasmStackFrame::GetFileName() {
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_obj_));
return handle(script->name(), isolate_);
}
Handle<Object> AsmJsWasmStackFrame::GetFunctionName() {
return wasm::GetWasmFunctionNameOrNull(isolate_, wasm_obj_, wasm_func_index_);
}
Handle<Object> AsmJsWasmStackFrame::GetScriptNameOrSourceUrl() {
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_obj_));
return ScriptNameOrSourceUrl(script, isolate_);
}
int AsmJsWasmStackFrame::GetPosition() const {
DCHECK_LE(0, offset_);
int byte_offset = code_->SourcePosition(offset_);
return wasm::GetAsmWasmSourcePosition(Handle<JSObject>::cast(wasm_obj_),
wasm_func_index_, byte_offset);
}
int AsmJsWasmStackFrame::GetLineNumber() {
DCHECK_LE(0, GetPosition());
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_obj_));
return Script::GetLineNumber(script, GetPosition()) + 1;
}
int AsmJsWasmStackFrame::GetColumnNumber() {
DCHECK_LE(0, GetPosition());
Handle<Script> script =
wasm::GetAsmWasmScript(Handle<JSObject>::cast(wasm_obj_));
return Script::GetColumnNumber(script, GetPosition()) + 1;
}
MaybeHandle<String> AsmJsWasmStackFrame::ToString() {
// The string should look exactly as the respective javascript frame string.
// Keep this method in line to JSStackFrame::ToString().
IncrementalStringBuilder builder(isolate_);
Handle<Object> function_name = GetFunctionName();
if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
builder.AppendCString(" (");
}
AppendFileLocation(isolate_, this, &builder);
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
RETURN_RESULT(isolate_, builder.Finish(), String);
}
FrameArrayIterator::FrameArrayIterator(Isolate* isolate, FrameArrayIterator::FrameArrayIterator(Isolate* isolate,
Handle<FrameArray> array, int frame_ix) Handle<FrameArray> array, int frame_ix)
: isolate_(isolate), array_(array), next_frame_ix_(frame_ix) {} : isolate_(isolate), array_(array), next_frame_ix_(frame_ix) {}
...@@ -680,13 +750,22 @@ void FrameArrayIterator::Next() { next_frame_ix_++; } ...@@ -680,13 +750,22 @@ void FrameArrayIterator::Next() { next_frame_ix_++; }
StackFrameBase* FrameArrayIterator::Frame() { StackFrameBase* FrameArrayIterator::Frame() {
DCHECK(HasNext()); DCHECK(HasNext());
const int flags = array_->Flags(next_frame_ix_)->value(); const int flags = array_->Flags(next_frame_ix_)->value();
const bool is_js_frame = (flags & FrameArray::kIsWasmFrame) == 0; switch (flags & (FrameArray::kIsWasmFrame | FrameArray::kIsAsmJsWasmFrame)) {
if (is_js_frame) { case 0:
js_frame_.FromFrameArray(isolate_, array_, next_frame_ix_); // JavaScript Frame.
return &js_frame_; js_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
} else { return &js_frame_;
wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_); case FrameArray::kIsWasmFrame:
return &wasm_frame_; // Wasm Frame;
wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
return &wasm_frame_;
case FrameArray::kIsAsmJsWasmFrame:
// Asm.js Wasm Frame:
asm_wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
return &asm_wasm_frame_;
default:
UNREACHABLE();
return nullptr;
} }
} }
......
...@@ -148,8 +148,7 @@ class WasmStackFrame : public StackFrameBase { ...@@ -148,8 +148,7 @@ class WasmStackFrame : public StackFrameBase {
MaybeHandle<String> ToString() override; MaybeHandle<String> ToString() override;
private: protected:
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
Handle<Object> Null() const; Handle<Object> Null() const;
Isolate* isolate_; Isolate* isolate_;
...@@ -159,9 +158,30 @@ class WasmStackFrame : public StackFrameBase { ...@@ -159,9 +158,30 @@ class WasmStackFrame : public StackFrameBase {
Handle<AbstractCode> code_; Handle<AbstractCode> code_;
int offset_; int offset_;
private:
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
friend class FrameArrayIterator; friend class FrameArrayIterator;
}; };
class AsmJsWasmStackFrame : public WasmStackFrame {
public:
virtual ~AsmJsWasmStackFrame() {}
Handle<Object> GetReceiver() const override;
Handle<Object> GetFunction() const override;
Handle<Object> GetFileName() override;
Handle<Object> GetFunctionName() override;
Handle<Object> GetScriptNameOrSourceUrl() override;
int GetPosition() const override;
int GetLineNumber() override;
int GetColumnNumber() override;
MaybeHandle<String> ToString() override;
};
class FrameArrayIterator { class FrameArrayIterator {
public: public:
FrameArrayIterator(Isolate* isolate, Handle<FrameArray> array, FrameArrayIterator(Isolate* isolate, Handle<FrameArray> array,
...@@ -179,6 +199,7 @@ class FrameArrayIterator { ...@@ -179,6 +199,7 @@ class FrameArrayIterator {
int next_frame_ix_; int next_frame_ix_;
WasmStackFrame wasm_frame_; WasmStackFrame wasm_frame_;
AsmJsWasmStackFrame asm_wasm_frame_;
JSStackFrame js_frame_; JSStackFrame js_frame_;
}; };
......
...@@ -2639,6 +2639,11 @@ bool FrameArray::IsWasmFrame(int frame_ix) const { ...@@ -2639,6 +2639,11 @@ bool FrameArray::IsWasmFrame(int frame_ix) const {
return (flags & kIsWasmFrame) != 0; return (flags & kIsWasmFrame) != 0;
} }
bool FrameArray::IsAsmJsWasmFrame(int frame_ix) const {
const int flags = Flags(frame_ix)->value();
return (flags & kIsAsmJsWasmFrame) != 0;
}
int FrameArray::FrameCount() const { int FrameArray::FrameCount() const {
const int frame_count = Smi::cast(get(kFrameCountIndex))->value(); const int frame_count = Smi::cast(get(kFrameCountIndex))->value();
DCHECK_LE(0, frame_count); DCHECK_LE(0, frame_count);
......
...@@ -2973,14 +2973,16 @@ class FrameArray : public FixedArray { ...@@ -2973,14 +2973,16 @@ class FrameArray : public FixedArray {
#undef DECLARE_FRAME_ARRAY_ACCESSORS #undef DECLARE_FRAME_ARRAY_ACCESSORS
inline bool IsWasmFrame(int frame_ix) const; inline bool IsWasmFrame(int frame_ix) const;
inline bool IsAsmJsWasmFrame(int frame_ix) const;
inline int FrameCount() const; inline int FrameCount() const;
void ShrinkToFit(); void ShrinkToFit();
// Flags. // Flags.
static const int kIsWasmFrame = 1 << 0; static const int kIsWasmFrame = 1 << 0;
static const int kIsStrict = 1 << 1; static const int kIsAsmJsWasmFrame = 1 << 1;
static const int kForceConstructor = 1 << 2; static const int kIsStrict = 1 << 2;
static const int kForceConstructor = 1 << 3;
static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in, static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in,
Handle<Object> receiver, Handle<Object> receiver,
......
...@@ -77,6 +77,12 @@ class WasmSectionIterator { ...@@ -77,6 +77,12 @@ class WasmSectionIterator {
return static_cast<uint32_t>(section_end_ - section_start_); return static_cast<uint32_t>(section_end_ - section_start_);
} }
inline const byte* payload_start() const { return payload_start_; }
inline uint32_t payload_length() const {
return static_cast<uint32_t>(section_end_ - payload_start_);
}
inline const byte* section_end() const { return section_end_; } inline const byte* section_end() const { return section_end_; }
// Advances to the next section, checking that decoding the current section // Advances to the next section, checking that decoding the current section
...@@ -97,6 +103,7 @@ class WasmSectionIterator { ...@@ -97,6 +103,7 @@ class WasmSectionIterator {
Decoder& decoder_; Decoder& decoder_;
WasmSectionCode section_code_; WasmSectionCode section_code_;
const byte* section_start_; const byte* section_start_;
const byte* payload_start_;
const byte* section_end_; const byte* section_end_;
// Reads the section code/name at the current position and sets up // Reads the section code/name at the current position and sets up
...@@ -111,6 +118,7 @@ class WasmSectionIterator { ...@@ -111,6 +118,7 @@ class WasmSectionIterator {
// Read and check the section size. // Read and check the section size.
uint32_t section_length = decoder_.consume_u32v("section length"); uint32_t section_length = decoder_.consume_u32v("section length");
section_start_ = decoder_.pc(); section_start_ = decoder_.pc();
payload_start_ = section_start_;
if (decoder_.checkAvailable(section_length)) { if (decoder_.checkAvailable(section_length)) {
// Get the limit of the section within the module. // Get the limit of the section within the module.
section_end_ = section_start_ + section_length; section_end_ = section_start_ + section_length;
...@@ -120,7 +128,7 @@ class WasmSectionIterator { ...@@ -120,7 +128,7 @@ class WasmSectionIterator {
} }
if (section_code == kUnknownSectionCode) { if (section_code == kUnknownSectionCode) {
// Check for the known "names" section. // Check for the known "name" section.
uint32_t string_length = decoder_.consume_u32v("section name length"); uint32_t string_length = decoder_.consume_u32v("section name length");
const byte* section_name_start = decoder_.pc(); const byte* section_name_start = decoder_.pc();
decoder_.consume_bytes(string_length, "section name"); decoder_.consume_bytes(string_length, "section name");
...@@ -129,6 +137,7 @@ class WasmSectionIterator { ...@@ -129,6 +137,7 @@ class WasmSectionIterator {
section_code_ = kUnknownSectionCode; section_code_ = kUnknownSectionCode;
return; return;
} }
payload_start_ = decoder_.pc();
TRACE(" +%d section name : \"%.*s\"\n", TRACE(" +%d section name : \"%.*s\"\n",
static_cast<int>(section_name_start - decoder_.start()), static_cast<int>(section_name_start - decoder_.start()),
...@@ -590,10 +599,7 @@ class ModuleDecoder : public Decoder { ...@@ -590,10 +599,7 @@ class ModuleDecoder : public Decoder {
uint32_t local_names_count = consume_u32v("local names count"); uint32_t local_names_count = consume_u32v("local names count");
for (uint32_t j = 0; ok() && j < local_names_count; j++) { for (uint32_t j = 0; ok() && j < local_names_count; j++) {
uint32_t unused = 0; skip_string();
uint32_t offset = consume_string(&unused, false);
USE(unused);
USE(offset);
} }
} }
section_iter.advance(); section_iter.advance();
...@@ -799,6 +805,12 @@ class ModuleDecoder : public Decoder { ...@@ -799,6 +805,12 @@ class ModuleDecoder : public Decoder {
return offset; return offset;
} }
// Skips over a length-prefixed string, but checks that it is within bounds.
void skip_string() {
uint32_t length = consume_u32v("string length");
consume_bytes(length, "string");
}
uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) { uint32_t consume_sig_index(WasmModule* module, FunctionSig** sig) {
const byte* pos = pc_; const byte* pos = pc_;
uint32_t sig_index = consume_u32v("signature index"); uint32_t sig_index = consume_u32v("signature index");
...@@ -1029,6 +1041,8 @@ class FunctionError : public FunctionResult { ...@@ -1029,6 +1041,8 @@ class FunctionError : public FunctionResult {
} }
}; };
// Find section with given section code. Return Vector of the payload, or null
// Vector if section is not found or module bytes are invalid.
Vector<const byte> FindSection(const byte* module_start, const byte* module_end, Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
WasmSectionCode code) { WasmSectionCode code) {
Decoder decoder(module_start, module_end); Decoder decoder(module_start, module_end);
...@@ -1042,10 +1056,10 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end, ...@@ -1042,10 +1056,10 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
WasmSectionIterator section_iter(decoder); WasmSectionIterator section_iter(decoder);
while (section_iter.more()) { while (section_iter.more()) {
if (section_iter.section_code() == code) { if (section_iter.section_code() == code) {
return Vector<const uint8_t>(section_iter.section_start(), return Vector<const uint8_t>(section_iter.payload_start(),
section_iter.section_length()); section_iter.payload_length());
} }
decoder.consume_bytes(section_iter.section_length(), "section payload"); decoder.consume_bytes(section_iter.payload_length(), "section payload");
section_iter.advance(); section_iter.advance();
} }
...@@ -1118,16 +1132,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets( ...@@ -1118,16 +1132,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
return decoder.toResult(std::move(table)); return decoder.toResult(std::move(table));
} }
// Reserve entries for the imported functions.
table.reserve(num_imported_functions);
for (uint32_t i = 0; i < num_imported_functions; i++) {
table.push_back(std::make_pair(0, 0));
}
uint32_t functions_count = decoder.consume_u32v("functions count"); uint32_t functions_count = decoder.consume_u32v("functions count");
// Take care of invalid input here. // Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(code_section.length()) / 2) if (functions_count < static_cast<unsigned>(code_section.length()) / 2) {
table.reserve(num_imported_functions + functions_count); table.reserve(num_imported_functions + functions_count);
}
// Add null entries for the imported functions.
table.resize(num_imported_functions);
int section_offset = static_cast<int>(code_section.start() - module_start); int section_offset = static_cast<int>(code_section.start() - module_start);
DCHECK_LE(0, section_offset); DCHECK_LE(0, section_offset);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) { for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
...@@ -1142,6 +1155,51 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets( ...@@ -1142,6 +1155,51 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
return decoder.toResult(std::move(table)); return decoder.toResult(std::move(table));
} }
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* tables_start,
const byte* tables_end,
uint32_t num_imported_functions) {
AsmJsOffsets table;
Decoder decoder(tables_start, tables_end);
uint32_t functions_count = decoder.consume_u32v("functions count");
// Reserve space for the entries, taking care of invalid input.
if (functions_count < static_cast<unsigned>(tables_end - tables_start)) {
table.reserve(num_imported_functions + functions_count);
}
// Add null entries for the imported functions.
table.resize(num_imported_functions);
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
uint32_t size = decoder.consume_u32v("table size");
if (size == 0) {
table.push_back(std::vector<std::pair<int, int>>());
continue;
}
if (!decoder.checkAvailable(size)) {
decoder.error("illegal asm function offset table size");
}
const byte* table_end = decoder.pc() + size;
uint32_t locals_size = decoder.consume_u32("locals size");
int last_byte_offset = locals_size;
int last_asm_position = 0;
std::vector<std::pair<int, int>> func_asm_offsets;
func_asm_offsets.reserve(size / 4); // conservative estimation
while (decoder.ok() && decoder.pc() < table_end) {
last_byte_offset += decoder.consume_u32v("byte offset delta");
last_asm_position += decoder.consume_i32v("asm position delta");
func_asm_offsets.push_back({last_byte_offset, last_asm_position});
}
if (decoder.pc() != table_end) {
decoder.error("broken asm offset table");
}
table.push_back(std::move(func_asm_offsets));
}
if (decoder.more()) decoder.error("unexpected additional bytes");
return decoder.toResult(std::move(table));
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -17,6 +17,8 @@ typedef Result<const WasmModule*> ModuleResult; ...@@ -17,6 +17,8 @@ typedef Result<const WasmModule*> ModuleResult;
typedef Result<WasmFunction*> FunctionResult; typedef Result<WasmFunction*> FunctionResult;
typedef std::vector<std::pair<int, int>> FunctionOffsets; typedef std::vector<std::pair<int, int>> FunctionOffsets;
typedef Result<FunctionOffsets> FunctionOffsetsResult; typedef Result<FunctionOffsets> FunctionOffsetsResult;
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}. // 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, Zone* zone,
...@@ -45,6 +47,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets( ...@@ -45,6 +47,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end); WasmInitExpr DecodeWasmInitExprForTesting(const byte* start, const byte* end);
// Extracts the mapping from wasm byte offset to asm.js source position per
// function.
// Returns a vector of vectors with <byte_offset, source_position> entries, or
// failure if the wasm bytes are detected as invalid. Note that this validation
// is not complete.
AsmJsOffsetsResult DecodeAsmJsOffsets(const byte* module_start,
const byte* module_end,
uint32_t num_imported_functions);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -21,6 +21,7 @@ enum { ...@@ -21,6 +21,7 @@ enum {
kWasmDebugInfoWasmBytesHash, kWasmDebugInfoWasmBytesHash,
kWasmDebugInfoFunctionByteOffsets, kWasmDebugInfoFunctionByteOffsets,
kWasmDebugInfoFunctionScripts, kWasmDebugInfoFunctionScripts,
kWasmDebugInfoAsmJsOffsets,
kWasmDebugInfoNumEntries kWasmDebugInfoNumEntries
}; };
...@@ -80,6 +81,57 @@ Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info, ...@@ -80,6 +81,57 @@ Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info,
offset_and_length.second); offset_and_length.second);
} }
FixedArray *GetOffsetTables(Handle<WasmDebugInfo> debug_info,
Isolate *isolate) {
Object *offset_tables = debug_info->get(kWasmDebugInfoAsmJsOffsets);
if (!offset_tables->IsUndefined(isolate)) {
return FixedArray::cast(offset_tables);
}
AsmJsOffsetsResult asm_offsets;
{
Handle<JSObject> wasm_object(debug_info->wasm_object(), isolate);
Handle<WasmCompiledModule> compiled_module =
handle(GetCompiledModule(*wasm_object), isolate);
DCHECK(compiled_module->has_asm_js_offset_tables());
Handle<ByteArray> asm_offset_tables =
compiled_module->asm_js_offset_tables();
uint32_t num_imported_functions =
static_cast<uint32_t>(wasm::GetNumImportedFunctions(wasm_object));
DisallowHeapAllocation no_gc;
const byte *bytes_start = asm_offset_tables->GetDataStartAddress();
const byte *bytes_end = bytes_start + asm_offset_tables->length();
asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end,
num_imported_functions);
}
// Wasm bytes must be valid and must contain asm.js offset table.
DCHECK(asm_offsets.ok());
DCHECK_GE(static_cast<size_t>(kMaxInt), asm_offsets.val.size());
int num_functions = static_cast<int>(asm_offsets.val.size());
DCHECK_EQ(wasm::GetNumberOfFunctions(handle(debug_info->wasm_object())),
num_functions);
Handle<FixedArray> all_tables =
isolate->factory()->NewFixedArray(num_functions);
debug_info->set(kWasmDebugInfoAsmJsOffsets, *all_tables);
for (int func = 0; func < num_functions; ++func) {
std::vector<std::pair<int, int>> &func_asm_offsets = asm_offsets.val[func];
if (func_asm_offsets.empty()) continue;
size_t array_size = 2 * kIntSize * func_asm_offsets.size();
CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
ByteArray *arr =
*isolate->factory()->NewByteArray(static_cast<int>(array_size));
all_tables->set(func, arr);
int idx = 0;
for (std::pair<int, int> p : func_asm_offsets) {
// Byte offsets must be strictly monotonously increasing:
DCHECK(idx == 0 || p.first > arr->get_int(idx - 2));
arr->set_int(idx++, p.first);
arr->set_int(idx++, p.second);
}
DCHECK_EQ(arr->length(), idx * kIntSize);
}
return *all_tables;
}
} // namespace } // namespace
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) { Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
...@@ -236,3 +288,29 @@ Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable( ...@@ -236,3 +288,29 @@ Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(
return offset_table; return offset_table;
} }
int WasmDebugInfo::GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
int func_index, int byte_offset) {
Isolate *isolate = debug_info->GetIsolate();
FixedArray *offset_tables = GetOffsetTables(debug_info, isolate);
DCHECK_LT(func_index, offset_tables->length());
ByteArray *offset_table = ByteArray::cast(offset_tables->get(func_index));
// Binary search for the current byte offset.
int left = 0; // inclusive
int right = offset_table->length() / kIntSize / 2; // exclusive
DCHECK_LT(left, right);
while (right - left > 1) {
int mid = left + (right - left) / 2;
if (offset_table->get_int(2 * mid) < byte_offset) {
left = mid;
} else {
right = mid;
}
}
// There should be an entry for each position that could show up on the stack
// trace:
DCHECK_EQ(byte_offset, offset_table->get_int(2 * left));
return offset_table->get_int(2 * left + 1);
}
...@@ -37,6 +37,11 @@ class WasmDebugInfo : public FixedArray { ...@@ -37,6 +37,11 @@ class WasmDebugInfo : public FixedArray {
// column. // column.
static Handle<FixedArray> GetFunctionOffsetTable( static Handle<FixedArray> GetFunctionOffsetTable(
Handle<WasmDebugInfo> debug_info, int func_index); Handle<WasmDebugInfo> debug_info, int func_index);
// Get the asm.js source position from a byte offset.
// Must only be called if the associated wasm object was created from asm.js.
static int GetAsmJsSourcePosition(Handle<WasmDebugInfo> debug_info,
int func_index, int byte_offset);
}; };
} // namespace wasm } // namespace wasm
......
...@@ -134,16 +134,16 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -134,16 +134,16 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
i::MaybeHandle<i::JSObject> InstantiateModule( i::MaybeHandle<i::JSObject> InstantiateModule(
const v8::FunctionCallbackInfo<v8::Value>& args, const byte* start, const v8::FunctionCallbackInfo<v8::Value>& args, const byte* start,
const byte* end, ErrorThrower* thrower, const byte* end, ErrorThrower* thrower) {
internal::wasm::ModuleOrigin origin = i::wasm::kWasmOrigin) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
// Decode but avoid a redundant pass over function bodies for verification. // Decode but avoid a redundant pass over function bodies for verification.
// Verification will happen during compilation. // Verification will happen during compilation.
i::Zone zone(isolate->allocator()); i::Zone zone(isolate->allocator());
i::MaybeHandle<i::JSObject> module_object = i::MaybeHandle<i::JSObject> module_object =
i::wasm::CreateModuleObjectFromBytes(isolate, start, end, thrower, i::wasm::CreateModuleObjectFromBytes(
origin); isolate, start, end, thrower, i::wasm::kWasmOrigin,
i::Handle<i::Script>::null(), nullptr, nullptr);
i::MaybeHandle<i::JSObject> object; i::MaybeHandle<i::JSObject> object;
if (!module_object.is_null()) { if (!module_object.is_null()) {
// Success. Instantiate the module and return the object. // Success. Instantiate the module and return the object.
...@@ -195,8 +195,8 @@ static i::MaybeHandle<i::JSObject> CreateModuleObject( ...@@ -195,8 +195,8 @@ static i::MaybeHandle<i::JSObject> CreateModuleObject(
DCHECK(source->IsArrayBuffer() || source->IsTypedArray()); DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
return i::wasm::CreateModuleObjectFromBytes( return i::wasm::CreateModuleObjectFromBytes(
i_isolate, buffer.start, buffer.end, thrower, i_isolate, buffer.start, buffer.end, thrower, i::wasm::kWasmOrigin,
i::wasm::ModuleOrigin::kWasmOrigin); i::Handle<i::Script>::null(), nullptr, nullptr);
} }
static bool ValidateModule(v8::Isolate* isolate, static bool ValidateModule(v8::Isolate* isolate,
......
...@@ -59,7 +59,8 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder) ...@@ -59,7 +59,8 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
i64_temps_(builder->zone()), i64_temps_(builder->zone()),
f32_temps_(builder->zone()), f32_temps_(builder->zone()),
f64_temps_(builder->zone()), f64_temps_(builder->zone()),
direct_calls_(builder->zone()) {} direct_calls_(builder->zone()),
asm_offsets_(builder->zone(), 8) {}
void WasmFunctionBuilder::EmitVarInt(uint32_t val) { void WasmFunctionBuilder::EmitVarInt(uint32_t val) {
byte buffer[8]; byte buffer[8];
...@@ -153,6 +154,20 @@ void WasmFunctionBuilder::SetName(Vector<const char> name) { ...@@ -153,6 +154,20 @@ void WasmFunctionBuilder::SetName(Vector<const char> name) {
memcpy(name_.data(), name.start(), name.length()); memcpy(name_.data(), name.start(), name.length());
} }
void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
// We only want to emit one mapping per byte offset:
DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
DCHECK_LE(body_.size(), kMaxUInt32);
uint32_t byte_offset = static_cast<uint32_t>(body_.size());
asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
last_asm_byte_offset_ = byte_offset;
DCHECK_GE(asm_position, 0);
asm_offsets_.write_i32v(asm_position - last_asm_source_position_);
last_asm_source_position_ = asm_position;
}
void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const { void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_); buffer.write_u32v(signature_index_);
} }
...@@ -188,6 +203,18 @@ void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const { ...@@ -188,6 +203,18 @@ void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
} }
} }
void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
if (asm_offsets_.size() == 0) {
buffer.write_size(0);
return;
}
buffer.write_size(asm_offsets_.size() + kInt32Size);
// Offset of the recorded byte offsets.
DCHECK_GE(kMaxUInt32, locals_.Size());
buffer.write_u32(static_cast<uint32_t>(locals_.Size()));
buffer.write(asm_offsets_.begin(), asm_offsets_.size());
}
WasmModuleBuilder::WasmModuleBuilder(Zone* zone) WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
: zone_(zone), : zone_(zone),
signatures_(zone), signatures_(zone),
...@@ -494,6 +521,15 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const { ...@@ -494,6 +521,15 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
FixupSection(buffer, start); FixupSection(buffer, start);
} }
} }
void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
// == Emit asm.js offset table ===============================================
buffer.write_size(functions_.size());
// Emit the offset table per function.
for (auto function : functions_) {
function->WriteAsmWasmOffsetTable(buffer);
}
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -88,10 +88,10 @@ class ZoneBuffer : public ZoneObject { ...@@ -88,10 +88,10 @@ class ZoneBuffer : public ZoneObject {
} }
} }
size_t offset() { return static_cast<size_t>(pos_ - buffer_); } size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
size_t size() { return static_cast<size_t>(pos_ - buffer_); } size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
const byte* begin() { return buffer_; } const byte* begin() const { return buffer_; }
const byte* end() { return pos_; } const byte* end() const { return pos_; }
void EnsureSpace(size_t size) { void EnsureSpace(size_t size) {
if ((pos_ + size) > end_) { if ((pos_ + size) > end_) {
...@@ -135,10 +135,12 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject { ...@@ -135,10 +135,12 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void Export(); void Export();
void ExportAs(Vector<const char> name); void ExportAs(Vector<const char> name);
void SetName(Vector<const char> name); void SetName(Vector<const char> name);
void AddAsmWasmOffset(int asm_position);
void WriteSignature(ZoneBuffer& buffer) const; void WriteSignature(ZoneBuffer& buffer) const;
void WriteExport(ZoneBuffer& buffer) const; void WriteExport(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const; void WriteBody(ZoneBuffer& buffer) const;
void WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const;
bool exported() { return exported_; } bool exported() { return exported_; }
uint32_t func_index() { return func_index_; } uint32_t func_index() { return func_index_; }
...@@ -167,6 +169,11 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject { ...@@ -167,6 +169,11 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
ZoneVector<uint32_t> f32_temps_; ZoneVector<uint32_t> f32_temps_;
ZoneVector<uint32_t> f64_temps_; ZoneVector<uint32_t> f64_temps_;
ZoneVector<DirectCallIndex> direct_calls_; ZoneVector<DirectCallIndex> direct_calls_;
// Delta-encoded mapping from wasm bytes to asm.js source positions.
ZoneBuffer asm_offsets_;
uint32_t last_asm_byte_offset_ = 0;
uint32_t last_asm_source_position_ = 0;
}; };
class WasmTemporary { class WasmTemporary {
...@@ -228,6 +235,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { ...@@ -228,6 +235,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
// Writing methods. // Writing methods.
void WriteTo(ZoneBuffer& buffer) const; void WriteTo(ZoneBuffer& buffer) const;
void WriteAsmJsOffsetTable(ZoneBuffer& buffer) const;
// TODO(titzer): use SignatureMap from signature-map.h here. // TODO(titzer): use SignatureMap from signature-map.h here.
// This signature map is zone-allocated, but the other is heap allocated. // This signature map is zone-allocated, but the other is heap allocated.
......
...@@ -660,8 +660,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner, ...@@ -660,8 +660,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter()); JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
JSObject* owner = *p; JSObject* owner = *p;
WasmCompiledModule* compiled_module = WasmCompiledModule* compiled_module = GetCompiledModule(owner);
WasmCompiledModule::cast(owner->GetInternalField(kWasmCompiledModule));
TRACE("Finalizing %d {\n", compiled_module->instance_id()); TRACE("Finalizing %d {\n", compiled_module->instance_id());
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate()); Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
DCHECK(compiled_module->has_weak_module_object()); DCHECK(compiled_module->has_weak_module_object());
...@@ -861,7 +860,7 @@ Object* GetOwningWasmInstance(Code* code) { ...@@ -861,7 +860,7 @@ Object* GetOwningWasmInstance(Code* code) {
int GetNumImportedFunctions(Handle<JSObject> wasm_object) { int GetNumImportedFunctions(Handle<JSObject> wasm_object) {
// TODO(wasm): Cache this number if it ever becomes a performance problem. // TODO(wasm): Cache this number if it ever becomes a performance problem.
DCHECK(IsWasmObject(*wasm_object)); DCHECK(IsWasmObject(*wasm_object));
Object* compiled_module = wasm_object->GetInternalField(kWasmCompiledModule); WasmCompiledModule* compiled_module = GetCompiledModule(*wasm_object);
Handle<FixedArray> imports = Handle<FixedArray> imports =
WasmCompiledModule::cast(compiled_module)->imports(); WasmCompiledModule::cast(compiled_module)->imports();
int num_imports = imports->length(); int num_imports = imports->length();
...@@ -1425,8 +1424,7 @@ class WasmInstanceBuilder { ...@@ -1425,8 +1424,7 @@ class WasmInstanceBuilder {
// we want all the publishing to happen free from GC interruptions, and // we want all the publishing to happen free from GC interruptions, and
// so we do it in // so we do it in
// one GC-free scope afterwards. // one GC-free scope afterwards.
original = handle(WasmCompiledModule::cast( original = handle(GetCompiledModule(*owner.ToHandleChecked()));
owner.ToHandleChecked()->GetInternalField(kWasmCompiledModule)));
link_to_original = factory->NewWeakCell(original.ToHandleChecked()); link_to_original = factory->NewWeakCell(original.ToHandleChecked());
} }
// Publish the new instance to the instances chain. // Publish the new instance to the instances chain.
...@@ -1958,8 +1956,8 @@ Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm, ...@@ -1958,8 +1956,8 @@ Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
uint32_t func_index) { uint32_t func_index) {
if (!wasm->IsUndefined(isolate)) { if (!wasm->IsUndefined(isolate)) {
DCHECK(IsWasmObject(*wasm)); DCHECK(IsWasmObject(*wasm));
WasmCompiledModule* compiled_module = WasmCompiledModule::cast( WasmCompiledModule* compiled_module =
Handle<JSObject>::cast(wasm)->GetInternalField(kWasmCompiledModule)); GetCompiledModule(JSObject::cast(*wasm));
Handle<ByteArray> func_names = compiled_module->function_names(); Handle<ByteArray> func_names = compiled_module->function_names();
// TODO(clemens): Extract this from the module bytes; skip whole function // TODO(clemens): Extract this from the module bytes; skip whole function
// name table. // name table.
...@@ -2002,10 +2000,33 @@ bool IsWasmObject(Object* object) { ...@@ -2002,10 +2000,33 @@ bool IsWasmObject(Object* object) {
return true; return true;
} }
WasmCompiledModule* GetCompiledModule(JSObject* wasm) {
return WasmCompiledModule::cast(wasm->GetInternalField(kWasmCompiledModule));
}
bool WasmIsAsmJs(Object* wasm, Isolate* isolate) {
if (wasm->IsUndefined(isolate)) return false;
DCHECK(IsWasmObject(wasm));
WasmCompiledModule* compiled_module = GetCompiledModule(JSObject::cast(wasm));
return compiled_module->has_asm_js_script();
}
Handle<Script> GetAsmWasmScript(Handle<JSObject> wasm) {
DCHECK(IsWasmObject(*wasm));
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
return compiled_module->asm_js_script();
}
int GetAsmWasmSourcePosition(Handle<JSObject> wasm, int func_index,
int byte_offset) {
return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(wasm), func_index,
byte_offset);
}
Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm) { Handle<SeqOneByteString> GetWasmBytes(Handle<JSObject> wasm) {
DCHECK(IsWasmObject(*wasm)); DCHECK(IsWasmObject(*wasm));
Object* compiled_module = wasm->GetInternalField(kWasmCompiledModule); WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
return WasmCompiledModule::cast(compiled_module)->module_bytes(); return compiled_module->module_bytes();
} }
Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm) { Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm) {
...@@ -2088,8 +2109,7 @@ void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size, ...@@ -2088,8 +2109,7 @@ void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
int GetNumberOfFunctions(Handle<JSObject> wasm) { int GetNumberOfFunctions(Handle<JSObject> wasm) {
DCHECK(IsWasmObject(*wasm)); DCHECK(IsWasmObject(*wasm));
WasmCompiledModule* compiled_module = WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
WasmCompiledModule::cast(wasm->GetInternalField(kWasmCompiledModule));
ByteArray* func_names_arr = compiled_module->ptr_to_function_names(); ByteArray* func_names_arr = compiled_module->ptr_to_function_names();
// TODO(clemensh): this looks inside an array constructed elsewhere. Refactor. // TODO(clemensh): this looks inside an array constructed elsewhere. Refactor.
return func_names_arr->get_int(0); return func_names_arr->get_int(0);
...@@ -2119,11 +2139,12 @@ Handle<JSObject> CreateCompiledModuleObject( ...@@ -2119,11 +2139,12 @@ Handle<JSObject> CreateCompiledModuleObject(
return module_obj; return module_obj;
} }
MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate, // TODO(clemensh): origin can be inferred from asm_js_script; remove it.
const byte* start, MaybeHandle<JSObject> CreateModuleObjectFromBytes(
const byte* end, Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ErrorThrower* thrower, ModuleOrigin origin, Handle<Script> asm_js_script,
ModuleOrigin origin) { const byte* asm_js_offset_tables_start,
const byte* asm_js_offset_tables_end) {
MaybeHandle<JSObject> nothing; MaybeHandle<JSObject> nothing;
Zone zone(isolate->allocator()); Zone zone(isolate->allocator());
ModuleResult result = ModuleResult result =
...@@ -2133,12 +2154,28 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate, ...@@ -2133,12 +2154,28 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
thrower->Failed("Wasm decoding failed", result); thrower->Failed("Wasm decoding failed", result);
return nothing; return nothing;
} }
MaybeHandle<WasmCompiledModule> compiled_module = MaybeHandle<WasmCompiledModule> maybe_compiled_module =
decoded_module->CompileFunctions(isolate, thrower); decoded_module->CompileFunctions(isolate, thrower);
if (compiled_module.is_null()) return nothing; if (maybe_compiled_module.is_null()) return nothing;
Handle<WasmCompiledModule> compiled_module =
return CreateCompiledModuleObject(isolate, compiled_module.ToHandleChecked(), maybe_compiled_module.ToHandleChecked();
origin);
DCHECK_EQ(origin == kAsmJsOrigin, !asm_js_script.is_null());
DCHECK(!compiled_module->has_asm_js_script());
DCHECK(!compiled_module->has_asm_js_offset_tables());
if (origin == kAsmJsOrigin) {
compiled_module->set_asm_js_script(asm_js_script);
size_t offset_tables_len =
asm_js_offset_tables_end - asm_js_offset_tables_start;
DCHECK_GE(static_cast<size_t>(kMaxInt), offset_tables_len);
Handle<ByteArray> offset_tables =
isolate->factory()->NewByteArray(static_cast<int>(offset_tables_len));
memcpy(offset_tables->GetDataStartAddress(), asm_js_offset_tables_start,
offset_tables_len);
compiled_module->set_asm_js_offset_tables(offset_tables);
}
return CreateCompiledModuleObject(isolate, compiled_module, origin);
} }
bool ValidateModuleBytes(Isolate* isolate, const byte* start, const byte* end, bool ValidateModuleBytes(Isolate* isolate, const byte* start, const byte* end,
...@@ -2166,9 +2203,8 @@ void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) { ...@@ -2166,9 +2203,8 @@ void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
DCHECK(IsWasmObject(*instance)); DCHECK(IsWasmObject(*instance));
instance->SetInternalField(kWasmMemArrayBuffer, buffer); instance->SetInternalField(kWasmMemArrayBuffer, buffer);
WasmCompiledModule* module = WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule)); compiled_module->set_ptr_to_heap(buffer);
module->set_ptr_to_heap(buffer);
} }
int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) { int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
...@@ -2275,8 +2311,7 @@ void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj) { ...@@ -2275,8 +2311,7 @@ void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj) {
void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance) { void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
CHECK(IsWasmObject(*instance)); CHECK(IsWasmObject(*instance));
WasmCompiledModule* compiled_module = WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule));
CHECK(compiled_module->has_weak_module_object()); CHECK(compiled_module->has_weak_module_object());
CHECK(compiled_module->ptr_to_weak_module_object()->cleared()); CHECK(compiled_module->ptr_to_weak_module_object()->cleared());
} }
......
...@@ -394,6 +394,8 @@ class WasmCompiledModule : public FixedArray { ...@@ -394,6 +394,8 @@ class WasmCompiledModule : public FixedArray {
MACRO(OBJECT, FixedArray, indirect_function_tables) \ MACRO(OBJECT, FixedArray, indirect_function_tables) \
MACRO(OBJECT, SeqOneByteString, module_bytes) \ MACRO(OBJECT, SeqOneByteString, module_bytes) \
MACRO(OBJECT, ByteArray, function_names) \ MACRO(OBJECT, ByteArray, function_names) \
MACRO(OBJECT, Script, asm_js_script) \
MACRO(OBJECT, ByteArray, asm_js_offset_tables) \
MACRO(SMALL_NUMBER, uint32_t, min_memory_pages) \ MACRO(SMALL_NUMBER, uint32_t, min_memory_pages) \
MACRO(OBJECT, FixedArray, data_segments_info) \ MACRO(OBJECT, FixedArray, data_segments_info) \
MACRO(OBJECT, ByteArray, data_segments) \ MACRO(OBJECT, ByteArray, data_segments) \
...@@ -499,6 +501,20 @@ Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate, ...@@ -499,6 +501,20 @@ Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate,
// else. // else.
bool IsWasmObject(Object* object); bool IsWasmObject(Object* object);
// Return the compiled module object for this wasm object.
WasmCompiledModule* GetCompiledModule(JSObject* wasm);
// Check whether the wasm module was generated from asm.js code.
bool WasmIsAsmJs(Object* wasm, Isolate* isolate);
// Get the script for the asm.js origin of the wasm module.
Handle<Script> GetAsmWasmScript(Handle<JSObject> wasm);
// Get the asm.js source position for the given byte offset in the given
// function.
int GetAsmWasmSourcePosition(Handle<JSObject> wasm, int func_index,
int byte_offset);
// Update memory references of code objects associated with the module // Update memory references of code objects associated with the module
bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start, bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start,
Address new_start, uint32_t old_size, Address new_start, uint32_t old_size,
...@@ -520,7 +536,8 @@ Handle<JSObject> CreateCompiledModuleObject( ...@@ -520,7 +536,8 @@ Handle<JSObject> CreateCompiledModuleObject(
V8_EXPORT_PRIVATE MaybeHandle<JSObject> CreateModuleObjectFromBytes( V8_EXPORT_PRIVATE MaybeHandle<JSObject> CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower, Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin); ModuleOrigin origin, Handle<Script> asm_js_script,
const byte* asm_offset_tables_start, const byte* asm_offset_tables_end);
V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start, V8_EXPORT_PRIVATE bool ValidateModuleBytes(Isolate* isolate, const byte* start,
const byte* end, const byte* end,
......
...@@ -62,7 +62,7 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate, ...@@ -62,7 +62,7 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
// again through the normal pipeline. // again through the normal pipeline.
MaybeHandle<JSObject> module_object = CreateModuleObjectFromBytes( MaybeHandle<JSObject> module_object = CreateModuleObjectFromBytes(
isolate, module->module_start, module->module_end, thrower, isolate, module->module_start, module->module_end, thrower,
ModuleOrigin::kWasmOrigin); ModuleOrigin::kWasmOrigin, Handle<Script>::null(), nullptr, nullptr);
if (module_object.is_null()) { if (module_object.is_null()) {
thrower->Error("Module pre-validation failed."); thrower->Error("Module pre-validation failed.");
return Handle<JSObject>::null(); return Handle<JSObject>::null();
......
...@@ -117,6 +117,9 @@ var assertUnoptimized; ...@@ -117,6 +117,9 @@ var assertUnoptimized;
// Assert that a string contains another expected substring. // Assert that a string contains another expected substring.
var assertContains; var assertContains;
// Assert that a string matches a given regex.
var assertMatches;
(function () { // Scope for utility functions. (function () { // Scope for utility functions.
...@@ -425,6 +428,15 @@ var assertContains; ...@@ -425,6 +428,15 @@ var assertContains;
} }
}; };
assertMatches = function(regexp, str, name_opt) {
if (!(regexp instanceof RegExp)) {
regexp = new RegExp(regexp);
}
if (!str.match(regexp)) {
fail("should match '" + regexp + "'", str, name_opt);
}
};
var OptimizationStatusImpl = undefined; var OptimizationStatusImpl = undefined;
var OptimizationStatus = function(fun, sync_opt) { var OptimizationStatus = function(fun, sync_opt) {
......
// 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: --validate-asm --allow-natives-syntax
var filename = '(?:[^ ]+/)?test/mjsunit/wasm/asm-wasm-stack.js';
filename = filename.replace(/\//g, '[/\\\\]');
function checkPreformattedStack(e, expected_lines) {
print('preformatted stack: ' + e.stack);
var lines = e.stack.split('\n');
assertEquals(expected_lines.length, lines.length);
for (var i = 0; i < lines.length; ++i) {
assertMatches(expected_lines[i], lines[i], 'line ' + i);
}
}
function checkFunctionsOnCallsites(e, locations) {
var stack = e.stack;
print('callsite objects (size ' + stack.length + '):');
for (var i = 0; i < stack.length; ++i) {
var s = stack[i];
print(
' [' + i + '] ' + s.getFunctionName() + ' (' + s.getFileName() + ':' +
s.getLineNumber() + ':' + s.getColumnNumber() + ')');
}
assertEquals(locations.length, stack.length, 'stack size');
for (var i = 0; i < locations.length; ++i) {
var cs = stack[i];
assertMatches('^' + filename + '$', cs.getFileName(), 'file name at ' + i);
assertEquals(
locations[i][0], cs.getFunctionName(), 'function name at ' + i);
assertEquals(locations[i][1], cs.getLineNumber(), 'line number at ' + i);
assertEquals(
locations[i][2], cs.getColumnNumber(), 'column number at ' + i);
assertNotNull(cs.getThis(), 'receiver should be global');
assertEquals(stack[0].getThis(), cs.getThis(), 'receiver should be global');
}
}
function throwException() {
throw new Error('exception from JS');
}
function generateWasmFromAsmJs(stdlib, foreign, heap) {
'use asm';
var throwFunc = foreign.throwFunc;
function callThrow() {
throwFunc();
}
function redirectFun() {
callThrow();
}
return redirectFun;
}
(function PreformattedStackTraceFromJS() {
var fun = generateWasmFromAsmJs(this, {throwFunc: throwException}, undefined);
var e = null;
try {
fun();
} catch (ex) {
e = ex;
}
assertInstanceof(e, Error, 'exception should have been thrown');
checkPreformattedStack(e, [
'^Error: exception from JS$',
'^ *at throwException \\(' + filename + ':43:9\\)$',
'^ *at callThrow \\(' + filename + ':50:5\\)$',
'^ *at redirectFun \\(' + filename + ':53:5\\)$',
'^ *at PreformattedStackTraceFromJS \\(' + filename + ':62:5\\)$',
'^ *at ' + filename + ':75:3$'
]);
})();
// Now collect the Callsite objects instead of just a string.
Error.prepareStackTrace = function(error, frames) {
return frames;
};
(function CallsiteObjectsFromJS() {
var fun = generateWasmFromAsmJs(this, {throwFunc: throwException}, undefined);
var e = null;
try {
fun();
} catch (ex) {
e = ex;
}
assertInstanceof(e, Error, 'exception should have been thrown');
checkFunctionsOnCallsites(e, [
['throwException', 43, 9], // --
['callThrow', 50, 5], // --
['redirectFun', 53, 5], // --
['CallsiteObjectsFromJS', 86, 5], // --
[null, 98, 3]
]);
})();
...@@ -47,8 +47,7 @@ namespace wasm { ...@@ -47,8 +47,7 @@ namespace wasm {
#define EMPTY_SIGNATURES_SECTION SECTION(Type, 1), 0 #define EMPTY_SIGNATURES_SECTION SECTION(Type, 1), 0
#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0 #define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0
#define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0 #define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0
#define SECTION_NAMES(size) \ #define SECTION_NAMES(size) SECTION(Unknown, size + 5), 4, 'n', 'a', 'm', 'e'
kUnknownSectionCode, U32V_1(size + 5), 4, 'n', 'a', 'm', 'e'
#define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0 #define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0
#define X1(...) __VA_ARGS__ #define X1(...) __VA_ARGS__
...@@ -1288,6 +1287,15 @@ TEST_F(WasmModuleVerifyTest, InitExpr_global) { ...@@ -1288,6 +1287,15 @@ TEST_F(WasmModuleVerifyTest, InitExpr_global) {
EXPECT_EQ(37, expr.val.global_index); EXPECT_EQ(37, expr.val.global_index);
} }
TEST_F(WasmModuleVerifyTest, Multiple_Named_Sections) {
static const byte data[] = {
SECTION(Unknown, 4), 1, 'X', 17, 18, // --
SECTION(Unknown, 9), 3, 'f', 'o', 'o', 5, 6, 7, 8, 9, // --
SECTION(Unknown, 8), 5, 'o', 't', 'h', 'e', 'r', 7, 8, // --
};
EXPECT_VERIFIES(data);
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
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