[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}
This commit is contained in:
parent
e7a0089149
commit
5d9fa102a7
@ -7239,9 +7239,10 @@ MaybeLocal<WasmCompiledModule> WasmCompiledModule::Compile(Isolate* isolate,
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
i::wasm::ErrorThrower thrower(i_isolate, "WasmCompiledModule::Deserialize()");
|
||||
i::MaybeHandle<i::JSObject> maybe_compiled =
|
||||
i::wasm::CreateModuleObjectFromBytes(i_isolate, start, start + length,
|
||||
&thrower,
|
||||
i::wasm::ModuleOrigin::kWasmOrigin);
|
||||
i::wasm::CreateModuleObjectFromBytes(
|
||||
i_isolate, start, start + length, &thrower,
|
||||
i::wasm::ModuleOrigin::kWasmOrigin, i::Handle<i::Script>::null(),
|
||||
nullptr, nullptr);
|
||||
if (maybe_compiled.is_null()) return MaybeLocal<WasmCompiledModule>();
|
||||
return Local<WasmCompiledModule>::Cast(
|
||||
Utils::ToLocal(maybe_compiled.ToHandleChecked()));
|
||||
|
@ -162,11 +162,14 @@ MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
|
||||
v8::internal::wasm::AsmWasmBuilder builder(info->isolate(), info->zone(),
|
||||
info->literal(), &typer);
|
||||
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(
|
||||
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());
|
||||
|
||||
wasm::AsmTyper::StdlibSet uses = typer.StdlibUses();
|
||||
|
@ -1359,11 +1359,13 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
uint32_t index = imported_function_table_.LookupOrInsertImport(
|
||||
vp->var(), sig.Build());
|
||||
VisitCallArgs(expr);
|
||||
current_function_builder_->AddAsmWasmOffset(expr->position());
|
||||
current_function_builder_->Emit(kExprCallFunction);
|
||||
current_function_builder_->EmitVarInt(index);
|
||||
} else {
|
||||
WasmFunctionBuilder* function = LookupOrInsertFunction(vp->var());
|
||||
VisitCallArgs(expr);
|
||||
current_function_builder_->AddAsmWasmOffset(expr->position());
|
||||
current_function_builder_->Emit(kExprCallFunction);
|
||||
current_function_builder_->EmitDirectCallIndex(
|
||||
function->func_index());
|
||||
@ -1389,6 +1391,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
VisitCallArgs(expr);
|
||||
|
||||
current_function_builder_->EmitGetLocal(tmp.index());
|
||||
current_function_builder_->AddAsmWasmOffset(expr->position());
|
||||
current_function_builder_->Emit(kExprCallIndirect);
|
||||
current_function_builder_->EmitVarInt(indices->signature_index);
|
||||
returns_value =
|
||||
@ -1870,13 +1873,16 @@ AsmWasmBuilder::AsmWasmBuilder(Isolate* isolate, Zone* zone,
|
||||
|
||||
// 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.
|
||||
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_);
|
||||
impl.Build();
|
||||
*foreign_args = impl.GetForeignArgs();
|
||||
ZoneBuffer* buffer = new (zone_) ZoneBuffer(zone_);
|
||||
impl.builder_->WriteTo(*buffer);
|
||||
return buffer;
|
||||
ZoneBuffer* module_buffer = new (zone_) ZoneBuffer(zone_);
|
||||
impl.builder_->WriteTo(*module_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__";
|
||||
|
@ -20,9 +20,14 @@ namespace wasm {
|
||||
|
||||
class AsmWasmBuilder {
|
||||
public:
|
||||
struct Result {
|
||||
ZoneBuffer* module_bytes;
|
||||
ZoneBuffer* asm_offset_table;
|
||||
};
|
||||
|
||||
explicit AsmWasmBuilder(Isolate* isolate, Zone* zone, FunctionLiteral* root,
|
||||
AsmTyper* typer);
|
||||
ZoneBuffer* Run(Handle<FixedArray>* foreign_args);
|
||||
Result Run(Handle<FixedArray>* foreign_args);
|
||||
|
||||
static const char* foreign_init_name;
|
||||
static const char* single_function_name;
|
||||
|
@ -520,9 +520,13 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
|
||||
DCHECK(wasm::IsWasmObject(*wasm_object) ||
|
||||
wasm_object->IsUndefined(this));
|
||||
|
||||
elements = FrameArray::AppendWasmFrame(
|
||||
elements, wasm_object, wasm_function_index, abstract_code, offset,
|
||||
FrameArray::kIsWasmFrame);
|
||||
int flags = wasm::WasmIsAsmJs(*wasm_object, this)
|
||||
? FrameArray::kIsAsmJsWasmFrame
|
||||
: FrameArray::kIsWasmFrame;
|
||||
|
||||
elements = FrameArray::AppendWasmFrame(elements, wasm_object,
|
||||
wasm_function_index,
|
||||
abstract_code, offset, flags);
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
113
src/messages.cc
113
src/messages.cc
@ -211,14 +211,6 @@ Handle<Object> JSStackFrame::GetFunctionName() {
|
||||
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 {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Handle<Object> JSStackFrame::GetScriptNameOrSourceUrl() {
|
||||
if (!HasScript()) return isolate_->factory()->null_value();
|
||||
return ScriptNameOrSourceUrl(GetScript(), isolate_);
|
||||
}
|
||||
|
||||
Handle<Object> JSStackFrame::GetMethodName() {
|
||||
if (receiver_->IsNull(isolate_) || receiver_->IsUndefined(isolate_)) {
|
||||
return isolate_->factory()->null_value();
|
||||
@ -455,7 +458,7 @@ bool IsNonEmptyString(Handle<Object> object) {
|
||||
return (object->IsString() && String::cast(*object)->length() > 0);
|
||||
}
|
||||
|
||||
void AppendFileLocation(Isolate* isolate, JSStackFrame* call_site,
|
||||
void AppendFileLocation(Isolate* isolate, StackFrameBase* call_site,
|
||||
IncrementalStringBuilder* builder) {
|
||||
if (call_site->IsNative()) {
|
||||
builder->AppendCString("native");
|
||||
@ -617,7 +620,8 @@ Handle<Script> JSStackFrame::GetScript() const {
|
||||
|
||||
void WasmStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
|
||||
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;
|
||||
wasm_obj_ = handle(array->WasmObject(frame_ix), isolate);
|
||||
wasm_func_index_ = array->WasmFunctionIndex(frame_ix)->value();
|
||||
@ -667,6 +671,72 @@ Handle<Object> WasmStackFrame::Null() const {
|
||||
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,
|
||||
Handle<FrameArray> array, int frame_ix)
|
||||
: isolate_(isolate), array_(array), next_frame_ix_(frame_ix) {}
|
||||
@ -680,13 +750,22 @@ void FrameArrayIterator::Next() { next_frame_ix_++; }
|
||||
StackFrameBase* FrameArrayIterator::Frame() {
|
||||
DCHECK(HasNext());
|
||||
const int flags = array_->Flags(next_frame_ix_)->value();
|
||||
const bool is_js_frame = (flags & FrameArray::kIsWasmFrame) == 0;
|
||||
if (is_js_frame) {
|
||||
js_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
||||
return &js_frame_;
|
||||
} else {
|
||||
wasm_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
||||
return &wasm_frame_;
|
||||
switch (flags & (FrameArray::kIsWasmFrame | FrameArray::kIsAsmJsWasmFrame)) {
|
||||
case 0:
|
||||
// JavaScript Frame.
|
||||
js_frame_.FromFrameArray(isolate_, array_, next_frame_ix_);
|
||||
return &js_frame_;
|
||||
case FrameArray::kIsWasmFrame:
|
||||
// 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 {
|
||||
|
||||
MaybeHandle<String> ToString() override;
|
||||
|
||||
private:
|
||||
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
|
||||
protected:
|
||||
Handle<Object> Null() const;
|
||||
|
||||
Isolate* isolate_;
|
||||
@ -159,9 +158,30 @@ class WasmStackFrame : public StackFrameBase {
|
||||
Handle<AbstractCode> code_;
|
||||
int offset_;
|
||||
|
||||
private:
|
||||
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
|
||||
|
||||
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 {
|
||||
public:
|
||||
FrameArrayIterator(Isolate* isolate, Handle<FrameArray> array,
|
||||
@ -179,6 +199,7 @@ class FrameArrayIterator {
|
||||
int next_frame_ix_;
|
||||
|
||||
WasmStackFrame wasm_frame_;
|
||||
AsmJsWasmStackFrame asm_wasm_frame_;
|
||||
JSStackFrame js_frame_;
|
||||
};
|
||||
|
||||
|
@ -2639,6 +2639,11 @@ bool FrameArray::IsWasmFrame(int frame_ix) const {
|
||||
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 {
|
||||
const int frame_count = Smi::cast(get(kFrameCountIndex))->value();
|
||||
DCHECK_LE(0, frame_count);
|
||||
|
@ -2973,14 +2973,16 @@ class FrameArray : public FixedArray {
|
||||
#undef DECLARE_FRAME_ARRAY_ACCESSORS
|
||||
|
||||
inline bool IsWasmFrame(int frame_ix) const;
|
||||
inline bool IsAsmJsWasmFrame(int frame_ix) const;
|
||||
inline int FrameCount() const;
|
||||
|
||||
void ShrinkToFit();
|
||||
|
||||
// Flags.
|
||||
static const int kIsWasmFrame = 1 << 0;
|
||||
static const int kIsStrict = 1 << 1;
|
||||
static const int kForceConstructor = 1 << 2;
|
||||
static const int kIsAsmJsWasmFrame = 1 << 1;
|
||||
static const int kIsStrict = 1 << 2;
|
||||
static const int kForceConstructor = 1 << 3;
|
||||
|
||||
static Handle<FrameArray> AppendJSFrame(Handle<FrameArray> in,
|
||||
Handle<Object> receiver,
|
||||
|
@ -77,6 +77,12 @@ class WasmSectionIterator {
|
||||
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_; }
|
||||
|
||||
// Advances to the next section, checking that decoding the current section
|
||||
@ -97,6 +103,7 @@ class WasmSectionIterator {
|
||||
Decoder& decoder_;
|
||||
WasmSectionCode section_code_;
|
||||
const byte* section_start_;
|
||||
const byte* payload_start_;
|
||||
const byte* section_end_;
|
||||
|
||||
// Reads the section code/name at the current position and sets up
|
||||
@ -111,6 +118,7 @@ class WasmSectionIterator {
|
||||
// Read and check the section size.
|
||||
uint32_t section_length = decoder_.consume_u32v("section length");
|
||||
section_start_ = decoder_.pc();
|
||||
payload_start_ = section_start_;
|
||||
if (decoder_.checkAvailable(section_length)) {
|
||||
// Get the limit of the section within the module.
|
||||
section_end_ = section_start_ + section_length;
|
||||
@ -120,7 +128,7 @@ class WasmSectionIterator {
|
||||
}
|
||||
|
||||
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");
|
||||
const byte* section_name_start = decoder_.pc();
|
||||
decoder_.consume_bytes(string_length, "section name");
|
||||
@ -129,6 +137,7 @@ class WasmSectionIterator {
|
||||
section_code_ = kUnknownSectionCode;
|
||||
return;
|
||||
}
|
||||
payload_start_ = decoder_.pc();
|
||||
|
||||
TRACE(" +%d section name : \"%.*s\"\n",
|
||||
static_cast<int>(section_name_start - decoder_.start()),
|
||||
@ -590,10 +599,7 @@ class ModuleDecoder : public Decoder {
|
||||
|
||||
uint32_t local_names_count = consume_u32v("local names count");
|
||||
for (uint32_t j = 0; ok() && j < local_names_count; j++) {
|
||||
uint32_t unused = 0;
|
||||
uint32_t offset = consume_string(&unused, false);
|
||||
USE(unused);
|
||||
USE(offset);
|
||||
skip_string();
|
||||
}
|
||||
}
|
||||
section_iter.advance();
|
||||
@ -799,6 +805,12 @@ class ModuleDecoder : public Decoder {
|
||||
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) {
|
||||
const byte* pos = pc_;
|
||||
uint32_t sig_index = consume_u32v("signature index");
|
||||
@ -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,
|
||||
WasmSectionCode code) {
|
||||
Decoder decoder(module_start, module_end);
|
||||
@ -1042,10 +1056,10 @@ Vector<const byte> FindSection(const byte* module_start, const byte* module_end,
|
||||
WasmSectionIterator section_iter(decoder);
|
||||
while (section_iter.more()) {
|
||||
if (section_iter.section_code() == code) {
|
||||
return Vector<const uint8_t>(section_iter.section_start(),
|
||||
section_iter.section_length());
|
||||
return Vector<const uint8_t>(section_iter.payload_start(),
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1118,16 +1132,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
|
||||
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");
|
||||
// Reserve space for the entries, taking care of invalid input.
|
||||
if (functions_count < static_cast<unsigned>(code_section.length()) / 2) {
|
||||
table.reserve(num_imported_functions + functions_count);
|
||||
}
|
||||
|
||||
uint32_t functions_count = decoder.consume_u32v("functions count");
|
||||
// Take care of invalid input here.
|
||||
if (functions_count < static_cast<unsigned>(code_section.length()) / 2)
|
||||
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);
|
||||
DCHECK_LE(0, section_offset);
|
||||
for (uint32_t i = 0; i < functions_count && decoder.ok(); ++i) {
|
||||
@ -1142,6 +1155,51 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
@ -17,6 +17,8 @@ typedef Result<const WasmModule*> ModuleResult;
|
||||
typedef Result<WasmFunction*> FunctionResult;
|
||||
typedef std::vector<std::pair<int, int>> FunctionOffsets;
|
||||
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}.
|
||||
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
|
||||
@ -45,6 +47,15 @@ FunctionOffsetsResult DecodeWasmFunctionOffsets(
|
||||
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
@ -21,6 +21,7 @@ enum {
|
||||
kWasmDebugInfoWasmBytesHash,
|
||||
kWasmDebugInfoFunctionByteOffsets,
|
||||
kWasmDebugInfoFunctionScripts,
|
||||
kWasmDebugInfoAsmJsOffsets,
|
||||
kWasmDebugInfoNumEntries
|
||||
};
|
||||
|
||||
@ -80,6 +81,57 @@ Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info,
|
||||
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
|
||||
|
||||
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
|
||||
@ -236,3 +288,29 @@ Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(
|
||||
|
||||
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 {
|
||||
// column.
|
||||
static Handle<FixedArray> GetFunctionOffsetTable(
|
||||
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
|
||||
|
@ -134,16 +134,16 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
i::MaybeHandle<i::JSObject> InstantiateModule(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args, const byte* start,
|
||||
const byte* end, ErrorThrower* thrower,
|
||||
internal::wasm::ModuleOrigin origin = i::wasm::kWasmOrigin) {
|
||||
const byte* end, ErrorThrower* thrower) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
|
||||
|
||||
// Decode but avoid a redundant pass over function bodies for verification.
|
||||
// Verification will happen during compilation.
|
||||
i::Zone zone(isolate->allocator());
|
||||
i::MaybeHandle<i::JSObject> module_object =
|
||||
i::wasm::CreateModuleObjectFromBytes(isolate, start, end, thrower,
|
||||
origin);
|
||||
i::wasm::CreateModuleObjectFromBytes(
|
||||
isolate, start, end, thrower, i::wasm::kWasmOrigin,
|
||||
i::Handle<i::Script>::null(), nullptr, nullptr);
|
||||
i::MaybeHandle<i::JSObject> object;
|
||||
if (!module_object.is_null()) {
|
||||
// Success. Instantiate the module and return the object.
|
||||
@ -195,8 +195,8 @@ static i::MaybeHandle<i::JSObject> CreateModuleObject(
|
||||
|
||||
DCHECK(source->IsArrayBuffer() || source->IsTypedArray());
|
||||
return i::wasm::CreateModuleObjectFromBytes(
|
||||
i_isolate, buffer.start, buffer.end, thrower,
|
||||
i::wasm::ModuleOrigin::kWasmOrigin);
|
||||
i_isolate, buffer.start, buffer.end, thrower, i::wasm::kWasmOrigin,
|
||||
i::Handle<i::Script>::null(), nullptr, nullptr);
|
||||
}
|
||||
|
||||
static bool ValidateModule(v8::Isolate* isolate,
|
||||
|
@ -59,7 +59,8 @@ WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
|
||||
i64_temps_(builder->zone()),
|
||||
f32_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) {
|
||||
byte buffer[8];
|
||||
@ -153,6 +154,20 @@ void WasmFunctionBuilder::SetName(Vector<const char> name) {
|
||||
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 {
|
||||
buffer.write_u32v(signature_index_);
|
||||
}
|
||||
@ -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)
|
||||
: zone_(zone),
|
||||
signatures_(zone),
|
||||
@ -494,6 +521,15 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
@ -88,10 +88,10 @@ class ZoneBuffer : public ZoneObject {
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset() { return static_cast<size_t>(pos_ - buffer_); }
|
||||
size_t size() { return static_cast<size_t>(pos_ - buffer_); }
|
||||
const byte* begin() { return buffer_; }
|
||||
const byte* end() { return pos_; }
|
||||
size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
|
||||
size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
|
||||
const byte* begin() const { return buffer_; }
|
||||
const byte* end() const { return pos_; }
|
||||
|
||||
void EnsureSpace(size_t size) {
|
||||
if ((pos_ + size) > end_) {
|
||||
@ -135,10 +135,12 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
|
||||
void Export();
|
||||
void ExportAs(Vector<const char> name);
|
||||
void SetName(Vector<const char> name);
|
||||
void AddAsmWasmOffset(int asm_position);
|
||||
|
||||
void WriteSignature(ZoneBuffer& buffer) const;
|
||||
void WriteExport(ZoneBuffer& buffer) const;
|
||||
void WriteBody(ZoneBuffer& buffer) const;
|
||||
void WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const;
|
||||
|
||||
bool exported() { return exported_; }
|
||||
uint32_t func_index() { return func_index_; }
|
||||
@ -167,6 +169,11 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
|
||||
ZoneVector<uint32_t> f32_temps_;
|
||||
ZoneVector<uint32_t> f64_temps_;
|
||||
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 {
|
||||
@ -228,6 +235,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
|
||||
|
||||
// Writing methods.
|
||||
void WriteTo(ZoneBuffer& buffer) const;
|
||||
void WriteAsmJsOffsetTable(ZoneBuffer& buffer) const;
|
||||
|
||||
// TODO(titzer): use SignatureMap from signature-map.h here.
|
||||
// This signature map is zone-allocated, but the other is heap allocated.
|
||||
|
@ -660,8 +660,7 @@ static void ResetCompiledModule(Isolate* isolate, JSObject* owner,
|
||||
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
|
||||
JSObject* owner = *p;
|
||||
WasmCompiledModule* compiled_module =
|
||||
WasmCompiledModule::cast(owner->GetInternalField(kWasmCompiledModule));
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(owner);
|
||||
TRACE("Finalizing %d {\n", compiled_module->instance_id());
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
DCHECK(compiled_module->has_weak_module_object());
|
||||
@ -861,7 +860,7 @@ Object* GetOwningWasmInstance(Code* code) {
|
||||
int GetNumImportedFunctions(Handle<JSObject> wasm_object) {
|
||||
// TODO(wasm): Cache this number if it ever becomes a performance problem.
|
||||
DCHECK(IsWasmObject(*wasm_object));
|
||||
Object* compiled_module = wasm_object->GetInternalField(kWasmCompiledModule);
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm_object);
|
||||
Handle<FixedArray> imports =
|
||||
WasmCompiledModule::cast(compiled_module)->imports();
|
||||
int num_imports = imports->length();
|
||||
@ -1425,8 +1424,7 @@ class WasmInstanceBuilder {
|
||||
// we want all the publishing to happen free from GC interruptions, and
|
||||
// so we do it in
|
||||
// one GC-free scope afterwards.
|
||||
original = handle(WasmCompiledModule::cast(
|
||||
owner.ToHandleChecked()->GetInternalField(kWasmCompiledModule)));
|
||||
original = handle(GetCompiledModule(*owner.ToHandleChecked()));
|
||||
link_to_original = factory->NewWeakCell(original.ToHandleChecked());
|
||||
}
|
||||
// Publish the new instance to the instances chain.
|
||||
@ -1958,8 +1956,8 @@ Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
|
||||
uint32_t func_index) {
|
||||
if (!wasm->IsUndefined(isolate)) {
|
||||
DCHECK(IsWasmObject(*wasm));
|
||||
WasmCompiledModule* compiled_module = WasmCompiledModule::cast(
|
||||
Handle<JSObject>::cast(wasm)->GetInternalField(kWasmCompiledModule));
|
||||
WasmCompiledModule* compiled_module =
|
||||
GetCompiledModule(JSObject::cast(*wasm));
|
||||
Handle<ByteArray> func_names = compiled_module->function_names();
|
||||
// TODO(clemens): Extract this from the module bytes; skip whole function
|
||||
// name table.
|
||||
@ -2002,10 +2000,33 @@ bool IsWasmObject(Object* object) {
|
||||
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) {
|
||||
DCHECK(IsWasmObject(*wasm));
|
||||
Object* compiled_module = wasm->GetInternalField(kWasmCompiledModule);
|
||||
return WasmCompiledModule::cast(compiled_module)->module_bytes();
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
|
||||
return compiled_module->module_bytes();
|
||||
}
|
||||
|
||||
Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm) {
|
||||
@ -2088,8 +2109,7 @@ void PopulateFunctionTable(Handle<FixedArray> table, uint32_t table_size,
|
||||
|
||||
int GetNumberOfFunctions(Handle<JSObject> wasm) {
|
||||
DCHECK(IsWasmObject(*wasm));
|
||||
WasmCompiledModule* compiled_module =
|
||||
WasmCompiledModule::cast(wasm->GetInternalField(kWasmCompiledModule));
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(*wasm);
|
||||
ByteArray* func_names_arr = compiled_module->ptr_to_function_names();
|
||||
// TODO(clemensh): this looks inside an array constructed elsewhere. Refactor.
|
||||
return func_names_arr->get_int(0);
|
||||
@ -2119,11 +2139,12 @@ Handle<JSObject> CreateCompiledModuleObject(
|
||||
return module_obj;
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
|
||||
const byte* start,
|
||||
const byte* end,
|
||||
ErrorThrower* thrower,
|
||||
ModuleOrigin origin) {
|
||||
// TODO(clemensh): origin can be inferred from asm_js_script; remove it.
|
||||
MaybeHandle<JSObject> CreateModuleObjectFromBytes(
|
||||
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
|
||||
ModuleOrigin origin, Handle<Script> asm_js_script,
|
||||
const byte* asm_js_offset_tables_start,
|
||||
const byte* asm_js_offset_tables_end) {
|
||||
MaybeHandle<JSObject> nothing;
|
||||
Zone zone(isolate->allocator());
|
||||
ModuleResult result =
|
||||
@ -2133,12 +2154,28 @@ MaybeHandle<JSObject> CreateModuleObjectFromBytes(Isolate* isolate,
|
||||
thrower->Failed("Wasm decoding failed", result);
|
||||
return nothing;
|
||||
}
|
||||
MaybeHandle<WasmCompiledModule> compiled_module =
|
||||
MaybeHandle<WasmCompiledModule> maybe_compiled_module =
|
||||
decoded_module->CompileFunctions(isolate, thrower);
|
||||
if (compiled_module.is_null()) return nothing;
|
||||
if (maybe_compiled_module.is_null()) return nothing;
|
||||
Handle<WasmCompiledModule> compiled_module =
|
||||
maybe_compiled_module.ToHandleChecked();
|
||||
|
||||
return CreateCompiledModuleObject(isolate, 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,
|
||||
@ -2166,9 +2203,8 @@ void SetInstanceMemory(Handle<JSObject> instance, JSArrayBuffer* buffer) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK(IsWasmObject(*instance));
|
||||
instance->SetInternalField(kWasmMemArrayBuffer, buffer);
|
||||
WasmCompiledModule* module =
|
||||
WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule));
|
||||
module->set_ptr_to_heap(buffer);
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
|
||||
compiled_module->set_ptr_to_heap(buffer);
|
||||
}
|
||||
|
||||
int32_t GetInstanceMemorySize(Isolate* isolate, Handle<JSObject> instance) {
|
||||
@ -2275,8 +2311,7 @@ void ValidateModuleState(Isolate* isolate, Handle<JSObject> module_obj) {
|
||||
void ValidateOrphanedInstance(Isolate* isolate, Handle<JSObject> instance) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
CHECK(IsWasmObject(*instance));
|
||||
WasmCompiledModule* compiled_module =
|
||||
WasmCompiledModule::cast(instance->GetInternalField(kWasmCompiledModule));
|
||||
WasmCompiledModule* compiled_module = GetCompiledModule(*instance);
|
||||
CHECK(compiled_module->has_weak_module_object());
|
||||
CHECK(compiled_module->ptr_to_weak_module_object()->cleared());
|
||||
}
|
||||
|
@ -394,6 +394,8 @@ class WasmCompiledModule : public FixedArray {
|
||||
MACRO(OBJECT, FixedArray, indirect_function_tables) \
|
||||
MACRO(OBJECT, SeqOneByteString, module_bytes) \
|
||||
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(OBJECT, FixedArray, data_segments_info) \
|
||||
MACRO(OBJECT, ByteArray, data_segments) \
|
||||
@ -499,6 +501,20 @@ Handle<JSFunction> WrapExportCodeAsJSFunction(Isolate* isolate,
|
||||
// else.
|
||||
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
|
||||
bool UpdateWasmModuleMemory(Handle<JSObject> object, Address old_start,
|
||||
Address new_start, uint32_t old_size,
|
||||
@ -520,7 +536,8 @@ Handle<JSObject> CreateCompiledModuleObject(
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<JSObject> CreateModuleObjectFromBytes(
|
||||
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,
|
||||
const byte* end,
|
||||
|
@ -62,7 +62,7 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
|
||||
// again through the normal pipeline.
|
||||
MaybeHandle<JSObject> module_object = CreateModuleObjectFromBytes(
|
||||
isolate, module->module_start, module->module_end, thrower,
|
||||
ModuleOrigin::kWasmOrigin);
|
||||
ModuleOrigin::kWasmOrigin, Handle<Script>::null(), nullptr, nullptr);
|
||||
if (module_object.is_null()) {
|
||||
thrower->Error("Module pre-validation failed.");
|
||||
return Handle<JSObject>::null();
|
||||
|
@ -117,6 +117,9 @@ var assertUnoptimized;
|
||||
// Assert that a string contains another expected substring.
|
||||
var assertContains;
|
||||
|
||||
// Assert that a string matches a given regex.
|
||||
var assertMatches;
|
||||
|
||||
|
||||
(function () { // Scope for utility functions.
|
||||
|
||||
@ -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 OptimizationStatus = function(fun, sync_opt) {
|
||||
|
98
test/mjsunit/wasm/asm-wasm-stack.js
Normal file
98
test/mjsunit/wasm/asm-wasm-stack.js
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 {
|
||||
#define EMPTY_SIGNATURES_SECTION SECTION(Type, 1), 0
|
||||
#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0
|
||||
#define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0
|
||||
#define SECTION_NAMES(size) \
|
||||
kUnknownSectionCode, U32V_1(size + 5), 4, 'n', 'a', 'm', 'e'
|
||||
#define SECTION_NAMES(size) SECTION(Unknown, size + 5), 4, 'n', 'a', 'm', 'e'
|
||||
#define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0
|
||||
|
||||
#define X1(...) __VA_ARGS__
|
||||
@ -1288,6 +1287,15 @@ TEST_F(WasmModuleVerifyTest, InitExpr_global) {
|
||||
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 internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user