blob: 78c7b9f49f30c0897f96b9a7d47e55f632e22715 [file] [log] [blame] [edit]
// 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 <algorithm> // For min
#include <cmath> // For isnan.
#include <limits>
#include <optional>
#include <sstream>
#include <string>
#include <utility> // For move
#include <vector>
#include "include/v8-array-buffer.h"
#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-external-memory-accounter.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-template.h"
#include "include/v8-unwinder-state.h"
#include "include/v8-util.h"
#include "include/v8-wasm.h"
#include "src/api/api-arguments.h"
#include "src/api/api-inl.h"
#include "src/api/api-natives.h"
#include "src/base/hashing.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/base/vector.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-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap-layout-inl.h"
#include "src/heap/heap-write-barrier.h"
#include "src/heap/safepoint.h"
#include "src/heap/visit-object.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/backing-store.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-objects.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/string.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/isolate.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 <signal.h>
#include <unistd.h>
#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 "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_ENABLE_ETW_STACK_WALKING)
#include "src/diagnostics/etw-jit-win.h"
#endif // V8_ENABLE_ETW_STACK_WALKING
// 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::DirectHandle<i::Script> script) {
i::DirectHandle<i::Object> scriptName(script->GetNameOrSourceURL(),
i_isolate);
i::DirectHandle<i::Object> source_map_url(script->source_mapping_url(),
i_isolate);
i::DirectHandle<i::Object> 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(
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);
base::FatalOOM(base::OOMType::kProcess, 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) {
base::OOMType type = details.is_heap_oom ? base::OOMType::kJavaScript
: base::OOMType::kProcess;
base::FatalOOM(type, location);
UNREACHABLE();
} 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 address 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::Sandbox::current()->is_initialized());
VirtualAddressSpace* vas = i::Sandbox::current()->address_space();
vas_ = vas;
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<base::RegionAllocator>(
backing_memory_base, backing_memory_size, kAllocationGranularity);
end_of_accessible_region_ = region_alloc_->begin();
// Install an on-merge callback to discard or decommit unused pages.
region_alloc_->set_on_merge_callback([this](i::Address start,
size_t size) {
mutex_.AssertHeld();
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_to_decommit =
end_of_accessible_region_ - new_end_of_accessible_region;
if (!vas_->DecommitPages(new_end_of_accessible_region,
size_to_decommit)) {
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::Sandbox::current()->is_initialized()) {
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_) {
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<void*>(region);
memset(mem, 0, length_to_memset);
return mem;
}
void Free(void* data) {
base::MutexGuard guard(&mutex_);
region_alloc_->FreeRegion(reinterpret_cast<i::Address>(data));
}
static BackendAllocator* SharedInstance() {
static base::LeakyObject<BackendAllocator> 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<base::RegionAllocator> region_alloc_;
size_t end_of_accessible_region_;
VirtualAddressSpace* vas_ = nullptr;
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); }
};
#endif // V8_ENABLE_SANDBOX
} // namespace
SnapshotCreator::SnapshotCreator(Isolate* v8_isolate,
const intptr_t* external_references,
const StartupData* existing_snapshot,
bool owns_isolate)
: impl_(new i::SnapshotCreatorImpl(
reinterpret_cast<i::Isolate*>(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(const v8::Isolate::CreateParams& params)
: impl_(new i::SnapshotCreatorImpl(params)) {}
SnapshotCreator::SnapshotCreator(v8::Isolate* isolate,
const v8::Isolate::CreateParams& params)
: impl_(new i::SnapshotCreatorImpl(reinterpret_cast<i::Isolate*>(isolate),
params)) {}
SnapshotCreator::~SnapshotCreator() {
DCHECK_NOT_NULL(impl_);
delete impl_;
}
Isolate* SnapshotCreator::GetIsolate() {
return reinterpret_cast<v8::Isolate*>(impl_->isolate());
}
void SnapshotCreator::SetDefaultContext(
Local<Context> context,
SerializeInternalFieldsCallback internal_fields_serializer,
SerializeContextDataCallback context_data_serializer,
SerializeAPIWrapperCallback api_wrapper_serializer) {
impl_->SetDefaultContext(
Utils::OpenDirectHandle(*context),
i::SerializeEmbedderFieldsCallback(internal_fields_serializer,
context_data_serializer,
api_wrapper_serializer));
}
size_t SnapshotCreator::AddContext(
Local<Context> context,
SerializeInternalFieldsCallback internal_fields_serializer,
SerializeContextDataCallback context_data_serializer,
SerializeAPIWrapperCallback api_wrapper_serializer) {
return impl_->AddContext(
Utils::OpenDirectHandle(*context),
i::SerializeEmbedderFieldsCallback(internal_fields_serializer,
context_data_serializer,
api_wrapper_serializer));
}
size_t SnapshotCreator::AddData(i::Address object) {
return impl_->AddData(object);
}
size_t SnapshotCreator::AddData(Local<Context> context, i::Address object) {
return impl_->AddData(Utils::OpenDirectHandle(*context), object);
}
StartupData SnapshotCreator::CreateBlob(
SnapshotCreator::FunctionCodeHandling function_code_handling) {
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::SetFatalErrorHandler(V8FatalErrorCallback that) {
v8::base::SetFatalFunction(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);
}
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)
: extension_(std::move(extension)) {}
// static
void RegisteredExtension::Register(std::unique_ptr<Extension> 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> 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<int>(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<size_t>(virtual_memory_limit / 8)));
}
}
#ifdef ENABLE_SLOW_DCHECKS
namespace api_internal {
void StackAllocated<true>::VerifyOnStack() const {
if (internal::StackAllocatedCheck::Get()) {
SLOW_DCHECK(::heap::base::Stack::IsOnStack(this));
}
}
} // namespace api_internal
#endif
namespace internal {
void VerifyHandleIsNonEmpty(bool is_empty) {
Utils::ApiCheck(!is_empty, "v8::ReturnValue",
"SetNonEmpty() called with empty handle.");
}
i::Address* GlobalizeTracedReference(
i::Isolate* i_isolate, i::Address value, internal::Address* slot,
TracedReferenceStoreMode store_mode,
TracedReferenceHandling reference_handling) {
return i_isolate->traced_handles()
->Create(value, slot, store_mode, reference_handling)
.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);
}
#if V8_STATIC_ROOTS_BOOL
// Check static root constants exposed in v8-internal.h.
namespace {
constexpr InstanceTypeChecker::TaggedAddressRange kStringMapRange =
*InstanceTypeChecker::UniqueMapRangeOfInstanceTypeRange(FIRST_STRING_TYPE,
LAST_STRING_TYPE);
} // namespace
#define EXPORTED_STATIC_ROOTS_PTR_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(StringMapLowerBound, kStringMapRange.first) \
V(StringMapUpperBound, kStringMapRange.second)
static_assert(std::is_same_v<Internals::Tagged_t, Tagged_t>);
// Ensure they have the correct value.
#define CHECK_STATIC_ROOT(name, value) \
static_assert(Internals::StaticReadOnlyRoot::k##name == value);
EXPORTED_STATIC_ROOTS_PTR_MAPPING(CHECK_STATIC_ROOT)
#undef CHECK_STATIC_ROOT
#define PLUS_ONE(...) +1
static constexpr int kNumberOfCheckedStaticRoots =
0 EXPORTED_STATIC_ROOTS_PTR_MAPPING(PLUS_ONE);
#undef EXPORTED_STATIC_ROOTS_PTR_MAPPING
static_assert(Internals::StaticReadOnlyRoot::kNumberOfExportedStaticRoots ==
kNumberOfCheckedStaticRoots);
#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::IndirectHandle<i::Object> result =
i_isolate->global_handles()->Create(value);
#ifdef VERIFY_HEAP
if (i::v8_flags.verify_heap) {
i::Object::ObjectVerify(i::Tagged<i::Object>(value), i_isolate);
}
#endif // VERIFY_HEAP
return result.location();
}
i::Address* CopyGlobalReference(i::Address* from) {
i::IndirectHandle<i::Object> 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<void>::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<i::Isolate*>(v8_isolate);
i::Tagged<i::Object> 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<i::Isolate*>(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++;
#ifdef V8_ENABLE_CHECKS
scope_level_ = current->level;
#endif
}
HandleScope::~HandleScope() {
#ifdef V8_ENABLE_CHECKS
CHECK_EQ(scope_level_, i_isolate_->handle_scope_data()->level);
#endif
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<i::Isolate*>(v8_isolate));
}
i::Address* HandleScope::CreateHandle(i::Isolate* i_isolate, i::Address value) {
return i::HandleScope::CreateHandle(i_isolate, value);
}
#ifdef V8_ENABLE_DIRECT_HANDLE
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_HANDLE
EscapableHandleScopeBase::EscapableHandleScopeBase(Isolate* v8_isolate) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
escape_slot_ = CreateHandle(
i_isolate, i::ReadOnlyRoots(i_isolate).the_hole_value().ptr());
Initialize(v8_isolate);
}
i::Address* EscapableHandleScopeBase::EscapeSlot(i::Address* escape_value) {
DCHECK_NOT_NULL(escape_value);
DCHECK(i::IsTheHole(i::Tagged<i::Object>(*escape_slot_),
reinterpret_cast<i::Isolate*>(GetIsolate())));
*escape_slot_ = *escape_value;
return escape_slot_;
}
SealHandleScope::SealHandleScope(Isolate* v8_isolate)
: i_isolate_(reinterpret_cast<i::Isolate*>(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_;
}
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<i::Object> self = *Utils::OpenDirectHandle(this);
if (i::IsSmi(self)) return true;
i::Tagged<i::HeapObject> heap_object = i::Cast<i::HeapObject>(self);
DCHECK(!IsTheHole(heap_object));
if (i::IsSymbol(heap_object)) {
return !i::Cast<i::Symbol>(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<i::NativeContext> 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() {
auto 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<Context> backup_incumbent_context)
: backup_incumbent_context_(backup_incumbent_context) {
DCHECK(!backup_incumbent_context_.IsEmpty());
auto 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);
// Enforce slow incumbent computation in order to make it find this
// BackupIncumbentScope.
i_isolate->clear_topmost_script_having_context();
}
Context::BackupIncumbentScope::~BackupIncumbentScope() {
auto 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::DirectHandle<i::EmbedderDataArray> EmbedderDataFor(
Context* context, int index, bool can_grow, const char* location) {
auto 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 {};
// TODO(ishell): remove cast once embedder_data slot has a proper type.
i::DirectHandle<i::EmbedderDataArray> data(
i::Cast<i::EmbedderDataArray>(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 {};
}
data = i::EmbedderDataArray::EnsureCapacity(i_isolate, data, index);
env->set_embedder_data(*data);
return data;
}
uint32_t Context::GetNumberOfEmbedderDataFields() {
auto 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<uint32_t>(
i::Cast<i::EmbedderDataArray>(context->embedder_data())->length());
}
v8::Local<v8::Value> Context::SlowGetEmbedderData(int index) {
const char* location = "v8::Context::GetEmbedderData()";
i::DirectHandle<i::EmbedderDataArray> data =
EmbedderDataFor(this, index, false, location);
if (data.is_null()) return Local<Value>();
i::Isolate* i_isolate = Utils::OpenDirectHandle(this)->GetIsolate();
return Utils::ToLocal(i::direct_handle(
i::EmbedderDataSlot(*data, index).load_tagged(), i_isolate));
}
void Context::SetEmbedderData(int index, v8::Local<Value> value) {
const char* location = "v8::Context::SetEmbedderData()";
i::DirectHandle<i::EmbedderDataArray> data =
EmbedderDataFor(this, index, true, location);
if (data.is_null()) return;
auto 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::DirectHandle<i::EmbedderDataArray> 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::DirectHandle<i::EmbedderDataArray> data =
EmbedderDataFor(this, index, true, location);
bool ok = i::EmbedderDataSlot(*data, index)
.store_aligned_pointer(i_isolate, *data, value);
Utils::ApiCheck(ok, location, "Pointer is not aligned");
DCHECK_EQ(value, GetAlignedPointerFromEmbedderData(index));
}
// --- T e m p l a t e ---
void Template::Set(v8::Local<Name> name, v8::Local<Data> value,
v8::PropertyAttribute attribute) {
auto templ = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = templ->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::HandleScope scope(i_isolate);
auto value_obj = Utils::OpenDirectHandle(*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::OpenDirectHandle(*name), value_obj,
static_cast<i::PropertyAttributes>(attribute));
}
void Template::SetPrivate(v8::Local<Private> name, v8::Local<Data> value,
v8::PropertyAttribute attribute) {
Set(Local<Name>::Cast(name), value, attribute);
}
void Template::SetAccessorProperty(v8::Local<v8::Name> name,
v8::Local<FunctionTemplate> getter,
v8::Local<FunctionTemplate> setter,
v8::PropertyAttribute attribute) {
auto templ = Utils::OpenDirectHandle(this);
auto i_isolate = templ->GetIsolateChecked();
i::DirectHandle<i::FunctionTemplateInfo> i_getter;
if (!getter.IsEmpty()) {
i_getter = Utils::OpenDirectHandle(*getter);
Utils::ApiCheck(i_getter->has_callback(i_isolate),
"v8::Template::SetAccessorProperty",
"Getter must have a call handler");
}
i::DirectHandle<i::FunctionTemplateInfo> i_setter;
if (!setter.IsEmpty()) {
i_setter = Utils::OpenDirectHandle(*setter);
Utils::ApiCheck(i_setter->has_callback(i_isolate),
"v8::Template::SetAccessorProperty",
"Setter must have a call handler");
}
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::OpenDirectHandle(*name), i_getter, i_setter,
static_cast<i::PropertyAttributes>(attribute));
}
// --- F u n c t i o n T e m p l a t e ---
Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::HeapObject> heap_obj(self->GetPrototypeTemplate(),
i_isolate);
if (i::IsUndefined(*heap_obj, i_isolate)) {
// Do not cache prototype objects.
constexpr bool do_not_cache = true;
i::DirectHandle<i::ObjectTemplateInfo> proto_template =
i_isolate->factory()->NewObjectTemplateInfo({}, do_not_cache);
i::FunctionTemplateInfo::SetPrototypeTemplate(i_isolate, self,
proto_template);
return Utils::ToLocal(proto_template);
}
return ToApiHandle<ObjectTemplate>(heap_obj);
}
void FunctionTemplate::SetPrototypeProviderTemplate(
Local<FunctionTemplate> prototype_provider) {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::FunctionTemplateInfo> result =
Utils::OpenDirectHandle(*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<i::FunctionTemplateInfo> info,
const char* func) {
DCHECK_IMPLIES(info->instantiated(), info->published());
Utils::ApiCheck(!info->published(), func,
"FunctionTemplate already instantiated");
}
i::DirectHandle<i::FunctionTemplateInfo> FunctionTemplateNew(
i::Isolate* i_isolate, FunctionCallback callback, v8::Local<Value> data,
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
bool do_not_cache,
v8::Local<Private> cached_property_name = v8::Local<Private>(),
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
const MemorySpan<const CFunction>& c_function_overloads = {}) {
i::DirectHandle<i::FunctionTemplateInfo> obj =
i_isolate->factory()->NewFunctionTemplateInfo(length, do_not_cache);
{
// Disallow GC until all fields of obj have acceptable types.
i::DisallowGarbageCollection no_gc;
i::Tagged<i::FunctionTemplateInfo> raw = *obj;
if (!signature.IsEmpty()) {
raw->set_signature(*Utils::OpenDirectHandle(*signature));
}
if (!cached_property_name.IsEmpty()) {
raw->set_cached_property_name(
*Utils::OpenDirectHandle(*cached_property_name));
}
if (behavior == ConstructorBehavior::kThrow) {
raw->set_remove_prototype(true);
}
}
if (callback != nullptr) {
Utils::ToLocal(obj)->SetCallHandler(callback, data, side_effect_type,
c_function_overloads);
}
return obj;
}
} // namespace
void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) {
auto info = Utils::OpenDirectHandle(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::OpenDirectHandle(*value));
}
Local<FunctionTemplate> FunctionTemplate::New(
Isolate* v8_isolate, FunctionCallback callback, v8::Local<Value> data,
v8::Local<Signature> 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<i::Isolate*>(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<FunctionTemplate>();
}
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::FunctionTemplateInfo> templ = FunctionTemplateNew(
i_isolate, callback, data, signature, length, behavior, false,
Local<Private>(), side_effect_type,
c_function ? MemorySpan<const CFunction>{c_function, 1}
: MemorySpan<const CFunction>{});
if (instance_type) {
if (!Utils::ApiCheck(
base::IsInRange(static_cast<int>(instance_type),
i::Internals::kFirstEmbedderJSApiObjectType,
i::Internals::kLastEmbedderJSApiObjectType),
"FunctionTemplate::New",
"instance_type is outside the range of valid JSApiObject types")) {
return Local<FunctionTemplate>();
}
templ->SetInstanceType(instance_type);
}
if (allowed_receiver_instance_type_range_start ||
allowed_receiver_instance_type_range_end) {
if (!Utils::ApiCheck(i::Internals::kFirstEmbedderJSApiObjectType <=
allowed_receiver_instance_type_range_start &&
allowed_receiver_instance_type_range_start <=
allowed_receiver_instance_type_range_end &&
allowed_receiver_instance_type_range_end <=
i::Internals::kLastEmbedderJSApiObjectType,
"FunctionTemplate::New",
"allowed receiver instance type range is outside the "
"range of valid JSApiObject types")) {
return Local<FunctionTemplate>();
}
templ->SetAllowedReceiverInstanceTypeRange(
allowed_receiver_instance_type_range_start,
allowed_receiver_instance_type_range_end);
}
return Utils::ToLocal(templ);
}
Local<FunctionTemplate> FunctionTemplate::NewWithCFunctionOverloads(
Isolate* v8_isolate, FunctionCallback callback, v8::Local<Value> data,
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
SideEffectType side_effect_type,
const MemorySpan<const CFunction>& c_function_overloads) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
API_RCS_SCOPE(i_isolate, FunctionTemplate, New);
// Check that all overloads of the fast API callback have different numbers of
// parameters. Since the number of overloads is supposed to be small, just
// comparing them with each other should be fine.
for (size_t i = 0; i < c_function_overloads.size(); ++i) {
for (size_t j = i + 1; j < c_function_overloads.size(); ++j) {
CHECK_NE(c_function_overloads.data()[i].ArgumentCount(),
c_function_overloads.data()[j].ArgumentCount());
}
}
if (!Utils::ApiCheck(
c_function_overloads.empty() ||
behavior == ConstructorBehavior::kThrow,
"FunctionTemplate::NewWithCFunctionOverloads",
"Fast API calls are not supported for constructor functions")) {
return Local<FunctionTemplate>();
}
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::FunctionTemplateInfo> templ = FunctionTemplateNew(
i_isolate, callback, data, signature, length, behavior, false,
Local<Private>(), side_effect_type, c_function_overloads);
return Utils::ToLocal(templ);
}
Local<FunctionTemplate> FunctionTemplate::NewWithCache(
Isolate* v8_isolate, FunctionCallback callback,
Local<Private> cache_property, Local<Value> data,
Local<Signature> signature, int length, SideEffectType side_effect_type) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
API_RCS_SCOPE(i_isolate, FunctionTemplate, NewWithCache);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::FunctionTemplateInfo> templ = FunctionTemplateNew(
i_isolate, callback, data, signature, length, ConstructorBehavior::kAllow,
false, cache_property, side_effect_type);
return Utils::ToLocal(templ);
}
Local<Signature> Signature::New(Isolate* v8_isolate,
Local<FunctionTemplate> receiver) {
return Local<Signature>::Cast(receiver);
}
#define SET_FIELD_WRAPPED(i_isolate, obj, setter, cdata, tag) \
do { \
i::DirectHandle<i::UnionOf<i::Smi, i::Foreign>> foreign = \
FromCData<tag>(i_isolate, cdata); \
(obj)->setter(*foreign); \
} while (false)
void FunctionTemplate::SetCallHandler(
FunctionCallback callback, v8::Local<Value> data,
SideEffectType side_effect_type,
const MemorySpan<const CFunction>& c_function_overloads) {
auto info = Utils::OpenDirectHandle(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);
info->set_has_side_effects(side_effect_type !=
SideEffectType::kHasNoSideEffect);
info->set_callback(i_isolate, reinterpret_cast<i::Address>(callback));
if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(i_isolate));
}
// "Release" callback and callback data fields.
info->set_callback_data(*Utils::OpenDirectHandle(*data), kReleaseStore);
if (!c_function_overloads.empty()) {
// 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::DirectHandle<i::FixedArray> function_overloads =
i_isolate->factory()->NewFixedArray(static_cast<int>(
c_function_overloads.size() *
i::FunctionTemplateInfo::kFunctionOverloadEntrySize));
int function_count = static_cast<int>(c_function_overloads.size());
for (int i = 0; i < function_count; i++) {
const CFunction& c_function = c_function_overloads.data()[i];
i::DirectHandle<i::Object> address = FromCData<internal::kCFunctionTag>(
i_isolate, c_function.GetAddress());
function_overloads->set(
i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i, *address);
i::DirectHandle<i::Object> signature =
FromCData<internal::kCFunctionInfoTag>(i_isolate,
c_function.GetTypeInfo());
function_overloads->set(
i::FunctionTemplateInfo::kFunctionOverloadEntrySize * i + 1,
*signature);
}
i::FunctionTemplateInfo::SetCFunctionOverloads(i_isolate, info,
function_overloads);
}
}
namespace {
template <typename Getter, typename Setter>
i::DirectHandle<i::AccessorInfo> MakeAccessorInfo(i::Isolate* i_isolate,
v8::Local<Name> name,
Getter getter, Setter setter,
v8::Local<Value> data,
bool replace_on_access) {
i::DirectHandle<i::AccessorInfo> obj =
i_isolate->factory()->NewAccessorInfo();
obj->set_getter(i_isolate, reinterpret_cast<i::Address>(getter));
DCHECK_IMPLIES(replace_on_access, setter == nullptr);
if (setter == nullptr) {
setter = reinterpret_cast<Setter>(&i::Accessors::ReconfigureToDataProperty);
}
obj->set_setter(i_isolate, reinterpret_cast<i::Address>(setter));
auto accessor_name = Utils::OpenDirectHandle(*name);
if (!IsUniqueName(*accessor_name)) {
accessor_name = i_isolate->factory()->InternalizeString(
i::Cast<i::String>(accessor_name));
}
i::DisallowGarbageCollection no_gc;
i::Tagged<i::AccessorInfo> raw_obj = *obj;
if (data.IsEmpty()) {
raw_obj->set_data(i::ReadOnlyRoots(i_isolate).undefined_value());
} else {
raw_obj->set_data(*Utils::OpenDirectHandle(*data));
}
raw_obj->set_name(*accessor_name);
raw_obj->set_replace_on_access(replace_on_access);
raw_obj->set_initial_property_attributes(i::NONE);
return obj;
}
} // namespace
Local<ObjectTemplate> FunctionTemplate::InstanceTemplate() {
auto constructor = Utils::OpenDirectHandle(this, true);
if (!Utils::ApiCheck(!constructor.is_null(),
"v8::FunctionTemplate::InstanceTemplate()",
"Reading from empty handle")) {
return Local<ObjectTemplate>();
}
i::Isolate* i_isolate = constructor->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
auto maybe_templ = constructor->GetInstanceTemplate();
if (!i::IsUndefined(maybe_templ, i_isolate)) {
return Utils::ToLocal(i::direct_handle(
i::Cast<i::ObjectTemplateInfo>(maybe_templ), i_isolate));
}
constexpr bool do_not_cache = false;
i::DirectHandle<i::ObjectTemplateInfo> templ =
i_isolate->factory()->NewObjectTemplateInfo(constructor, do_not_cache);
i::FunctionTemplateInfo::SetInstanceTemplate(i_isolate, constructor, templ);
return Utils::ToLocal(templ);
}
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<String> 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::OpenDirectHandle(*name));
}
void FunctionTemplate::SetInterfaceName(Local<String> name) {
auto info = Utils::OpenDirectHandle(this);
EnsureNotPublished(info, "v8::FunctionTemplate::SetInterfaceName");
i::Isolate* i_isolate = info->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
info->set_interface_name(*Utils::OpenDirectHandle(*name));
}
void FunctionTemplate::SetExceptionContext(ExceptionContext context) {
auto info = Utils::OpenDirectHandle(this);
EnsureNotPublished(info, "v8::FunctionTemplate::SetExceptionContext");
i::Isolate* i_isolate = info->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
info->set_exception_context(static_cast<uint32_t>(context));
}
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> ObjectTemplate::New(
Isolate* v8_isolate, v8::Local<FunctionTemplate> constructor) {
auto i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
API_RCS_SCOPE(i_isolate, ObjectTemplate, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
constexpr bool do_not_cache = false;
i::DirectHandle<i::ObjectTemplateInfo> obj =
i_isolate->factory()->NewObjectTemplateInfo(
Utils::OpenDirectHandle(*constructor, true), do_not_cache);
return Utils::ToLocal(obj);
}
namespace {
// Ensure that the object template has a constructor. If no
// constructor is available we create one.
i::DirectHandle<i::FunctionTemplateInfo> EnsureConstructor(
i::Isolate* i_isolate, ObjectTemplate* object_template) {
i::Tagged<i::Object> obj =
Utils::OpenDirectHandle(object_template)->constructor();
if (!IsUndefined(obj, i_isolate)) {
i::Tagged<i::FunctionTemplateInfo> info =
i::Cast<i::FunctionTemplateInfo>(obj);
return i::DirectHandle<i::FunctionTemplateInfo>(info, i_isolate);
}
Local<FunctionTemplate> templ =
FunctionTemplate::New(reinterpret_cast<Isolate*>(i_isolate));
auto constructor = Utils::OpenDirectHandle(*templ);
i::FunctionTemplateInfo::SetInstanceTemplate(
i_isolate, constructor, Utils::OpenDirectHandle(object_template));
Utils::OpenDirectHandle(object_template)->set_constructor(*constructor);
return constructor;
}
template <typename Getter, typename Setter, typename Data, typename Template>
void TemplateSetAccessor(Template* template_obj, v8::Local<Name> name,
Getter getter, Setter setter, Data data,
PropertyAttribute attribute, bool replace_on_access,
SideEffectType getter_side_effect_type,
SideEffectType setter_side_effect_type) {
auto info = Utils::OpenDirectHandle(template_obj);
auto i_isolate = info->GetIsolateChecked();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::HandleScope scope(i_isolate);
i::DirectHandle<i::AccessorInfo> accessor_info = MakeAccessorInfo(
i_isolate, name, getter, setter, data, replace_on_access);
{
i::DisallowGarbageCollection no_gc;
i::Tagged<i::AccessorInfo> raw = *accessor_info;
raw->set_initial_property_attributes(
static_cast<i::PropertyAttributes>(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> name,
AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter,
v8::Local<Value> data,
PropertyAttribute attribute,
SideEffectType getter_side_effect_type,
SideEffectType setter_side_effect_type) {
TemplateSetAccessor(this, name, getter, setter, data, attribute, false,
getter_side_effect_type, setter_side_effect_type);
}
void Template::SetLazyDataProperty(v8::Local<Name> name,
AccessorNameGetterCallback getter,
v8::Local<Value> data,
PropertyAttribute attribute,
SideEffectType getter_side_effect_type,
SideEffectType setter_side_effect_type) {
TemplateSetAccessor(
this, name, getter, static_cast<AccessorNameSetterCallback>(nullptr),
data, attribute, true, getter_side_effect_type, setter_side_effect_type);
}
void Template::SetIntrinsicDataProperty(Local<Name> name, Intrinsic intrinsic,
PropertyAttribute attribute) {
auto templ = Utils::OpenDirectHandle(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::OpenDirectHandle(*name), intrinsic,
static_cast<i::PropertyAttributes>(attribute));
}
namespace {
enum class PropertyType { kNamed, kIndexed };
template <PropertyType property_type, typename Getter, typename Setter,
typename Query, typename Descriptor, typename Deleter,
typename Enumerator, typename Definer>
i::DirectHandle<i::InterceptorInfo> CreateInterceptorInfo(
i::Isolate* i_isolate, Getter getter, Setter setter, Query query,
Descriptor descriptor, Deleter remover, Enumerator enumerator,
Definer definer, Local<Value> data,
base::Flags<PropertyHandlerFlags> flags) {
// TODO(saelo): instead of an in-sandbox struct with a lot of external
// pointers (with different tags), consider creating an object in trusted
// space instead. That way, only a single reference going out of the sandbox
// would be required.
auto obj = i::Cast<i::InterceptorInfo>(i_isolate->factory()->NewStruct(
i::INTERCEPTOR_INFO_TYPE, i::AllocationType::kOld));
obj->set_flags(0);
#define CALLBACK_TAG(NAME) \
property_type == PropertyType::kNamed \
? internal::kApiNamedProperty##NAME##CallbackTag \
: internal::kApiIndexedProperty##NAME##CallbackTag;
if (getter != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Getter);
SET_FIELD_WRAPPED(i_isolate, obj, set_getter, getter, tag);
}
if (setter != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Setter);
SET_FIELD_WRAPPED(i_isolate, obj, set_setter, setter, tag);
}
if (query != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Query);
SET_FIELD_WRAPPED(i_isolate, obj, set_query, query, tag);
}
if (descriptor != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Descriptor);
SET_FIELD_WRAPPED(i_isolate, obj, set_descriptor, descriptor, tag);
}
if (remover != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Deleter);
SET_FIELD_WRAPPED(i_isolate, obj, set_deleter, remover, tag);
}
if (enumerator != nullptr) {
SET_FIELD_WRAPPED(i_isolate, obj, set_enumerator, enumerator,
internal::kApiIndexedPropertyEnumeratorCallbackTag);
}
if (definer != nullptr) {
constexpr internal::ExternalPointerTag tag = CALLBACK_TAG(Definer);
SET_FIELD_WRAPPED(i_isolate, obj, set_definer, definer, tag);
}
#undef CALLBACK_TAG
obj->set_can_intercept_symbols(
!(flags & PropertyHandlerFlags::kOnlyInterceptStrings));
obj->set_non_masking(flags & PropertyHandlerFlags::kNonMasking);
obj->set_has_no_side_effect(flags & PropertyHandlerFlags::kHasNoSideEffect);
if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(i_isolate));
}
obj->set_data(*Utils::OpenDirectHandle(*data));
return obj;
}
template <typename Getter, typename Setter, typename Query, typename Descriptor,
typename Deleter, typename Enumerator, typename Definer>
i::DirectHandle<i::InterceptorInfo> CreateNamedInterceptorInfo(
i::Isolate* i_isolate, Getter getter, Setter setter, Query query,
Descriptor descriptor, Deleter remover, Enumerator enumerator,
Definer definer, Local<Value> data,
base::Flags<PropertyHandlerFlags> flags) {
auto interceptor = CreateInterceptorInfo<PropertyType::kNamed>(
i_isolate, getter, setter, query, descriptor, remover, enumerator,
definer, data, flags);
interceptor->set_is_named(true);
return interceptor;
}
template <typename Getter, typename Setter, typename Query, typename Descriptor,
typename Deleter, typename Enumerator, typename Definer>
i::DirectHandle<i::InterceptorInfo> CreateIndexedInterceptorInfo(
i::Isolate* i_isolate, Getter getter, Setter setter, Query query,
Descriptor descriptor, Deleter remover, Enumerator enumerator,
Definer definer, Local<Value> data,
base::Flags<PropertyHandlerFlags> flags) {
auto interceptor = CreateInterceptorInfo<PropertyType::kIndexed>(
i_isolate, getter, setter, query, descriptor, remover, enumerator,
definer, data, flags);
interceptor->set_is_named(false);
return interceptor;
}
template <typename Getter, typename Setter, typename Query, typename Descriptor,
typename Deleter, typename Enumerator, typename Definer>
void ObjectTemplateSetNamedPropertyHandler(
ObjectTemplate* templ, Getter getter, Setter setter, Query query,
Descriptor descriptor, Deleter remover, Enumerator enumerator,
Definer definer, Local<Value> data, PropertyHandlerFlags flags) {
i::Isolate* i_isolate = Utils::OpenDirectHandle(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<Value> 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::DirectHandle<i::Struct> struct_info = i_isolate->factory()->NewStruct(
i::ACCESS_CHECK_INFO_TYPE, i::AllocationType::kOld);
auto info = i::Cast<i::AccessCheckInfo>(struct_info);
SET_FIELD_WRAPPED(i_isolate, info, set_callback, callback,
internal::kApiAccessCheckCallbackTag);
info->set_named_interceptor(i::Smi::zero());
info->set_indexed_interceptor(i::Smi::zero());
if (data.IsEmpty()) {
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(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<Value> 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::DirectHandle<i::Struct> struct_info = i_isolate->factory()->NewStruct(
i::ACCESS_CHECK_INFO_TYPE, i::AllocationType::kOld);
auto info = i::Cast<i::AccessCheckInfo>(struct_info);
SET_FIELD_WRAPPED(i_isolate, info, set_callback, callback,
internal::kApiAccessCheckCallbackTag);
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<v8::Isolate*>(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<Value> 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");
DCHECK_NOT_NULL(callback);
// This template is just a container for callback and data values and thus
// it's not supposed to be instantiated. Don't cache it.
constexpr bool do_not_cache = true;
constexpr int length = 0;
i::DirectHandle<i::FunctionTemplateInfo> templ =
i_isolate->factory()->NewFunctionTemplateInfo(length, do_not_cache);
templ->set_is_object_template_call_handler(true);
Utils::ToLocal(templ)->SetCallHandler(callback, data);
i::FunctionTemplateInfo::SetInstanceCallHandler(i_isolate, cons, templ);
}
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);
}
Local<DictionaryTemplate> DictionaryTemplate::New(
Isolate* isolate, MemorySpan<const std::string_view> names) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
API_RCS_SCOPE(i_isolate, DictionaryTemplate, New);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return Utils::ToLocal(i::DictionaryTemplateInfo::Create(i_isolate, names));
}
Local<Object> DictionaryTemplate::NewInstance(
Local<Context> context, MemorySpan<MaybeLocal<Value>> property_values) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
API_RCS_SCOPE(i_isolate, DictionaryTemplate, NewInstance);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
auto self = Utils::OpenDirectHandle(this);
return ToApiHandle<Object>(i::DictionaryTemplateInfo::NewInstance(
Utils::OpenDirectHandle(*context), self, property_values));
}
// --- 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::CachedData::CompatibilityCheckResult
ScriptCompiler::CachedData::CompatibilityCheck(Isolate* isolate) {
i::AlignedCachedData aligned(data, length);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::SerializedCodeSanityCheckResult result;
i::SerializedCodeData scd =
i::SerializedCodeData::FromCachedDataWithoutSource(
i_isolate->AsLocalIsolate(), &aligned, &result);
return static_cast<ScriptCompiler::CachedData::CompatibilityCheckResult>(
result);
}
ScriptCompiler::StreamedSource::StreamedSource(
std::unique_ptr<ExternalSourceStream> stream, Encoding encoding)
: impl_(new i::ScriptStreamingData(std::move(stream), encoding)) {}
ScriptCompiler::StreamedSource::~StreamedSource() = default;
Local<Script> UnboundScript::BindToCurrentContext() {
auto function_info = Utils::OpenDirectHandle(this);
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*function_info));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*function_info);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::JSFunction> function =
i::Factory::JSFunctionBuilder{i_isolate, function_info,
i_isolate->native_context()}
.Build();
return ToApiHandle<Script>(function);
}
int UnboundScript::GetId() const {
auto function_info = Utils::OpenDirectHandle(this);
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*function_info));
API_RCS_SCOPE(i::GetIsolateFromWritableObject(*function_info), UnboundScript,
GetId);
return i::Cast<i::Script>(function_info->script())->id();
}
int UnboundScript::GetLineNumber(int code_pos) {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
API_RCS_SCOPE(i_isolate, UnboundScript, GetLineNumber);
i::DirectHandle<i::Script> script(i::Cast<i::Script>(obj->script()),
i_isolate);
return i::Script::GetLineNumber(script, code_pos);
} else {
return -1;
}
}
int UnboundScript::GetColumnNumber(int code_pos) {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
API_RCS_SCOPE(i_isolate, UnboundScript, GetColumnNumber);
i::DirectHandle<i::Script> script(i::Cast<i::Script>(obj->script()),
i_isolate);
return i::Script::GetColumnNumber(script, code_pos);
} else {
return -1;
}
}
Local<Value> UnboundScript::GetScriptName() {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
API_RCS_SCOPE(i_isolate, UnboundScript, GetName);
i::Tagged<i::Object> name = i::Cast<i::Script>(obj->script())->name();
return Utils::ToLocal(i::direct_handle(name, i_isolate));
} else {
return Local<String>();
}
}
Local<Value> UnboundScript::GetSourceURL() {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
API_RCS_SCOPE(i_isolate, UnboundScript, GetSourceURL);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Tagged<i::Object> url = i::Cast<i::Script>(obj->script())->source_url();
return Utils::ToLocal(i::direct_handle(url, i_isolate));
} else {
return Local<String>();
}
}
Local<Value> UnboundScript::GetSourceMappingURL() {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
API_RCS_SCOPE(i_isolate, UnboundScript, GetSourceMappingURL);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Tagged<i::Object> url =
i::Cast<i::Script>(obj->script())->source_mapping_url();
return Utils::ToLocal(i::direct_handle(url, i_isolate));
} else {
return Local<String>();
}
}
Local<Value> UnboundModuleScript::GetSourceURL() {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
API_RCS_SCOPE(i_isolate, UnboundModuleScript, GetSourceURL);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Tagged<i::Object> url = i::Cast<i::Script>(obj->script())->source_url();
return Utils::ToLocal(i::direct_handle(url, i_isolate));
} else {
return Local<String>();
}
}
Local<Value> UnboundModuleScript::GetSourceMappingURL() {
auto obj = Utils::OpenDirectHandle(this);
if (i::IsScript(obj->script())) {
// TODO(jgruber): Remove this DCHECK once Function::GetUnboundScript is
// gone.
DCHECK(!i::HeapLayout::InReadOnlySpace(*obj));
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
API_RCS_SCOPE(i_isolate, UnboundModuleScript, GetSourceMappingURL);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Tagged<i::Object> url =
i::Cast<i::Script>(obj->script())->source_mapping_url();
return Utils::ToLocal(i::direct_handle(url, i_isolate));
} else {
return Local<String>();
}
}
MaybeLocal<Value> Script::Run(Local<Context> context) {
return Run(context, Local<Data>());
}
MaybeLocal<Value> Script::Run(Local<Context> context,
Local<Data> host_defined_options) {
auto v8_isolate = context->GetIsolate();
auto i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
TRACE_EVENT_CALL_STATS_SCOPED(i_isolate, "v8", "V8.Execute");
ENTER_V8(i_isolate, context, Script, Run, InternalEscapableScope);
i::TimerEventScope<i::TimerEventExecute> timer_scope(i_isolate);
i::NestedTimedHistogramScope execute_timer(i_isolate->counters()->execute(),
i_isolate);
i::AggregatingHistogramTimerScope histogram_timer(
i_isolate->counters()->compile_lazy());
#if defined(V8_ENABLE_ETW_STACK_WALKING)
// In case ETW has been activated, tasks to log existing code are
// created. But in case the task runner does not run those before
// starting to execute code (as it happens in d8, that will run
// first the code from prompt), then that code will not have
// JIT instrumentation on time.
//
// To avoid this, on running scripts check first if JIT code log is
// pending and generate immediately.
if (i::v8_flags.enable_etw_stack_walking ||
i::v8_flags.enable_etw_by_custom_filter_only) {
i::ETWJITInterface::MaybeSetHandlerNow(i_isolate);
}
#endif // V8_ENABLE_ETW_STACK_WALKING
auto fun = i::Cast<i::JSFunction>(Utils::OpenDirectHandle(this));
i::DirectHandle<i::Object> receiver = i_isolate->global_proxy();
// TODO(cbruni, chromium:1244145): Remove once migrated to the context.
i::DirectHandle<i::Object> options(
i::Cast<i::Script>(fun->shared()->script())->host_defined_options(),
i_isolate);
Local<Value> result;
has_exception = !ToLocal<Value>(
i::Execution::CallScript(i_isolate, fun, receiver, options), &result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
Local<Value> ScriptOrModule::GetResourceName() {
auto obj = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return ToApiHandle<Value>(i::direct_handle(obj->resource_name(), i_isolate));
}
Local<Data> ScriptOrModule::HostDefinedOptions() {
auto obj = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = i::GetIsolateFromWritableObject(*obj);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return ToApiHandle<Data>(
i::direct_handle(obj->host_defined_options(), i_isolate));
}
Local<UnboundScript> Script::GetUnboundScript() {
i::DisallowGarbageCollection no_gc;
auto obj = Utils::OpenDirectHandle(this);
i::DirectHandle<i::SharedFunctionInfo> sfi(obj->shared(), obj->GetIsolate());
DCHECK(!i::HeapLayout::InReadOnlySpace(*sfi));
return ToApiHandle<UnboundScript>(sfi);
}
Local<Value> Script::GetResourceName() {
i::DisallowGarbageCollection no_gc;
auto func = Utils::OpenDirectHandle(this);
i::Tagged<i::SharedFunctionInfo> sfi = func->shared();
CHECK(IsScript(sfi->script()));
i::Isolate* i_isolate = func->GetIsolate();
return ToApiHandle<Value>(
i::direct_handle(i::Cast<i::Script>(sfi->script())->name(), i_isolate));
}
std::vector<int> Script::GetProducedCompileHints() const {
i::DisallowGarbageCollection no_gc;
auto func = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = func->GetIsolate();
i::Tagged<i::SharedFunctionInfo> sfi = func->shared();
CHECK(IsScript(sfi->script()));
i::Tagged<i::Script> script = i::Cast<i::Script>(sfi->script());
i::Tagged<i::Object> maybe_array_list =
script->compiled_lazy_function_positions();
std::vector<int> result;
if (!IsUndefined(maybe_array_list, i_isolate)) {
i::Tagged<i::ArrayList> array_list =
i::Cast<i::ArrayList>(maybe_array_list);
result.reserve(array_list->length());
for (int i = 0; i < array_list->length(); ++i) {
i::Tagged<i::Object> item = array_list->get(i);
CHECK(IsSmi(item));
result.push_back(i::Smi::ToInt(item));
}
}
return result;
}
Local<CompileHintsCollector> Script::GetCompileHintsCollector() const {
i::DisallowGarbageCollection no_gc;
auto func = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = func->GetIsolate();
i::Tagged<i::SharedFunctionInfo> sfi = func->shared();
CHECK(IsScript(sfi->script()));
i::DirectHandle<i::Script> script(i::Cast<i::Script>(sfi->script()),
i_isolate);
return ToApiHandle<CompileHintsCollector>(script);
}
std::vector<int> CompileHintsCollector::GetCompileHints(
Isolate* v8_isolate) const {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::Tagged<i::Object> maybe_array_list =
script->compiled_lazy_function_positions();
std::vector<int> result;
if (!IsUndefined(maybe_array_list, i_isolate)) {
i::Tagged<i::ArrayList> array_list =
i::Cast<i::ArrayList>(maybe_array_list);
result.reserve(array_list->length());
for (int i = 0; i < array_list->length(); ++i) {
i::Tagged<i::Object> item = array_list->get(i);
CHECK(IsSmi(item));
result.push_back(i::Smi::ToInt(item));
}
}
return result;
}
// static
Local<PrimitiveArray> PrimitiveArray::New(Isolate* v8_isolate, int length) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
Utils::ApiCheck(length >= 0, "v8::PrimitiveArray::New",
"length must be equal or greater than zero");
i::DirectHandle<i::FixedArray> array =
i_isolate->factory()->NewFixedArray(length);
return ToApiHandle<PrimitiveArray>(array);
}
int PrimitiveArray::Length() const {
return Utils::OpenDirectHandle(this)->length();
}
void PrimitiveArray::Set(Isolate* v8_isolate, int index,
Local<Primitive> item) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto array = Utils::OpenDirectHandle(this);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
Utils::ApiCheck(index >= 0 && index < array->length(),
"v8::PrimitiveArray::Set",
"index must be greater than or equal to 0 and less than the "
"array length");
array->set(index, *Utils::OpenDirectHandle(*item));
}
Local<Primitive> PrimitiveArray::Get(Isolate* v8_isolate, int index) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto array = Utils::OpenDirectHandle(this);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
Utils::ApiCheck(index >= 0 && index < array->length(),
"v8::PrimitiveArray::Get",
"index must be greater than or equal to 0 and less than the "
"array length");
return ToApiHandle<Primitive>(i::direct_handle(array->get(index), i_isolate));
}
void v8::PrimitiveArray::CheckCast(v8::Data* that) {
auto obj = Utils::OpenDirectHandle(that);
Utils::ApiCheck(
i::IsFixedArray(*obj), "v8::PrimitiveArray::Cast",
"Value is not a PrimitiveArray; this is a temporary issue, v8::Data and "
"v8::PrimitiveArray will not be compatible in the future");
}
int FixedArray::Length() const {
return Utils::OpenDirectHandle(this)->length();
}
Local<Data> FixedArray::Get(Local<Context> context, int i) const {
auto self = Utils::OpenDirectHandle(this);
auto i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
CHECK_LT(i, self->length());
return ToApiHandle<Data>(i::direct_handle(self->get(i), i_isolate));
}
Local<String> ModuleRequest::GetSpecifier() const {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolate();
return ToApiHandle<String>(i::direct_handle(self->specifier(), i_isolate));
}
ModuleImportPhase ModuleRequest::GetPhase() const {
auto self = Utils::OpenDirectHandle(this);
return self->phase();
}
int ModuleRequest::GetSourceOffset() const {
return Utils::OpenDirectHandle(this)->position();
}
Local<FixedArray> ModuleRequest::GetImportAttributes() const {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolate();
return ToApiHandle<FixedArray>(
i::direct_handle(self->import_attributes(), i_isolate));
}
Module::Status Module::GetStatus() const {
auto self = Utils::OpenDirectHandle(this);
switch (self->status()) {
case i::Module::kUnlinked:
case i::Module::kPreLinking:
return kUninstantiated;
case i::Module::kLinking:
return kInstantiating;
case i::Module::kLinked:
return kInstantiated;
case i::Module::kEvaluating:
return kEvaluating;
case i::Module::kEvaluatingAsync:
// TODO(syg): Expose kEvaluatingAsync in API as well.
case i::Module::kEvaluated:
return kEvaluated;
case i::Module::kErrored:
return kErrored;
}
UNREACHABLE();
}
Local<Value> Module::GetException() const {
Utils::ApiCheck(GetStatus() == kErrored, "v8::Module::GetException",
"Module status must be kErrored");
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return ToApiHandle<Value>(i::direct_handle(self->GetException(), i_isolate));
}
Local<FixedArray> Module::GetModuleRequests() const {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolate();
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
if (i::IsSyntheticModule(*self)) {
// Synthetic modules are leaf nodes in the module graph. They have no
// ModuleRequests.
return ToApiHandle<FixedArray>(i_isolate->factory()->empty_fixed_array());
} else {
return ToApiHandle<FixedArray>(i::direct_handle(
i::Cast<i::SourceTextModule>(self)->info()->module_requests(),
i_isolate));
}
}
Location Module::SourceOffsetToLocation(int offset) const {
auto self = Utils::OpenDirectHandle(this);
i::Isolate* i_isolate = self->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::HandleScope scope(i_isolate);
Utils::ApiCheck(
i::IsSourceTextModule(*self), "v8::Module::SourceOffsetToLocation",
"v8::Module::SourceOffsetToLocation must be used on an SourceTextModule");
i::DirectHandle<i::Script> script(
i::Cast<i::SourceTextModule>(self)->GetScript(), i_isolate);
i::Script::PositionInfo info;
i::Script::GetPositionInfo(script, offset, &info);
return v8::Location(info.line, info.column);
}
Local<Value> Module::GetModuleNamespace() {
Utils::ApiCheck(
GetStatus() >= kInstantiated, "v8::Module::GetModuleNamespace",
"v8::Module::GetModuleNamespace must be used on an instantiated module");
auto self = Utils::OpenHandle(this);
auto i_isolate = self->GetIsolate();
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::DirectHandle<i::JSModuleNamespace> module_namespace =
i::Module::GetModuleNamespace(i_isolate, self);
return ToApiHandle<Value>(module_namespace);
}
Local<UnboundModuleScript> Module::GetUnboundModuleScript() {
auto self = Utils::OpenDirectHandle(this);
Utils::ApiCheck(
i::IsSourceTextModule(*self), "v8::Module::GetUnboundModuleScript",
"v8::Module::GetUnboundModuleScript must be used on an SourceTextModule");
auto i_isolate = self->GetIsolate();
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return ToApiHandle<UnboundModuleScript>(i::direct_handle(
i::Cast<i::SourceTextModule>(self)->GetSharedFunctionInfo(), i_isolate));
}
int Module::ScriptId() const {
i::Tagged<i::Module> self = *Utils::OpenDirectHandle(this);
Utils::ApiCheck(i::IsSourceTextModule(self), "v8::Module::ScriptId",
"v8::Module::ScriptId must be used on an SourceTextModule");
DCHECK_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate());
return i::Cast<i::SourceTextModule>(self)->GetScript()->id();
}
bool Module::HasTopLevelAwait() const {
i::Tagged<i::Module> self = *Utils::OpenDirectHandle(this);
if (!i::IsSourceTextModule(self)) return false;
return i::Cast<i::SourceTextModule>(self)->has_toplevel_await();
}
bool Module::IsGraphAsync() const {
Utils::ApiCheck(
GetStatus() >= kInstantiated, "v8::Module::IsGraphAsync",
"v8::Module::IsGraphAsync must be used on an instantiated module");
i::Tagged<i::Module> self = *Utils::OpenDirectHandle(this);
auto i_isolate = self->GetIsolate();
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return self->IsGraphAsync(i_isolate);
}
bool Module::IsSourceTextModule() const {
auto self = Utils::OpenDirectHandle(this);
DCHECK_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate());
return i::IsSourceTextModule(*self);
}
bool Module::IsSyntheticModule() const {
auto self = Utils::OpenDirectHandle(this);
DCHECK_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate());
return i::IsSyntheticModule(*self);
}
int Module::GetIdentityHash() const {
auto self = Utils::OpenDirectHandle(this);
DCHECK_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate());
return self->hash();
}
Maybe<bool> Module::InstantiateModule(Local<Context> context,
ResolveModuleCallback module_callback,
ResolveSourceCallback source_callback) {
auto i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
ENTER_V8(i_isolate, context, Module, InstantiateModule, i::HandleScope);
has_exception =
!i::Module::Instantiate(i_isolate, Utils::OpenHandle(this), context,
module_callback, source_callback);
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
MaybeLocal<Value> Module::Evaluate(Local<Context> context) {
auto i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
TRACE_EVENT_CALL_STATS_SCOPED(i_isolate, "v8", "V8.Execute");
ENTER_V8(i_isolate, context, Module, Evaluate, InternalEscapableScope);
i::TimerEventScope<i::TimerEventExecute> timer_scope(i_isolate);
i::NestedTimedHistogramScope execute_timer(i_isolate->counters()->execute(),
i_isolate);
i::AggregatingHistogramTimerScope timer(
i_isolate->counters()->compile_lazy());
auto self = Utils::OpenHandle(this);
Utils::ApiCheck(self->status() >= i::Module::kLinked, "Module::Evaluate",
"Expected instantiated module");
Local<Value> result;
has_exception = !ToLocal(i::Module::Evaluate(i_isolate, self), &result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
Local<Module> Module::CreateSyntheticModule(
Isolate* v8_isolate, Local<String> module_name,
const MemorySpan<const Local<String>>& export_names,
v8::Module::SyntheticModuleEvaluationSteps evaluation_steps) {
auto i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
auto i_module_name = Utils::OpenDirectHandle(*module_name);
i::DirectHandle<i::FixedArray> i_export_names =
i_isolate->factory()->NewFixedArray(
static_cast<int>(export_names.size()));
for (int i = 0; i < i_export_names->length(); ++i) {
i::DirectHandle<i::String> str = i_isolate->factory()->InternalizeString(
Utils::OpenDirectHandle(*export_names[i]));
i_export_names->set(i, *str);
}
return v8::Utils::ToLocal(
i::DirectHandle<i::Module>(i_isolate->factory()->NewSyntheticModule(
i_module_name, i_export_names, evaluation_steps)));
}
Maybe<bool> Module::SetSyntheticModuleExport(Isolate* v8_isolate,
Local<String> export_name,
Local<v8::Value> export_value) {
auto i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto i_export_name = Utils::OpenDirectHandle(*export_name);
auto i_export_value = Utils::OpenDirectHandle(*export_value);
auto self = Utils::OpenDirectHandle(this);
Utils::ApiCheck(i::IsSyntheticModule(*self),
"v8::Module::SyntheticModuleSetExport",
"v8::Module::SyntheticModuleSetExport must only be called on "
"a SyntheticModule");
ENTER_V8_NO_SCRIPT(i_isolate, v8_isolate->GetCurrentContext(), Module,
SetSyntheticModuleExport, i::HandleScope);
has_exception = i::SyntheticModule::SetExport(
i_isolate, i::Cast<i::SyntheticModule>(self),
i_export_name, i_export_value)
.IsNothing();
RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool);
return Just(true);
}
std::pair<LocalVector<Module>, LocalVector<Message>>
Module::GetStalledTopLevelAwaitMessages(Isolate* isolate) {
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate);
auto self = Utils::OpenDirectHandle(this);
Utils::ApiCheck(i::IsSourceTextModule(*self),
"v8::Module::GetStalledTopLevelAwaitMessages",
"v8::Module::GetStalledTopLevelAwaitMessages must only be "
"called on a SourceTextModule");
std::pair<i::DirectHandleVector<i::SourceTextModule>,
i::DirectHandleVector<i::JSMessageObject>>
stalled_awaits =
i::Cast<i::SourceTextModule>(self)->GetStalledTopLevelAwaitMessages(
i_isolate);
LocalVector<Module> modules(isolate);
if (size_t stalled_awaits_count = stalled_awaits.first.size();
stalled_awaits_count > 0) {
modules.reserve(stalled_awaits_count);
for (auto module : stalled_awaits.first)
modules.push_back(ToApiHandle<Module>(module));
}
LocalVector<Message> messages(isolate);
if (size_t stalled_awaits_count = stalled_awaits.second.size();
stalled_awaits_count > 0) {
messages.reserve(stalled_awaits_count);
for (auto message : stalled_awaits.second)
messages.push_back(ToApiHandle<Message>(message));
}
return {modules, messages};
}
namespace {
i::ScriptDetails GetScriptDetails(
i::Isolate* i_isolate, Local<Value> resource_name, int resource_line_offset,
int resource_column_offset, Local<Value> source_map_url,
Local<Data> host_defined_options, ScriptOriginOptions origin_options) {
i::ScriptDetails script_details(Utils::OpenHandle(*(resource_name), true),
origin_options);
script_details.line_offset = resource_line_offset;
script_details.column_offset = resource_column_offset;
script_details.host_defined_options =
host_defined_options.IsEmpty()
? i_isolate->factory()->empty_fixed_array()
: Utils::OpenHandle(*(host_defined_options));
if (!source_map_url.IsEmpty()) {
script_details.source_map_url = Utils::OpenHandle(*(source_map_url));
}
return script_details;
}
} // namespace
MaybeLocal<UnboundScript> ScriptCompiler::CompileUnboundInternal(
Isolate* v8_isolate, Source* source, CompileOptions options,
NoCacheReason no_cache_reason) {
auto i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
TRACE_EVENT_CALL_STATS_SCOPED(i_isolate, "v8", "V8.ScriptCompiler");
ENTER_V8_NO_SCRIPT(i_isolate, v8_isolate->GetCurrentContext(), ScriptCompiler,
CompileUnbound, InternalEscapableScope);
auto str = Utils::OpenHandle(*(source->source_string));
i::DirectHandle<i::SharedFunctionInfo> result;
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileScript");
i::ScriptDetails script_details = GetScriptDetails(
i_isolate, source->resource_name, source->resource_line_offset,
source->resource_column_offset, source->source_map_url,
source->host_defined_options, source->resource_options);
i::MaybeDirectHandle<i::SharedFunctionInfo> maybe_function_info;
if (options & kConsumeCodeCache) {
if (source->consume_cache_task) {
// Take ownership of the internal deserialization task and clear it off
// the consume task on the source.
DCHECK_NOT_NULL(source->consume_cache_task->impl_);
std::unique_ptr<i::BackgroundDeserializeTask> deserialize_task =
std::move(source->consume_cache_task->impl_);
maybe_function_info =
i::Compiler::GetSharedFunctionInfoForScriptWithDeserializeTask(
i_isolate, str, script_details, deserialize_task.get(), options,
no_cache_reason, i::NOT_NATIVES_CODE,
&source->compilation_details);
source->cached_data->rejected = deserialize_task->rejected();
} else {
DCHECK(source->cached_data);
// AlignedCachedData takes care of pointer-aligning the data.
auto cached_data = std::make_unique<i::AlignedCachedData>(
source->cached_data->data, source->cached_data->length);
maybe_function_info =
i::Compiler::GetSharedFunctionInfoForScriptWithCachedData(
i_isolate, str, script_details, cached_data.get(), options,
no_cache_reason, i::NOT_NATIVES_CODE,
&source->compilation_details);
source->cached_data->rejected = cached_data->rejected();
}
} else if (options & kConsumeCompileHints) {
maybe_function_info =
i::Compiler::GetSharedFunctionInfoForScriptWithCompileHints(
i_isolate, str, script_details, source->compile_hint_callback,
source->compile_hint_callback_data, options, no_cache_reason,
i::NOT_NATIVES_CODE, &source->compilation_details);
} else {
// Compile without any cache.
maybe_function_info = i::Compiler::GetSharedFunctionInfoForScript(
i_isolate, str, script_details, options, no_cache_reason,
i::NOT_NATIVES_CODE, &source->compilation_details);
}
has_exception = !maybe_function_info.ToHandle(&result);
DCHECK_IMPLIES(!has_exception, !i::HeapLayout::InReadOnlySpace(*result));
RETURN_ON_FAILED_EXECUTION(UnboundScript);
RETURN_ESCAPED(ToApiHandle<UnboundScript>(result));
}
MaybeLocal<UnboundScript> ScriptCompiler::CompileUnboundScript(
Isolate* v8_isolate, Source* source, CompileOptions options,
NoCacheReason no_cache_reason) {
Utils::ApiCheck(
!source->GetResourceOptions().IsModule(),
"v8::ScriptCompiler::CompileUnbo