Commit c613eb97 authored by Alex Kodat's avatar Alex Kodat Committed by Commit Bot

[api] Add StackFrame GetScriptSource and GetScriptSourceMappingURL

These simplify production of extra information in stack traces or
dereferencing source maps in processing stack traces. While these
can be managed externally, this can be very complicated in
environments where scripts come from many different sources,
possibly not even under embedder control. Since V8 already has
easy access to this information, it's nice to share it with
embedders.

Bug: v8:11509
Change-Id: Ic5a1685adf4cdf456bdf7191ce815f728cf491e2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2724571Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73148}
parent d0d9a33b
......@@ -2167,6 +2167,8 @@ class V8_EXPORT Message {
*/
Isolate* GetIsolate() const;
V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSource(
Local<Context> context) const;
V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSourceLine(
Local<Context> context) const;
......@@ -2341,6 +2343,17 @@ class V8_EXPORT StackFrame {
*/
Local<String> GetScriptNameOrSourceURL() const;
/**
* Returns the source of the script for the function for this StackFrame.
*/
Local<String> GetScriptSource() const;
/**
* Returns the source mapping URL (if one is present) of the script for
* the function for this StackFrame.
*/
Local<String> GetScriptSourceMappingURL() const;
/**
* Returns the name of the function associated with this stack frame.
*/
......
......@@ -2902,6 +2902,15 @@ bool Message::IsOpaque() const {
return self->script().origin_options().IsOpaque();
}
MaybeLocal<String> Message::GetSource(Local<Context> context) const {
auto self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
EscapableHandleScope handle_scope(reinterpret_cast<Isolate*>(isolate));
i::Handle<i::String> source(self->GetSource(), isolate);
RETURN_ESCAPED(Utils::ToLocal(source));
}
MaybeLocal<String> Message::GetSourceLine(Local<Context> context) const {
auto self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
......@@ -2971,6 +2980,23 @@ Local<String> StackFrame::GetScriptNameOrSourceURL() const {
return Local<String>::Cast(Utils::ToLocal(name_or_url));
}
Local<String> StackFrame::GetScriptSource() const {
auto self = Utils::OpenHandle(this);
auto isolate = self->GetIsolate();
i::Handle<i::Object> source(self->GetScriptSource(), isolate);
if (!source->IsString()) return {};
return Local<String>::Cast(Utils::ToLocal(source));
}
Local<String> StackFrame::GetScriptSourceMappingURL() const {
auto self = Utils::OpenHandle(this);
auto isolate = self->GetIsolate();
i::Handle<i::Object> sourceMappingURL(self->GetScriptSourceMappingURL(),
isolate);
if (!sourceMappingURL->IsString()) return {};
return Local<String>::Cast(Utils::ToLocal(sourceMappingURL));
}
Local<String> StackFrame::GetFunctionName() const {
auto self = Utils::OpenHandle(this);
auto name = i::StackFrameInfo::GetFunctionName(self);
......
......@@ -5314,6 +5314,15 @@ int JSMessageObject::GetColumnNumber() const {
return info.column; // Note: No '+1' in contrast to GetLineNumber.
}
String JSMessageObject::GetSource() const {
Script script_object = script();
if (script_object.HasValidSource()) {
Object source = script_object.source();
if (source.IsString()) return String::cast(source);
}
return ReadOnlyRoots(GetIsolate()).empty_string();
}
Handle<String> JSMessageObject::GetSourceLine() const {
Isolate* isolate = GetIsolate();
Handle<Script> the_script(script(), isolate);
......
......@@ -1118,6 +1118,9 @@ class JSMessageObject : public JSObject {
// EnsureSourcePositionsAvailable must have been called before calling this.
V8_EXPORT_PRIVATE int GetColumnNumber() const;
// Returns the source code
V8_EXPORT_PRIVATE String GetSource() const;
// Returns the source code line containing the given source
// position, or the empty string if the position is invalid.
// EnsureSourcePositionsAvailable must have been called before calling this.
......
......@@ -145,6 +145,22 @@ Object StackFrameInfo::GetScriptNameOrSourceURL() const {
return ReadOnlyRoots(GetIsolate()).null_value();
}
Object StackFrameInfo::GetScriptSource() const {
if (auto script = GetScript()) {
if (script->HasValidSource()) {
return script->source();
}
}
return ReadOnlyRoots(GetIsolate()).null_value();
}
Object StackFrameInfo::GetScriptSourceMappingURL() const {
if (auto script = GetScript()) {
return script->source_mapping_url();
}
return ReadOnlyRoots(GetIsolate()).null_value();
}
namespace {
MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) {
......
......@@ -56,6 +56,8 @@ class StackFrameInfo
int GetScriptId() const;
Object GetScriptName() const;
Object GetScriptNameOrSourceURL() const;
Object GetScriptSource() const;
Object GetScriptSourceMappingURL() const;
static Handle<PrimitiveHeapObject> GetEvalOrigin(Handle<StackFrameInfo> info);
static Handle<Object> GetFunctionName(Handle<StackFrameInfo> info);
......
This diff is collapsed.
......@@ -4732,6 +4732,13 @@ namespace {
// some particular way by calling the supplied |tester| function. The tests that
// use this purposely test only a single getter as the getter updates the cached
// state of the object which could affect the results of other functions.
const char message_attributes_script[] =
R"javascript(
(function() {
throw new Error();
})();
)javascript";
void CheckMessageAttributes(std::function<void(v8::Local<v8::Context> context,
v8::Local<v8::Message> message)>
tester) {
......@@ -4739,12 +4746,7 @@ void CheckMessageAttributes(std::function<void(v8::Local<v8::Context> context,
v8::HandleScope scope(context->GetIsolate());
TryCatch try_catch(context->GetIsolate());
CompileRun(
R"javascript(
(function() {
throw new Error();
})();
)javascript");
CompileRun(message_attributes_script);
CHECK(try_catch.HasCaught());
v8::Local<v8::Value> error = try_catch.Exception();
......@@ -4767,38 +4769,47 @@ TEST(MessageGetLineNumber) {
TEST(MessageGetStartColumn) {
CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
CHECK_EQ(14, message->GetStartColumn(context).FromJust());
CHECK_EQ(12, message->GetStartColumn(context).FromJust());
});
}
TEST(MessageGetEndColumn) {
CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
CHECK_EQ(15, message->GetEndColumn(context).FromJust());
CHECK_EQ(13, message->GetEndColumn(context).FromJust());
});
}
TEST(MessageGetStartPosition) {
CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
CHECK_EQ(35, message->GetStartPosition());
CHECK_EQ(31, message->GetStartPosition());
});
}
TEST(MessageGetEndPosition) {
CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
CHECK_EQ(36, message->GetEndPosition());
CHECK_EQ(32, message->GetEndPosition());
});
}
TEST(MessageGetSource) {
CheckMessageAttributes([](v8::Local<v8::Context> context,
v8::Local<v8::Message> message) {
std::string result(*v8::String::Utf8Value(
context->GetIsolate(), message->GetSource(context).ToLocalChecked()));
CHECK_EQ(message_attributes_script, result);
});
}
TEST(MessageGetSourceLine) {
CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
std::string result(*v8::String::Utf8Value(
context->GetIsolate(),
message->GetSourceLine(context).ToLocalChecked()));
CHECK_EQ(" throw new Error();", result);
CHECK_EQ(" throw new Error();", result);
});
}
......@@ -74,13 +74,21 @@ void CheckExceptionInfos(v8::internal::Isolate* i_isolate, Handle<Object> exc,
// Line and column are 1-based in v8::StackFrame, just as in ExceptionInfo.
CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
v8::Local<v8::String> scriptSource = frame->GetScriptSource();
if (frame->IsWasm()) {
CHECK(scriptSource.IsEmpty());
} else {
CHECK(scriptSource->IsString());
}
}
CheckComputeLocation(i_isolate, exc, excInfos[0]);
CheckComputeLocation(i_isolate, exc, excInfos[0],
stack->GetFrame(v8_isolate, 0));
}
void CheckComputeLocation(v8::internal::Isolate* i_isolate, Handle<Object> exc,
const ExceptionInfo& topLocation) {
const ExceptionInfo& topLocation,
const v8::Local<v8::StackFrame> stackFrame) {
MessageLocation loc;
CHECK(i_isolate->ComputeLocationFromStackTrace(&loc, exc));
printf("loc start: %d, end: %d\n", loc.start_pos(), loc.end_pos());
......@@ -97,6 +105,13 @@ void CheckComputeLocation(v8::internal::Isolate* i_isolate, Handle<Object> exc,
// whether Script::PositionInfo.column should be the offset
// relative to the module or relative to the function.
// CHECK_EQ(topLocation.column - 1, message->GetColumnNumber());
String scriptSource = message->GetSource();
CHECK(scriptSource.IsString());
if (stackFrame->IsWasm()) {
CHECK_EQ(scriptSource.length(), 0);
} else {
CHECK_GT(scriptSource.length(), 0);
}
}
#undef CHECK_CSTREQ
......
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