Commit 44a8a7d6 authored by Simon Zünd's avatar Simon Zünd Committed by V8 LUCI CQ

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/+/3382818Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78645}
parent bd1cc7b0
......@@ -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());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment