[wasm] Make wasm info available on the stack trace
This changes different locations to extract the reference to the wasm object and the function index from the stack trace, and make it available through all the APIs which process stack traces. The javascript CallSite object now has the new methods isWasm(), getWasmObject() and getWasmFunctionIndex(); the byte offset is available via getPosition(). Function names of wasm frames should be fully functional with this commit, position information works reliably for calls, but not for traps like unreachable or out-of-bounds accesses. R=titzer@chromium.org, yangguo@chromium.org Review-Url: https://codereview.chromium.org/1909353002 Cr-Commit-Position: refs/heads/master@{#36067}
This commit is contained in:
parent
3b7ff999f3
commit
a4cd1eef0a
@ -3014,13 +3014,8 @@ class WasmCompilationUnit {
|
||||
isolate_->factory()->NewFixedArray(2, TENURED);
|
||||
if (!module_env_->instance->js_object.is_null()) {
|
||||
deopt_data->set(0, *module_env_->instance->js_object);
|
||||
deopt_data->set(1, Smi::FromInt(function_->func_index));
|
||||
} else if (info_.GetDebugName().get() != nullptr) {
|
||||
MaybeHandle<String> maybe_name = isolate_->factory()->NewStringFromUtf8(
|
||||
CStrVector(info_.GetDebugName().get()));
|
||||
if (!maybe_name.is_null())
|
||||
deopt_data->set(0, *maybe_name.ToHandleChecked());
|
||||
}
|
||||
deopt_data->set(1, Smi::FromInt(function_->func_index));
|
||||
deopt_data->set_length(2);
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/safepoint-table.h"
|
||||
#include "src/string-stream.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -617,21 +618,6 @@ void ExitFrame::FillState(Address fp, Address sp, State* state) {
|
||||
state->constant_pool_address = NULL;
|
||||
}
|
||||
|
||||
void StandardFrame::Summarize(List<FrameSummary>* functions,
|
||||
FrameSummary::Mode mode) const {
|
||||
DCHECK(functions->length() == 0);
|
||||
// default implementation: no summary added
|
||||
}
|
||||
|
||||
JSFunction* StandardFrame::function() const {
|
||||
// this default implementation is overridden by JS and WASM frames
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object* StandardFrame::receiver() const {
|
||||
return isolate()->heap()->undefined_value();
|
||||
}
|
||||
|
||||
Address StandardFrame::GetExpressionAddress(int n) const {
|
||||
const int offset = StandardFrameConstants::kExpressionsOffset;
|
||||
return fp() + offset - n * kPointerSize;
|
||||
@ -1343,32 +1329,38 @@ Code* WasmFrame::unchecked_code() const {
|
||||
return static_cast<Code*>(isolate()->FindCodeObject(pc()));
|
||||
}
|
||||
|
||||
JSFunction* WasmFrame::function() const {
|
||||
// TODO(clemensh): generate the right JSFunctions once per wasm function and
|
||||
// cache them
|
||||
Factory* factory = isolate()->factory();
|
||||
Handle<JSFunction> fun =
|
||||
factory->NewFunction(factory->NewStringFromAsciiChecked("<WASM>"));
|
||||
return *fun;
|
||||
}
|
||||
|
||||
void WasmFrame::Summarize(List<FrameSummary>* functions,
|
||||
FrameSummary::Mode mode) const {
|
||||
DCHECK(functions->length() == 0);
|
||||
Code* code = LookupCode();
|
||||
int offset = static_cast<int>(pc() - code->instruction_start());
|
||||
AbstractCode* abstract_code = AbstractCode::cast(code);
|
||||
Handle<JSFunction> fun(function(), isolate());
|
||||
FrameSummary summary(receiver(), *fun, abstract_code, offset, false);
|
||||
functions->Add(summary);
|
||||
}
|
||||
|
||||
void WasmFrame::Iterate(ObjectVisitor* v) const { IterateCompiledFrame(v); }
|
||||
|
||||
Address WasmFrame::GetCallerStackPointer() const {
|
||||
return fp() + ExitFrameConstants::kCallerSPOffset;
|
||||
}
|
||||
|
||||
Object* WasmFrame::wasm_obj() {
|
||||
FixedArray* deopt_data = LookupCode()->deoptimization_data();
|
||||
DCHECK(deopt_data->length() == 2);
|
||||
return deopt_data->get(0);
|
||||
}
|
||||
|
||||
uint32_t WasmFrame::function_index() {
|
||||
FixedArray* deopt_data = LookupCode()->deoptimization_data();
|
||||
DCHECK(deopt_data->length() == 2);
|
||||
Object* func_index_obj = deopt_data->get(1);
|
||||
if (func_index_obj->IsUndefined()) return static_cast<uint32_t>(-1);
|
||||
if (func_index_obj->IsSmi()) return Smi::cast(func_index_obj)->value();
|
||||
DCHECK(func_index_obj->IsHeapNumber());
|
||||
uint32_t val = static_cast<uint32_t>(-1);
|
||||
func_index_obj->ToUint32(&val);
|
||||
DCHECK(val != static_cast<uint32_t>(-1));
|
||||
return val;
|
||||
}
|
||||
|
||||
Object* WasmFrame::function_name() {
|
||||
Object* wasm_object = wasm_obj();
|
||||
if (wasm_object->IsUndefined()) return wasm_object;
|
||||
Handle<JSObject> wasm = handle(JSObject::cast(wasm_object));
|
||||
return *wasm::GetWasmFunctionName(wasm, function_index());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
38
src/frames.h
38
src/frames.h
@ -635,8 +635,8 @@ class JavaScriptFrame;
|
||||
|
||||
class FrameSummary BASE_EMBEDDED {
|
||||
public:
|
||||
// Mode for StandardFrame::Summarize. Exact summary is required to produce an
|
||||
// exact stack trace. It will trigger an assertion failure if that is not
|
||||
// Mode for JavaScriptFrame::Summarize. Exact summary is required to produce
|
||||
// an exact stack trace. It will trigger an assertion failure if that is not
|
||||
// possible, e.g., because of missing deoptimization information. The
|
||||
// approximate mode should produce a summary even without deoptimization
|
||||
// information, but it might miss frames.
|
||||
@ -684,15 +684,6 @@ class StandardFrame : public StackFrame {
|
||||
return static_cast<StandardFrame*>(frame);
|
||||
}
|
||||
|
||||
// Build a list with summaries for this frame including all inlined frames.
|
||||
virtual void Summarize(
|
||||
List<FrameSummary>* frames,
|
||||
FrameSummary::Mode mode = FrameSummary::kExactSummary) const;
|
||||
|
||||
// Accessors.
|
||||
virtual JSFunction* function() const;
|
||||
virtual Object* receiver() const;
|
||||
|
||||
protected:
|
||||
inline explicit StandardFrame(StackFrameIteratorBase* iterator);
|
||||
|
||||
@ -737,8 +728,14 @@ class JavaScriptFrame : public StandardFrame {
|
||||
public:
|
||||
Type type() const override { return JAVA_SCRIPT; }
|
||||
|
||||
JSFunction* function() const override;
|
||||
Object* receiver() const override;
|
||||
// Build a list with summaries for this frame including all inlined frames.
|
||||
virtual void Summarize(
|
||||
List<FrameSummary>* frames,
|
||||
FrameSummary::Mode mode = FrameSummary::kExactSummary) const;
|
||||
|
||||
// Accessors.
|
||||
virtual JSFunction* function() const;
|
||||
virtual Object* receiver() const;
|
||||
|
||||
inline void set_receiver(Object* value);
|
||||
|
||||
@ -786,10 +783,6 @@ class JavaScriptFrame : public StandardFrame {
|
||||
// Return a list with JSFunctions of this frame.
|
||||
virtual void GetFunctions(List<JSFunction*>* functions) const;
|
||||
|
||||
void Summarize(
|
||||
List<FrameSummary>* frames,
|
||||
FrameSummary::Mode mode = FrameSummary::kExactSummary) const override;
|
||||
|
||||
// Lookup exception handler for current {pc}, returns -1 if none found. Also
|
||||
// returns data associated with the handler site specific to the frame type:
|
||||
// - JavaScriptFrame : Data is the stack depth at entry of the try-block.
|
||||
@ -975,17 +968,16 @@ class WasmFrame : public StandardFrame {
|
||||
// Determine the code for the frame.
|
||||
Code* unchecked_code() const override;
|
||||
|
||||
Object* wasm_obj();
|
||||
uint32_t function_index();
|
||||
|
||||
Object* function_name();
|
||||
|
||||
static WasmFrame* cast(StackFrame* frame) {
|
||||
DCHECK(frame->is_wasm());
|
||||
return static_cast<WasmFrame*>(frame);
|
||||
}
|
||||
|
||||
JSFunction* function() const override;
|
||||
|
||||
void Summarize(
|
||||
List<FrameSummary>* frames,
|
||||
FrameSummary::Mode mode = FrameSummary::kExactSummary) const override;
|
||||
|
||||
protected:
|
||||
inline explicit WasmFrame(StackFrameIteratorBase* iterator);
|
||||
|
||||
|
@ -139,6 +139,8 @@
|
||||
V(call_site_position_symbol) \
|
||||
V(call_site_receiver_symbol) \
|
||||
V(call_site_strict_symbol) \
|
||||
V(call_site_wasm_obj_symbol) \
|
||||
V(call_site_wasm_func_index_symbol) \
|
||||
V(class_end_position_symbol) \
|
||||
V(class_start_position_symbol) \
|
||||
V(detailed_stack_trace_symbol) \
|
||||
|
231
src/isolate.cc
231
src/isolate.cc
@ -433,14 +433,13 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
|
||||
Code* code = wasm_frame->unchecked_code();
|
||||
Handle<AbstractCode> abstract_code =
|
||||
Handle<AbstractCode>(AbstractCode::cast(code));
|
||||
Handle<JSFunction> fun = factory()->NewFunction(
|
||||
factory()->NewStringFromAsciiChecked("<WASM>"));
|
||||
int offset =
|
||||
static_cast<int>(wasm_frame->pc() - code->instruction_start());
|
||||
elements = MaybeGrow(this, elements, cursor, cursor + 4);
|
||||
// TODO(jfb) Pass module object.
|
||||
elements->set(cursor++, *factory()->undefined_value());
|
||||
elements->set(cursor++, *fun);
|
||||
elements->set(cursor++, wasm_frame->wasm_obj());
|
||||
elements->set(cursor++, Smi::FromInt(wasm_frame->function_index()));
|
||||
elements->set(cursor++, *abstract_code);
|
||||
elements->set(cursor++, Internals::IntToSmi(0));
|
||||
elements->set(cursor++, Smi::FromInt(offset));
|
||||
frames_seen++;
|
||||
} break;
|
||||
|
||||
@ -542,65 +541,64 @@ class CaptureStackTraceHelper {
|
||||
}
|
||||
}
|
||||
|
||||
Handle<JSObject> NewStackFrameObject(FrameSummary& summ) {
|
||||
int position = summ.abstract_code()->SourcePosition(summ.code_offset());
|
||||
return NewStackFrameObject(summ.function(), position,
|
||||
summ.is_constructor());
|
||||
}
|
||||
|
||||
Handle<JSObject> NewStackFrameObject(Handle<JSFunction> fun, int position,
|
||||
bool is_constructor) {
|
||||
Handle<JSObject> stack_frame =
|
||||
factory()->NewJSObject(isolate_->object_function());
|
||||
Handle<Script> script(Script::cast(fun->shared()->script()));
|
||||
|
||||
// TODO(clemensh): this can be changed to a DCHECK once also WASM frames
|
||||
// define a script
|
||||
if (!fun->shared()->script()->IsUndefined()) {
|
||||
Handle<Script> script(Script::cast(fun->shared()->script()));
|
||||
|
||||
if (!line_key_.is_null()) {
|
||||
int script_line_offset = script->line_offset();
|
||||
int line_number = Script::GetLineNumber(script, position);
|
||||
// line_number is already shifted by the script_line_offset.
|
||||
int relative_line_number = line_number - script_line_offset;
|
||||
if (!column_key_.is_null() && relative_line_number >= 0) {
|
||||
Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
|
||||
int start = (relative_line_number == 0)
|
||||
? 0
|
||||
: Smi::cast(line_ends->get(relative_line_number - 1))
|
||||
->value() +
|
||||
1;
|
||||
int column_offset = position - start;
|
||||
if (relative_line_number == 0) {
|
||||
// For the case where the code is on the same line as the script
|
||||
// tag.
|
||||
column_offset += script->column_offset();
|
||||
}
|
||||
JSObject::AddProperty(
|
||||
stack_frame, column_key_,
|
||||
handle(Smi::FromInt(column_offset + 1), isolate_), NONE);
|
||||
if (!line_key_.is_null()) {
|
||||
int script_line_offset = script->line_offset();
|
||||
int line_number = Script::GetLineNumber(script, position);
|
||||
// line_number is already shifted by the script_line_offset.
|
||||
int relative_line_number = line_number - script_line_offset;
|
||||
if (!column_key_.is_null() && relative_line_number >= 0) {
|
||||
Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
|
||||
int start =
|
||||
(relative_line_number == 0)
|
||||
? 0
|
||||
: Smi::cast(line_ends->get(relative_line_number - 1))->value() +
|
||||
1;
|
||||
int column_offset = position - start;
|
||||
if (relative_line_number == 0) {
|
||||
// For the case where the code is on the same line as the script tag.
|
||||
column_offset += script->column_offset();
|
||||
}
|
||||
JSObject::AddProperty(stack_frame, line_key_,
|
||||
handle(Smi::FromInt(line_number + 1), isolate_),
|
||||
JSObject::AddProperty(stack_frame, column_key_,
|
||||
handle(Smi::FromInt(column_offset + 1), isolate_),
|
||||
NONE);
|
||||
}
|
||||
JSObject::AddProperty(stack_frame, line_key_,
|
||||
handle(Smi::FromInt(line_number + 1), isolate_),
|
||||
NONE);
|
||||
}
|
||||
|
||||
if (!script_id_key_.is_null()) {
|
||||
JSObject::AddProperty(stack_frame, script_id_key_,
|
||||
handle(Smi::FromInt(script->id()), isolate_),
|
||||
NONE);
|
||||
}
|
||||
if (!script_id_key_.is_null()) {
|
||||
JSObject::AddProperty(stack_frame, script_id_key_,
|
||||
handle(Smi::FromInt(script->id()), isolate_), NONE);
|
||||
}
|
||||
|
||||
if (!script_name_key_.is_null()) {
|
||||
JSObject::AddProperty(stack_frame, script_name_key_,
|
||||
handle(script->name(), isolate_), NONE);
|
||||
}
|
||||
if (!script_name_key_.is_null()) {
|
||||
JSObject::AddProperty(stack_frame, script_name_key_,
|
||||
handle(script->name(), isolate_), NONE);
|
||||
}
|
||||
|
||||
if (!script_name_or_source_url_key_.is_null()) {
|
||||
Handle<Object> result = Script::GetNameOrSourceURL(script);
|
||||
JSObject::AddProperty(stack_frame, script_name_or_source_url_key_,
|
||||
result, NONE);
|
||||
}
|
||||
if (!script_name_or_source_url_key_.is_null()) {
|
||||
Handle<Object> result = Script::GetNameOrSourceURL(script);
|
||||
JSObject::AddProperty(stack_frame, script_name_or_source_url_key_, result,
|
||||
NONE);
|
||||
}
|
||||
|
||||
if (!eval_key_.is_null()) {
|
||||
Handle<Object> is_eval = factory()->ToBoolean(
|
||||
script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
|
||||
JSObject::AddProperty(stack_frame, eval_key_, is_eval, NONE);
|
||||
}
|
||||
if (!eval_key_.is_null()) {
|
||||
Handle<Object> is_eval = factory()->ToBoolean(
|
||||
script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
|
||||
JSObject::AddProperty(stack_frame, eval_key_, is_eval, NONE);
|
||||
}
|
||||
|
||||
if (!function_key_.is_null()) {
|
||||
@ -613,6 +611,35 @@ class CaptureStackTraceHelper {
|
||||
JSObject::AddProperty(stack_frame, constructor_key_, is_constructor_obj,
|
||||
NONE);
|
||||
}
|
||||
return stack_frame;
|
||||
}
|
||||
|
||||
Handle<JSObject> NewStackFrameObject(WasmFrame* frame) {
|
||||
Handle<JSObject> stack_frame =
|
||||
factory()->NewJSObject(isolate_->object_function());
|
||||
|
||||
if (!function_key_.is_null()) {
|
||||
Handle<Object> fun_name = handle(frame->function_name(), isolate_);
|
||||
if (fun_name->IsUndefined())
|
||||
fun_name = isolate_->factory()->InternalizeUtf8String(
|
||||
Vector<const char>("<WASM>"));
|
||||
JSObject::AddProperty(stack_frame, function_key_, fun_name, NONE);
|
||||
}
|
||||
// Encode the function index as line number.
|
||||
if (!line_key_.is_null()) {
|
||||
JSObject::AddProperty(
|
||||
stack_frame, line_key_,
|
||||
isolate_->factory()->NewNumberFromInt(frame->function_index()), NONE);
|
||||
}
|
||||
// Encode the byte offset as column.
|
||||
if (!column_key_.is_null()) {
|
||||
Code* code = frame->LookupCode();
|
||||
int offset = static_cast<int>(frame->pc() - code->instruction_start());
|
||||
int position = code->SourcePosition(offset);
|
||||
JSObject::AddProperty(stack_frame, column_key_,
|
||||
isolate_->factory()->NewNumberFromInt(position),
|
||||
NONE);
|
||||
}
|
||||
|
||||
return stack_frame;
|
||||
}
|
||||
@ -691,29 +718,34 @@ Handle<JSArray> Isolate::CaptureCurrentStackTrace(
|
||||
// Ensure no negative values.
|
||||
int limit = Max(frame_limit, 0);
|
||||
Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit);
|
||||
Handle<FixedArray> stack_trace_elems(
|
||||
FixedArray::cast(stack_trace->elements()), this);
|
||||
|
||||
StackTraceFrameIterator it(this);
|
||||
int frames_seen = 0;
|
||||
while (!it.done() && (frames_seen < limit)) {
|
||||
for (StackTraceFrameIterator it(this); !it.done() && (frames_seen < limit);
|
||||
it.Advance()) {
|
||||
StandardFrame* frame = it.frame();
|
||||
// Set initial size to the maximum inlining level + 1 for the outermost
|
||||
// function.
|
||||
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
|
||||
frame->Summarize(&frames);
|
||||
for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
|
||||
Handle<JSFunction> fun = frames[i].function();
|
||||
// Filter frames from other security contexts.
|
||||
if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) &&
|
||||
!this->context()->HasSameSecurityTokenAs(fun->context())) continue;
|
||||
int position =
|
||||
frames[i].abstract_code()->SourcePosition(frames[i].code_offset());
|
||||
Handle<JSObject> stack_frame =
|
||||
helper.NewStackFrameObject(fun, position, frames[i].is_constructor());
|
||||
|
||||
FixedArray::cast(stack_trace->elements())->set(frames_seen, *stack_frame);
|
||||
if (frame->is_java_script()) {
|
||||
// Set initial size to the maximum inlining level + 1 for the outermost
|
||||
// function.
|
||||
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
|
||||
JavaScriptFrame::cast(frame)->Summarize(&frames);
|
||||
for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
|
||||
Handle<JSFunction> fun = frames[i].function();
|
||||
// Filter frames from other security contexts.
|
||||
if (!(options & StackTrace::kExposeFramesAcrossSecurityOrigins) &&
|
||||
!this->context()->HasSameSecurityTokenAs(fun->context()))
|
||||
continue;
|
||||
Handle<JSObject> new_frame_obj = helper.NewStackFrameObject(frames[i]);
|
||||
stack_trace_elems->set(frames_seen, *new_frame_obj);
|
||||
frames_seen++;
|
||||
}
|
||||
} else {
|
||||
WasmFrame* wasm_frame = WasmFrame::cast(frame);
|
||||
Handle<JSObject> new_frame_obj = helper.NewStackFrameObject(wasm_frame);
|
||||
stack_trace_elems->set(frames_seen, *new_frame_obj);
|
||||
frames_seen++;
|
||||
}
|
||||
it.Advance();
|
||||
}
|
||||
|
||||
stack_trace->set_length(Smi::FromInt(frames_seen));
|
||||
@ -1332,15 +1364,20 @@ void Isolate::PrintCurrentStackTrace(FILE* out) {
|
||||
InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
|
||||
pos = iframe->GetBytecodeArray()->SourcePosition(
|
||||
iframe->GetBytecodeOffset());
|
||||
} else {
|
||||
} else if (frame->is_java_script()) {
|
||||
Code* code = frame->LookupCode();
|
||||
int offset = static_cast<int>(frame->pc() - code->instruction_start());
|
||||
pos = frame->LookupCode()->SourcePosition(offset);
|
||||
} else {
|
||||
DCHECK(frame->is_wasm());
|
||||
// TODO(clemensh): include wasm frames here
|
||||
continue;
|
||||
}
|
||||
JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
|
||||
Handle<Object> pos_obj(Smi::FromInt(pos), this);
|
||||
// Fetch function and receiver.
|
||||
Handle<JSFunction> fun(frame->function());
|
||||
Handle<Object> recv(frame->receiver(), this);
|
||||
Handle<JSFunction> fun(js_frame->function());
|
||||
Handle<Object> recv(js_frame->receiver(), this);
|
||||
// Advance to the next JavaScript frame and determine if the
|
||||
// current frame is the top-level frame.
|
||||
it.Advance();
|
||||
@ -1355,31 +1392,29 @@ void Isolate::PrintCurrentStackTrace(FILE* out) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Isolate::ComputeLocation(MessageLocation* target) {
|
||||
StackTraceFrameIterator it(this);
|
||||
if (!it.done()) {
|
||||
StandardFrame* frame = it.frame();
|
||||
JSFunction* fun = frame->function();
|
||||
Object* script = fun->shared()->script();
|
||||
if (script->IsScript() &&
|
||||
!(Script::cast(script)->source()->IsUndefined())) {
|
||||
Handle<Script> casted_script(Script::cast(script));
|
||||
// Compute the location from the function and the relocation info of the
|
||||
// baseline code. For optimized code this will use the deoptimization
|
||||
// information to get canonical location information.
|
||||
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
|
||||
frame->Summarize(&frames);
|
||||
FrameSummary& summary = frames.last();
|
||||
int pos = summary.abstract_code()->SourcePosition(summary.code_offset());
|
||||
*target = MessageLocation(casted_script, pos, pos + 1, handle(fun));
|
||||
return true;
|
||||
}
|
||||
if (it.done()) return false;
|
||||
StandardFrame* frame = it.frame();
|
||||
// TODO(clemensh): handle wasm frames
|
||||
if (!frame->is_java_script()) return false;
|
||||
JSFunction* fun = JavaScriptFrame::cast(frame)->function();
|
||||
Object* script = fun->shared()->script();
|
||||
if (!script->IsScript() || (Script::cast(script)->source()->IsUndefined())) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
Handle<Script> casted_script(Script::cast(script));
|
||||
// Compute the location from the function and the relocation info of the
|
||||
// baseline code. For optimized code this will use the deoptimization
|
||||
// information to get canonical location information.
|
||||
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
|
||||
JavaScriptFrame::cast(frame)->Summarize(&frames);
|
||||
FrameSummary& summary = frames.last();
|
||||
int pos = summary.abstract_code()->SourcePosition(summary.code_offset());
|
||||
*target = MessageLocation(casted_script, pos, pos + 1, handle(fun));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Isolate::ComputeLocationFromException(MessageLocation* target,
|
||||
Handle<Object> exception) {
|
||||
if (!exception->IsJSObject()) return false;
|
||||
@ -1420,8 +1455,12 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
|
||||
int elements_limit = Smi::cast(simple_stack_trace->length())->value();
|
||||
|
||||
for (int i = 1; i < elements_limit; i += 4) {
|
||||
Handle<JSFunction> fun =
|
||||
handle(JSFunction::cast(elements->get(i + 1)), this);
|
||||
Handle<Object> fun_obj = handle(elements->get(i + 1), this);
|
||||
if (fun_obj->IsSmi()) {
|
||||
// TODO(clemensh): handle wasm frames
|
||||
return false;
|
||||
}
|
||||
Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj);
|
||||
if (!fun->shared()->IsSubjectToDebugging()) continue;
|
||||
|
||||
Object* script = fun->shared()->script();
|
||||
|
@ -23,6 +23,10 @@ var callSitePositionSymbol =
|
||||
utils.ImportNow("call_site_position_symbol");
|
||||
var callSiteStrictSymbol =
|
||||
utils.ImportNow("call_site_strict_symbol");
|
||||
var callSiteWasmObjectSymbol =
|
||||
utils.ImportNow("call_site_wasm_obj_symbol");
|
||||
var callSiteWasmFunctionIndexSymbol =
|
||||
utils.ImportNow("call_site_wasm_func_index_symbol");
|
||||
var Float32x4ToString;
|
||||
var formattedStackTraceSymbol =
|
||||
utils.ImportNow("formatted_stack_trace_symbol");
|
||||
@ -553,7 +557,9 @@ function GetStackTraceLine(recv, fun, pos, isGlobal) {
|
||||
// Error implementation
|
||||
|
||||
function CallSite(receiver, fun, pos, strict_mode) {
|
||||
if (!IS_FUNCTION(fun)) {
|
||||
// For wasm frames, receiver is the wasm object and fun is the function index
|
||||
// instead of an actual function.
|
||||
if (!IS_FUNCTION(fun) && !IS_NUMBER(fun)) {
|
||||
throw MakeTypeError(kCallSiteExpectsFunction, typeof fun);
|
||||
}
|
||||
|
||||
@ -561,14 +567,19 @@ function CallSite(receiver, fun, pos, strict_mode) {
|
||||
return new CallSite(receiver, fun, pos, strict_mode);
|
||||
}
|
||||
|
||||
SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
|
||||
SET_PRIVATE(this, callSiteFunctionSymbol, fun);
|
||||
if (IS_FUNCTION(fun)) {
|
||||
SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
|
||||
SET_PRIVATE(this, callSiteFunctionSymbol, fun);
|
||||
} else {
|
||||
SET_PRIVATE(this, callSiteWasmObjectSymbol, receiver);
|
||||
SET_PRIVATE(this, callSiteWasmFunctionIndexSymbol, TO_UINT32(fun));
|
||||
}
|
||||
SET_PRIVATE(this, callSitePositionSymbol, TO_INT32(pos));
|
||||
SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
|
||||
}
|
||||
|
||||
function CheckCallSite(obj, name) {
|
||||
if (!IS_RECEIVER(obj) || !HAS_PRIVATE(obj, callSiteFunctionSymbol)) {
|
||||
if (!IS_RECEIVER(obj) || !HAS_PRIVATE(obj, callSitePositionSymbol)) {
|
||||
throw MakeTypeError(kCallSiteMethod, name);
|
||||
}
|
||||
}
|
||||
@ -619,6 +630,12 @@ function CallSiteGetScriptNameOrSourceURL() {
|
||||
function CallSiteGetFunctionName() {
|
||||
// See if the function knows its own name
|
||||
CheckCallSite(this, "getFunctionName");
|
||||
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
|
||||
var wasm = GET_PRIVATE(this, callSiteWasmObjectSymbol);
|
||||
var func_index = GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
|
||||
if (IS_UNDEFINED(wasm)) return "<WASM>";
|
||||
return %WasmGetFunctionName(wasm, func_index);
|
||||
}
|
||||
return %CallSiteGetFunctionNameRT(this);
|
||||
}
|
||||
|
||||
@ -635,6 +652,9 @@ function CallSiteGetFileName() {
|
||||
}
|
||||
|
||||
function CallSiteGetLineNumber() {
|
||||
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
|
||||
return GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
|
||||
}
|
||||
CheckCallSite(this, "getLineNumber");
|
||||
return %CallSiteGetLineNumberRT(this);
|
||||
}
|
||||
@ -655,6 +675,13 @@ function CallSiteIsConstructor() {
|
||||
}
|
||||
|
||||
function CallSiteToString() {
|
||||
if (HAS_PRIVATE(this, callSiteWasmObjectSymbol)) {
|
||||
var funName = this.getFunctionName();
|
||||
var funcIndex = GET_PRIVATE(this, callSiteWasmFunctionIndexSymbol);
|
||||
var pos = this.getPosition();
|
||||
return funName + " (<WASM>:" + funcIndex + ":" + pos + ")";
|
||||
}
|
||||
|
||||
var fileName;
|
||||
var fileLocation = "";
|
||||
if (this.isNative()) {
|
||||
@ -799,7 +826,7 @@ function GetStackFrames(raw_stack) {
|
||||
var fun = raw_stack[i + 1];
|
||||
var code = raw_stack[i + 2];
|
||||
var pc = raw_stack[i + 3];
|
||||
var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc);
|
||||
var pos = %FunctionGetPositionForOffset(code, pc);
|
||||
sloppy_frames--;
|
||||
frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/keys.h"
|
||||
#include "src/string-builder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -169,11 +170,25 @@ CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
|
||||
: isolate_(isolate) {
|
||||
Handle<Object> maybe_function = JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_function_symbol());
|
||||
if (!maybe_function->IsJSFunction()) return;
|
||||
if (maybe_function->IsJSFunction()) {
|
||||
// javascript
|
||||
fun_ = Handle<JSFunction>::cast(maybe_function);
|
||||
receiver_ = JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_receiver_symbol());
|
||||
} else {
|
||||
Handle<Object> maybe_wasm_func_index = JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_wasm_func_index_symbol());
|
||||
if (!maybe_wasm_func_index->IsSmi()) {
|
||||
// invalid: neither javascript nor wasm
|
||||
return;
|
||||
}
|
||||
// wasm
|
||||
wasm_obj_ = Handle<JSObject>::cast(JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_wasm_obj_symbol()));
|
||||
wasm_func_index_ = Smi::cast(*maybe_wasm_func_index)->value();
|
||||
DCHECK(static_cast<int>(wasm_func_index_) >= 0);
|
||||
}
|
||||
|
||||
fun_ = Handle<JSFunction>::cast(maybe_function);
|
||||
receiver_ = JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_receiver_symbol());
|
||||
CHECK(JSObject::GetDataProperty(
|
||||
call_site_obj, isolate->factory()->call_site_position_symbol())
|
||||
->ToInt32(&pos_));
|
||||
@ -181,15 +196,22 @@ CallSite::CallSite(Isolate* isolate, Handle<JSObject> call_site_obj)
|
||||
|
||||
|
||||
Handle<Object> CallSite::GetFileName() {
|
||||
Handle<Object> script(fun_->shared()->script(), isolate_);
|
||||
if (script->IsScript()) {
|
||||
return Handle<Object>(Handle<Script>::cast(script)->name(), isolate_);
|
||||
}
|
||||
return isolate_->factory()->null_value();
|
||||
if (!IsJavaScript()) return isolate_->factory()->null_value();
|
||||
Object* script = fun_->shared()->script();
|
||||
if (!script->IsScript()) return isolate_->factory()->null_value();
|
||||
return Handle<Object>(Script::cast(script)->name(), isolate_);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> CallSite::GetFunctionName() {
|
||||
if (IsWasm()) {
|
||||
if (wasm_obj_->IsUndefined()) return isolate_->factory()->null_value();
|
||||
// wasm_obj_ can be a String if we generate WASM code directly in a test
|
||||
// case.
|
||||
if (wasm_obj_->IsString()) return wasm_obj_;
|
||||
return wasm::GetWasmFunctionName(Handle<JSObject>::cast(wasm_obj_),
|
||||
wasm_func_index_);
|
||||
}
|
||||
Handle<String> result = JSFunction::GetName(fun_);
|
||||
if (result->length() != 0) return result;
|
||||
|
||||
@ -202,19 +224,16 @@ Handle<Object> CallSite::GetFunctionName() {
|
||||
return isolate_->factory()->null_value();
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> CallSite::GetScriptNameOrSourceUrl() {
|
||||
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
|
||||
if (script_obj->IsScript()) {
|
||||
Handle<Script> script = Handle<Script>::cast(script_obj);
|
||||
Object* source_url = script->source_url();
|
||||
if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
|
||||
return Handle<Object>(script->name(), isolate_);
|
||||
}
|
||||
return isolate_->factory()->null_value();
|
||||
if (!IsJavaScript()) return isolate_->factory()->null_value();
|
||||
Object* script_obj = fun_->shared()->script();
|
||||
if (!script_obj->IsScript()) return isolate_->factory()->null_value();
|
||||
Handle<Script> script(Script::cast(script_obj), isolate_);
|
||||
Object* source_url = script->source_url();
|
||||
if (source_url->IsString()) return Handle<Object>(source_url, isolate_);
|
||||
return Handle<Object>(script->name(), isolate_);
|
||||
}
|
||||
|
||||
|
||||
bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
|
||||
Handle<JSFunction> fun,
|
||||
LookupIterator::Configuration config) {
|
||||
@ -234,7 +253,7 @@ bool CheckMethodName(Isolate* isolate, Handle<JSObject> obj, Handle<Name> name,
|
||||
|
||||
|
||||
Handle<Object> CallSite::GetMethodName() {
|
||||
if (receiver_->IsNull() || receiver_->IsUndefined()) {
|
||||
if (!IsJavaScript() || receiver_->IsNull() || receiver_->IsUndefined()) {
|
||||
return isolate_->factory()->null_value();
|
||||
}
|
||||
Handle<JSReceiver> receiver =
|
||||
@ -293,7 +312,7 @@ Handle<Object> CallSite::GetMethodName() {
|
||||
|
||||
|
||||
int CallSite::GetLineNumber() {
|
||||
if (pos_ >= 0) {
|
||||
if (pos_ >= 0 && IsJavaScript()) {
|
||||
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
|
||||
if (script_obj->IsScript()) {
|
||||
Handle<Script> script = Handle<Script>::cast(script_obj);
|
||||
@ -305,7 +324,7 @@ int CallSite::GetLineNumber() {
|
||||
|
||||
|
||||
int CallSite::GetColumnNumber() {
|
||||
if (pos_ >= 0) {
|
||||
if (pos_ >= 0 && IsJavaScript()) {
|
||||
Handle<Object> script_obj(fun_->shared()->script(), isolate_);
|
||||
if (script_obj->IsScript()) {
|
||||
Handle<Script> script = Handle<Script>::cast(script_obj);
|
||||
@ -317,6 +336,7 @@ int CallSite::GetColumnNumber() {
|
||||
|
||||
|
||||
bool CallSite::IsNative() {
|
||||
if (!IsJavaScript()) return false;
|
||||
Handle<Object> script(fun_->shared()->script(), isolate_);
|
||||
return script->IsScript() &&
|
||||
Handle<Script>::cast(script)->type() == Script::TYPE_NATIVE;
|
||||
@ -324,12 +344,14 @@ bool CallSite::IsNative() {
|
||||
|
||||
|
||||
bool CallSite::IsToplevel() {
|
||||
if (IsWasm()) return false;
|
||||
return receiver_->IsJSGlobalProxy() || receiver_->IsNull() ||
|
||||
receiver_->IsUndefined();
|
||||
}
|
||||
|
||||
|
||||
bool CallSite::IsEval() {
|
||||
if (!IsJavaScript()) return false;
|
||||
Handle<Object> script(fun_->shared()->script(), isolate_);
|
||||
return script->IsScript() &&
|
||||
Handle<Script>::cast(script)->compilation_type() ==
|
||||
@ -338,7 +360,7 @@ bool CallSite::IsEval() {
|
||||
|
||||
|
||||
bool CallSite::IsConstructor() {
|
||||
if (!receiver_->IsJSObject()) return false;
|
||||
if (!IsJavaScript() || !receiver_->IsJSObject()) return false;
|
||||
Handle<Object> constructor =
|
||||
JSReceiver::GetDataProperty(Handle<JSObject>::cast(receiver_),
|
||||
isolate_->factory()->constructor_string());
|
||||
|
@ -59,13 +59,16 @@ class CallSite {
|
||||
bool IsEval();
|
||||
bool IsConstructor();
|
||||
|
||||
bool IsValid() { return !fun_.is_null(); }
|
||||
bool IsJavaScript() { return !fun_.is_null(); }
|
||||
bool IsWasm() { return !wasm_obj_.is_null(); }
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
Handle<Object> receiver_;
|
||||
Handle<JSFunction> fun_;
|
||||
int32_t pos_;
|
||||
int32_t pos_ = -1;
|
||||
Handle<JSObject> wasm_obj_;
|
||||
uint32_t wasm_func_index_ = static_cast<uint32_t>(-1);
|
||||
};
|
||||
|
||||
#define MESSAGE_TEMPLATES(T) \
|
||||
@ -96,7 +99,7 @@ class CallSite {
|
||||
T(CalledOnNonObject, "% called on non-object") \
|
||||
T(CalledOnNullOrUndefined, "% called on null or undefined") \
|
||||
T(CallSiteExpectsFunction, \
|
||||
"CallSite expects function as second argument, got %") \
|
||||
"CallSite expects function or number as second argument, got %") \
|
||||
T(CallSiteMethod, "CallSite method % expects CallSite as receiver") \
|
||||
T(CannotConvertToPrimitive, "Cannot convert object to primitive value") \
|
||||
T(CannotPreventExt, "Cannot prevent extensions") \
|
||||
|
@ -230,9 +230,9 @@ void AllocationTracker::AllocationEvent(Address addr, int size) {
|
||||
|
||||
Isolate* isolate = heap->isolate();
|
||||
int length = 0;
|
||||
StackTraceFrameIterator it(isolate);
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
while (!it.done() && length < kMaxAllocationTraceLength) {
|
||||
StandardFrame* frame = it.frame();
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
SharedFunctionInfo* shared = frame->function()->shared();
|
||||
SnapshotObjectId id = ids_->FindOrAddEntry(
|
||||
shared->address(), shared->Size(), false);
|
||||
|
@ -155,10 +155,10 @@ SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() {
|
||||
AllocationNode* node = &profile_root_;
|
||||
|
||||
std::vector<SharedFunctionInfo*> stack;
|
||||
StackTraceFrameIterator it(isolate_);
|
||||
JavaScriptFrameIterator it(isolate_);
|
||||
int frames_captured = 0;
|
||||
while (!it.done() && frames_captured < stack_depth_) {
|
||||
StandardFrame* frame = it.frame();
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
SharedFunctionInfo* shared = frame->function()->shared();
|
||||
stack.push_back(shared);
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/messages.h"
|
||||
#include "src/profiler/cpu-profiler.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -302,5 +303,15 @@ RUNTIME_FUNCTION(Runtime_FunctionToString) {
|
||||
: *JSFunction::ToString(Handle<JSFunction>::cast(function));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmGetFunctionName) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, wasm, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(func_index, 1);
|
||||
|
||||
return *wasm::GetWasmFunctionName(wasm, func_index);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -334,15 +334,15 @@ RUNTIME_FUNCTION(Runtime_FormatMessageString) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
#define CALLSITE_GET(NAME, RETURN) \
|
||||
RUNTIME_FUNCTION(Runtime_CallSite##NAME##RT) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 1); \
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, call_site_obj, 0); \
|
||||
Handle<String> result; \
|
||||
CallSite call_site(isolate, call_site_obj); \
|
||||
RUNTIME_ASSERT(call_site.IsValid()); \
|
||||
return RETURN(call_site.NAME(), isolate); \
|
||||
#define CALLSITE_GET(NAME, RETURN) \
|
||||
RUNTIME_FUNCTION(Runtime_CallSite##NAME##RT) { \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK(args.length() == 1); \
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, call_site_obj, 0); \
|
||||
Handle<String> result; \
|
||||
CallSite call_site(isolate, call_site_obj); \
|
||||
RUNTIME_ASSERT(call_site.IsJavaScript() || call_site.IsWasm()); \
|
||||
return RETURN(call_site.NAME(), isolate); \
|
||||
}
|
||||
|
||||
static inline Object* ReturnDereferencedHandle(Handle<Object> obj,
|
||||
|
@ -314,7 +314,8 @@ namespace internal {
|
||||
F(GetOrdinaryHasInstance, 0, 1) \
|
||||
F(GetAndResetRuntimeCallStats, 0, 1) \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(RunMicrotasks, 0, 1)
|
||||
F(RunMicrotasks, 0, 1) \
|
||||
F(WasmGetFunctionName, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_JSON(F) \
|
||||
F(QuoteJSONString, 1, 1) \
|
||||
|
@ -23,10 +23,11 @@ namespace {
|
||||
do { \
|
||||
const char* exp_ = (exp); \
|
||||
const char* found_ = (found); \
|
||||
if (V8_UNLIKELY(strcmp(exp_, found_) != 0)) { \
|
||||
DCHECK_NOT_NULL(exp); \
|
||||
if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) { \
|
||||
V8_Fatal(__FILE__, __LINE__, \
|
||||
"Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
|
||||
exp_, found_); \
|
||||
exp_, found_ ? found_ : "<null>"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -44,8 +45,9 @@ void PrintStackTrace(v8::Local<v8::StackTrace> stack) {
|
||||
}
|
||||
|
||||
struct ExceptionInfo {
|
||||
const char* funcName;
|
||||
int lineNr;
|
||||
const char* func_name;
|
||||
int line_nr;
|
||||
int column;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
@ -64,8 +66,9 @@ void CheckExceptionInfos(Isolate* isolate, Handle<Object> exc,
|
||||
for (int frameNr = 0; frameNr < N; ++frameNr) {
|
||||
v8::Local<v8::StackFrame> frame = stack->GetFrame(frameNr);
|
||||
v8::String::Utf8Value funName(frame->GetFunctionName());
|
||||
CHECK_CSTREQ(excInfos[frameNr].funcName, *funName);
|
||||
CHECK_EQ(excInfos[frameNr].lineNr, frame->GetLineNumber());
|
||||
CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
|
||||
CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
|
||||
CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +86,8 @@ TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
|
||||
sigs.v_v(),
|
||||
"(function js() {\n function a() {\n throw new Error(); };\n a(); })");
|
||||
|
||||
BUILD(comp1, WASM_CALL_FUNCTION0(js_throwing_index));
|
||||
// Add a nop such that we don't always get position 1.
|
||||
BUILD(comp1, WASM_NOP, WASM_CALL_FUNCTION0(js_throwing_index));
|
||||
uint32_t wasm_index = comp1.CompileAndAdd();
|
||||
|
||||
WasmFunctionCompiler comp2(sigs.v_v(), &module);
|
||||
@ -108,11 +112,12 @@ TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
|
||||
|
||||
// Line number is 1-based, with 0 == kNoLineNumberInfo.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"a", 3}, // Comment to prevent clang-format complaints
|
||||
{"js", 4}, // -
|
||||
{"<WASM>", 0}, // -
|
||||
{"<WASM>", 0}, // -
|
||||
{"callFn", 1}};
|
||||
{"a", 3, 8}, // -
|
||||
{"js", 4, 2}, // -
|
||||
{"<WASM>", static_cast<int>(wasm_index), 2}, // -
|
||||
{"<WASM>", static_cast<int>(wasm_index_2), 1}, // -
|
||||
{"callFn", 1, 24} // -
|
||||
};
|
||||
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
|
||||
expected_exceptions);
|
||||
}
|
||||
@ -152,9 +157,11 @@ TEST(CollectDetailedWasmStack_WasmError) {
|
||||
|
||||
// Line number is 1-based, with 0 == kNoLineNumberInfo.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"<WASM>", 0}, // Comment to prevent clang-format complaints.
|
||||
{"<WASM>", 0},
|
||||
{"callFn", 1}};
|
||||
// TODO(clemens): position should be 1
|
||||
{"<WASM>", static_cast<int>(wasm_index), -1}, // -
|
||||
{"<WASM>", static_cast<int>(wasm_index_2), 1}, // -
|
||||
{"callFn", 1, 24} //-
|
||||
};
|
||||
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
|
||||
expected_exceptions);
|
||||
}
|
||||
|
@ -495,17 +495,12 @@ class WasmFunctionCompiler : public HandleAndZoneScope,
|
||||
Handle<Code> code = info.code();
|
||||
|
||||
// Length is always 2, since usually <wasm_obj, func_index> is stored in the
|
||||
// deopt data. Here, we store <func_name, undef> instead.
|
||||
// deopt data. Here, we only store the function index.
|
||||
DCHECK(code->deoptimization_data() == nullptr ||
|
||||
code->deoptimization_data()->length() == 0);
|
||||
Handle<FixedArray> deopt_data =
|
||||
isolate()->factory()->NewFixedArray(2, TENURED);
|
||||
if (debug_name_.start() != nullptr) {
|
||||
MaybeHandle<String> maybe_name =
|
||||
isolate()->factory()->NewStringFromUtf8(debug_name_, TENURED);
|
||||
if (!maybe_name.is_null())
|
||||
deopt_data->set(0, *maybe_name.ToHandleChecked());
|
||||
}
|
||||
deopt_data->set(1, Smi::FromInt(function_index_));
|
||||
deopt_data->set_length(2);
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
|
@ -16,14 +16,23 @@ function stripPath(s) {
|
||||
function verifyStack(frames, expected) {
|
||||
assertEquals(expected.length, frames.length, "number of frames mismatch");
|
||||
expected.forEach(function(exp, i) {
|
||||
assertEquals(exp[0], frames[i].getFunctionName(),
|
||||
"["+i+"].getFunctionName()");
|
||||
assertEquals(exp[1], frames[i].getLineNumber(),
|
||||
"["+i+"].getLineNumber()");
|
||||
assertContains(exp[2], frames[i].getFileName(),
|
||||
"["+i+"].getFileName()");
|
||||
assertContains(exp[3], frames[i].toString(),
|
||||
"["+i+"].toString()");
|
||||
if (exp[1] != "?") {
|
||||
assertEquals(exp[1], frames[i].getFunctionName(),
|
||||
"["+i+"].getFunctionName()");
|
||||
}
|
||||
assertEquals(exp[2], frames[i].getLineNumber(), "["+i+"].getLineNumber()");
|
||||
if (exp[0])
|
||||
assertEquals(exp[3], frames[i].getPosition(),
|
||||
"["+i+"].getPosition()");
|
||||
assertContains(exp[4], frames[i].getFileName(), "["+i+"].getFileName()");
|
||||
var toString;
|
||||
if (exp[0]) {
|
||||
var funName = exp[1] == "?" ? "" : exp[1];
|
||||
toString = funName + " (<WASM>:" + exp[2] + ":" + exp[3] + ")";
|
||||
} else {
|
||||
toString = exp[4] + ":" + exp[2] + ":";
|
||||
}
|
||||
assertContains(toString, frames[i].toString(), "["+i+"].toString()");
|
||||
});
|
||||
}
|
||||
|
||||
@ -46,13 +55,13 @@ builder.addFunction("exec_unreachable", kSig_v_v)
|
||||
.addBody([kExprUnreachable])
|
||||
.exportAs("exec_unreachable");
|
||||
|
||||
// make this function unnamed, just to test also this case
|
||||
// Make this function unnamed, just to test also this case.
|
||||
var mem_oob_func = builder.addFunction(undefined, kSig_v_v)
|
||||
// access the memory at offset -1
|
||||
// Access the memory at offset -1, to provoke a trap.
|
||||
.addBody([kExprI32Const, 0x7f, kExprI32LoadMem8S, 0, 0])
|
||||
.exportAs("mem_out_of_bounds");
|
||||
|
||||
// call the mem_out_of_bounds function, in order to have two WASM stack frames
|
||||
// Call the mem_out_of_bounds function, in order to have two WASM stack frames.
|
||||
builder.addFunction("call_mem_out_of_bounds", kSig_v_v)
|
||||
.addBody([kExprCallFunction, kArity0, mem_oob_func.index])
|
||||
.exportAs("call_mem_out_of_bounds");
|
||||
@ -62,10 +71,10 @@ var module = builder.instantiate({func: STACK});
|
||||
(function testSimpleStack() {
|
||||
var expected_string = "Error\n" +
|
||||
// The line numbers below will change as this test gains / loses lines..
|
||||
" at STACK (stack.js:33:11)\n" + // --
|
||||
" at <WASM> (<anonymous>)\n" + // TODO(jfb): wasm stack here.
|
||||
" at testSimpleStack (stack.js:70:18)\n" + // --
|
||||
" at stack.js:72:3"; // --
|
||||
" at STACK (stack.js:42:11)\n" + // --
|
||||
" at main (<WASM>:0:1)\n" + // --
|
||||
" at testSimpleStack (stack.js:79:18)\n" + // --
|
||||
" at stack.js:81:3"; // --
|
||||
|
||||
module.exports.main();
|
||||
assertEquals(expected_string, stripPath(stack));
|
||||
@ -80,13 +89,12 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
(function testStackFrames() {
|
||||
module.exports.main();
|
||||
|
||||
// TODO(clemensh): add a isWasm() method or similar, and test it
|
||||
verifyStack(stack, [
|
||||
// function line file toString
|
||||
[ "STACK", 33, "stack.js", "stack.js:33:11"],
|
||||
[ "<WASM>", null, null, "<WASM>"],
|
||||
["testStackFrames", 81, "stack.js", "stack.js:81:18"],
|
||||
[ null, 91, "stack.js", "stack.js:91:3"]
|
||||
// isWasm function line pos file
|
||||
[ false, "STACK", 42, 0, "stack.js"],
|
||||
[ true, "main", 0, 1, null],
|
||||
[ false, "testStackFrames", 90, 0, "stack.js"],
|
||||
[ false, null, 99, 0, "stack.js"]
|
||||
]);
|
||||
})();
|
||||
|
||||
@ -97,10 +105,11 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
} catch (e) {
|
||||
assertContains("unreachable", e.message);
|
||||
verifyStack(e.stack, [
|
||||
// function line file toString
|
||||
[ "<WASM>", null, null, "<WASM>"],
|
||||
["testWasmUnreachable", 95, "stack.js", "stack.js:95:20"],
|
||||
[ null, 106, "stack.js", "stack.js:106:3"]
|
||||
// isWasm function line pos file
|
||||
// TODO(clemensh): pos should be 1
|
||||
[ true, "exec_unreachable", 1, -1, null],
|
||||
[ false, "testWasmUnreachable", 103, 0, "stack.js"],
|
||||
[ false, null, 115, 0, "stack.js"]
|
||||
]);
|
||||
}
|
||||
})();
|
||||
@ -112,11 +121,12 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
} catch (e) {
|
||||
assertContains("out of bounds", e.message);
|
||||
verifyStack(e.stack, [
|
||||
// function line file toString
|
||||
[ "<WASM>", null, null, "<WASM>"],
|
||||
[ "<WASM>", null, null, "<WASM>"],
|
||||
["testWasmMemOutOfBounds", 110, "stack.js", "stack.js:110:20"],
|
||||
[ null, 122, "stack.js", "stack.js:122:3"]
|
||||
// isWasm function line pos file
|
||||
// TODO(clemensh): pos should be 3
|
||||
[ true, "?", 2, -1, null],
|
||||
[ true, "call_mem_out_of_bounds", 3, 1, null],
|
||||
[ false, "testWasmMemOutOfBounds", 119, 0, "stack.js"],
|
||||
[ false, null, 132, 0, "stack.js"]
|
||||
]);
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user