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);
......
......@@ -179,6 +179,8 @@ TEST(StackTrace) {
// Checks that a StackFrame has certain expected values.
static void checkStackFrame(const char* expected_script_name,
const char* expected_script_source,
const char* expected_script_source_mapping_url,
const char* expected_func_name,
int expected_line_number, int expected_column,
bool is_eval, bool is_constructor,
......@@ -186,12 +188,24 @@ static void checkStackFrame(const char* expected_script_name,
v8::HandleScope scope(CcTest::isolate());
v8::String::Utf8Value func_name(CcTest::isolate(), frame->GetFunctionName());
v8::String::Utf8Value script_name(CcTest::isolate(), frame->GetScriptName());
v8::String::Utf8Value script_source(CcTest::isolate(),
frame->GetScriptSource());
v8::String::Utf8Value script_source_mapping_url(
CcTest::isolate(), frame->GetScriptSourceMappingURL());
if (*script_name == nullptr) {
// The situation where there is no associated script, like for evals.
CHECK_NULL(expected_script_name);
} else {
CHECK_NOT_NULL(strstr(*script_name, expected_script_name));
}
CHECK_NOT_NULL(strstr(*script_source, expected_script_source));
if (*script_source_mapping_url == nullptr) {
CHECK_NULL(expected_script_source_mapping_url);
} else {
CHECK_NOT_NULL(expected_script_source_mapping_url);
CHECK_NOT_NULL(
strstr(*script_source_mapping_url, expected_script_source_mapping_url));
}
if (!frame->GetFunctionName().IsEmpty()) {
CHECK_NOT_NULL(strstr(*func_name, expected_func_name));
}
......@@ -202,6 +216,67 @@ static void checkStackFrame(const char* expected_script_name,
CHECK(frame->IsUserJavaScript());
}
// Tests the C++ StackTrace API.
// Test getting OVERVIEW information. Should ignore information that is not
// script name, function name, line number, and column offset.
const char* overview_source_eval = "new foo();";
const char* overview_source =
"function bar() {\n"
" var y; AnalyzeStackInNativeCode(1);\n"
"}\n"
"function foo() {\n"
"\n"
" bar();\n"
"}\n"
"//# sourceMappingURL=http://foobar.com/overview.ts\n"
"var x;eval('new foo();');";
// Test getting DETAILED information.
const char* detailed_source =
"function bat() {AnalyzeStackInNativeCode(2);\n"
"}\n"
"\n"
"function baz() {\n"
" bat();\n"
"}\n"
"eval('new baz();');";
// Test using function.name and function.displayName in stack trace
const char function_name_source[] =
"function bar(function_name, display_name, testGroup) {\n"
" var f = new Function(`AnalyzeStackInNativeCode(${testGroup});`);\n"
" if (function_name) {\n"
" Object.defineProperty(f, 'name', { value: function_name });\n"
" }\n"
" if (display_name) {\n"
" f.displayName = display_name;"
" }\n"
" f()\n"
"}\n"
"bar('function.name', undefined, 3);\n"
"bar('function.name', 'function.displayName', 4);\n"
"bar(239, undefined, 5);\n";
// Maybe it's a bit pathological to depend on the exact format of the wrapper
// the Function constructor puts around it's input string. If this becomes a
// hassle, maybe come up with some regex matching approach?
const char function_name_source_anon3[] =
"(function anonymous(\n"
") {\n"
"AnalyzeStackInNativeCode(3);\n"
"})";
const char function_name_source_anon4[] =
"(function anonymous(\n"
") {\n"
"AnalyzeStackInNativeCode(4);\n"
"})";
const char function_name_source_anon5[] =
"(function anonymous(\n"
") {\n"
"AnalyzeStackInNativeCode(5);\n"
"})";
static void AnalyzeStackInNativeCode(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
......@@ -221,53 +296,55 @@ static void AnalyzeStackInNativeCode(
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 10, v8::StackTrace::kOverview);
CHECK_EQ(4, stackTrace->GetFrameCount());
checkStackFrame(origin, "bar", 2, 10, false, false,
checkStackFrame(origin, overview_source, "//foobar.com/overview.ts", "bar",
2, 10, false, false,
stackTrace->GetFrame(args.GetIsolate(), 0));
checkStackFrame(origin, "foo", 6, 3, false, true,
stackTrace->GetFrame(isolate, 1));
checkStackFrame(origin, overview_source, "//foobar.com/overview.ts", "foo",
6, 3, false, true, stackTrace->GetFrame(isolate, 1));
// This is the source string inside the eval which has the call to foo.
checkStackFrame(nullptr, "", 1, 1, true, false,
checkStackFrame(nullptr, "new foo();", nullptr, "", 1, 1, true, false,
stackTrace->GetFrame(isolate, 2));
// The last frame is an anonymous function which has the initial eval call.
checkStackFrame(origin, "", 8, 7, false, false,
stackTrace->GetFrame(isolate, 3));
checkStackFrame(origin, overview_source, "//foobar.com/overview.ts", "", 9,
7, false, false, stackTrace->GetFrame(isolate, 3));
} else if (testGroup == kDetailedTest) {
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 10, v8::StackTrace::kDetailed);
CHECK_EQ(4, stackTrace->GetFrameCount());
checkStackFrame(origin, "bat", 4, 22, false, false,
stackTrace->GetFrame(isolate, 0));
checkStackFrame(origin, "baz", 8, 3, false, true,
checkStackFrame(origin, detailed_source, nullptr, "bat", 4, 22, false,
false, stackTrace->GetFrame(isolate, 0));
checkStackFrame(origin, detailed_source, nullptr, "baz", 8, 3, false, true,
stackTrace->GetFrame(isolate, 1));
bool is_eval = true;
// This is the source string inside the eval which has the call to baz.
checkStackFrame(nullptr, "", 1, 1, is_eval, false,
checkStackFrame(nullptr, "new baz();", nullptr, "", 1, 1, is_eval, false,
stackTrace->GetFrame(isolate, 2));
// The last frame is an anonymous function which has the initial eval call.
checkStackFrame(origin, "", 10, 1, false, false,
checkStackFrame(origin, detailed_source, nullptr, "", 10, 1, false, false,
stackTrace->GetFrame(isolate, 3));
} else if (testGroup == kFunctionName) {
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(nullptr, "function.name", 3, 1, true, false,
checkStackFrame(nullptr, function_name_source_anon3, nullptr,
"function.name", 3, 1, true, false,
stackTrace->GetFrame(isolate, 0));
} else if (testGroup == kFunctionNameAndDisplayName) {
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(nullptr, "function.name", 3, 1, true, false,
checkStackFrame(nullptr, function_name_source_anon4, nullptr,
"function.name", 3, 1, true, false,
stackTrace->GetFrame(isolate, 0));
} else if (testGroup == kFunctionNameIsNotString) {
v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(nullptr, "", 3, 1, true, false,
stackTrace->GetFrame(isolate, 0));
checkStackFrame(nullptr, function_name_source_anon5, nullptr, "", 3, 1,
true, false, stackTrace->GetFrame(isolate, 0));
}
}
// Tests the C++ StackTrace API.
// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
// THREADED_TEST(CaptureStackTrace) {
TEST(CaptureStackTrace) {
......@@ -279,17 +356,6 @@ TEST(CaptureStackTrace) {
v8::FunctionTemplate::New(isolate, AnalyzeStackInNativeCode));
LocalContext context(nullptr, templ);
// Test getting OVERVIEW information. Should ignore information that is not
// script name, function name, line number, and column offset.
const char* overview_source =
"function bar() {\n"
" var y; AnalyzeStackInNativeCode(1);\n"
"}\n"
"function foo() {\n"
"\n"
" bar();\n"
"}\n"
"var x;eval('new foo();');";
v8::Local<v8::String> overview_src = v8_str(overview_source);
v8::ScriptCompiler::Source script_source(overview_src,
v8::ScriptOrigin(isolate, origin));
......@@ -302,15 +368,6 @@ TEST(CaptureStackTrace) {
CHECK(!overview_result.IsEmpty());
CHECK(overview_result->IsObject());
// Test getting DETAILED information.
const char* detailed_source =
"function bat() {AnalyzeStackInNativeCode(2);\n"
"}\n"
"\n"
"function baz() {\n"
" bat();\n"
"}\n"
"eval('new baz();');";
v8::Local<v8::String> detailed_src = v8_str(detailed_source);
// Make the script using a non-zero line and column offset.
v8::ScriptOrigin detailed_origin(isolate, origin, 3, 5);
......@@ -324,21 +381,6 @@ TEST(CaptureStackTrace) {
CHECK(!detailed_result.IsEmpty());
CHECK(detailed_result->IsObject());
// Test using function.name and function.displayName in stack trace
const char function_name_source[] =
"function bar(function_name, display_name, testGroup) {\n"
" var f = new Function(`AnalyzeStackInNativeCode(${testGroup});`);\n"
" if (function_name) {\n"
" Object.defineProperty(f, 'name', { value: function_name });\n"
" }\n"
" if (display_name) {\n"
" f.displayName = display_name;"
" }\n"
" f()\n"
"}\n"
"bar('function.name', undefined, 3);\n"
"bar('function.name', 'function.displayName', 4);\n"
"bar(239, undefined, 5);\n";
v8::Local<v8::String> function_name_src =
v8::String::NewFromUtf8Literal(isolate, function_name_source);
v8::ScriptCompiler::Source script_source3(function_name_src,
......@@ -353,33 +395,37 @@ TEST(CaptureStackTrace) {
}
static int report_count = 0;
// Test uncaught exception
const char uncaught_exception_source[] =
"function foo() {\n"
" throw 1;\n"
"};\n"
"function bar() {\n"
" foo();\n"
"};";
static void StackTraceForUncaughtExceptionListener(
v8::Local<v8::Message> message, v8::Local<Value>) {
report_count++;
v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
CHECK_EQ(2, stack_trace->GetFrameCount());
checkStackFrame("origin", "foo", 2, 3, false, false,
checkStackFrame("origin", uncaught_exception_source, nullptr, "foo", 2, 3,
false, false,
stack_trace->GetFrame(message->GetIsolate(), 0));
checkStackFrame("origin", "bar", 5, 3, false, false,
checkStackFrame("origin", uncaught_exception_source, nullptr, "bar", 5, 3,
false, false,
stack_trace->GetFrame(message->GetIsolate(), 1));
}
TEST(CaptureStackTraceForUncaughtException) {
report_count = 0;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
isolate->AddMessageListener(StackTraceForUncaughtExceptionListener);
isolate->SetCaptureStackTraceForUncaughtExceptions(true);
CompileRunWithOrigin(
"function foo() {\n"
" throw 1;\n"
"};\n"
"function bar() {\n"
" foo();\n"
"};",
"origin");
CompileRunWithOrigin(uncaught_exception_source, "origin");
v8::Local<v8::Object> global = env->Global();
Local<Value> trouble =
global->Get(env.local(), v8_str("bar")).ToLocalChecked();
......@@ -392,40 +438,100 @@ TEST(CaptureStackTraceForUncaughtException) {
CHECK_EQ(1, report_count);
}
// Test uncaught exception in a setter
const char uncaught_setter_exception_source[] =
"var setters = ['column', 'lineNumber', 'scriptName',\n"
" 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
" 'isConstructor'];\n"
"for (let i = 0; i < setters.length; i++) {\n"
" let prop = setters[i];\n"
" Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
"}\n";
static void StackTraceForUncaughtExceptionAndSettersListener(
v8::Local<v8::Message> message, v8::Local<Value> value) {
CHECK(value->IsObject());
v8::Isolate* isolate = message->GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
report_count++;
v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
CHECK_EQ(1, stack_trace->GetFrameCount());
checkStackFrame(nullptr, "throw 'exception';", nullptr, nullptr, 1, 1, false,
false, stack_trace->GetFrame(isolate, 0));
v8::Local<v8::StackFrame> stack_frame = stack_trace->GetFrame(isolate, 0);
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
CHECK(object
->Set(context,
v8::String::NewFromUtf8Literal(isolate, "lineNumber"),
v8::Integer::New(isolate, stack_frame->GetLineNumber()))
.IsNothing());
}
TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
report_count = 0;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object = v8::Object::New(isolate);
isolate->AddMessageListener(StackTraceForUncaughtExceptionAndSettersListener,
object);
isolate->SetCaptureStackTraceForUncaughtExceptions(true, 1024,
v8::StackTrace::kDetailed);
CompileRun(
"var setters = ['column', 'lineNumber', 'scriptName',\n"
" 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
" 'isConstructor'];\n"
"for (var i = 0; i < setters.length; i++) {\n"
" var prop = setters[i];\n"
" Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
"}\n");
CompileRun(uncaught_setter_exception_source);
CompileRun("throw 'exception';");
isolate->SetCaptureStackTraceForUncaughtExceptions(false);
}
isolate->RemoveMessageListeners(
StackTraceForUncaughtExceptionAndSettersListener);
CHECK(object
->Get(isolate->GetCurrentContext(),
v8::String::NewFromUtf8Literal(isolate, "lineNumber"))
.ToLocalChecked()
->IsUndefined());
CHECK_EQ(report_count, 1);
}
const char functions_with_function_name[] =
"function gen(name, counter) {\n"
" var f = function foo() {\n"
" if (counter === 0)\n"
" throw 1;\n"
" gen(name, counter - 1)();\n"
" };\n"
" if (counter == 3) {\n"
" Object.defineProperty(f, 'name', {get: function(){ throw 239; }});\n"
" } else {\n"
" Object.defineProperty(f, 'name', {writable:true});\n"
" if (counter == 2)\n"
" f.name = 42;\n"
" else\n"
" f.name = name + ':' + counter;\n"
" }\n"
" return f;\n"
"};"
"//# sourceMappingURL=local/functional.sc";
const char functions_with_function_name_caller[] = "gen('foo', 3)();";
static void StackTraceFunctionNameListener(v8::Local<v8::Message> message,
v8::Local<Value>) {
v8::Local<v8::StackTrace> stack_trace = message->GetStackTrace();
v8::Isolate* isolate = message->GetIsolate();
CHECK_EQ(5, stack_trace->GetFrameCount());
checkStackFrame("origin", "foo:0", 4, 7, false, false,
checkStackFrame("origin", functions_with_function_name, "local/functional.sc",
"foo:0", 4, 7, false, false,
stack_trace->GetFrame(isolate, 0));
checkStackFrame("origin", "foo:1", 5, 27, false, false,
checkStackFrame("origin", functions_with_function_name, "local/functional.sc",
"foo:1", 5, 27, false, false,
stack_trace->GetFrame(isolate, 1));
checkStackFrame("origin", "foo", 5, 27, false, false,
checkStackFrame("origin", functions_with_function_name, "local/functional.sc",
"foo", 5, 27, false, false,
stack_trace->GetFrame(isolate, 2));
checkStackFrame("origin", "foo", 5, 27, false, false,
checkStackFrame("origin", functions_with_function_name, "local/functional.sc",
"foo", 5, 27, false, false,
stack_trace->GetFrame(isolate, 3));
checkStackFrame("origin", "", 1, 14, false, false,
stack_trace->GetFrame(isolate, 4));
checkStackFrame("origin", functions_with_function_name_caller, nullptr, "", 1,
14, false, false, stack_trace->GetFrame(isolate, 4));
}
TEST(GetStackTraceContainsFunctionsWithFunctionName) {
......@@ -433,29 +539,11 @@ TEST(GetStackTraceContainsFunctionsWithFunctionName) {
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
CompileRunWithOrigin(
"function gen(name, counter) {\n"
" var f = function foo() {\n"
" if (counter === 0)\n"
" throw 1;\n"
" gen(name, counter - 1)();\n"
" };\n"
" if (counter == 3) {\n"
" Object.defineProperty(f, 'name', {get: function(){ throw 239; }});\n"
" } else {\n"
" Object.defineProperty(f, 'name', {writable:true});\n"
" if (counter == 2)\n"
" f.name = 42;\n"
" else\n"
" f.name = name + ':' + counter;\n"
" }\n"
" return f;\n"
"};",
"origin");
CompileRunWithOrigin(functions_with_function_name, "origin");
isolate->AddMessageListener(StackTraceFunctionNameListener);
isolate->SetCaptureStackTraceForUncaughtExceptions(true);
CompileRunWithOrigin("gen('foo', 3)();", "origin");
CompileRunWithOrigin(functions_with_function_name_caller, "origin");
isolate->SetCaptureStackTraceForUncaughtExceptions(false);
isolate->RemoveMessageListeners(StackTraceFunctionNameListener);
}
......
......@@ -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