#ifndef V8_TEST_COMMON_ASSEMBLER_TESTER_H_
#define V8_TEST_COMMON_ASSEMBLER_TESTER_H_
#include <memory>
#include "src/codegen/assembler.h"
#include "src/codegen/flush-instruction-cache.h"
#include "src/common/code-memory-access.h"
namespace v8 {
namespace internal {
class TestingAssemblerBuffer : public AssemblerBuffer {
public:
TestingAssemblerBuffer(size_t requested, void* address,
JitPermission jit_permission = JitPermission::kNoJit)
: protection_reconfiguration_is_allowed_(true) {
size_t page_size = v8::internal::AllocatePageSize();
size_t alloc_size = RoundUp(requested, page_size);
CHECK_GE(kMaxInt, alloc_size);
reservation_ = VirtualMemory(
GetPlatformPageAllocator(), alloc_size,
v8::PageAllocator::AllocationHint().WithAddress(address), page_size,
jit_permission == JitPermission::kNoJit
? v8::PageAllocator::Permission::kNoAccess
: v8::PageAllocator::Permission::kNoAccessWillJitLater);
CHECK(reservation_.IsReserved());
MakeWritable();
}
~TestingAssemblerBuffer() override { reservation_.Free(); }
uint8_t* start() const override {
return reinterpret_cast<uint8_t*>(reservation_.address());
}
int size() const override { return static_cast<int>(reservation_.size()); }
std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
FATAL("Cannot grow TestingAssemblerBuffer");
}
std::unique_ptr<AssemblerBuffer> CreateView() const {
return ExternalAssemblerBuffer(start(), size());
}
void MakeExecutable() {
FlushInstructionCache(start(), size());
if (protection_reconfiguration_is_allowed_) {
bool result = SetPermissions(GetPlatformPageAllocator(), start(), size(),
v8::PageAllocator::kReadExecute);
CHECK(result);
}
}
void MakeWritable() {
if (protection_reconfiguration_is_allowed_) {
bool result = SetPermissions(GetPlatformPageAllocator(), start(), size(),
v8::PageAllocator::kReadWrite);
CHECK(result);
}
}
void MakeWritableAndExecutable() {
bool result = SetPermissions(GetPlatformPageAllocator(), start(), size(),
v8::PageAllocator::kReadWriteExecute);
CHECK(result);
protection_reconfiguration_is_allowed_ =
!V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT &&
!V8_HEAP_USE_BECORE_JIT_WRITE_PROTECT &&
protection_reconfiguration_is_allowed_;
}
private:
VirtualMemory reservation_;
bool protection_reconfiguration_is_allowed_;
};
class V8_NODISCARD AssemblerBufferWriteScope final {
public:
explicit AssemblerBufferWriteScope(TestingAssemblerBuffer& buffer)
: buffer_(buffer) {
buffer_.MakeWritable();
}
~AssemblerBufferWriteScope() { buffer_.MakeExecutable(); }
AssemblerBufferWriteScope(const AssemblerBufferWriteScope&) = delete;
AssemblerBufferWriteScope& operator=(const AssemblerBufferWriteScope&) =
delete;
private:
RwxMemoryWriteScopeForTesting rwx_write_scope_;
TestingAssemblerBuffer& buffer_;
};
static inline std::unique_ptr<TestingAssemblerBuffer> AllocateAssemblerBuffer(
size_t requested = v8::internal::AssemblerBase::kDefaultBufferSize,
void* address = nullptr,
JitPermission jit_permission = JitPermission::kMapAsJittable) {
return std::make_unique<TestingAssemblerBuffer>(requested, address,
jit_permission);
}
}
}
#endif