Introduce v8::StackTrace::CurrentScriptNameOrSourceURL
This CL introduces a dedicated API to retrieve the current (w.r.t. the JS stack) script name or sourceURL. Currently, API clients will collect multiple stack traces in increasing sizes to accomplish the same goal. The new method walks the JS stack in the same way as the stack trace collection mechanic but doesn't create/allocate stack info or callsite objects along the way. R=bmeurer@chromium.org, yangguo@chromium.org Doc: https://bit.ly/v8-current-script-name Bug: chromium:1286677 Change-Id: Id53e4f04bf17349d34f3d581bc712b1f4aa055db Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3382818 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/main@{#78645}
This commit is contained in:
parent
bd1cc7b009
commit
44a8a7d685
@ -149,6 +149,18 @@ class V8_EXPORT StackTrace {
|
||||
*/
|
||||
static Local<StackTrace> CurrentStackTrace(
|
||||
Isolate* isolate, int frame_limit, StackTraceOptions options = kDetailed);
|
||||
|
||||
/**
|
||||
* Returns the first valid script name or source URL starting at the top of
|
||||
* the JS stack. The returned string is either an empty handle if no script
|
||||
* name/url was found or a non-zero-length string.
|
||||
*
|
||||
* This method is equivalent to calling StackTrace::CurrentStackTrace and
|
||||
* walking the resulting frames from the beginning until a non-empty script
|
||||
* name/url is found. The difference is that this method won't allocate
|
||||
* a stack trace.
|
||||
*/
|
||||
static Local<String> CurrentScriptNameOrSourceURL(Isolate* isolate);
|
||||
};
|
||||
|
||||
} // namespace v8
|
||||
|
@ -3248,6 +3248,14 @@ Local<StackTrace> StackTrace::CurrentStackTrace(Isolate* isolate,
|
||||
return Utils::StackTraceToLocal(stackTrace);
|
||||
}
|
||||
|
||||
Local<String> StackTrace::CurrentScriptNameOrSourceURL(Isolate* v8_isolate) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
i::Handle<i::String> name_or_source_url =
|
||||
isolate->CurrentScriptNameOrSourceURL();
|
||||
return Utils::ToLocal(name_or_source_url);
|
||||
}
|
||||
|
||||
// --- S t a c k F r a m e ---
|
||||
|
||||
Location StackFrame::GetLocation() const {
|
||||
|
@ -1338,6 +1338,47 @@ Handle<FixedArray> Isolate::CaptureDetailedStackTrace(
|
||||
return stack_trace;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CurrentScriptNameStackVisitor {
|
||||
public:
|
||||
explicit CurrentScriptNameStackVisitor(Isolate* isolate)
|
||||
: isolate_(isolate) {}
|
||||
|
||||
bool Visit(FrameSummary& summary) {
|
||||
// Skip frames that aren't subject to debugging. Keep this in sync with
|
||||
// StackFrameBuilder::Visit so both visitors visit the same frames.
|
||||
if (!summary.is_subject_to_debugging()) return true;
|
||||
|
||||
// Frames that are subject to debugging always have a valid script object.
|
||||
Handle<Script> script = Handle<Script>::cast(summary.script());
|
||||
Handle<Object> name_or_url_obj =
|
||||
handle(script->GetNameOrSourceURL(), isolate_);
|
||||
if (!name_or_url_obj->IsString()) return true;
|
||||
|
||||
Handle<String> name_or_url = Handle<String>::cast(name_or_url_obj);
|
||||
if (!name_or_url->length()) return true;
|
||||
|
||||
name_or_url_ = name_or_url;
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<String> CurrentScriptNameOrSourceURL() const { return name_or_url_; }
|
||||
|
||||
private:
|
||||
Isolate* const isolate_;
|
||||
Handle<String> name_or_url_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<String> Isolate::CurrentScriptNameOrSourceURL() {
|
||||
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), __func__);
|
||||
CurrentScriptNameStackVisitor visitor(this);
|
||||
VisitStack(this, &visitor);
|
||||
return visitor.CurrentScriptNameOrSourceURL();
|
||||
}
|
||||
|
||||
void Isolate::PrintStack(FILE* out, PrintStackMode mode) {
|
||||
if (stack_trace_nesting_level_ == 0) {
|
||||
stack_trace_nesting_level_++;
|
||||
|
@ -905,6 +905,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
Handle<Object> caller);
|
||||
Handle<FixedArray> GetDetailedStackTrace(Handle<JSReceiver> error_object);
|
||||
Handle<FixedArray> GetSimpleStackTrace(Handle<JSReceiver> error_object);
|
||||
// Walks the JS stack to find the first frame with a script name or
|
||||
// source URL. The inspected frames are the same as for the detailed stack
|
||||
// trace.
|
||||
Handle<String> CurrentScriptNameOrSourceURL();
|
||||
|
||||
Address GetAbstractPC(int* line, int* column);
|
||||
|
||||
|
@ -887,3 +887,58 @@ UNINITIALIZED_TEST(CaptureStackTraceForStackOverflow) {
|
||||
isolate->Exit();
|
||||
isolate->Dispose();
|
||||
}
|
||||
|
||||
void AnalyzeScriptNameInStack(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Local<v8::String> name =
|
||||
v8::StackTrace::CurrentScriptNameOrSourceURL(args.GetIsolate());
|
||||
CHECK(!name.IsEmpty());
|
||||
CHECK(name->StringEquals(v8_str("test.js")));
|
||||
}
|
||||
|
||||
TEST(CurrentScriptNameOrSourceURL_Name) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
||||
templ->Set(
|
||||
isolate, "AnalyzeScriptNameInStack",
|
||||
v8::FunctionTemplate::New(CcTest::isolate(), AnalyzeScriptNameInStack));
|
||||
LocalContext context(nullptr, templ);
|
||||
|
||||
const char* source = R"(
|
||||
function foo() {
|
||||
AnalyzeScriptNameInStack();
|
||||
}
|
||||
foo();
|
||||
)";
|
||||
|
||||
CHECK(CompileRunWithOrigin(source, "test.js")->IsUndefined());
|
||||
}
|
||||
|
||||
void AnalyzeScriptURLInStack(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Local<v8::String> name =
|
||||
v8::StackTrace::CurrentScriptNameOrSourceURL(args.GetIsolate());
|
||||
CHECK(!name.IsEmpty());
|
||||
CHECK(name->StringEquals(v8_str("foo.js")));
|
||||
}
|
||||
|
||||
TEST(CurrentScriptNameOrSourceURL_SourceURL) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
|
||||
templ->Set(
|
||||
isolate, "AnalyzeScriptURLInStack",
|
||||
v8::FunctionTemplate::New(CcTest::isolate(), AnalyzeScriptURLInStack));
|
||||
LocalContext context(nullptr, templ);
|
||||
|
||||
const char* source = R"(
|
||||
function foo() {
|
||||
AnalyzeScriptURLInStack();
|
||||
}
|
||||
foo();
|
||||
//# sourceURL=foo.js
|
||||
)";
|
||||
|
||||
CHECK(CompileRunWithOrigin(source, "")->IsUndefined());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user