// 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 "src/execution/isolate.h" #include "include/libplatform/libplatform.h" #include "include/v8-platform.h" #include "include/v8-template.h" #include "src/base/platform/semaphore.h" #include "src/init/v8.h" #include "test/unittests/test-utils.h" #include "testing/gtest/include/gtest/gtest.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; MemoryPressureTask(const MemoryPressureTask&) = delete; MemoryPressureTask& operator=(const MemoryPressureTask&) = delete; // v8::Task implementation. void Run() override { isolate_->MemoryPressureNotification(MemoryPressureLevel::kCritical); semaphore_->Signal(); } private: Isolate* isolate_; base::Semaphore* semaphore_; }; } // 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(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(isolate()); base::Semaphore semaphore(0); internal::V8::GetCurrentPlatform()->CallOnWorkerThread( std::make_unique(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).ToLocalChecked(); }; auto Run = [&](Local 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 get_incumbent_global = FunctionTemplate::New( isolate(), [](const FunctionCallbackInfo& info) { Local incumbent_context = info.GetIsolate()->GetIncumbentContext(); info.GetReturnValue().Set(incumbent_context->Global()); }); Local global_template = ObjectTemplate::New(isolate()); global_template->Set(isolate(), "getIncumbentGlobal", get_incumbent_global); Local context_a = Context::New(isolate(), nullptr, global_template); Local context_b = Context::New(isolate(), nullptr, global_template); Local context_c = Context::New(isolate(), nullptr, global_template); Local global_a = context_a->Global(); Local global_b = context_b->Global(); Local global_c = context_c->Global(); Local 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 { thread_local std::multimap crash_keys; void CrashKeyCallback(v8::CrashKeyId id, const std::string& value) { crash_keys.insert({id, value}); } } // namespace TEST_F(IsolateTest, SetAddCrashKeyCallback) { isolate()->SetAddCrashKeyCallback(CrashKeyCallback); i::Isolate* i_isolate = reinterpret_cast(isolate()); i::Heap* heap = i_isolate->heap(); size_t expected_keys_count = 5; EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kIsolateAddress), 1u); EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kReadonlySpaceFirstPageAddress), 1u); EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kOldSpaceFirstPageAddress), 1u); EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kSnapshotChecksumCalculated), 1u); EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kSnapshotChecksumExpected), 1u); if (heap->code_range_base()) { ++expected_keys_count; EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kCodeRangeBaseAddress), 1u); } if (heap->code_space()->first_page()) { ++expected_keys_count; EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kCodeSpaceFirstPageAddress), 1u); } EXPECT_EQ(crash_keys.size(), expected_keys_count); } } // namespace v8