| // Copyright 2018 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 <memory> |
| |
| #include "include/v8.h" |
| |
| #include "src/codegen/code-desc.h" |
| #include "src/execution/isolate.h" |
| #include "src/handles/handles-inl.h" |
| #include "test/cctest/cctest.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace test_factory { |
| |
| namespace { |
| |
| // This needs to be large enough to create a new nosnap Isolate, but smaller |
| // than kMaximalCodeRangeSize so we can recover from the OOM. |
| constexpr int kInstructionSize = 100 * MB; |
| STATIC_ASSERT(kInstructionSize < kMaximalCodeRangeSize || |
| !kPlatformRequiresCodeRange); |
| |
| size_t NearHeapLimitCallback(void* raw_bool, size_t current_heap_limit, |
| size_t initial_heap_limit) { |
| bool* oom_triggered = static_cast<bool*>(raw_bool); |
| *oom_triggered = true; |
| return kInstructionSize * 2; |
| } |
| |
| class SetupIsolateWithSmallHeap { |
| public: |
| SetupIsolateWithSmallHeap() { |
| FLAG_max_old_space_size = kInstructionSize / MB / 2; // In MB. |
| v8::Isolate::CreateParams create_params; |
| create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
| isolate_ = reinterpret_cast<Isolate*>(v8::Isolate::New(create_params)); |
| isolate_->heap()->AddNearHeapLimitCallback(NearHeapLimitCallback, |
| &oom_triggered_); |
| } |
| |
| ~SetupIsolateWithSmallHeap() { |
| reinterpret_cast<v8::Isolate*>(isolate_)->Dispose(); |
| } |
| |
| Isolate* isolate() { return isolate_; } |
| bool oom_triggered() const { return oom_triggered_; } |
| |
| private: |
| Isolate* isolate_; |
| bool oom_triggered_ = false; |
| }; |
| |
| } // namespace |
| |
| TEST(Factory_CodeBuilder) { |
| Isolate* isolate = CcTest::i_isolate(); |
| HandleScope scope(isolate); |
| |
| // Create a big function that ends up in CODE_LO_SPACE. |
| const int instruction_size = |
| MemoryChunkLayout::MaxRegularCodeObjectSize() + 1; |
| std::unique_ptr<byte[]> instructions(new byte[instruction_size]); |
| |
| CodeDesc desc; |
| desc.buffer = instructions.get(); |
| desc.buffer_size = instruction_size; |
| desc.instr_size = instruction_size; |
| desc.reloc_size = 0; |
| desc.constant_pool_size = 0; |
| desc.unwinding_info = nullptr; |
| desc.unwinding_info_size = 0; |
| desc.origin = nullptr; |
| Handle<Code> code = |
| Factory::CodeBuilder(isolate, desc, CodeKind::WASM_FUNCTION).Build(); |
| |
| CHECK(isolate->heap()->InSpace(*code, CODE_LO_SPACE)); |
| #if VERIFY_HEAP |
| code->ObjectVerify(isolate); |
| #endif |
| } |
| |
| UNINITIALIZED_TEST(Factory_CodeBuilder_BuildOOM) { |
| SetupIsolateWithSmallHeap isolate_scope; |
| HandleScope scope(isolate_scope.isolate()); |
| std::unique_ptr<byte[]> instructions(new byte[kInstructionSize]); |
| CodeDesc desc; |
| desc.instr_size = kInstructionSize; |
| desc.buffer = instructions.get(); |
| |
| const Handle<Code> code = Factory::CodeBuilder(isolate_scope.isolate(), desc, |
| CodeKind::WASM_FUNCTION) |
| .Build(); |
| |
| CHECK(!code.is_null()); |
| CHECK(isolate_scope.oom_triggered()); |
| } |
| |
| UNINITIALIZED_TEST(Factory_CodeBuilder_TryBuildOOM) { |
| SetupIsolateWithSmallHeap isolate_scope; |
| HandleScope scope(isolate_scope.isolate()); |
| std::unique_ptr<byte[]> instructions(new byte[kInstructionSize]); |
| CodeDesc desc; |
| desc.instr_size = kInstructionSize; |
| desc.buffer = instructions.get(); |
| |
| const MaybeHandle<Code> code = |
| Factory::CodeBuilder(isolate_scope.isolate(), desc, |
| CodeKind::WASM_FUNCTION) |
| .TryBuild(); |
| |
| CHECK(code.is_null()); |
| CHECK(!isolate_scope.oom_triggered()); |
| } |
| |
| } // namespace test_factory |
| } // namespace internal |
| } // namespace v8 |