| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #include "include/libplatform/libplatform.h" |
| #include "include/v8-platform.h" |
| #include "include/v8.h" |
| #include "src/base/macros.h" |
| #include "src/base/platform/semaphore.h" |
| #include "src/base/template-utils.h" |
| #include "src/execution/execution.h" |
| #include "src/execution/isolate.h" |
| #include "src/init/v8.h" |
| #include "test/unittests/test-utils.h" |
| |
| namespace v8 { |
| |
| using IsolateTest = TestWithIsolate; |
| |
| namespace { |
| |
| class MemoryPressureTask : public v8::Task { |
| public: |
| MemoryPressureTask(Isolate* isolate, base::Semaphore* semaphore) |
| : isolate_(isolate), semaphore_(semaphore) {} |
| ~MemoryPressureTask() override = default; |
| |
| // v8::Task implementation. |
| void Run() override { |
| isolate_->MemoryPressureNotification(MemoryPressureLevel::kCritical); |
| semaphore_->Signal(); |
| } |
| |
| private: |
| Isolate* isolate_; |
| base::Semaphore* semaphore_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MemoryPressureTask); |
| }; |
| |
| } // namespace |
| |
| // Check that triggering a memory pressure notification on the isolate thread |
| // doesn't request a GC interrupt. |
| TEST_F(IsolateTest, MemoryPressureNotificationForeground) { |
| internal::Isolate* i_isolate = |
| reinterpret_cast<internal::Isolate*>(isolate()); |
| |
| ASSERT_FALSE(i_isolate->stack_guard()->CheckGC()); |
| isolate()->MemoryPressureNotification(MemoryPressureLevel::kCritical); |
| ASSERT_FALSE(i_isolate->stack_guard()->CheckGC()); |
| } |
| |
| // Check that triggering a memory pressure notification on an background thread |
| // requests a GC interrupt. |
| TEST_F(IsolateTest, MemoryPressureNotificationBackground) { |
| internal::Isolate* i_isolate = |
| reinterpret_cast<internal::Isolate*>(isolate()); |
| |
| base::Semaphore semaphore(0); |
| |
| internal::V8::GetCurrentPlatform()->CallOnWorkerThread( |
| base::make_unique<MemoryPressureTask>(isolate(), &semaphore)); |
| |
| semaphore.Wait(); |
| |
| ASSERT_TRUE(i_isolate->stack_guard()->CheckGC()); |
| v8::platform::PumpMessageLoop(internal::V8::GetCurrentPlatform(), isolate()); |
| } |
| |
| using IncumbentContextTest = TestWithIsolate; |
| |
| // Check that Isolate::GetIncumbentContext() returns the correct one in basic |
| // scenarios. |
| TEST_F(IncumbentContextTest, Basic) { |
| auto Str = [&](const char* s) { |
| return String::NewFromUtf8(isolate(), s, NewStringType::kNormal) |
| .ToLocalChecked(); |
| }; |
| auto Run = [&](Local<Context> context, const char* script) { |
| Context::Scope scope(context); |
| return Script::Compile(context, Str(script)) |
| .ToLocalChecked() |
| ->Run(context) |
| .ToLocalChecked(); |
| }; |
| |
| // Set up the test environment; three contexts with getIncumbentGlobal() |
| // function. |
| Local<FunctionTemplate> get_incumbent_global = FunctionTemplate::New( |
| isolate(), [](const FunctionCallbackInfo<Value>& info) { |
| Local<Context> incumbent_context = |
| info.GetIsolate()->GetIncumbentContext(); |
| info.GetReturnValue().Set(incumbent_context->Global()); |
| }); |
| Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate()); |
| global_template->Set(Str("getIncumbentGlobal"), get_incumbent_global); |
| |
| Local<Context> context_a = Context::New(isolate(), nullptr, global_template); |
| Local<Context> context_b = Context::New(isolate(), nullptr, global_template); |
| Local<Context> context_c = Context::New(isolate(), nullptr, global_template); |
| Local<Object> global_a = context_a->Global(); |
| Local<Object> global_b = context_b->Global(); |
| Local<Object> global_c = context_c->Global(); |
| |
| Local<String> security_token = Str("security_token"); |
| context_a->SetSecurityToken(security_token); |
| context_b->SetSecurityToken(security_token); |
| context_c->SetSecurityToken(security_token); |
| |
| global_a->Set(context_a, Str("b"), global_b).ToChecked(); |
| global_b->Set(context_b, Str("c"), global_c).ToChecked(); |
| |
| // Test scenario 2: A -> B -> C, then the incumbent is C. |
| Run(context_a, "funcA = function() { return b.funcB(); }"); |
| Run(context_b, "funcB = function() { return c.getIncumbentGlobal(); }"); |
| // Without BackupIncumbentScope. |
| EXPECT_EQ(global_b, Run(context_a, "funcA()")); |
| { |
| // With BackupIncumbentScope. |
| Context::BackupIncumbentScope backup_incumbent(context_a); |
| EXPECT_EQ(global_b, Run(context_a, "funcA()")); |
| } |
| |
| // Test scenario 2: A -> B -> C -> C, then the incumbent is C. |
| Run(context_a, "funcA = function() { return b.funcB(); }"); |
| Run(context_b, "funcB = function() { return c.funcC(); }"); |
| Run(context_c, "funcC = function() { return getIncumbentGlobal(); }"); |
| // Without BackupIncumbentScope. |
| EXPECT_EQ(global_c, Run(context_a, "funcA()")); |
| { |
| // With BackupIncumbentScope. |
| Context::BackupIncumbentScope backup_incumbent(context_a); |
| EXPECT_EQ(global_c, Run(context_a, "funcA()")); |
| } |
| } |
| |
| } // namespace v8 |