// Copyright 2012 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/api/api.h" #include // For min #include // For isnan. #include #include #include #include // For move #include #include "include/v8-callbacks.h" #include "include/v8-cppgc.h" #include "include/v8-date.h" #include "include/v8-embedder-state-scope.h" #include "include/v8-extension.h" #include "include/v8-fast-api-calls.h" #include "include/v8-function.h" #include "include/v8-json.h" #include "include/v8-locker.h" #include "include/v8-primitive-object.h" #include "include/v8-profiler.h" #include "include/v8-source-location.h" #include "include/v8-unwinder-state.h" #include "include/v8-util.h" #include "include/v8-wasm.h" #include "src/api/api-inl.h" #include "src/api/api-natives.h" #include "src/base/functional.h" #include "src/base/logging.h" #include "src/base/platform/memory.h" #include "src/base/platform/platform.h" #include "src/base/platform/time.h" #include "src/base/safe_conversions.h" #include "src/base/utils/random-number-generator.h" #include "src/builtins/accessors.h" #include "src/builtins/builtins-utils.h" #include "src/codegen/compilation-cache.h" #include "src/codegen/compiler.h" #include "src/codegen/cpu-features.h" #include "src/codegen/script-details.h" #include "src/common/assert-scope.h" #include "src/common/globals.h" #include "src/compiler-dispatcher/lazy-compile-dispatcher.h" #include "src/date/date.h" #include "src/debug/debug.h" #include "src/deoptimizer/deoptimizer.h" #include "src/execution/embedder-state.h" #include "src/execution/execution.h" #include "src/execution/frames-inl.h" #include "src/execution/isolate-inl.h" #include "src/execution/messages.h" #include "src/execution/microtask-queue.h" #include "src/execution/simulator.h" #include "src/execution/v8threads.h" #include "src/execution/vm-state-inl.h" #include "src/handles/global-handles.h" #include "src/handles/persistent-handles.h" #include "src/handles/shared-object-conveyor-handles.h" #include "src/handles/traced-handles.h" #include "src/heap/heap-inl.h" #include "src/heap/heap-write-barrier.h" #include "src/heap/safepoint.h" #include "src/init/bootstrapper.h" #include "src/init/icu_util.h" #include "src/init/startup-data-util.h" #include "src/init/v8.h" #include "src/json/json-parser.h" #include "src/json/json-stringifier.h" #include "src/logging/counters-scopes.h" #include "src/logging/metrics.h" #include "src/logging/runtime-call-stats-scope.h" #include "src/logging/tracing-flags.h" #include "src/numbers/conversions-inl.h" #include "src/objects/api-callbacks.h" #include "src/objects/contexts.h" #include "src/objects/embedder-data-array-inl.h" #include "src/objects/embedder-data-slot-inl.h" #include "src/objects/hash-table-inl.h" #include "src/objects/heap-object.h" #include "src/objects/instance-type-inl.h" #include "src/objects/instance-type.h" #include "src/objects/js-array-buffer-inl.h" #include "src/objects/js-array-inl.h" #include "src/objects/js-collection-inl.h" #include "src/objects/js-promise-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/js-weak-refs-inl.h" #include "src/objects/module-inl.h" #include "src/objects/objects-inl.h" #include "src/objects/oddball.h" #include "src/objects/ordered-hash-table-inl.h" #include "src/objects/primitive-heap-object.h" #include "src/objects/property-descriptor.h" #include "src/objects/property-details.h" #include "src/objects/property.h" #include "src/objects/prototype.h" #include "src/objects/shared-function-info.h" #include "src/objects/slots.h" #include "src/objects/smi.h" #include "src/objects/synthetic-module-inl.h" #include "src/objects/templates.h" #include "src/objects/value-serializer.h" #include "src/parsing/parse-info.h" #include "src/parsing/parser.h" #include "src/parsing/pending-compilation-error-handler.h" #include "src/parsing/scanner-character-streams.h" #include "src/profiler/cpu-profiler.h" #include "src/profiler/heap-profiler.h" #include "src/profiler/heap-snapshot-generator-inl.h" #include "src/profiler/profile-generator-inl.h" #include "src/profiler/tick-sample.h" #include "src/regexp/regexp-utils.h" #include "src/roots/static-roots.h" #include "src/runtime/runtime.h" #include "src/sandbox/external-pointer.h" #include "src/sandbox/sandbox.h" #include "src/snapshot/code-serializer.h" #include "src/snapshot/embedded/embedded-data.h" #include "src/snapshot/snapshot.h" #include "src/strings/char-predicates-inl.h" #include "src/strings/string-hasher.h" #include "src/strings/unicode-inl.h" #include "src/tracing/trace-event.h" #include "src/utils/detachable-vector.h" #include "src/utils/identity-map.h" #include "src/utils/version.h" #if V8_ENABLE_WEBASSEMBLY #include "src/debug/debug-wasm-objects.h" #include "src/trap-handler/trap-handler.h" #include "src/wasm/streaming-decoder.h" #include "src/wasm/value-type.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-js.h" #include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-result.h" #include "src/wasm/wasm-serialization.h" #endif // V8_ENABLE_WEBASSEMBLY #if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD #include #include #if V8_ENABLE_WEBASSEMBLY #include "include/v8-wasm-trap-handler-posix.h" #include "src/trap-handler/handler-inside-posix.h" #endif // V8_ENABLE_WEBASSEMBLY #endif // V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD #if V8_OS_WIN #include // This has to come after windows.h. #include #include "include/v8-wasm-trap-handler-win.h" #include "src/trap-handler/handler-inside-win.h" #if defined(V8_OS_WIN64) #include "src/base/platform/wrappers.h" #include "src/diagnostics/unwinding-info-win64.h" #endif // V8_OS_WIN64 #endif // V8_OS_WIN #if defined(V8_OS_WIN) && defined(V8_ENABLE_ETW_STACK_WALKING) #include "src/diagnostics/etw-jit-win.h" #endif // Has to be the last include (doesn't have include guards): #include "src/api/api-macros.h" namespace v8 { static OOMErrorCallback g_oom_error_callback = nullptr; static ScriptOrigin GetScriptOriginForScript(i::Isolate* i_isolate, i::Handle script) { i::Handle scriptName(script->GetNameOrSourceURL(), i_isolate); i::Handle source_map_url(script->source_mapping_url(), i_isolate); i::Handle host_defined_options(script->host_defined_options(), i_isolate); ScriptOriginOptions options(script->origin_options()); bool is_wasm = false; #if V8_ENABLE_WEBASSEMBLY is_wasm = script->type() == i::Script::Type::kWasm; #endif // V8_ENABLE_WEBASSEMBLY v8::ScriptOrigin origin( reinterpret_cast(i_isolate), Utils::ToLocal(scriptName), script->line_offset(), script->column_offset(), options.IsSharedCrossOrigin(), script->id(), Utils::ToLocal(source_map_url), options.IsOpaque(), is_wasm, options.IsModule(), Utils::ToLocal(host_defined_options)); return origin; } // --- E x c e p t i o n B e h a v i o r --- // When V8 cannot allocate memory FatalProcessOutOfMemory is called. The default // OOM error handler is called and execution is stopped. void i::V8::FatalProcessOutOfMemory(i::Isolate* i_isolate, const char* location, const OOMDetails& details) { char last_few_messages[Heap::kTraceRingBufferSize + 1]; char js_stacktrace[Heap::kStacktraceBufferSize + 1]; i::HeapStats heap_stats; if (i_isolate == nullptr) { i_isolate = Isolate::TryGetCurrent(); } if (i_isolate == nullptr) { // If the Isolate is not available for the current thread we cannot retrieve // memory information from the Isolate. Write easy-to-recognize values on // the stack. memset(last_few_messages, 0x0BADC0DE, Heap::kTraceRingBufferSize + 1); memset(js_stacktrace, 0x0BADC0DE, Heap::kStacktraceBufferSize + 1); memset(&heap_stats, 0xBADC0DE, sizeof(heap_stats)); // Give the embedder a chance to handle the condition. If it doesn't, // just crash. if (g_oom_error_callback) g_oom_error_callback(location, details); // Note: The error message needs to be consistent with other OOM error // messages (e.g. below) so that ClusterFuzz recognizes it. FATAL("Fatal process out of memory: %s", location); UNREACHABLE(); } memset(last_few_messages, 0, Heap::kTraceRingBufferSize + 1); memset(js_stacktrace, 0, Heap::kStacktraceBufferSize + 1); intptr_t start_marker; heap_stats.start_marker = &start_marker; size_t ro_space_size; heap_stats.ro_space_size = &ro_space_size; size_t ro_space_capacity; heap_stats.ro_space_capacity = &ro_space_capacity; size_t new_space_size; heap_stats.new_space_size = &new_space_size; size_t new_space_capacity; heap_stats.new_space_capacity = &new_space_capacity; size_t old_space_size; heap_stats.old_space_size = &old_space_size; size_t old_space_capacity; heap_stats.old_space_capacity = &old_space_capacity; size_t code_space_size; heap_stats.code_space_size = &code_space_size; size_t code_space_capacity; heap_stats.code_space_capacity = &code_space_capacity; size_t map_space_size; heap_stats.map_space_size = &map_space_size; size_t map_space_capacity; heap_stats.map_space_capacity = &map_space_capacity; size_t lo_space_size; heap_stats.lo_space_size = &lo_space_size; size_t code_lo_space_size; heap_stats.code_lo_space_size = &code_lo_space_size; size_t global_handle_count; heap_stats.global_handle_count = &global_handle_count; size_t weak_global_handle_count; heap_stats.weak_global_handle_count = &weak_global_handle_count; size_t pending_global_handle_count; heap_stats.pending_global_handle_count = &pending_global_handle_count; size_t near_death_global_handle_count; heap_stats.near_death_global_handle_count = &near_death_global_handle_count; size_t free_global_handle_count; heap_stats.free_global_handle_count = &free_global_handle_count; size_t memory_allocator_size; heap_stats.memory_allocator_size = &memory_allocator_size; size_t memory_allocator_capacity; heap_stats.memory_allocator_capacity = &memory_allocator_capacity; size_t malloced_memory; heap_stats.malloced_memory = &malloced_memory; size_t malloced_peak_memory; heap_stats.malloced_peak_memory = &malloced_peak_memory; size_t objects_per_type[LAST_TYPE + 1] = {0}; heap_stats.objects_per_type = objects_per_type; size_t size_per_type[LAST_TYPE + 1] = {0}; heap_stats.size_per_type = size_per_type; int os_error; heap_stats.os_error = &os_error; heap_stats.last_few_messages = last_few_messages; heap_stats.js_stacktrace = js_stacktrace; intptr_t end_marker; heap_stats.end_marker = &end_marker; if (i_isolate->heap()->HasBeenSetUp()) { // BUG(1718): Don't use the take_snapshot since we don't support // HeapObjectIterator here without doing a special GC. i_isolate->heap()->RecordStats(&heap_stats, false); if (!v8_flags.correctness_fuzzer_suppressions) { char* first_newline = strchr(last_few_messages, '\n'); if (first_newline == nullptr || first_newline[1] == '\0') first_newline = last_few_messages; base::OS::PrintError("\n<--- Last few GCs --->\n%s\n", first_newline); base::OS::PrintError("\n<--- JS stacktrace --->\n%s\n", js_stacktrace); } } Utils::ReportOOMFailure(i_isolate, location, details); if (g_oom_error_callback) g_oom_error_callback(location, details); // If the fatal error handler returns, we stop execution. FATAL("API fatal error handler returned after process out of memory"); } void i::V8::FatalProcessOutOfMemory(i::Isolate* i_isolate, const char* location, const char* detail) { OOMDetails details; details.detail = detail; FatalProcessOutOfMemory(i_isolate, location, details); } void Utils::ReportApiFailure(const char* location, const char* message) { i::Isolate* i_isolate = i::Isolate::TryGetCurrent(); FatalErrorCallback callback = nullptr; if (i_isolate != nullptr) { callback = i_isolate->exception_behavior(); } if (callback == nullptr) { base::OS::PrintError("\n#\n# Fatal error in %s\n# %s\n#\n\n", location, message); base::OS::Abort(); } else { callback(location, message); } i_isolate->SignalFatalError(); } void Utils::ReportOOMFailure(i::Isolate* i_isolate, const char* location, const OOMDetails& details) { if (auto oom_callback = i_isolate->oom_behavior()) { oom_callback(location, details); } else { // TODO(wfh): Remove this fallback once Blink is setting OOM handler. See // crbug.com/614440. FatalErrorCallback fatal_callback = i_isolate->exception_behavior(); if (fatal_callback == nullptr) { // Be careful when changing the error message below; it's matched by // ClusterFuzz. base::OS::PrintError("\n#\n# Fatal %s out of memory: %s\n#\n\n", details.is_heap_oom ? "JavaScript" : "process", location); #ifdef V8_FUZZILLI // Ignore OOM crashes for fuzzing but exit with an error such that // samples are discarded by Fuzzilli. _exit(1); #else base::OS::Abort(); #endif // V8_FUZZILLI } else { fatal_callback(location, details.is_heap_oom ? "Allocation failed - JavaScript heap out of memory" : "Allocation failed - process out of memory"); } } i_isolate->SignalFatalError(); } void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) { i::V8::SetSnapshotBlob(snapshot_blob); } namespace { #ifdef V8_ENABLE_SANDBOX // ArrayBufferAllocator to use when the sandbox is enabled in which case all // ArrayBuffer backing stores need to be allocated inside the sandbox. class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: void* Allocate(size_t length) override { return allocator_->Allocate(length); } void* AllocateUninitialized(size_t length) override { return Allocate(length); } void Free(void* data, size_t length) override { return allocator_->Free(data); } private: // Backend allocator shared by all ArrayBufferAllocator instances. This way, // there is a single region of virtual addres space reserved inside the // sandbox from which all ArrayBufferAllocators allocate their memory, // instead of each allocator creating their own region, which may cause // address space exhaustion inside the sandbox. // TODO(chromium:1340224): replace this with a more efficient allocator. class BackendAllocator { public: BackendAllocator() { CHECK(i::GetProcessWideSandbox()->is_initialized()); VirtualAddressSpace* vas = i::GetProcessWideSandbox()->address_space(); constexpr size_t max_backing_memory_size = 8ULL * i::GB; constexpr size_t min_backing_memory_size = 1ULL * i::GB; size_t backing_memory_size = max_backing_memory_size; i::Address backing_memory_base = 0; while (!backing_memory_base && backing_memory_size >= min_backing_memory_size) { backing_memory_base = vas->AllocatePages( VirtualAddressSpace::kNoHint, backing_memory_size, kChunkSize, PagePermissions::kNoAccess); if (!backing_memory_base) { backing_memory_size /= 2; } } if (!backing_memory_base) { i::V8::FatalProcessOutOfMemory( nullptr, "Could not reserve backing memory for ArrayBufferAllocators"); } DCHECK(IsAligned(backing_memory_base, kChunkSize)); region_alloc_ = std::make_unique( backing_memory_base, backing_memory_size, kAllocationGranularity); end_of_accessible_region_ = region_alloc_->begin(); // Install a on-merge callback to discard or decommit unused pages. region_alloc_->set_on_merge_callback([this](i::Address start, size_t size) { mutex_.AssertHeld(); VirtualAddressSpace* vas = i::GetProcessWideSandbox()->address_space(); i::Address end = start + size; if (end == region_alloc_->end() && start <= end_of_accessible_region_ - kChunkSize) { // Can shrink the accessible region. i::Address new_end_of_accessible_region = RoundUp(start, kChunkSize); size_t size = end_of_accessible_region_ - new_end_of_accessible_region; if (!vas->DecommitPages(new_end_of_accessible_region, size)) { i::V8::FatalProcessOutOfMemory( nullptr, "ArrayBufferAllocator::BackendAllocator()"); } end_of_accessible_region_ = new_end_of_accessible_region; } else if (size >= 2 * kChunkSize) { // Can discard pages. The pages stay accessible, so the size of the // accessible region doesn't change. i::Address chunk_start = RoundUp(start, kChunkSize); i::Address chunk_end = RoundDown(start + size, kChunkSize); if (!vas->DiscardSystemPages(chunk_start, chunk_end - chunk_start)) { i::V8::FatalProcessOutOfMemory( nullptr, "ArrayBufferAllocator::BackendAllocator()"); } } }); } ~BackendAllocator() { // The sandbox may already have been torn down, in which case there's no // need to free any memory. if (i::GetProcessWideSandbox()->is_initialized()) { VirtualAddressSpace* vas = i::GetProcessWideSandbox()->address_space(); vas->FreePages(region_alloc_->begin(), region_alloc_->size()); } } BackendAllocator(const BackendAllocator&) = delete; BackendAllocator& operator=(const BackendAllocator&) = delete; void* Allocate(size_t length) { base::MutexGuard guard(&mutex_); length = RoundUp(length, kAllocationGranularity); i::Address region = region_alloc_->AllocateRegion(length); if (region == base::RegionAllocator::kAllocationFailure) return nullptr; // Check if the memory is inside the accessible region. If not, grow it. i::Address end = region + length; size_t length_to_memset = length; if (end > end_of_accessible_region_) { VirtualAddressSpace* vas = i::GetProcessWideSandbox()->address_space(); i::Address new_end_of_accessible_region = RoundUp(end, kChunkSize); size_t size = new_end_of_accessible_region - end_of_accessible_region_; if (!vas->SetPagePermissions(end_of_accessible_region_, size, PagePermissions::kReadWrite)) { if (!region_alloc_->FreeRegion(region)) { i::V8::FatalProcessOutOfMemory( nullptr, "ArrayBufferAllocator::BackendAllocator::Allocate()"); } return nullptr; } // The pages that were inaccessible are guaranteed to be zeroed, so only // memset until the previous end of the accessible region. length_to_memset = end_of_accessible_region_ - region; end_of_accessible_region_ = new_end_of_accessible_region; } void* mem = reinterpret_cast(region); memset(mem, 0, length_to_memset); return mem; } void Free(void* data) { base::MutexGuard guard(&mutex_); region_alloc_->FreeRegion(reinterpret_cast(data)); } static BackendAllocator* SharedInstance() { static base::LeakyObject instance; return instance.get(); } private: // Use a region allocator with a "page size" of 128 bytes as a reasonable // compromise between the number of regions it has to manage and the amount // of memory wasted due to rounding allocation sizes up to the page size. static constexpr size_t kAllocationGranularity = 128; // The backing memory's accessible region is grown in chunks of this size. static constexpr size_t kChunkSize = 1 * i::MB; std::unique_ptr region_alloc_; size_t end_of_accessible_region_; base::Mutex mutex_; }; BackendAllocator* allocator_ = BackendAllocator::SharedInstance(); }; #else class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: void* Allocate(size_t length) override { return base::Calloc(length, 1); } void* AllocateUninitialized(size_t length) override { return base::Malloc(length); } void Free(void* data, size_t) override { base::Free(data); } void* Reallocate(void* data, size_t old_length, size_t new_length) override { void* new_data = base::Realloc(data, new_length); if (new_length > old_length) { memset(reinterpret_cast(new_data) + old_length, 0, new_length - old_length); } return new_data; } }; #endif // V8_ENABLE_SANDBOX } // namespace SnapshotCreator::SnapshotCreator(Isolate* v8_isolate, const intptr_t* external_references, const StartupData* existing_snapshot, bool owns_isolate) : data_(new i::SnapshotCreatorImpl( reinterpret_cast(v8_isolate), external_references, existing_snapshot, owns_isolate)) {} SnapshotCreator::SnapshotCreator(const intptr_t* external_references, const StartupData* existing_snapshot) : SnapshotCreator(nullptr, external_references, existing_snapshot) {} SnapshotCreator::~SnapshotCreator() { DCHECK_NOT_NULL(data_); auto impl = static_cast(data_); delete impl; } Isolate* SnapshotCreator::GetIsolate() { auto impl = static_cast(data_); return reinterpret_cast(impl->isolate()); } void SnapshotCreator::SetDefaultContext( Local context, SerializeInternalFieldsCallback callback) { auto impl = static_cast(data_); impl->SetDefaultContext(Utils::OpenHandle(*context), callback); } size_t SnapshotCreator::AddContext(Local context, SerializeInternalFieldsCallback callback) { auto impl = static_cast(data_); return impl->AddContext(Utils::OpenHandle(*context), callback); } size_t SnapshotCreator::AddData(i::Address object) { auto impl = static_cast(data_); return impl->AddData(object); } size_t SnapshotCreator::AddData(Local context, i::Address object) { auto impl = static_cast(data_); return impl->AddData(Utils::OpenHandle(*context), object); } StartupData SnapshotCreator::CreateBlob( SnapshotCreator::FunctionCodeHandling function_code_handling) { auto impl = static_cast(data_); return impl->CreateBlob(function_code_handling); } bool StartupData::CanBeRehashed() const { DCHECK(i::Snapshot::VerifyChecksum(this)); return i::Snapshot::ExtractRehashability(this); } bool StartupData::IsValid() const { return i::Snapshot::VersionIsValid(this); } void V8::SetDcheckErrorHandler(DcheckErrorCallback that) { v8::base::SetDcheckFunction(that); } void V8::SetFlagsFromString(const char* str) { SetFlagsFromString(str, strlen(str)); } void V8::SetFlagsFromString(const char* str, size_t length) { i::FlagList::SetFlagsFromString(str, length); i::FlagList::EnforceFlagImplications(); } void V8::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags) { using HelpOptions = i::FlagList::HelpOptions; i::FlagList::SetFlagsFromCommandLine(argc, argv, remove_flags, HelpOptions(HelpOptions::kDontExit)); } RegisteredExtension* RegisteredExtension::first_extension_ = nullptr; RegisteredExtension::RegisteredExtension(std::unique_ptr extension) : extension_(std::move(extension)) {} // static void RegisteredExtension::Register(std::unique_ptr extension) { RegisteredExtension* new_extension = new RegisteredExtension(std::move(extension)); new_extension->next_ = first_extension_; first_extension_ = new_extension; } // static void RegisteredExtension::UnregisterAll() { RegisteredExtension* re = first_extension_; while (re != nullptr) { RegisteredExtension* next = re->next(); delete re; re = next; } first_extension_ = nullptr; } namespace { class ExtensionResource : public String::ExternalOneByteStringResource { public: ExtensionResource() : data_(nullptr), length_(0) {} ExtensionResource(const char* data, size_t length) : data_(data), length_(length) {} const char* data() const override { return data_; } size_t length() const override { return length_; } void Dispose() override {} private: const char* data_; size_t length_; }; } // anonymous namespace void RegisterExtension(std::unique_ptr extension) { RegisteredExtension::Register(std::move(extension)); } Extension::Extension(const char* name, const char* source, int dep_count, const char** deps, int source_length) : name_(name), source_length_(source_length >= 0 ? source_length : (source ? static_cast(strlen(source)) : 0)), dep_count_(dep_count), deps_(deps), auto_enable_(false) { source_ = new ExtensionResource(source, source_length_); CHECK(source != nullptr || source_length_ == 0); } void ResourceConstraints::ConfigureDefaultsFromHeapSize( size_t initial_heap_size_in_bytes, size_t maximum_heap_size_in_bytes) { CHECK_LE(initial_heap_size_in_bytes, maximum_heap_size_in_bytes); if (maximum_heap_size_in_bytes == 0) { return; } size_t young_generation, old_generation; i::Heap::GenerationSizesFromHeapSize(maximum_heap_size_in_bytes, &young_generation, &old_generation); set_max_young_generation_size_in_bytes( std::max(young_generation, i::Heap::MinYoungGenerationSize())); set_max_old_generation_size_in_bytes( std::max(old_generation, i::Heap::MinOldGenerationSize())); if (initial_heap_size_in_bytes > 0) { i::Heap::GenerationSizesFromHeapSize(initial_heap_size_in_bytes, &young_generation, &old_generation); // We do not set lower bounds for the initial sizes. set_initial_young_generation_size_in_bytes(young_generation); set_initial_old_generation_size_in_bytes(old_generation); } if (i::kPlatformRequiresCodeRange) { set_code_range_size_in_bytes( std::min(i::kMaximalCodeRangeSize, maximum_heap_size_in_bytes)); } } void ResourceConstraints::ConfigureDefaults(uint64_t physical_memory, uint64_t virtual_memory_limit) { size_t heap_size = i::Heap::HeapSizeFromPhysicalMemory(physical_memory); size_t young_generation, old_generation; i::Heap::GenerationSizesFromHeapSize(heap_size, &young_generation, &old_generation); set_max_young_generation_size_in_bytes(young_generation); set_max_old_generation_size_in_bytes(old_generation); if (virtual_memory_limit > 0 && i::kPlatformRequiresCodeRange) { set_code_range_size_in_bytes( std::min(i::kMaximalCodeRangeSize, static_cast(virtual_memory_limit / 8))); } } namespace internal { i::Address* GlobalizeTracedReference(i::Isolate* i_isolate, i::Address value, internal::Address* slot, GlobalHandleStoreMode store_mode) { API_RCS_SCOPE(i_isolate, TracedGlobal, New); #ifdef DEBUG Utils::ApiCheck((slot != nullptr), "v8::GlobalizeTracedReference", "the address slot must be not null"); #endif auto result = i_isolate->traced_handles()->Create(value, slot, store_mode); #ifdef VERIFY_HEAP if (i::v8_flags.verify_heap) { Object::ObjectVerify(i::Object(value), i_isolate); } #endif // VERIFY_HEAP return result.location(); } void MoveTracedReference(internal::Address** from, internal::Address** to) { TracedHandles::Move(from, to); } void CopyTracedReference(const internal::Address* const* from, internal::Address** to) { TracedHandles::Copy(from, to); } void DisposeTracedReference(internal::Address* location) { TracedHandles::Destroy(location); } // static void v8::internal::HandleHelper::VerifyOnStack(const void* ptr) { DCHECK_LE(v8::base::Stack::GetCurrentStackPosition(), ptr); DCHECK_GE(v8::base::Stack::GetStackStartUnchecked(), ptr); } #if V8_STATIC_ROOTS_BOOL // Initialize static root constants exposed in v8-internal.h. namespace { constexpr InstanceTypeChecker::RootIndexRange kStringMapRange = *InstanceTypeChecker::UniqueMapRangeOfInstanceTypeRange(FIRST_STRING_TYPE, LAST_STRING_TYPE); constexpr Tagged_t kFirstStringMapPtr = StaticReadOnlyRootsPointerTable[static_cast(kStringMapRange.first)]; constexpr Tagged_t kLastStringMapPtr = StaticReadOnlyRootsPointerTable[static_cast( kStringMapRange.second)]; } // namespace #define EXPORTED_STATIC_ROOTS_MAPPING(V) \ V(UndefinedValue, i::StaticReadOnlyRoot::kUndefinedValue) \ V(NullValue, i::StaticReadOnlyRoot::kNullValue) \ V(TrueValue, i::StaticReadOnlyRoot::kTrueValue) \ V(FalseValue, i::StaticReadOnlyRoot::kFalseValue) \ V(EmptyString, i::StaticReadOnlyRoot::kempty_string) \ V(TheHoleValue, i::StaticReadOnlyRoot::kTheHoleValue) \ V(FirstStringMap, kFirstStringMapPtr) \ V(LastStringMap, kLastStringMapPtr) static_assert(std::is_same::value); #define DEF_STATIC_ROOT(name, internal_value) \ const Internals::Tagged_t Internals::StaticReadOnlyRoot::k##name = \ internal_value; EXPORTED_STATIC_ROOTS_MAPPING(DEF_STATIC_ROOT) #undef DEF_STATIC_ROOT #undef EXPORTED_STATIC_ROOTS_MAPPING #endif // V8_STATIC_ROOTS_BOOL } // namespace internal namespace api_internal { i::Address* GlobalizeReference(i::Isolate* i_isolate, i::Address value) { API_RCS_SCOPE(i_isolate, Persistent, New); i::Handle result = i_isolate->global_handles()->Create(value); #ifdef VERIFY_HEAP if (i::v8_flags.verify_heap) { i::Object::ObjectVerify(i::Object(value), i_isolate); } #endif // VERIFY_HEAP return result.location(); } i::Address* CopyGlobalReference(i::Address* from) { i::Handle result = i::GlobalHandles::CopyGlobal(from); return result.location(); } void MoveGlobalReference(internal::Address** from, internal::Address** to) { i::GlobalHandles::MoveGlobal(from, to); } void MakeWeak(i::Address* location, void* parameter, WeakCallbackInfo::Callback weak_callback, WeakCallbackType type) { i::GlobalHandles::MakeWeak(location, parameter, weak_callback, type); } void MakeWeak(i::Address** location_addr) { i::GlobalHandles::MakeWeak(location_addr); } void* ClearWeak(i::Address* location) { return i::GlobalHandles::ClearWeakness(location); } void AnnotateStrongRetainer(i::Address* location, const char* label) { i::GlobalHandles::AnnotateStrongRetainer(location, label); } void DisposeGlobal(i::Address* location) { i::GlobalHandles::Destroy(location); } i::Address* Eternalize(Isolate* v8_isolate, Value* value) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); i::Tagged object = *Utils::OpenDirectHandle(value); int index = -1; i_isolate->eternal_handles()->Create(i_isolate, object, &index); return i_isolate->eternal_handles()->Get(index).location(); } void FromJustIsNothing() { Utils::ApiCheck(false, "v8::FromJust", "Maybe value is Nothing"); } void ToLocalEmpty() { Utils::ApiCheck(false, "v8::ToLocalChecked", "Empty MaybeLocal"); } void InternalFieldOutOfBounds(int index) { Utils::ApiCheck(0 <= index && index < kInternalFieldsInWeakCallback, "WeakCallbackInfo::GetInternalField", "Internal field out of bounds"); } } // namespace api_internal // --- H a n d l e s --- HandleScope::HandleScope(Isolate* v8_isolate) { Initialize(v8_isolate); } void HandleScope::Initialize(Isolate* v8_isolate) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); // We do not want to check the correct usage of the Locker class all over the // place, so we do it only here: Without a HandleScope, an embedder can do // almost nothing, so it is enough to check in this central place. // We make an exception if the serializer is enabled, which means that the // Isolate is exclusively used to create a snapshot. Utils::ApiCheck(!i_isolate->was_locker_ever_used() || i_isolate->thread_manager()->IsLockedByCurrentThread() || i_isolate->serializer_enabled(), "HandleScope::HandleScope", "Entering the V8 API without proper locking in place"); i::HandleScopeData* current = i_isolate->handle_scope_data(); i_isolate_ = i_isolate; prev_next_ = current->next; prev_limit_ = current->limit; current->level++; } HandleScope::~HandleScope() { i::HandleScope::CloseScope(i_isolate_, prev_next_, prev_limit_); } void* HandleScope::operator new(size_t) { base::OS::Abort(); } void* HandleScope::operator new[](size_t) { base::OS::Abort(); } void HandleScope::operator delete(void*, size_t) { base::OS::Abort(); } void HandleScope::operator delete[](void*, size_t) { base::OS::Abort(); } int HandleScope::NumberOfHandles(Isolate* v8_isolate) { return i::HandleScope::NumberOfHandles( reinterpret_cast(v8_isolate)); } i::Address* HandleScope::CreateHandle(i::Isolate* i_isolate, i::Address value) { return i::HandleScope::CreateHandle(i_isolate, value); } #ifdef V8_ENABLE_DIRECT_LOCAL i::Address* HandleScope::CreateHandleForCurrentIsolate(i::Address value) { i::Isolate* i_isolate = i::Isolate::Current(); return i::HandleScope::CreateHandle(i_isolate, value); } #endif // V8_ENABLE_DIRECT_LOCAL EscapableHandleScope::EscapableHandleScope(Isolate* v8_isolate) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); escape_slot_ = CreateHandle( i_isolate, i::ReadOnlyRoots(i_isolate).the_hole_value().ptr()); Initialize(v8_isolate); } i::Address* EscapableHandleScope::Escape(i::Address* escape_value) { i::Heap* heap = reinterpret_cast(GetIsolate())->heap(); Utils::ApiCheck(i::IsTheHole(i::Object(*escape_slot_), heap->isolate()), "EscapableHandleScope::Escape", "Escape value set twice"); if (escape_value == nullptr) { *escape_slot_ = i::ReadOnlyRoots(heap).undefined_value().ptr(); return nullptr; } *escape_slot_ = *escape_value; return escape_slot_; } void* EscapableHandleScope::operator new(size_t) { base::OS::Abort(); } void* EscapableHandleScope::operator new[](size_t) { base::OS::Abort(); } void EscapableHandleScope::operator delete(void*, size_t) { base::OS::Abort(); } void EscapableHandleScope::operator delete[](void*, size_t) { base::OS::Abort(); } SealHandleScope::SealHandleScope(Isolate* v8_isolate) : i_isolate_(reinterpret_cast(v8_isolate)) { i::HandleScopeData* current = i_isolate_->handle_scope_data(); prev_limit_ = current->limit; current->limit = current->next; prev_sealed_level_ = current->sealed_level; current->sealed_level = current->level; } SealHandleScope::~SealHandleScope() { i::HandleScopeData* current = i_isolate_->handle_scope_data(); DCHECK_EQ(current->next, current->limit); current->limit = prev_limit_; DCHECK_EQ(current->level, current->sealed_level); current->sealed_level = prev_sealed_level_; } void* SealHandleScope::operator new(size_t) { base::OS::Abort(); } void* SealHandleScope::operator new[](size_t) { base::OS::Abort(); } void SealHandleScope::operator delete(void*, size_t) { base::OS::Abort(); } void SealHandleScope::operator delete[](void*, size_t) { base::OS::Abort(); } bool Data::IsModule() const { return i::IsModule(*Utils::OpenDirectHandle(this)); } bool Data::IsFixedArray() const { return i::IsFixedArray(*Utils::OpenDirectHandle(this)); } bool Data::IsValue() const { i::DisallowGarbageCollection no_gc; i::Tagged self = *Utils::OpenDirectHandle(this); if (i::IsSmi(self)) return true; i::Tagged heap_object = i::HeapObject::cast(self); DCHECK(!IsTheHole(heap_object)); if (i::IsSymbol(heap_object)) { return !i::Symbol::cast(heap_object)->is_private(); } return IsPrimitiveHeapObject(heap_object) || IsJSReceiver(heap_object); } bool Data::IsPrivate() const { return i::IsPrivateSymbol(*Utils::OpenDirectHandle(this)); } bool Data::IsObjectTemplate() const { return i::IsObjectTemplateInfo(*Utils::OpenDirectHandle(this)); } bool Data::IsFunctionTemplate() const { return i::IsFunctionTemplateInfo(*Utils::OpenDirectHandle(this)); } bool Data::IsContext() const { return i::IsContext(*Utils::OpenDirectHandle(this)); } void Context::Enter() { i::DisallowGarbageCollection no_gc; i::Tagged env = *Utils::OpenDirectHandle(this); i::Isolate* i_isolate = env->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScopeImplementer* impl = i_isolate->handle_scope_implementer(); impl->EnterContext(env); impl->SaveContext(i_isolate->context()); i_isolate->set_context(env); } void Context::Exit() { i::DirectHandle env = Utils::OpenDirectHandle(this); i::Isolate* i_isolate = env->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScopeImplementer* impl = i_isolate->handle_scope_implementer(); if (!Utils::ApiCheck(impl->LastEnteredContextWas(*env), "v8::Context::Exit()", "Cannot exit non-entered context")) { return; } impl->LeaveContext(); i_isolate->set_context(impl->RestoreContext()); } Context::BackupIncumbentScope::BackupIncumbentScope( Local backup_incumbent_context) : backup_incumbent_context_(backup_incumbent_context) { DCHECK(!backup_incumbent_context_.IsEmpty()); i::DirectHandle env = Utils::OpenDirectHandle(*backup_incumbent_context_); i::Isolate* i_isolate = env->GetIsolate(); js_stack_comparable_address_ = i::SimulatorStack::RegisterJSStackComparableAddress(i_isolate); prev_ = i_isolate->top_backup_incumbent_scope(); i_isolate->set_top_backup_incumbent_scope(this); } Context::BackupIncumbentScope::~BackupIncumbentScope() { i::DirectHandle env = Utils::OpenDirectHandle(*backup_incumbent_context_); i::Isolate* i_isolate = env->GetIsolate(); i::SimulatorStack::UnregisterJSStackComparableAddress(i_isolate); i_isolate->set_top_backup_incumbent_scope(prev_); } static_assert(i::Internals::kEmbedderDataSlotSize == i::kEmbedderDataSlotSize); static_assert(i::Internals::kEmbedderDataSlotExternalPointerOffset == i::EmbedderDataSlot::kExternalPointerOffset); static i::Handle EmbedderDataFor(Context* context, int index, bool can_grow, const char* location) { i::DirectHandle env = Utils::OpenDirectHandle(context); i::Isolate* i_isolate = env->GetIsolate(); DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate); bool ok = Utils::ApiCheck(i::IsNativeContext(*env), location, "Not a native context") && Utils::ApiCheck(index >= 0, location, "Negative index"); if (!ok) return i::Handle(); // TODO(ishell): remove cast once embedder_data slot has a proper type. i::Handle data( i::EmbedderDataArray::cast(env->embedder_data()), i_isolate); if (index < data->length()) return data; if (!Utils::ApiCheck(can_grow && index < i::EmbedderDataArray::kMaxLength, location, "Index too large")) { return i::Handle(); } data = i::EmbedderDataArray::EnsureCapacity(i_isolate, data, index); env->set_embedder_data(*data); return data; } uint32_t Context::GetNumberOfEmbedderDataFields() { i::DirectHandle context = Utils::OpenDirectHandle(this); DCHECK_NO_SCRIPT_NO_EXCEPTION(context->GetIsolate()); Utils::ApiCheck(i::IsNativeContext(*context), "Context::GetNumberOfEmbedderDataFields", "Not a native context"); // TODO(ishell): remove cast once embedder_data slot has a proper type. return static_cast( i::EmbedderDataArray::cast(context->embedder_data())->length()); } v8::Local Context::SlowGetEmbedderData(int index) { const char* location = "v8::Context::GetEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, false, location); if (data.is_null()) return Local(); i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); i::Handle result(i::EmbedderDataSlot(*data, index).load_tagged(), i_isolate); return Utils::ToLocal(result); } void Context::SetEmbedderData(int index, v8::Local value) { const char* location = "v8::Context::SetEmbedderData()"; i::Handle data = EmbedderDataFor(this, index, true, location); if (data.is_null()) return; i::DirectHandle val = Utils::OpenDirectHandle(*value); i::EmbedderDataSlot::store_tagged(*data, index, *val); DCHECK_EQ(*Utils::OpenDirectHandle(*value), *Utils::OpenDirectHandle(*GetEmbedderData(index))); } void* Context::SlowGetAlignedPointerFromEmbedderData(int index) { const char* location = "v8::Context::GetAlignedPointerFromEmbedderData()"; i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); i::HandleScope handle_scope(i_isolate); i::Handle data = EmbedderDataFor(this, index, false, location); if (data.is_null()) return nullptr; void* result; Utils::ApiCheck( i::EmbedderDataSlot(*data, index).ToAlignedPointer(i_isolate, &result), location, "Pointer is not aligned"); return result; } void Context::SetAlignedPointerInEmbedderData(int index, void* value) { const char* location = "v8::Context::SetAlignedPointerInEmbedderData()"; i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); i::Handle data = EmbedderDataFor(this, index, true, location); bool ok = i::EmbedderDataSlot(*data, index).store_aligned_pointer(i_isolate, value); Utils::ApiCheck(ok, location, "Pointer is not aligned"); DCHECK_EQ(value, GetAlignedPointerFromEmbedderData(index)); } // --- T e m p l a t e --- static void InitializeTemplate(i::Tagged that, int type, bool do_not_cache) { that->set_number_of_properties(0); that->set_tag(type); int serial_number = do_not_cache ? i::TemplateInfo::kDoNotCache : i::TemplateInfo::kUncached; that->set_serial_number(serial_number); } void Template::Set(v8::Local name, v8::Local value, v8::PropertyAttribute attribute) { auto templ = Utils::OpenHandle(this); i::Isolate* i_isolate = templ->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto value_obj = Utils::OpenHandle(*value); Utils::ApiCheck(!IsJSReceiver(*value_obj) || IsTemplateInfo(*value_obj), "v8::Template::Set", "Invalid value, must be a primitive or a Template"); // The template cache only performs shallow clones, if we set an // ObjectTemplate as a property value then we can not cache the receiver // template. if (i::IsObjectTemplateInfo(*value_obj)) { templ->set_serial_number(i::TemplateInfo::kDoNotCache); } i::ApiNatives::AddDataProperty(i_isolate, templ, Utils::OpenHandle(*name), value_obj, static_cast(attribute)); } void Template::SetPrivate(v8::Local name, v8::Local value, v8::PropertyAttribute attribute) { Set(Utils::ToLocal(Utils::OpenHandle(reinterpret_cast(*name))), value, attribute); } void Template::SetAccessorProperty(v8::Local name, v8::Local getter, v8::Local setter, v8::PropertyAttribute attribute, v8::AccessControl access_control) { Utils::ApiCheck( getter.IsEmpty() || !IsUndefined( Utils::OpenDirectHandle(*getter)->call_code(kAcquireLoad)), "v8::Template::SetAccessorProperty", "Getter must have a call handler"); Utils::ApiCheck( setter.IsEmpty() || !IsUndefined( Utils::OpenDirectHandle(*setter)->call_code(kAcquireLoad)), "v8::Template::SetAccessorProperty", "Setter must have a call handler"); // TODO(verwaest): Remove |access_control|. DCHECK_EQ(v8::DEFAULT, access_control); auto templ = Utils::OpenHandle(this); auto i_isolate = templ->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); DCHECK(!name.IsEmpty()); DCHECK(!getter.IsEmpty() || !setter.IsEmpty()); i::HandleScope scope(i_isolate); i::ApiNatives::AddAccessorProperty( i_isolate, templ, Utils::OpenHandle(*name), Utils::OpenHandle(*getter, true), Utils::OpenHandle(*setter, true), static_cast(attribute)); } // --- F u n c t i o n T e m p l a t e --- static void InitializeFunctionTemplate(i::Tagged info, bool do_not_cache) { InitializeTemplate(info, Consts::FUNCTION_TEMPLATE, do_not_cache); info->set_flag(0, kRelaxedStore); } namespace { Local ObjectTemplateNew(i::Isolate* i_isolate, v8::Local constructor, bool do_not_cache) { API_RCS_SCOPE(i_isolate, ObjectTemplate, New); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::Handle struct_obj = i_isolate->factory()->NewStruct( i::OBJECT_TEMPLATE_INFO_TYPE, i::AllocationType::kOld); i::Handle obj = i::Handle::cast(struct_obj); { // Disallow GC until all fields of obj have acceptable types. i::DisallowGarbageCollection no_gc; i::Tagged raw = *obj; InitializeTemplate(raw, Consts::OBJECT_TEMPLATE, do_not_cache); raw->set_data(0); if (!constructor.IsEmpty()) { raw->set_constructor(*Utils::OpenDirectHandle(*constructor)); } } return Utils::ToLocal(obj); } } // namespace Local FunctionTemplate::PrototypeTemplate() { auto self = Utils::OpenHandle(this); i::Isolate* i_isolate = self->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::Handle result(self->GetPrototypeTemplate(), i_isolate); if (i::IsUndefined(*result, i_isolate)) { // Do not cache prototype objects. result = Utils::OpenHandle( *ObjectTemplateNew(i_isolate, Local(), true)); i::FunctionTemplateInfo::SetPrototypeTemplate(i_isolate, self, result); } return ToApiHandle(result); } void FunctionTemplate::SetPrototypeProviderTemplate( Local prototype_provider) { auto self = Utils::OpenHandle(this); i::Isolate* i_isolate = self->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::Handle result = Utils::OpenHandle(*prototype_provider); Utils::ApiCheck(i::IsUndefined(self->GetPrototypeTemplate(), i_isolate), "v8::FunctionTemplate::SetPrototypeProviderTemplate", "Protoype must be undefined"); Utils::ApiCheck(i::IsUndefined(self->GetParentTemplate(), i_isolate), "v8::FunctionTemplate::SetPrototypeProviderTemplate", "Prototype provider must be empty"); i::FunctionTemplateInfo::SetPrototypeProviderTemplate(i_isolate, self, result); } namespace { static void EnsureNotPublished(i::DirectHandle info, const char* func) { DCHECK_IMPLIES(info->instantiated(), info->published()); Utils::ApiCheck(!info->published(), func, "FunctionTemplate already instantiated"); } Local FunctionTemplateNew( i::Isolate* i_isolate, FunctionCallback callback, v8::Local data, v8::Local signature, int length, ConstructorBehavior behavior, bool do_not_cache, v8::Local cached_property_name = v8::Local(), SideEffectType side_effect_type = SideEffectType::kHasSideEffect, const MemorySpan& c_function_overloads = {}, uint8_t instance_type = 0, uint8_t allowed_receiver_instance_type_range_start = 0, uint8_t allowed_receiver_instance_type_range_end = 0) { i::Handle struct_obj = i_isolate->factory()->NewStruct( i::FUNCTION_TEMPLATE_INFO_TYPE, i::AllocationType::kOld); i::Handle obj = i::Handle::cast(struct_obj); { // Disallow GC until all fields of obj have acceptable types. i::DisallowGarbageCollection no_gc; i::Tagged raw = *obj; InitializeFunctionTemplate(raw, do_not_cache); raw->set_length(length); raw->set_undetectable(false); raw->set_needs_access_check(false); raw->set_accept_any_receiver(true); if (!signature.IsEmpty()) { raw->set_signature(*Utils::OpenDirectHandle(*signature)); } raw->set_cached_property_name( cached_property_name.IsEmpty() ? i::ReadOnlyRoots(i_isolate).the_hole_value() : *Utils::OpenDirectHandle(*cached_property_name)); if (behavior == ConstructorBehavior::kThrow) raw->set_remove_prototype(true); raw->SetInstanceType(instance_type); raw->set_allowed_receiver_instance_type_range_start( allowed_receiver_instance_type_range_start); raw->set_allowed_receiver_instance_type_range_end( allowed_receiver_instance_type_range_end); } if (callback != nullptr) { Utils::ToLocal(obj)->SetCallHandler(callback, data, side_effect_type, c_function_overloads); } return Utils::ToLocal(obj); } } // namespace void FunctionTemplate::Inherit(v8::Local value) { auto info = Utils::OpenHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::Inherit"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); Utils::ApiCheck( i::IsUndefined(info->GetPrototypeProviderTemplate(), i_isolate), "v8::FunctionTemplate::Inherit", "Protoype provider must be empty"); i::FunctionTemplateInfo::SetParentTemplate(i_isolate, info, Utils::OpenHandle(*value)); } Local FunctionTemplate::New( Isolate* v8_isolate, FunctionCallback callback, v8::Local data, v8::Local signature, int length, ConstructorBehavior behavior, SideEffectType side_effect_type, const CFunction* c_function, uint16_t instance_type, uint16_t allowed_receiver_instance_type_range_start, uint16_t allowed_receiver_instance_type_range_end) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); // Changes to the environment cannot be captured in the snapshot. Expect no // function templates when the isolate is created for serialization. API_RCS_SCOPE(i_isolate, FunctionTemplate, New); if (!Utils::ApiCheck( !c_function || behavior == ConstructorBehavior::kThrow, "FunctionTemplate::New", "Fast API calls are not supported for constructor functions")) { return Local(); } if (instance_type != 0) { if (!Utils::ApiCheck( instance_type >= i::Internals::kFirstJSApiObjectType && instance_type <= i::Internals::kLastJSApiObjectType, "FunctionTemplate::New", "instance_type is outside the range of valid JSApiObject types")) { return Local(); } } ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); return FunctionTemplateNew( i_isolate, callback, data, signature, length, behavior, false, Local(), side_effect_type, c_function ? MemorySpan{c_function, 1} : MemorySpan{}, instance_type, allowed_receiver_instance_type_range_start, allowed_receiver_instance_type_range_end); } Local FunctionTemplate::NewWithCFunctionOverloads( Isolate* v8_isolate, FunctionCallback callback, v8::Local data, v8::Local signature, int length, ConstructorBehavior behavior, SideEffectType side_effect_type, const MemorySpan& c_function_overloads) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); API_RCS_SCOPE(i_isolate, FunctionTemplate, New); if (!Utils::ApiCheck( c_function_overloads.size() == 0 || behavior == ConstructorBehavior::kThrow, "FunctionTemplate::NewWithCFunctionOverloads", "Fast API calls are not supported for constructor functions")) { return Local(); } ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); return FunctionTemplateNew(i_isolate, callback, data, signature, length, behavior, false, Local(), side_effect_type, c_function_overloads); } Local FunctionTemplate::NewWithCache( Isolate* v8_isolate, FunctionCallback callback, Local cache_property, Local data, Local signature, int length, SideEffectType side_effect_type) { i::Isolate* i_isolate = reinterpret_cast(v8_isolate); API_RCS_SCOPE(i_isolate, FunctionTemplate, NewWithCache); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); return FunctionTemplateNew(i_isolate, callback, data, signature, length, ConstructorBehavior::kAllow, false, cache_property, side_effect_type); } Local Signature::New(Isolate* v8_isolate, Local receiver) { return Local::Cast(receiver); } #define SET_FIELD_WRAPPED(i_isolate, obj, setter, cdata) \ do { \ i::Handle foreign = FromCData(i_isolate, cdata); \ (obj)->setter(*foreign); \ } while (false) void FunctionTemplate::SetCallHandler( FunctionCallback callback, v8::Local data, SideEffectType side_effect_type, const MemorySpan& c_function_overloads) { auto info = Utils::OpenHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::SetCallHandler"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); i::Handle obj = i_isolate->factory()->NewCallHandlerInfo( side_effect_type == SideEffectType::kHasNoSideEffect); obj->set_owner_template(*info); obj->set_callback(i_isolate, reinterpret_cast(callback)); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(i_isolate)); } obj->set_data(*Utils::OpenHandle(*data)); if (c_function_overloads.size() > 0) { // Stores the data for a sequence of CFunction overloads into a single // FixedArray, as [address_0, signature_0, ... address_n-1, signature_n-1]. i::Handle function_overloads = i_isolate->factory()->NewFixedArray(static_cast( c_function_overloads.size() * i::FunctionTemplateInfo::kFunctionOverloadEntrySize)); int function_count = static_cast(c_function_overloads.size()); for (int i = 0; i < function_count; i++) { const CFunction& c_function = c_function_overloads.data()[i]; i::Handle address = FromCData(i_isolate, c_function.GetAddress()); function_overloads->set( i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i, *address); i::Handle signature = FromCData(i_isolate, c_function.GetTypeInfo()); function_overloads->set( i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i + 1, *signature); } i::FunctionTemplateInfo::SetCFunctionOverloads(i_isolate, info, function_overloads); } info->set_call_code(*obj, kReleaseStore); } namespace { template i::Handle MakeAccessorInfo( i::Isolate* i_isolate, v8::Local name, Getter getter, Setter setter, v8::Local data, v8::AccessControl settings, bool is_special_data_property, bool replace_on_access) { i::Handle obj = i_isolate->factory()->NewAccessorInfo(); obj->set_getter(i_isolate, reinterpret_cast(getter)); DCHECK_IMPLIES(replace_on_access, is_special_data_property && setter == nullptr); if (is_special_data_property && setter == nullptr) { setter = reinterpret_cast(&i::Accessors::ReconfigureToDataProperty); } obj->set_setter(i_isolate, reinterpret_cast(setter)); i::Handle accessor_name = Utils::OpenHandle(*name); if (!IsUniqueName(*accessor_name)) { accessor_name = i_isolate->factory()->InternalizeString( i::Handle::cast(accessor_name)); } i::DisallowGarbageCollection no_gc; i::Tagged raw_obj = *obj; if (data.IsEmpty()) { raw_obj->set_data(i::ReadOnlyRoots(i_isolate).undefined_value()); } else { raw_obj->set_data(*Utils::OpenHandle(*data)); } raw_obj->set_name(*accessor_name); raw_obj->set_is_special_data_property(is_special_data_property); raw_obj->set_replace_on_access(replace_on_access); if (settings & ALL_CAN_READ) raw_obj->set_all_can_read(true); if (settings & ALL_CAN_WRITE) raw_obj->set_all_can_write(true); raw_obj->set_initial_property_attributes(i::NONE); return obj; } } // namespace Local FunctionTemplate::InstanceTemplate() { i::Handle handle = Utils::OpenHandle(this, true); if (!Utils::ApiCheck(!handle.is_null(), "v8::FunctionTemplate::InstanceTemplate()", "Reading from empty handle")) { return Local(); } i::Isolate* i_isolate = handle->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); if (i::IsUndefined(handle->GetInstanceTemplate(), i_isolate)) { Local templ = ObjectTemplate::New(i_isolate, ToApiHandle(handle)); i::FunctionTemplateInfo::SetInstanceTemplate(i_isolate, handle, Utils::OpenHandle(*templ)); } i::Handle result( i::ObjectTemplateInfo::cast(handle->GetInstanceTemplate()), i_isolate); return Utils::ToLocal(result); } void FunctionTemplate::SetLength(int length) { auto info = Utils::OpenDirectHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::SetLength"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); info->set_length(length); } void FunctionTemplate::SetClassName(Local name) { auto info = Utils::OpenDirectHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::SetClassName"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); info->set_class_name(*Utils::OpenHandle(*name)); } void FunctionTemplate::SetAcceptAnyReceiver(bool value) { auto info = Utils::OpenDirectHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::SetAcceptAnyReceiver"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); info->set_accept_any_receiver(value); } void FunctionTemplate::ReadOnlyPrototype() { auto info = Utils::OpenDirectHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::ReadOnlyPrototype"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); info->set_read_only_prototype(true); } void FunctionTemplate::RemovePrototype() { auto info = Utils::OpenDirectHandle(this); EnsureNotPublished(info, "v8::FunctionTemplate::RemovePrototype"); i::Isolate* i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); info->set_remove_prototype(true); } // --- O b j e c t T e m p l a t e --- Local ObjectTemplate::New( Isolate* v8_isolate, v8::Local constructor) { return New(reinterpret_cast(v8_isolate), constructor); } Local ObjectTemplate::New( i::Isolate* i_isolate, v8::Local constructor) { return ObjectTemplateNew(i_isolate, constructor, false); } namespace { // Ensure that the object template has a constructor. If no // constructor is available we create one. i::Handle EnsureConstructor( i::Isolate* i_isolate, ObjectTemplate* object_template) { i::Tagged obj = Utils::OpenDirectHandle(object_template)->constructor(); if (!IsUndefined(obj, i_isolate)) { i::Tagged info = i::FunctionTemplateInfo::cast(obj); return i::Handle(info, i_isolate); } Local templ = FunctionTemplate::New(reinterpret_cast(i_isolate)); i::Handle constructor = Utils::OpenHandle(*templ); i::FunctionTemplateInfo::SetInstanceTemplate( i_isolate, constructor, Utils::OpenHandle(object_template)); Utils::OpenDirectHandle(object_template)->set_constructor(*constructor); return constructor; } template void TemplateSetAccessor(Template* template_obj, v8::Local name, Getter getter, Setter setter, Data data, AccessControl settings, PropertyAttribute attribute, bool is_special_data_property, bool replace_on_access, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { auto info = Utils::OpenHandle(template_obj); auto i_isolate = info->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); i::Handle accessor_info = MakeAccessorInfo(i_isolate, name, getter, setter, data, settings, is_special_data_property, replace_on_access); { i::DisallowGarbageCollection no_gc; i::Tagged raw = *accessor_info; raw->set_initial_property_attributes( static_cast(attribute)); raw->set_getter_side_effect_type(getter_side_effect_type); raw->set_setter_side_effect_type(setter_side_effect_type); } i::ApiNatives::AddNativeDataProperty(i_isolate, info, accessor_info); } } // namespace void Template::SetNativeDataProperty(v8::Local name, AccessorGetterCallback getter, AccessorSetterCallback setter, v8::Local data, PropertyAttribute attribute, AccessControl settings, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, true, false, getter_side_effect_type, setter_side_effect_type); } void Template::SetNativeDataProperty(v8::Local name, AccessorNameGetterCallback getter, AccessorNameSetterCallback setter, v8::Local data, PropertyAttribute attribute, AccessControl settings, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, true, false, getter_side_effect_type, setter_side_effect_type); } void Template::SetLazyDataProperty(v8::Local name, AccessorNameGetterCallback getter, v8::Local data, PropertyAttribute attribute, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { TemplateSetAccessor(this, name, getter, static_cast(nullptr), data, DEFAULT, attribute, true, true, getter_side_effect_type, setter_side_effect_type); } void Template::SetIntrinsicDataProperty(Local name, Intrinsic intrinsic, PropertyAttribute attribute) { auto templ = Utils::OpenHandle(this); i::Isolate* i_isolate = templ->GetIsolateChecked(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); i::ApiNatives::AddDataProperty(i_isolate, templ, Utils::OpenHandle(*name), intrinsic, static_cast(attribute)); } void ObjectTemplate::SetAccessor(v8::Local name, AccessorGetterCallback getter, AccessorSetterCallback setter, v8::Local data, AccessControl settings, PropertyAttribute attribute, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, i::v8_flags.disable_old_api_accessors, false, getter_side_effect_type, setter_side_effect_type); } void ObjectTemplate::SetAccessor(v8::Local name, AccessorNameGetterCallback getter, AccessorNameSetterCallback setter, v8::Local data, AccessControl settings, PropertyAttribute attribute, SideEffectType getter_side_effect_type, SideEffectType setter_side_effect_type) { TemplateSetAccessor(this, name, getter, setter, data, settings, attribute, i::v8_flags.disable_old_api_accessors, false, getter_side_effect_type, setter_side_effect_type); } namespace { template i::Handle CreateInterceptorInfo( i::Isolate* i_isolate, Getter getter, Setter setter, Query query, Descriptor descriptor, Deleter remover, Enumerator enumerator, Definer definer, Local data, PropertyHandlerFlags flags) { auto obj = i::Handle::cast(i_isolate->factory()->NewStruct( i::INTERCEPTOR_INFO_TYPE, i::AllocationType::kOld)); obj->set_flags(0); if (getter != nullptr) SET_FIELD_WRAPPED(i_isolate, obj, set_getter, getter); if (setter != nullptr) SET_FIELD_WRAPPED(i_isolate, obj, set_setter, setter); if (query != nullptr) SET_FIELD_WRAPPED(i_isolate, obj, set_query, query); if (descriptor != nullptr) { SET_FIELD_WRAPPED(i_isolate, obj, set_descriptor, descriptor); } if (remover != nullptr) { SET_FIELD_WRAPPED(i_isolate, obj, set_deleter, remover); } if (enumerator != nullptr) { SET_FIELD_WRAPPED(i_isolate, obj, set_enumerator, enumerator); } if (definer != nullptr) { SET_FIELD_WRAPPED(i_isolate, obj, set_definer, definer); } obj->set_can_intercept_symbols( !(static_cast(flags) & static_cast(PropertyHandlerFlags::kOnlyInterceptStrings))); obj->set_all_can_read(static_cast(flags) & static_cast(PropertyHandlerFlags::kAllCanRead)); obj->set_non_masking(static_cast(flags) & static_cast(PropertyHandlerFlags::kNonMasking)); obj->set_has_no_side_effect( static_cast(flags) & static_cast(PropertyHandlerFlags::kHasNoSideEffect)); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(i_isolate)); } obj->set_data(*Utils::OpenDirectHandle(*data)); return obj; } template i::Handle CreateNamedInterceptorInfo( i::Isolate* i_isolate, Getter getter, Setter setter, Query query, Descriptor descriptor, Deleter remover, Enumerator enumerator, Definer definer, Local data, PropertyHandlerFlags flags) { auto interceptor = CreateInterceptorInfo(i_isolate, getter, setter, query, descriptor, remover, enumerator, definer, data, flags); interceptor->set_is_named(true); return interceptor; } template i::Handle CreateIndexedInterceptorInfo( i::Isolate* i_isolate, Getter getter, Setter setter, Query query, Descriptor descriptor, Deleter remover, Enumerator enumerator, Definer definer, Local data, PropertyHandlerFlags flags) { auto interceptor = CreateInterceptorInfo(i_isolate, getter, setter, query, descriptor, remover, enumerator, definer, data, flags); interceptor->set_is_named(false); return interceptor; } template void ObjectTemplateSetNamedPropertyHandler( ObjectTemplate* templ, Getter getter, Setter setter, Query query, Descriptor descriptor, Deleter remover, Enumerator enumerator, Definer definer, Local data, PropertyHandlerFlags flags) { i::Isolate* i_isolate = Utils::OpenHandle(templ)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, templ); EnsureNotPublished(cons, "ObjectTemplateSetNamedPropertyHandler"); auto obj = CreateNamedInterceptorInfo(i_isolate, getter, setter, query, descriptor, remover, enumerator, definer, data, flags); i::FunctionTemplateInfo::SetNamedPropertyHandler(i_isolate, cons, obj); } } // namespace void ObjectTemplate::SetHandler( const NamedPropertyHandlerConfiguration& config) { ObjectTemplateSetNamedPropertyHandler( this, config.getter, config.setter, config.query, config.descriptor, config.deleter, config.enumerator, config.definer, config.data, config.flags); } void ObjectTemplate::MarkAsUndetectable() { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, this); EnsureNotPublished(cons, "v8::ObjectTemplate::MarkAsUndetectable"); cons->set_undetectable(true); } void ObjectTemplate::SetAccessCheckCallback(AccessCheckCallback callback, Local data) { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, this); EnsureNotPublished(cons, "v8::ObjectTemplate::SetAccessCheckCallback"); i::Handle struct_info = i_isolate->factory()->NewStruct( i::ACCESS_CHECK_INFO_TYPE, i::AllocationType::kOld); i::Handle info = i::Handle::cast(struct_info); SET_FIELD_WRAPPED(i_isolate, info, set_callback, callback); info->set_named_interceptor(i::Object()); info->set_indexed_interceptor(i::Object()); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(i_isolate)); } info->set_data(*Utils::OpenDirectHandle(*data)); i::FunctionTemplateInfo::SetAccessCheckInfo(i_isolate, cons, info); cons->set_needs_access_check(true); } void ObjectTemplate::SetAccessCheckCallbackAndHandler( AccessCheckCallback callback, const NamedPropertyHandlerConfiguration& named_handler, const IndexedPropertyHandlerConfiguration& indexed_handler, Local data) { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, this); EnsureNotPublished(cons, "v8::ObjectTemplate::SetAccessCheckCallbackWithHandler"); i::Handle struct_info = i_isolate->factory()->NewStruct( i::ACCESS_CHECK_INFO_TYPE, i::AllocationType::kOld); i::Handle info = i::Handle::cast(struct_info); SET_FIELD_WRAPPED(i_isolate, info, set_callback, callback); auto named_interceptor = CreateNamedInterceptorInfo( i_isolate, named_handler.getter, named_handler.setter, named_handler.query, named_handler.descriptor, named_handler.deleter, named_handler.enumerator, named_handler.definer, named_handler.data, named_handler.flags); info->set_named_interceptor(*named_interceptor); auto indexed_interceptor = CreateIndexedInterceptorInfo( i_isolate, indexed_handler.getter, indexed_handler.setter, indexed_handler.query, indexed_handler.descriptor, indexed_handler.deleter, indexed_handler.enumerator, indexed_handler.definer, indexed_handler.data, indexed_handler.flags); info->set_indexed_interceptor(*indexed_interceptor); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(i_isolate)); } info->set_data(*Utils::OpenDirectHandle(*data)); i::FunctionTemplateInfo::SetAccessCheckInfo(i_isolate, cons, info); cons->set_needs_access_check(true); } void ObjectTemplate::SetHandler( const IndexedPropertyHandlerConfiguration& config) { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, this); EnsureNotPublished(cons, "v8::ObjectTemplate::SetHandler"); auto obj = CreateIndexedInterceptorInfo( i_isolate, config.getter, config.setter, config.query, config.descriptor, config.deleter, config.enumerator, config.definer, config.data, config.flags); i::FunctionTemplateInfo::SetIndexedPropertyHandler(i_isolate, cons, obj); } void ObjectTemplate::SetCallAsFunctionHandler(FunctionCallback callback, Local data) { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::HandleScope scope(i_isolate); auto cons = EnsureConstructor(i_isolate, this); EnsureNotPublished(cons, "v8::ObjectTemplate::SetCallAsFunctionHandler"); i::Handle obj = i_isolate->factory()->NewCallHandlerInfo(); obj->set_owner_template(*Utils::OpenDirectHandle(this)); obj->set_callback(i_isolate, reinterpret_cast(callback)); if (data.IsEmpty()) { data = v8::Undefined(reinterpret_cast(i_isolate)); } obj->set_data(*Utils::OpenDirectHandle(*data)); i::FunctionTemplateInfo::SetInstanceCallHandler(i_isolate, cons, obj); } int ObjectTemplate::InternalFieldCount() const { return Utils::OpenDirectHandle(this)->embedder_field_count(); } void ObjectTemplate::SetInternalFieldCount(int value) { i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate(); if (!Utils::ApiCheck(i::Smi::IsValid(value), "v8::ObjectTemplate::SetInternalFieldCount()", "Invalid embedder field count")) { return; } ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); if (value > 0) { // The embedder field count is set by the constructor function's // construct code, so we ensure that there is a constructor // function to do the setting. EnsureConstructor(i_isolate, this); } Utils::OpenDirectHandle(this)->set_embedder_field_count(value); } bool ObjectTemplate::IsImmutableProto() const { return Utils::OpenDirectHandle(this)->immutable_proto(); } void ObjectTemplate::SetImmutableProto() { auto self = Utils::OpenDirectHandle(this); i::Isolate* i_isolate = self->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); self->set_immutable_proto(true); } bool ObjectTemplate::IsCodeLike() const { return Utils::OpenDirectHandle(this)->code_like(); } void ObjectTemplate::SetCodeLike() { auto self = Utils::OpenDirectHandle(this); i::Isolate* i_isolate = self->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); self->set_code_like(true); } // --- S c r i p t s --- // Internally, UnboundScript and UnboundModuleScript are SharedFunctionInfos, // and Script is a JSFunction. ScriptCompiler::CachedData::CachedData(const uint8_t* data_, int length_, BufferPolicy buffer_policy_) : data(data_), length(length_), rejected(false), buffer_policy(buffer_policy_) {} ScriptCompiler::CachedData::~CachedData() { if (buffer_policy == BufferOwned) { delete[] data; } } ScriptCompiler::StreamedSource::StreamedSource( std::unique_ptr stream, Encoding encoding) : impl_(new i::ScriptStreamingData(std::move(stream), encoding)) {} ScriptCompiler::StreamedSource::~StreamedSource() = default; Local