Commit e2679ec7 authored by jameslahm's avatar jameslahm Committed by V8 LUCI CQ

[test] Migrate TEST(TryCatch...) in cctest/test-api to unittests.

- Add RunJSNoChecked to run the script which possibly throws.
- Add CompileRun to run the script outside TEST_F, e.g., in
FunctionTemplate and helpers etc.

Bug: v8:12781
Change-Id: Ibab2e19cf1f7c76f7a81a90fc5894e7e6bfb7cdf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3586770Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Cr-Commit-Position: refs/heads/main@{#80025}
parent ea0496e6
......@@ -4548,26 +4548,6 @@ THREADED_TEST(ScriptException) {
CHECK_EQ(0, strcmp(*exception_value, "panama!"));
}
TEST(TryCatchCustomException) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::TryCatch try_catch(isolate);
CompileRun(
"function CustomError() { this.a = 'b'; }"
"(function f() { throw new CustomError(); })();");
CHECK(try_catch.HasCaught());
CHECK(try_catch.Exception()
->ToObject(env.local())
.ToLocalChecked()
->Get(env.local(), v8_str("a"))
.ToLocalChecked()
->Equals(env.local(), v8_str("b"))
.FromJust());
}
bool message_received;
......@@ -5581,23 +5561,6 @@ void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetIsolate()->ThrowException(v8_str("konto"));
}
void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 1) {
args.GetReturnValue().Set(false);
return;
}
v8::HandleScope scope(args.GetIsolate());
v8::TryCatch try_catch(args.GetIsolate());
Local<Value> result =
CompileRun(args[0]
->ToString(args.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
CHECK(!try_catch.HasCaught() || result.IsEmpty());
args.GetReturnValue().Set(try_catch.HasCaught());
}
THREADED_TEST(APICatch) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
......@@ -5631,32 +5594,6 @@ THREADED_TEST(APIThrowTryCatch) {
CHECK(try_catch.HasCaught());
}
// Test that a try-finally block doesn't shadow a try-catch block
// when setting up an external handler.
//
// BUG(271): Some of the exception propagation does not work on the
// ARM simulator because the simulator separates the C++ stack and the
// JS stack. This test therefore fails on the simulator. The test is
// not threaded to allow the threading tests to run on the simulator.
TEST(TryCatchInTryFinally) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(isolate, "CCatcher", v8::FunctionTemplate::New(isolate, CCatcher));
LocalContext context(nullptr, templ);
Local<Value> result = CompileRun(
"try {"
" try {"
" CCatcher('throw 7;');"
" } finally {"
" }"
"} catch (e) {"
"}");
CHECK(result->IsTrue());
}
static void check_custom_error_tostring(v8::Local<v8::Message> message,
v8::Local<v8::Value> data) {
const char* uncaught_error = "Uncaught MyError toString";
......@@ -6132,57 +6069,6 @@ THREADED_TEST(TryCatchAndFinally) {
CHECK(try_catch.HasCaught());
}
static void TryCatchNested1Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(CcTest::isolate());
try_catch.SetVerbose(true);
TryCatchNested1Helper(depth - 1);
CHECK(try_catch.HasCaught());
try_catch.ReThrow();
} else {
CcTest::isolate()->ThrowException(v8_str("E1"));
}
}
static void TryCatchNested2Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(CcTest::isolate());
try_catch.SetVerbose(true);
TryCatchNested2Helper(depth - 1);
CHECK(try_catch.HasCaught());
try_catch.ReThrow();
} else {
CompileRun("throw 'E2';");
}
}
TEST(TryCatchNested) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
{
// Test nested try-catch with a native throw in the end.
v8::TryCatch try_catch(context->GetIsolate());
TryCatchNested1Helper(5);
CHECK(try_catch.HasCaught());
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(),
try_catch.Exception()),
"E1"));
}
{
// Test nested try-catch with a JavaScript throw in the end.
v8::TryCatch try_catch(context->GetIsolate());
TryCatchNested2Helper(5);
CHECK(try_catch.HasCaught());
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(context->GetIsolate(),
try_catch.Exception()),
"E2"));
}
}
void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
Local<Message> message = try_catch->Message();
......@@ -9046,66 +8932,6 @@ TEST(CompilationErrorUsingTryCatchHandler) {
CHECK(try_catch.HasCaught());
}
TEST(TryCatchFinallyUsingTryCatchHandler) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::TryCatch try_catch(env->GetIsolate());
CompileRun("try { throw ''; } catch (e) {}");
CHECK(!try_catch.HasCaught());
CompileRun("try { throw ''; } finally {}");
CHECK(try_catch.HasCaught());
try_catch.Reset();
CompileRun(
"(function() {"
"try { throw ''; } finally { return; }"
"})()");
CHECK(!try_catch.HasCaught());
CompileRun(
"(function()"
" { try { throw ''; } finally { throw 0; }"
"})()");
CHECK(try_catch.HasCaught());
}
void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
CompileRun(args[0]
->ToString(args.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
}
TEST(TryCatchFinallyStoresMessageUsingTryCatchHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
templ->Set(isolate, "CEvaluate",
v8::FunctionTemplate::New(isolate, CEvaluate));
LocalContext context(nullptr, templ);
v8::TryCatch try_catch(isolate);
CompileRun("try {"
" CEvaluate('throw 1;');"
"} finally {"
"}");
CHECK(try_catch.HasCaught());
CHECK(!try_catch.Message().IsEmpty());
String::Utf8Value exception_value(isolate, try_catch.Exception());
CHECK_EQ(0, strcmp(*exception_value, "1"));
try_catch.Reset();
CompileRun("try {"
" CEvaluate('throw 1;');"
"} finally {"
" throw 2;"
"}");
CHECK(try_catch.HasCaught());
CHECK(!try_catch.Message().IsEmpty());
String::Utf8Value finally_exception_value(isolate, try_catch.Exception());
CHECK_EQ(0, strcmp(*finally_exception_value, "2"));
}
// For use within the TestSecurityHandler() test.
static bool g_security_callback_result = false;
static bool SecurityTestCallback(Local<v8::Context> accessing_context,
......@@ -20760,49 +20586,6 @@ THREADED_TEST(CheckIsLeafTemplateForApiObject) {
CHECK(!templ->IsLeafTemplateForApiObject(instance));
}
TEST(TryFinallyMessage) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
{
// Test that the original error message is not lost if there is a
// recursive call into Javascript is done in the finally block, e.g. to
// initialize an IC. (crbug.com/129171)
TryCatch try_catch(context->GetIsolate());
const char* trigger_ic =
"try { \n"
" throw new Error('test'); \n"
"} finally { \n"
" var x = 0; \n"
" x++; \n" // Trigger an IC initialization here.
"} \n";
CompileRun(trigger_ic);
CHECK(try_catch.HasCaught());
Local<Message> message = try_catch.Message();
CHECK(!message.IsEmpty());
CHECK_EQ(2, message->GetLineNumber(context.local()).FromJust());
}
{
// Test that the original exception message is indeed overwritten if
// a new error is thrown in the finally block.
TryCatch try_catch(context->GetIsolate());
const char* throw_again =
"try { \n"
" throw new Error('test'); \n"
"} finally { \n"
" var x = 0; \n"
" x++; \n"
" throw new Error('again'); \n" // This is the new uncaught error.
"} \n";
CompileRun(throw_again);
CHECK(try_catch.HasCaught());
Local<Message> message = try_catch.Message();
CHECK(!message.IsEmpty());
CHECK_EQ(6, message->GetLineNumber(context.local()).FromJust());
}
}
static void Helper137002(bool do_store,
bool polymorphic,
bool remove_accessor,
......@@ -7,6 +7,7 @@
#include "include/v8-isolate.h"
#include "include/v8-local-handle.h"
#include "include/v8-persistent-handle.h"
#include "include/v8-template.h"
#include "src/flags/flags.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -14,7 +15,32 @@
namespace v8 {
namespace {
using APIExceptionTest = TestWithIsolate;
class APIExceptionTest : public TestWithIsolate {
public:
static void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
TryRunJS(args.GetIsolate(),
args[0]
->ToString(args.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
}
static void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() < 1) {
args.GetReturnValue().Set(false);
return;
}
v8::HandleScope scope(args.GetIsolate());
v8::TryCatch try_catch(args.GetIsolate());
MaybeLocal<Value> result =
TryRunJS(args.GetIsolate(),
args[0]
->ToString(args.GetIsolate()->GetCurrentContext())
.ToLocalChecked());
CHECK(!try_catch.HasCaught() || result.IsEmpty());
args.GetReturnValue().Set(try_catch.HasCaught());
}
};
class V8_NODISCARD ScopedExposeGc {
public:
......@@ -45,5 +71,196 @@ TEST_F(APIExceptionTest, ExceptionMessageDoesNotKeepContextAlive) {
EXPECT_TRUE(weak_context.IsEmpty());
}
TEST_F(APIExceptionTest, TryCatchCustomException) {
v8::HandleScope scope(isolate());
v8::Local<Context> context = Context::New(isolate());
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(isolate());
TryRunJS(
"function CustomError() { this.a = 'b'; }"
"(function f() { throw new CustomError(); })();");
EXPECT_TRUE(try_catch.HasCaught());
EXPECT_TRUE(try_catch.Exception()
->ToObject(context)
.ToLocalChecked()
->Get(context, NewString("a"))
.ToLocalChecked()
->Equals(context, NewString("b"))
.FromJust());
}
class TryCatchNestedTest : public TestWithIsolate {
public:
void TryCatchNested1Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(isolate());
try_catch.SetVerbose(true);
TryCatchNested1Helper(depth - 1);
EXPECT_TRUE(try_catch.HasCaught());
try_catch.ReThrow();
} else {
isolate()->ThrowException(NewString("E1"));
}
}
void TryCatchNested2Helper(int depth) {
if (depth > 0) {
v8::TryCatch try_catch(isolate());
try_catch.SetVerbose(true);
TryCatchNested2Helper(depth - 1);
EXPECT_TRUE(try_catch.HasCaught());
try_catch.ReThrow();
} else {
TryRunJS("throw 'E2';");
}
}
};
TEST_F(TryCatchNestedTest, TryCatchNested) {
v8::HandleScope scope(isolate());
Local<Context> context = Context::New(isolate());
v8::Context::Scope context_scope(context);
{
// Test nested try-catch with a native throw in the end.
v8::TryCatch try_catch(isolate());
TryCatchNested1Helper(5);
EXPECT_TRUE(try_catch.HasCaught());
EXPECT_EQ(
0,
strcmp(*v8::String::Utf8Value(isolate(), try_catch.Exception()), "E1"));
}
{
// Test nested try-catch with a JavaScript throw in the end.
v8::TryCatch try_catch(isolate());
TryCatchNested2Helper(5);
EXPECT_TRUE(try_catch.HasCaught());
EXPECT_EQ(
0,
strcmp(*v8::String::Utf8Value(isolate(), try_catch.Exception()), "E2"));
}
}
TEST_F(APIExceptionTest, TryCatchFinallyUsingTryCatchHandler) {
v8::HandleScope scope(isolate());
Local<Context> context = Context::New(isolate());
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(isolate());
TryRunJS("try { throw ''; } catch (e) {}");
EXPECT_TRUE(!try_catch.HasCaught());
TryRunJS("try { throw ''; } finally {}");
EXPECT_TRUE(try_catch.HasCaught());
try_catch.Reset();
TryRunJS(
"(function() {"
"try { throw ''; } finally { return; }"
"})()");
EXPECT_TRUE(!try_catch.HasCaught());
TryRunJS(
"(function()"
" { try { throw ''; } finally { throw 0; }"
"})()");
EXPECT_TRUE(try_catch.HasCaught());
}
TEST_F(APIExceptionTest, TryFinallyMessage) {
v8::HandleScope scope(isolate());
v8::Local<Context> context = Context::New(isolate());
v8::Context::Scope context_scope(context);
{
// Test that the original error message is not lost if there is a
// recursive call into Javascript is done in the finally block, e.g. to
// initialize an IC. (crbug.com/129171)
TryCatch try_catch(isolate());
const char* trigger_ic =
"try { \n"
" throw new Error('test'); \n"
"} finally { \n"
" var x = 0; \n"
" x++; \n" // Trigger an IC initialization here.
"} \n";
TryRunJS(trigger_ic);
EXPECT_TRUE(try_catch.HasCaught());
Local<Message> message = try_catch.Message();
EXPECT_TRUE(!message.IsEmpty());
EXPECT_EQ(2, message->GetLineNumber(context).FromJust());
}
{
// Test that the original exception message is indeed overwritten if
// a new error is thrown in the finally block.
TryCatch try_catch(isolate());
const char* throw_again =
"try { \n"
" throw new Error('test'); \n"
"} finally { \n"
" var x = 0; \n"
" x++; \n"
" throw new Error('again'); \n" // This is the new uncaught error.
"} \n";
TryRunJS(throw_again);
EXPECT_TRUE(try_catch.HasCaught());
Local<Message> message = try_catch.Message();
EXPECT_TRUE(!message.IsEmpty());
EXPECT_EQ(6, message->GetLineNumber(context).FromJust());
}
}
// Test that a try-finally block doesn't shadow a try-catch block
// when setting up an external handler.
//
// BUG(271): Some of the exception propagation does not work on the
// ARM simulator because the simulator separates the C++ stack and the
// JS stack. This test therefore fails on the simulator. The test is
// not threaded to allow the threading tests to run on the simulator.
TEST_F(APIExceptionTest, TryCatchInTryFinally) {
v8::HandleScope scope(isolate());
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate());
templ->Set(isolate(), "CCatcher",
v8::FunctionTemplate::New(isolate(), CCatcher));
Local<Context> context = Context::New(isolate(), nullptr, templ);
v8::Context::Scope context_scope(context);
Local<Value> result = RunJS(
"try {"
" try {"
" CCatcher('throw 7;');"
" } finally {"
" }"
"} catch (e) {"
"}");
EXPECT_TRUE(result->IsTrue());
}
TEST_F(APIExceptionTest, TryCatchFinallyStoresMessageUsingTryCatchHandler) {
v8::HandleScope scope(isolate());
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate());
templ->Set(isolate(), "CEvaluate",
v8::FunctionTemplate::New(isolate(), CEvaluate));
Local<Context> context = Context::New(isolate(), nullptr, templ);
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(isolate());
TryRunJS(
"try {"
" CEvaluate('throw 1;');"
"} finally {"
"}");
EXPECT_TRUE(try_catch.HasCaught());
EXPECT_TRUE(!try_catch.Message().IsEmpty());
String::Utf8Value exception_value(isolate(), try_catch.Exception());
EXPECT_EQ(0, strcmp(*exception_value, "1"));
try_catch.Reset();
TryRunJS(
"try {"
" CEvaluate('throw 1;');"
"} finally {"
" throw 2;"
"}");
EXPECT_TRUE(try_catch.HasCaught());
EXPECT_TRUE(!try_catch.Message().IsEmpty());
String::Utf8Value finally_exception_value(isolate(), try_catch.Exception());
EXPECT_EQ(0, strcmp(*finally_exception_value, "2"));
}
} // namespace
} // namespace v8
......@@ -106,6 +106,19 @@ class WithIsolateScopeMixin : public TMixin {
v8::String::NewFromUtf8(this->v8_isolate(), source).ToLocalChecked());
}
MaybeLocal<Value> TryRunJS(const char* source) {
return TryRunJS(
v8::String::NewFromUtf8(this->v8_isolate(), source).ToLocalChecked());
}
static MaybeLocal<Value> TryRunJS(Isolate* isolate, Local<String> source) {
auto context = isolate->GetCurrentContext();
v8::Local<v8::Value> result;
Local<Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
return script->Run(context);
}
Local<Value> RunJS(v8::String::ExternalOneByteStringResource* source) {
return RunJS(v8::String::NewExternalOneByte(this->v8_isolate(), source)
.ToLocalChecked());
......@@ -137,10 +150,11 @@ class WithIsolateScopeMixin : public TMixin {
private:
Local<Value> RunJS(Local<String> source) {
auto context = this->v8_isolate()->GetCurrentContext();
Local<Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
return script->Run(context).ToLocalChecked();
return TryRunJS(source).ToLocalChecked();
}
MaybeLocal<Value> TryRunJS(Local<String> source) {
return TryRunJS(this->v8_isolate(), source);
}
v8::Isolate::Scope isolate_scope_;
......
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