Commit 552ffd38 authored by jameslahm's avatar jameslahm Committed by V8 LUCI CQ

[test] Migrate cctest/test-api-accessors.cc to unittests/

... api/accessor-unittest.cc.

- Add IsInt32, IsString, IsUndefined matcher in
testing/gmock-support.h.

Bug: v8:12781
Change-Id: I764491d7643e35fb8bc1621e857873aa24f64ccd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3593573Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80577}
parent df73fd60
...@@ -178,7 +178,6 @@ v8_source_set("cctest_sources") { ...@@ -178,7 +178,6 @@ v8_source_set("cctest_sources") {
"test-accessor-assembler.cc", "test-accessor-assembler.cc",
"test-accessors.cc", "test-accessors.cc",
"test-allocation.cc", "test-allocation.cc",
"test-api-accessors.cc",
"test-api-array-buffer.cc", "test-api-array-buffer.cc",
"test-api-icu.cc", "test-api-icu.cc",
"test-api-interceptors.cc", "test-api-interceptors.cc",
......
...@@ -210,6 +210,7 @@ v8_source_set("unittests_sources") { ...@@ -210,6 +210,7 @@ v8_source_set("unittests_sources") {
"../../testing/gmock-support.h", "../../testing/gmock-support.h",
"../../testing/gtest-support.h", "../../testing/gtest-support.h",
"api/access-check-unittest.cc", "api/access-check-unittest.cc",
"api/accessor-unittest.cc",
"api/deserialize-unittest.cc", "api/deserialize-unittest.cc",
"api/exception-unittest.cc", "api/exception-unittest.cc",
"api/interceptor-unittest.cc", "api/interceptor-unittest.cc",
......
...@@ -12,102 +12,109 @@ ...@@ -12,102 +12,109 @@
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/execution/isolate.h" #include "src/execution/isolate.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "test/cctest/cctest.h" #include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace i = v8::internal; namespace i = v8::internal;
using testing::IsInt32;
using testing::IsString;
using testing::IsUndefined;
using AccessorTest = v8::TestWithContext;
// The goal is to avoid the callback. // The goal is to avoid the callback.
static void UnreachableCallback( static void UnreachableCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) { const v8::FunctionCallbackInfo<v8::Value>& info) {
UNREACHABLE(); UNREACHABLE();
} }
TEST(CachedAccessor) { TEST_F(AccessorTest, CachedAccessor) {
// TurboFan support for fast accessors is not implemented; turbofanned // TurboFan support for fast accessors is not implemented; turbofanned
// code uses the slow accessor which breaks this test's expectations. // code uses the slow accessor which breaks this test's expectations.
v8::internal::FLAG_always_turbofan = false; v8::internal::FLAG_always_turbofan = false;
LocalContext env; v8::Isolate* isolate = context()->GetIsolate();
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
// Create 'foo' class, with a hidden property. // Create 'foo' class, with a hidden property.
v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Private> priv = v8::Local<v8::Private> priv =
v8::Private::ForApi(isolate, v8_str("Foo#draft")); v8::Private::ForApi(isolate, NewString("Foo#draft"));
foo->SetAccessorProperty(v8_str("draft"), v8::FunctionTemplate::NewWithCache( foo->SetAccessorProperty(
isolate, UnreachableCallback, NewString("draft"),
priv, v8::Local<v8::Value>())); v8::FunctionTemplate::NewWithCache(isolate, UnreachableCallback, priv,
v8::Local<v8::Value>()));
// Create 'obj', instance of 'foo'. // Create 'obj', instance of 'foo'.
v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = foo->NewInstance(context()).ToLocalChecked();
// Install the private property on the instance. // Install the private property on the instance.
CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv, CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv,
v8::Undefined(isolate)) v8::Undefined(isolate))
.FromJust()); .FromJust());
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
// Access cached accessor. // Access cached accessor.
ExpectUndefined("obj.draft"); EXPECT_THAT(RunJS("obj.draft"), IsUndefined());
// Set hidden property. // Set hidden property.
CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv, CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv,
v8_str("Shhh, I'm private!")) NewString("Shhh, I'm private!"))
.FromJust()); .FromJust());
ExpectString("obj.draft", "Shhh, I'm private!"); EXPECT_THAT(RunJS("obj.draft"), IsString("Shhh, I'm private!"));
// Stress the accessor to use the IC. // Stress the accessor to use the IC.
ExpectString( EXPECT_THAT(RunJS("var result = '';"
"var result = '';" "for (var i = 0; i < 10; ++i) { "
"for (var i = 0; i < 10; ++i) { " " result = obj.draft; "
" result = obj.draft; " "} "
"} " "result; "),
"result; ", IsString("Shhh, I'm private!"));
"Shhh, I'm private!");
} }
TEST(CachedAccessorTurboFan) { TEST_F(AccessorTest, CachedAccessorTurboFan) {
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
// v8::internal::FLAG_always_turbofan = false; // v8::internal::FLAG_always_turbofan = false;
LocalContext env; v8::Isolate* isolate = context()->GetIsolate();
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
// Create 'foo' class, with a hidden property. // Create 'foo' class, with a hidden property.
v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> foo = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Private> priv = v8::Local<v8::Private> priv =
v8::Private::ForApi(isolate, v8_str("Foo#draft")); v8::Private::ForApi(isolate, NewString("Foo#draft"));
// Install the private property on the template. // Install the private property on the template.
// foo->SetPrivate(priv, v8::Undefined(isolate)); // foo->SetPrivate(priv, v8::Undefined(isolate));
foo->SetAccessorProperty(v8_str("draft"), v8::FunctionTemplate::NewWithCache( foo->SetAccessorProperty(
isolate, UnreachableCallback, NewString("draft"),
priv, v8::Local<v8::Value>())); v8::FunctionTemplate::NewWithCache(isolate, UnreachableCallback, priv,
v8::Local<v8::Value>()));
// Create 'obj', instance of 'foo'. // Create 'obj', instance of 'foo'.
v8::Local<v8::Object> obj = foo->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = foo->NewInstance(context()).ToLocalChecked();
// Install the private property on the instance. // Install the private property on the instance.
CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv, CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv,
v8::Undefined(isolate)) v8::Undefined(isolate))
.FromJust()); .FromJust());
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
// Access surrogate accessor. // Access surrogate accessor.
ExpectUndefined("obj.draft"); EXPECT_THAT(RunJS("obj.draft"), IsUndefined());
// Set hidden property. // Set hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 123)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate, 123))
.FromJust()); .FromJust());
// Test ICs. // Test ICs.
CompileRun( RunJS(
"function f() {" "function f() {"
" var x;" " var x;"
" for (var i = 0; i < 100; i++) {" " for (var i = 0; i < 100; i++) {"
...@@ -117,21 +124,21 @@ TEST(CachedAccessorTurboFan) { ...@@ -117,21 +124,21 @@ TEST(CachedAccessorTurboFan) {
"};" "};"
"%PrepareFunctionForOptimization(f);"); "%PrepareFunctionForOptimization(f);");
ExpectInt32("f()", 123); EXPECT_THAT(RunJS("f()"), IsInt32(123));
// Reset hidden property. // Reset hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 456)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate, 456))
.FromJust()); .FromJust());
// Test TurboFan. // Test TurboFan.
CompileRun("%OptimizeFunctionOnNextCall(f);"); RunJS("%OptimizeFunctionOnNextCall(f);");
ExpectInt32("f()", 456); EXPECT_THAT(RunJS("f()"), IsInt32(456));
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 456)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate, 456))
.FromJust()); .FromJust());
// Test non-global ICs. // Test non-global ICs.
CompileRun( RunJS(
"function g() {" "function g() {"
" var x = obj;" " var x = obj;"
" var r = 0;" " var r = 0;"
...@@ -142,56 +149,53 @@ TEST(CachedAccessorTurboFan) { ...@@ -142,56 +149,53 @@ TEST(CachedAccessorTurboFan) {
"};" "};"
"%PrepareFunctionForOptimization(g);"); "%PrepareFunctionForOptimization(g);");
ExpectInt32("g()", 456); EXPECT_THAT(RunJS("g()"), IsInt32(456));
// Reset hidden property. // Reset hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 789)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate, 789))
.FromJust()); .FromJust());
// Test non-global access in TurboFan. // Test non-global access in TurboFan.
CompileRun("%OptimizeFunctionOnNextCall(g);"); RunJS("%OptimizeFunctionOnNextCall(g);");
ExpectInt32("g()", 789); EXPECT_THAT(RunJS("g()"), IsInt32(789));
} }
TEST(CachedAccessorOnGlobalObject) { TEST_F(AccessorTest, CachedAccessorOnGlobalObject) {
i::FLAG_allow_natives_syntax = true; i::FLAG_allow_natives_syntax = true;
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::FunctionTemplate> templ = v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate());
v8::FunctionTemplate::New(CcTest::isolate());
v8::Local<v8::ObjectTemplate> object_template = templ->InstanceTemplate(); v8::Local<v8::ObjectTemplate> object_template = templ->InstanceTemplate();
v8::Local<v8::Private> priv = v8::Local<v8::Private> priv =
v8::Private::ForApi(isolate, v8_str("Foo#draft")); v8::Private::ForApi(isolate(), NewString("Foo#draft"));
object_template->SetAccessorProperty( object_template->SetAccessorProperty(
v8_str("draft"), NewString("draft"),
v8::FunctionTemplate::NewWithCache(isolate, UnreachableCallback, priv, v8::FunctionTemplate::NewWithCache(isolate(), UnreachableCallback, priv,
v8::Local<v8::Value>())); v8::Local<v8::Value>()));
v8::Local<v8::Context> ctx = v8::Local<v8::Context> ctx =
v8::Context::New(CcTest::isolate(), nullptr, object_template); v8::Context::New(isolate(), nullptr, object_template);
v8::Local<v8::Object> obj = ctx->Global(); v8::Local<v8::Object> obj = ctx->Global();
// Install the private property on the instance. // Install the private property on the instance.
CHECK(obj->SetPrivate(isolate->GetCurrentContext(), priv, CHECK(obj->SetPrivate(isolate()->GetCurrentContext(), priv,
v8::Undefined(isolate)) v8::Undefined(isolate()))
.FromJust()); .FromJust());
{ {
v8::Context::Scope context_scope(ctx); v8::Context::Scope context_scope(ctx);
// Access surrogate accessor. // Access surrogate accessor.
ExpectUndefined("draft"); EXPECT_THAT(RunJS("draft"), IsUndefined());
// Set hidden property. // Set hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 123)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate(), 123))
.FromJust()); .FromJust());
// Test ICs. // Test ICs.
CompileRun( RunJS(
"function f() {" "function f() {"
" var x;" " var x;"
" for (var i = 0; i < 100; i++) {" " for (var i = 0; i < 100; i++) {"
...@@ -201,21 +205,21 @@ TEST(CachedAccessorOnGlobalObject) { ...@@ -201,21 +205,21 @@ TEST(CachedAccessorOnGlobalObject) {
"}" "}"
"%PrepareFunctionForOptimization(f);"); "%PrepareFunctionForOptimization(f);");
ExpectInt32("f()", 123); EXPECT_THAT(RunJS("f()"), IsInt32(123));
// Reset hidden property. // Reset hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 456)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate(), 456))
.FromJust()); .FromJust());
// Test TurboFan. // Test TurboFan.
CompileRun("%OptimizeFunctionOnNextCall(f);"); RunJS("%OptimizeFunctionOnNextCall(f);");
ExpectInt32("f()", 456); EXPECT_THAT(RunJS("f()"), IsInt32(456));
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 456)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate(), 456))
.FromJust()); .FromJust());
// Test non-global ICs. // Test non-global ICs.
CompileRun( RunJS(
"var x = this;" "var x = this;"
"function g() {" "function g() {"
" var r = 0;" " var r = 0;"
...@@ -226,16 +230,16 @@ TEST(CachedAccessorOnGlobalObject) { ...@@ -226,16 +230,16 @@ TEST(CachedAccessorOnGlobalObject) {
"}" "}"
"%PrepareFunctionForOptimization(g);"); "%PrepareFunctionForOptimization(g);");
ExpectInt32("g()", 456); EXPECT_THAT(RunJS("g()"), IsInt32(456));
// Reset hidden property. // Reset hidden property.
CHECK(obj->SetPrivate(env.local(), priv, v8::Integer::New(isolate, 789)) CHECK(obj->SetPrivate(context(), priv, v8::Integer::New(isolate(), 789))
.FromJust()); .FromJust());
// Test non-global access in TurboFan. // Test non-global access in TurboFan.
CompileRun("%OptimizeFunctionOnNextCall(g);"); RunJS("%OptimizeFunctionOnNextCall(g);");
ExpectInt32("g()", 789); EXPECT_THAT(RunJS("g()"), IsInt32(789));
} }
} }
...@@ -244,7 +248,9 @@ namespace { ...@@ -244,7 +248,9 @@ namespace {
// Getter return value should be non-null to trigger lazy property paths. // Getter return value should be non-null to trigger lazy property paths.
static void Getter(v8::Local<v8::Name> name, static void Getter(v8::Local<v8::Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_str("return value")); info.GetReturnValue().Set(
v8::String::NewFromUtf8(info.GetIsolate(), "return value")
.ToLocalChecked());
} }
static void StringGetter(v8::Local<v8::String> name, static void StringGetter(v8::Local<v8::String> name,
...@@ -261,25 +267,23 @@ static void EmptyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {} ...@@ -261,25 +267,23 @@ static void EmptyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {}
} // namespace } // namespace
// Re-declaration of non-configurable accessors should throw. // Re-declaration of non-configurable accessors should throw.
TEST(RedeclareAccessor) { TEST_F(AccessorTest, RedeclareAccessor) {
v8::HandleScope scope(CcTest::isolate()); v8::HandleScope scope(isolate());
LocalContext env;
v8::Local<v8::FunctionTemplate> templ = v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate());
v8::FunctionTemplate::New(CcTest::isolate());
v8::Local<v8::ObjectTemplate> object_template = templ->InstanceTemplate(); v8::Local<v8::ObjectTemplate> object_template = templ->InstanceTemplate();
object_template->SetAccessor( object_template->SetAccessor(
v8_str("foo"), nullptr, Setter, v8::Local<v8::Value>(), NewString("foo"), nullptr, Setter, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::DontDelete); v8::AccessControl::DEFAULT, v8::PropertyAttribute::DontDelete);
v8::Local<v8::Context> ctx = v8::Local<v8::Context> ctx =
v8::Context::New(CcTest::isolate(), nullptr, object_template); v8::Context::New(isolate(), nullptr, object_template);
// Declare function. // Declare function.
v8::Local<v8::String> code = v8_str("function foo() {};"); v8::Local<v8::String> code = NewString("function foo() {};");
v8::TryCatch try_catch(CcTest::isolate()); v8::TryCatch try_catch(isolate());
v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty(); v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty();
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
...@@ -295,7 +299,8 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate, ...@@ -295,7 +299,8 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate,
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::Script> func = v8_compile(call_getter); v8::Local<v8::Script> func =
v8::Script::Compile(context, call_getter).ToLocalChecked();
// Check getter. Run enough number of times to ensure IC creates data handler. // Check getter. Run enough number of times to ensure IC creates data handler.
for (int i = 0; i < kIterationsCountForICProgression; i++) { for (int i = 0; i < kIterationsCountForICProgression; i++) {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
...@@ -311,7 +316,7 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate, ...@@ -311,7 +316,7 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate,
CHECK(!func->Run(context).IsEmpty()); CHECK(!func->Run(context).IsEmpty());
} }
func = v8_compile(call_setter); func = v8::Script::Compile(context, call_setter).ToLocalChecked();
// Check setter. Run enough number of times to ensure IC creates data handler. // Check setter. Run enough number of times to ensure IC creates data handler.
for (int i = 0; i < kIterationsCountForICProgression; i++) { for (int i = 0; i < kIterationsCountForICProgression; i++) {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
...@@ -328,202 +333,188 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate, ...@@ -328,202 +333,188 @@ static void CheckSideEffectFreeAccesses(v8::Isolate* isolate,
} }
} }
TEST(AccessorsWithSideEffects) { TEST_F(AccessorTest, AccessorsWithSideEffects) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
v8::Local<v8::FunctionTemplate> templ_with_sideffect = v8::Local<v8::FunctionTemplate> templ_with_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(), v8::FunctionTemplate::New(
v8::Local<v8::Signature>(), 0, isolate(), EmptyCallback, v8::Local<v8::Value>(),
v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasSideEffect); v8::SideEffectType::kHasSideEffect);
v8::Local<v8::FunctionTemplate> templ_no_sideffect = v8::Local<v8::FunctionTemplate> templ_no_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(), v8::FunctionTemplate::New(
v8::Local<v8::Signature>(), 0, isolate(), EmptyCallback, v8::Local<v8::Value>(),
v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
// Install non-native properties with side effects // Install non-native properties with side effects
obj->SetAccessorProperty( obj->SetAccessorProperty(
v8_str("get"), NewString("get"),
templ_with_sideffect->GetFunction(context).ToLocalChecked(), {}, templ_with_sideffect->GetFunction(context()).ToLocalChecked(), {},
v8::PropertyAttribute::None, v8::AccessControl::DEFAULT); v8::PropertyAttribute::None, v8::AccessControl::DEFAULT);
obj->SetAccessorProperty( obj->SetAccessorProperty(
v8_str("set"), templ_no_sideffect->GetFunction(context).ToLocalChecked(), NewString("set"),
templ_with_sideffect->GetFunction(context).ToLocalChecked(), templ_no_sideffect->GetFunction(context()).ToLocalChecked(),
templ_with_sideffect->GetFunction(context()).ToLocalChecked(),
v8::PropertyAttribute::None, v8::AccessControl::DEFAULT); v8::PropertyAttribute::None, v8::AccessControl::DEFAULT);
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"), CheckSideEffectFreeAccesses(isolate(), NewString("obj.get"),
v8_str("obj.set = 123;")); NewString("obj.set = 123;"));
} }
TEST(TemplateAccessorsWithSideEffects) { TEST_F(AccessorTest, TemplateAccessorsWithSideEffects) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::FunctionTemplate> templ_with_sideffect = v8::Local<v8::FunctionTemplate> templ_with_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(), v8::FunctionTemplate::New(
v8::Local<v8::Signature>(), 0, isolate(), EmptyCallback, v8::Local<v8::Value>(),
v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasSideEffect); v8::SideEffectType::kHasSideEffect);
v8::Local<v8::FunctionTemplate> templ_no_sideffect = v8::Local<v8::FunctionTemplate> templ_no_sideffect =
v8::FunctionTemplate::New(isolate, EmptyCallback, v8::Local<v8::Value>(), v8::FunctionTemplate::New(
v8::Local<v8::Signature>(), 0, isolate(), EmptyCallback, v8::Local<v8::Value>(),
v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
templ->SetAccessorProperty(v8_str("get"), templ_with_sideffect); templ->SetAccessorProperty(NewString("get"), templ_with_sideffect);
templ->SetAccessorProperty(v8_str("set"), templ_no_sideffect, templ->SetAccessorProperty(NewString("set"), templ_no_sideffect,
templ_with_sideffect); templ_with_sideffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"), CheckSideEffectFreeAccesses(isolate(), NewString("obj.get"),
v8_str("obj.set = 123;")); NewString("obj.set = 123;"));
} }
TEST(NativeTemplateAccessorWithSideEffects) { TEST_F(AccessorTest, NativeTemplateAccessorWithSideEffects) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
templ->SetAccessor(v8_str("get"), Getter, nullptr, v8::Local<v8::Value>(), templ->SetAccessor(NewString("get"), Getter, nullptr, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None, v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::SideEffectType::kHasSideEffect); v8::SideEffectType::kHasSideEffect);
templ->SetAccessor(v8_str("set"), Getter, Setter, v8::Local<v8::Value>(), templ->SetAccessor(NewString("set"), Getter, Setter, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None, v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect, v8::SideEffectType::kHasNoSideEffect,
v8::SideEffectType::kHasSideEffect); v8::SideEffectType::kHasSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"), CheckSideEffectFreeAccesses(isolate(), NewString("obj.get"),
v8_str("obj.set = 123;")); NewString("obj.set = 123;"));
} }
TEST(NativeAccessorsWithSideEffects) { TEST_F(AccessorTest, NativeAccessorsWithSideEffects) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
// Install native data property with side effects. // Install native data property with side effects.
obj->SetAccessor(context, v8_str("get"), Getter, nullptr, obj->SetAccessor(context(), NewString("get"), Getter, nullptr,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT, v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasSideEffect) v8::SideEffectType::kHasSideEffect)
.ToChecked(); .ToChecked();
obj->SetAccessor(context, v8_str("set"), Getter, Setter, obj->SetAccessor(context(), NewString("set"), Getter, Setter,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT, v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect, v8::SideEffectType::kHasNoSideEffect,
v8::SideEffectType::kHasSideEffect) v8::SideEffectType::kHasSideEffect)
.ToChecked(); .ToChecked();
CheckSideEffectFreeAccesses(isolate, v8_str("obj.get"), CheckSideEffectFreeAccesses(isolate(), NewString("obj.get"),
v8_str("obj.set = 123;")); NewString("obj.set = 123;"));
} }
// Accessors can be allowlisted as side-effect-free via SetAccessor. // Accessors can be allowlisted as side-effect-free via SetAccessor.
TEST(AccessorSetHasNoSideEffect) { TEST_F(AccessorTest, AccessorSetHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
obj->SetAccessor(context, v8_str("foo"), Getter).ToChecked(); obj->SetAccessor(context(), NewString("foo"), Getter).ToChecked();
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
obj->SetAccessor(context, v8_str("foo"), Getter, nullptr, obj->SetAccessor(context(), NewString("foo"), Getter, nullptr,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT, v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect) v8::SideEffectType::kHasNoSideEffect)
.ToChecked(); .ToChecked();
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo = 1"), isolate(), NewString("obj.foo = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
CHECK_EQ(0, set_accessor_call_count); CHECK_EQ(0, set_accessor_call_count);
} }
// Set accessors can be allowlisted as side-effect-free via SetAccessor. // Set accessors can be allowlisted as side-effect-free via SetAccessor.
TEST(SetAccessorSetSideEffectReceiverCheck1) { TEST_F(AccessorTest, SetAccessorSetSideEffectReceiverCheck1) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
obj->SetAccessor(env.local(), v8_str("foo"), Getter, Setter, obj->SetAccessor(context(), NewString("foo"), Getter, Setter,
v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT, v8::MaybeLocal<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect, v8::SideEffectType::kHasNoSideEffect,
v8::SideEffectType::kHasSideEffectToReceiver) v8::SideEffectType::kHasSideEffectToReceiver)
.ToChecked(); .ToChecked();
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked() .ToLocalChecked()
->Equals(env.local(), v8_str("return value")) ->Equals(context(), NewString("return value"))
.FromJust()); .FromJust());
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo = 1"), isolate(), NewString("obj.foo = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
...@@ -533,284 +524,279 @@ TEST(SetAccessorSetSideEffectReceiverCheck1) { ...@@ -533,284 +524,279 @@ TEST(SetAccessorSetSideEffectReceiverCheck1) {
static void ConstructCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { static void ConstructCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
} }
TEST(SetAccessorSetSideEffectReceiverCheck2) { TEST_F(AccessorTest, SetAccessorSetSideEffectReceiverCheck2) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New( v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(
isolate, ConstructCallback, v8::Local<v8::Value>(), isolate(), ConstructCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow, v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kAllow,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
templ->InstanceTemplate()->SetAccessor( templ->InstanceTemplate()->SetAccessor(
v8_str("bar"), Getter, Setter, v8::Local<v8::Value>(), NewString("bar"), Getter, Setter, v8::Local<v8::Value>(),
v8::AccessControl::DEFAULT, v8::PropertyAttribute::None, v8::AccessControl::DEFAULT, v8::PropertyAttribute::None,
v8::SideEffectType::kHasSideEffectToReceiver, v8::SideEffectType::kHasSideEffectToReceiver,
v8::SideEffectType::kHasSideEffectToReceiver); v8::SideEffectType::kHasSideEffectToReceiver);
CHECK(env->Global() CHECK(context()
->Set(env.local(), v8_str("f"), ->Global()
templ->GetFunction(env.local()).ToLocalChecked()) ->Set(context(), NewString("f"),
templ->GetFunction(context()).ToLocalChecked())
.FromJust()); .FromJust());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("new f().bar"), isolate(), NewString("new f().bar"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked() .ToLocalChecked()
->Equals(env.local(), v8_str("return value")) ->Equals(context(), NewString("return value"))
.FromJust()); .FromJust());
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("new f().bar = 1"), isolate(), NewString("new f().bar = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
CHECK_EQ(1, set_accessor_call_count); CHECK_EQ(1, set_accessor_call_count);
} }
// Accessors can be allowlisted as side-effect-free via SetNativeDataProperty. // Accessors can be allowlisted as side-effect-free via SetNativeDataProperty.
TEST(AccessorSetNativeDataPropertyHasNoSideEffect) { TEST_F(AccessorTest, AccessorSetNativeDataPropertyHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
obj->SetNativeDataProperty(context, v8_str("foo"), Getter).ToChecked(); obj->SetNativeDataProperty(context(), NewString("foo"), Getter).ToChecked();
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
obj->SetNativeDataProperty( obj->SetNativeDataProperty(
context, v8_str("foo"), Getter, nullptr, v8::Local<v8::Value>(), context(), NewString("foo"), Getter, nullptr, v8::Local<v8::Value>(),
v8::PropertyAttribute::None, v8::SideEffectType::kHasNoSideEffect) v8::PropertyAttribute::None, v8::SideEffectType::kHasNoSideEffect)
.ToChecked(); .ToChecked();
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo = 1"), isolate(), NewString("obj.foo = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
} }
// Accessors can be allowlisted as side-effect-free via SetLazyDataProperty. // Accessors can be allowlisted as side-effect-free via SetLazyDataProperty.
TEST(AccessorSetLazyDataPropertyHasNoSideEffect) { TEST_F(AccessorTest, AccessorSetLazyDataPropertyHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
obj->SetLazyDataProperty(context, v8_str("foo"), Getter).ToChecked(); obj->SetLazyDataProperty(context(), NewString("foo"), Getter).ToChecked();
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
obj->SetLazyDataProperty(context, v8_str("foo"), Getter, obj->SetLazyDataProperty(context(), NewString("foo"), Getter,
v8::Local<v8::Value>(), v8::PropertyAttribute::None, v8::Local<v8::Value>(), v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect) v8::SideEffectType::kHasNoSideEffect)
.ToChecked(); .ToChecked();
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo = 1"), isolate(), NewString("obj.foo = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
} }
TEST(ObjectTemplateSetAccessorHasNoSideEffect) { TEST_F(AccessorTest, ObjectTemplateSetAccessorHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
templ->SetAccessor(v8_str("foo"), StringGetter); templ->SetAccessor(NewString("foo"), StringGetter);
templ->SetAccessor(v8_str("foo2"), StringGetter, nullptr, templ->SetAccessor(NewString("foo2"), StringGetter, nullptr,
v8::Local<v8::Value>(), v8::AccessControl::DEFAULT, v8::Local<v8::Value>(), v8::AccessControl::DEFAULT,
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2"), isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2 = 1"), isolate(), NewString("obj.foo2 = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
} }
TEST(ObjectTemplateSetNativePropertyHasNoSideEffect) { TEST_F(AccessorTest, ObjectTemplateSetNativePropertyHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
templ->SetNativeDataProperty(v8_str("foo"), Getter); templ->SetNativeDataProperty(NewString("foo"), Getter);
templ->SetNativeDataProperty( templ->SetNativeDataProperty(
v8_str("foo2"), Getter, nullptr, v8::Local<v8::Value>(), NewString("foo2"), Getter, nullptr, v8::Local<v8::Value>(),
v8::PropertyAttribute::None, v8::AccessControl::DEFAULT, v8::PropertyAttribute::None, v8::AccessControl::DEFAULT,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2"), isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2 = 1"), isolate(), NewString("obj.foo2 = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
} }
TEST(ObjectTemplateSetLazyPropertyHasNoSideEffect) { TEST_F(AccessorTest, ObjectTemplateSetLazyPropertyHasNoSideEffect) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate());
NoopDelegate delegate; NoopDelegate delegate;
i_isolate->debug()->SetDebugDelegate(&delegate); i_isolate->debug()->SetDebugDelegate(&delegate);
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate); v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate());
templ->SetLazyDataProperty(v8_str("foo"), Getter); templ->SetLazyDataProperty(NewString("foo"), Getter);
templ->SetLazyDataProperty(v8_str("foo2"), Getter, v8::Local<v8::Value>(), templ->SetLazyDataProperty(NewString("foo2"), Getter, v8::Local<v8::Value>(),
v8::PropertyAttribute::None, v8::PropertyAttribute::None,
v8::SideEffectType::kHasNoSideEffect); v8::SideEffectType::kHasNoSideEffect);
v8::Local<v8::Object> obj = templ->NewInstance(env.local()).ToLocalChecked(); v8::Local<v8::Object> obj = templ->NewInstance(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("obj"), obj).FromJust()); CHECK(context()->Global()->Set(context(), NewString("obj"), obj).FromJust());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo"), isolate(), NewString("obj.foo"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
v8::debug::EvaluateGlobal( v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2"), isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.ToLocalChecked(); .ToLocalChecked();
// Check that setter is not allowlisted. // Check that setter is not allowlisted.
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(v8::debug::EvaluateGlobal( CHECK(v8::debug::EvaluateGlobal(
isolate, v8_str("obj.foo2 = 1"), isolate(), NewString("obj.foo2 = 1"),
v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
.IsEmpty()); .IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
CHECK_NE(1, v8::debug::EvaluateGlobal(isolate, v8_str("obj.foo2"), CHECK_NE(1, v8::debug::EvaluateGlobal(isolate(), NewString("obj.foo2"),
v8::debug::EvaluateGlobalMode::kDefault) v8::debug::EvaluateGlobalMode::kDefault)
.ToLocalChecked() .ToLocalChecked()
->Int32Value(env.local()) ->Int32Value(context())
.FromJust()); .FromJust());
} }
namespace { namespace {
void FunctionNativeGetter(v8::Local<v8::String> property, void FunctionNativeGetter(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetIsolate()->ThrowError(v8_str("side effect in getter")); info.GetIsolate()->ThrowError(
v8::String::NewFromUtf8(info.GetIsolate(), "side effect in getter")
.ToLocalChecked());
} }
} // namespace } // namespace
TEST(BindFunctionTemplateSetNativeDataProperty) { TEST_F(AccessorTest, BindFunctionTemplateSetNativeDataProperty) {
LocalContext env; v8::HandleScope scope(isolate());
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
// Check that getter is called on Function.prototype.bind. // Check that getter is called on Function.prototype.bind.
{ {
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); v8::Local<v8::FunctionTemplate> templ =
templ->SetNativeDataProperty(v8_str("name"), FunctionNativeGetter); v8::FunctionTemplate::New(isolate());
templ->SetNativeDataProperty(NewString("name"), FunctionNativeGetter);
v8::Local<v8::Function> func = v8::Local<v8::Function> func =
templ->GetFunction(env.local()).ToLocalChecked(); templ->GetFunction(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("func"), func).FromJust()); CHECK(context()
->Global()
->Set(context(), NewString("func"), func)
.FromJust());
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(CompileRun("func.bind()").IsEmpty()); CHECK(TryRunJS("func.bind()").IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
// Check that getter is called on Function.prototype.bind. // Check that getter is called on Function.prototype.bind.
{ {
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); v8::Local<v8::FunctionTemplate> templ =
templ->SetNativeDataProperty(v8_str("length"), FunctionNativeGetter); v8::FunctionTemplate::New(isolate());
templ->SetNativeDataProperty(NewString("length"), FunctionNativeGetter);
v8::Local<v8::Function> func = v8::Local<v8::Function> func =
templ->GetFunction(env.local()).ToLocalChecked(); templ->GetFunction(context()).ToLocalChecked();
CHECK(env->Global()->Set(env.local(), v8_str("func"), func).FromJust()); CHECK(context()
->Global()
->Set(context(), NewString("func"), func)
.FromJust());
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK(CompileRun("func.bind()").IsEmpty()); CHECK(TryRunJS("func.bind()").IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
} }
...@@ -826,24 +812,30 @@ v8::MaybeLocal<v8::Context> TestHostCreateShadowRealmContextCallback( ...@@ -826,24 +812,30 @@ v8::MaybeLocal<v8::Context> TestHostCreateShadowRealmContextCallback(
// Check that getter is called on Function.prototype.bind. // Check that getter is called on Function.prototype.bind.
global_template->SetNativeDataProperty( global_template->SetNativeDataProperty(
v8_str("func1"), [](v8::Local<v8::String> property, v8::String::NewFromUtf8(isolate, "func1").ToLocalChecked(),
const v8::PropertyCallbackInfo<v8::Value>& info) { [](v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate(); v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::FunctionTemplate> templ = v8::Local<v8::FunctionTemplate> templ =
v8::FunctionTemplate::New(isolate); v8::FunctionTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("name"), FunctionNativeGetter); templ->SetNativeDataProperty(
v8::String::NewFromUtf8(isolate, "name").ToLocalChecked(),
FunctionNativeGetter);
info.GetReturnValue().Set( info.GetReturnValue().Set(
templ->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()); templ->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
}); });
// Check that getter is called on Function.prototype.bind. // Check that getter is called on Function.prototype.bind.
global_template->SetNativeDataProperty( global_template->SetNativeDataProperty(
v8_str("func2"), [](v8::Local<v8::String> property, v8::String::NewFromUtf8(isolate, "func2").ToLocalChecked(),
const v8::PropertyCallbackInfo<v8::Value>& info) { [](v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate(); v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::FunctionTemplate> templ = v8::Local<v8::FunctionTemplate> templ =
v8::FunctionTemplate::New(isolate); v8::FunctionTemplate::New(isolate);
templ->SetNativeDataProperty(v8_str("length"), FunctionNativeGetter); templ->SetNativeDataProperty(
v8::String::NewFromUtf8(isolate, "length").ToLocalChecked(),
FunctionNativeGetter);
info.GetReturnValue().Set( info.GetReturnValue().Set(
templ->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()); templ->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
}); });
...@@ -852,26 +844,22 @@ v8::MaybeLocal<v8::Context> TestHostCreateShadowRealmContextCallback( ...@@ -852,26 +844,22 @@ v8::MaybeLocal<v8::Context> TestHostCreateShadowRealmContextCallback(
} }
} // namespace } // namespace
TEST(WrapFunctionTemplateSetNativeDataProperty) { TEST_F(AccessorTest, WrapFunctionTemplateSetNativeDataProperty) {
i::FLAG_harmony_shadow_realm = true; i::FLAG_harmony_shadow_realm = true;
LocalContext env; isolate()->SetHostCreateShadowRealmContextCallback(
v8::Isolate* isolate = env->GetIsolate();
isolate->SetHostCreateShadowRealmContextCallback(
TestHostCreateShadowRealmContextCallback); TestHostCreateShadowRealmContextCallback);
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate());
// Check that getter is called on WrappedFunctionCreate. // Check that getter is called on WrappedFunctionCreate.
{ {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK( CHECK(TryRunJS("new ShadowRealm().evaluate('globalThis.func1')").IsEmpty());
CompileRun("new ShadowRealm().evaluate('globalThis.func1')").IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
// Check that getter is called on WrappedFunctionCreate. // Check that getter is called on WrappedFunctionCreate.
{ {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate());
CHECK( CHECK(TryRunJS("new ShadowRealm().evaluate('globalThis.func2')").IsEmpty());
CompileRun("new ShadowRealm().evaluate('globalThis.func2')").IsEmpty());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
} }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <cstring> #include <cstring>
#include <string> #include <string>
#include "include/v8-isolate.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
namespace testing { namespace testing {
...@@ -72,6 +73,30 @@ MATCHER_P(BitEq, x, std::string(negation ? "isn't" : "is") + ...@@ -72,6 +73,30 @@ MATCHER_P(BitEq, x, std::string(negation ? "isn't" : "is") +
return std::memcmp(&arg, &x, sizeof(x)) == 0; return std::memcmp(&arg, &x, sizeof(x)) == 0;
} }
// Creates a polymorphic matcher that matches JSValue to Int32.
MATCHER_P(IsInt32, expected,
std::string(negation ? "isn't" : "is") + " Int32 " +
PrintToString(expected)) {
return arg->IsInt32() &&
arg->Int32Value(v8::Isolate::GetCurrent()->GetCurrentContext())
.FromJust() == expected;
}
// Creates a polymorphic matcher that matches JSValue to String.
MATCHER_P(IsString, expected,
std::string(negation ? "isn't" : "is") + " String " +
PrintToString(expected)) {
if (!arg->IsString()) {
return false;
}
v8::String::Utf8Value utf8(v8::Isolate::GetCurrent(), arg);
return strcmp(expected, *utf8) == 0;
}
// Creates a polymorphic matcher that matches JSValue to Undefined.
MATCHER(IsUndefined, std::string(negation ? "isn't" : "is") + " Undefined") {
return arg->IsUndefined();
}
// CaptureEq(capture) captures the value passed in during matching as long as it // CaptureEq(capture) captures the value passed in during matching as long as it
// is unset, and once set, compares the value for equality with the argument. // is unset, and once set, compares the value for equality with the argument.
......
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