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 { ...@@ -2167,6 +2167,8 @@ class V8_EXPORT Message {
*/ */
Isolate* GetIsolate() const; Isolate* GetIsolate() const;
V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSource(
Local<Context> context) const;
V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSourceLine( V8_WARN_UNUSED_RESULT MaybeLocal<String> GetSourceLine(
Local<Context> context) const; Local<Context> context) const;
...@@ -2341,6 +2343,17 @@ class V8_EXPORT StackFrame { ...@@ -2341,6 +2343,17 @@ class V8_EXPORT StackFrame {
*/ */
Local<String> GetScriptNameOrSourceURL() const; 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. * Returns the name of the function associated with this stack frame.
*/ */
......
...@@ -2902,6 +2902,15 @@ bool Message::IsOpaque() const { ...@@ -2902,6 +2902,15 @@ bool Message::IsOpaque() const {
return self->script().origin_options().IsOpaque(); 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 { MaybeLocal<String> Message::GetSourceLine(Local<Context> context) const {
auto self = Utils::OpenHandle(this); auto self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate(); i::Isolate* isolate = self->GetIsolate();
...@@ -2971,6 +2980,23 @@ Local<String> StackFrame::GetScriptNameOrSourceURL() const { ...@@ -2971,6 +2980,23 @@ Local<String> StackFrame::GetScriptNameOrSourceURL() const {
return Local<String>::Cast(Utils::ToLocal(name_or_url)); 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 { Local<String> StackFrame::GetFunctionName() const {
auto self = Utils::OpenHandle(this); auto self = Utils::OpenHandle(this);
auto name = i::StackFrameInfo::GetFunctionName(self); auto name = i::StackFrameInfo::GetFunctionName(self);
......
...@@ -5314,6 +5314,15 @@ int JSMessageObject::GetColumnNumber() const { ...@@ -5314,6 +5314,15 @@ int JSMessageObject::GetColumnNumber() const {
return info.column; // Note: No '+1' in contrast to GetLineNumber. 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 { Handle<String> JSMessageObject::GetSourceLine() const {
Isolate* isolate = GetIsolate(); Isolate* isolate = GetIsolate();
Handle<Script> the_script(script(), isolate); Handle<Script> the_script(script(), isolate);
......
...@@ -1118,6 +1118,9 @@ class JSMessageObject : public JSObject { ...@@ -1118,6 +1118,9 @@ class JSMessageObject : public JSObject {
// EnsureSourcePositionsAvailable must have been called before calling this. // EnsureSourcePositionsAvailable must have been called before calling this.
V8_EXPORT_PRIVATE int GetColumnNumber() const; 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 // Returns the source code line containing the given source
// position, or the empty string if the position is invalid. // position, or the empty string if the position is invalid.
// EnsureSourcePositionsAvailable must have been called before calling this. // EnsureSourcePositionsAvailable must have been called before calling this.
......
...@@ -145,6 +145,22 @@ Object StackFrameInfo::GetScriptNameOrSourceURL() const { ...@@ -145,6 +145,22 @@ Object StackFrameInfo::GetScriptNameOrSourceURL() const {
return ReadOnlyRoots(GetIsolate()).null_value(); 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 { namespace {
MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) { MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) {
......
...@@ -56,6 +56,8 @@ class StackFrameInfo ...@@ -56,6 +56,8 @@ class StackFrameInfo
int GetScriptId() const; int GetScriptId() const;
Object GetScriptName() const; Object GetScriptName() const;
Object GetScriptNameOrSourceURL() const; Object GetScriptNameOrSourceURL() const;
Object GetScriptSource() const;
Object GetScriptSourceMappingURL() const;
static Handle<PrimitiveHeapObject> GetEvalOrigin(Handle<StackFrameInfo> info); static Handle<PrimitiveHeapObject> GetEvalOrigin(Handle<StackFrameInfo> info);
static Handle<Object> GetFunctionName(Handle<StackFrameInfo> info); static Handle<Object> GetFunctionName(Handle<StackFrameInfo> info);
......
This diff is collapsed.
...@@ -4732,6 +4732,13 @@ namespace { ...@@ -4732,6 +4732,13 @@ namespace {
// some particular way by calling the supplied |tester| function. The tests that // 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 // 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. // 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, void CheckMessageAttributes(std::function<void(v8::Local<v8::Context> context,
v8::Local<v8::Message> message)> v8::Local<v8::Message> message)>
tester) { tester) {
...@@ -4739,12 +4746,7 @@ void CheckMessageAttributes(std::function<void(v8::Local<v8::Context> context, ...@@ -4739,12 +4746,7 @@ void CheckMessageAttributes(std::function<void(v8::Local<v8::Context> context,
v8::HandleScope scope(context->GetIsolate()); v8::HandleScope scope(context->GetIsolate());
TryCatch try_catch(context->GetIsolate()); TryCatch try_catch(context->GetIsolate());
CompileRun( CompileRun(message_attributes_script);
R"javascript(
(function() {
throw new Error();
})();
)javascript");
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
v8::Local<v8::Value> error = try_catch.Exception(); v8::Local<v8::Value> error = try_catch.Exception();
...@@ -4767,38 +4769,47 @@ TEST(MessageGetLineNumber) { ...@@ -4767,38 +4769,47 @@ TEST(MessageGetLineNumber) {
TEST(MessageGetStartColumn) { TEST(MessageGetStartColumn) {
CheckMessageAttributes( CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) { [](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) { TEST(MessageGetEndColumn) {
CheckMessageAttributes( CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) { [](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) { TEST(MessageGetStartPosition) {
CheckMessageAttributes( CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) { [](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
CHECK_EQ(35, message->GetStartPosition()); CHECK_EQ(31, message->GetStartPosition());
}); });
} }
TEST(MessageGetEndPosition) { TEST(MessageGetEndPosition) {
CheckMessageAttributes( CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) { [](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) { TEST(MessageGetSourceLine) {
CheckMessageAttributes( CheckMessageAttributes(
[](v8::Local<v8::Context> context, v8::Local<v8::Message> message) { [](v8::Local<v8::Context> context, v8::Local<v8::Message> message) {
std::string result(*v8::String::Utf8Value( std::string result(*v8::String::Utf8Value(
context->GetIsolate(), context->GetIsolate(),
message->GetSourceLine(context).ToLocalChecked())); 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, ...@@ -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. // 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].line_nr, frame->GetLineNumber());
CHECK_EQ(excInfos[frameNr].column, frame->GetColumn()); 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, void CheckComputeLocation(v8::internal::Isolate* i_isolate, Handle<Object> exc,
const ExceptionInfo& topLocation) { const ExceptionInfo& topLocation,
const v8::Local<v8::StackFrame> stackFrame) {
MessageLocation loc; MessageLocation loc;
CHECK(i_isolate->ComputeLocationFromStackTrace(&loc, exc)); CHECK(i_isolate->ComputeLocationFromStackTrace(&loc, exc));
printf("loc start: %d, end: %d\n", loc.start_pos(), loc.end_pos()); 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, ...@@ -97,6 +105,13 @@ void CheckComputeLocation(v8::internal::Isolate* i_isolate, Handle<Object> exc,
// whether Script::PositionInfo.column should be the offset // whether Script::PositionInfo.column should be the offset
// relative to the module or relative to the function. // relative to the module or relative to the function.
// CHECK_EQ(topLocation.column - 1, message->GetColumnNumber()); // 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 #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