// 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.h" #include // For memcpy, strlen. #include // For isnan. #include #include #include "src/api-inl.h" #include "include/v8-profiler.h" #include "include/v8-testing.h" #include "include/v8-util.h" #include "src/accessors.h" #include "src/api-natives.h" #include "src/assert-scope.h" #include "src/base/functional.h" #include "src/base/logging.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/bootstrapper.h" #include "src/builtins/builtins-utils.h" #include "src/char-predicates-inl.h" #include "src/code-stubs.h" #include "src/compiler-dispatcher/compiler-dispatcher.h" #include "src/compiler.h" #include "src/contexts.h" #include "src/conversions-inl.h" #include "src/counters.h" #include "src/debug/debug-coverage.h" #include "src/debug/debug-evaluate.h" #include "src/debug/debug-type-profile.h" #include "src/debug/debug.h" #include "src/debug/liveedit.h" #include "src/deoptimizer.h" #include "src/detachable-vector.h" #include "src/execution.h" #include "src/frames-inl.h" #include "src/gdb-jit.h" #include "src/global-handles.h" #include "src/globals.h" #include "src/icu_util.h" #include "src/isolate-inl.h" #include "src/json-parser.h" #include "src/json-stringifier.h" #include "src/messages.h" #include "src/objects-inl.h" #include "src/objects/api-callbacks.h" #include "src/objects/js-array-inl.h" #include "src/objects/js-collection-inl.h" #include "src/objects/js-generator-inl.h" #include "src/objects/js-promise-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/module-inl.h" #include "src/objects/ordered-hash-table-inl.h" #include "src/objects/stack-frame-info-inl.h" #include "src/objects/templates.h" #include "src/parsing/parse-info.h" #include "src/parsing/parser.h" #include "src/parsing/scanner-character-streams.h" #include "src/pending-compilation-error-handler.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/property-descriptor.h" #include "src/property-details.h" #include "src/property.h" #include "src/prototype.h" #include "src/runtime-profiler.h" #include "src/runtime/runtime.h" #include "src/simulator.h" #include "src/snapshot/builtin-serializer.h" #include "src/snapshot/code-serializer.h" #include "src/snapshot/natives.h" #include "src/snapshot/snapshot.h" #include "src/startup-data-util.h" #include "src/string-hasher.h" #include "src/tracing/trace-event.h" #include "src/trap-handler/trap-handler.h" #include "src/unicode-cache-inl.h" #include "src/unicode-inl.h" #include "src/v8.h" #include "src/v8threads.h" #include "src/value-serializer.h" #include "src/version.h" #include "src/vm-state-inl.h" #include "src/wasm/streaming-decoder.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-result.h" #include "src/wasm/wasm-serialization.h" namespace v8 { /* * Most API methods should use one of the three macros: * * ENTER_V8, ENTER_V8_NO_SCRIPT, ENTER_V8_NO_SCRIPT_NO_EXCEPTION. * * The latter two assume that no script is executed, and no exceptions are * scheduled in addition (respectively). Creating a pending exception and * removing it before returning is ok. * * Exceptions should be handled either by invoking one of the * RETURN_ON_FAILED_EXECUTION* macros. * * Don't use macros with DO_NOT_USE in their name. * * TODO(jochen): Document debugger specific macros. * TODO(jochen): Document LOG_API and other RuntimeCallStats macros. * TODO(jochen): All API methods should invoke one of the ENTER_V8* macros. * TODO(jochen): Remove calls form API methods to DO_NOT_USE macros. */ #define LOG_API(isolate, class_name, function_name) \ i::RuntimeCallTimerScope _runtime_timer( \ isolate, i::RuntimeCallCounterId::kAPI_##class_name##_##function_name); \ LOG(isolate, ApiEntryCall("v8::" #class_name "::" #function_name)) #define ENTER_V8_DO_NOT_USE(isolate) i::VMState __state__((isolate)) #define ENTER_V8_HELPER_DO_NOT_USE(isolate, context, class_name, \ function_name, bailout_value, \ HandleScopeClass, do_callback) \ if (IsExecutionTerminatingCheck(isolate)) { \ return bailout_value; \ } \ HandleScopeClass handle_scope(isolate); \ CallDepthScope call_depth_scope(isolate, context); \ LOG_API(isolate, class_name, function_name); \ i::VMState __state__((isolate)); \ bool has_pending_exception = false #define PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(isolate, T) \ if (IsExecutionTerminatingCheck(isolate)) { \ return MaybeLocal(); \ } \ InternalEscapableScope handle_scope(isolate); \ CallDepthScope call_depth_scope(isolate, v8::Local()); \ i::VMState __state__((isolate)); \ bool has_pending_exception = false #define PREPARE_FOR_EXECUTION_WITH_CONTEXT(context, class_name, function_name, \ bailout_value, HandleScopeClass, \ do_callback) \ auto isolate = context.IsEmpty() \ ? i::Isolate::Current() \ : reinterpret_cast(context->GetIsolate()); \ ENTER_V8_HELPER_DO_NOT_USE(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass, do_callback); #define PREPARE_FOR_EXECUTION(context, class_name, function_name, T) \ PREPARE_FOR_EXECUTION_WITH_CONTEXT(context, class_name, function_name, \ MaybeLocal(), InternalEscapableScope, \ false) #define ENTER_V8(isolate, context, class_name, function_name, bailout_value, \ HandleScopeClass) \ ENTER_V8_HELPER_DO_NOT_USE(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass, true) #ifdef DEBUG #define ENTER_V8_NO_SCRIPT(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass) \ ENTER_V8_HELPER_DO_NOT_USE(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass, false); \ i::DisallowJavascriptExecutionDebugOnly __no_script__((isolate)) #define ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate) \ i::VMState __state__((isolate)); \ i::DisallowJavascriptExecutionDebugOnly __no_script__((isolate)); \ i::DisallowExceptions __no_exceptions__((isolate)) #define ENTER_V8_FOR_NEW_CONTEXT(isolate) \ i::VMState __state__((isolate)); \ i::DisallowExceptions __no_exceptions__((isolate)) #else #define ENTER_V8_NO_SCRIPT(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass) \ ENTER_V8_HELPER_DO_NOT_USE(isolate, context, class_name, function_name, \ bailout_value, HandleScopeClass, false) #define ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate) \ i::VMState __state__((isolate)); #define ENTER_V8_FOR_NEW_CONTEXT(isolate) \ i::VMState __state__((isolate)); #endif // DEBUG #define EXCEPTION_BAILOUT_CHECK_SCOPED_DO_NOT_USE(isolate, value) \ do { \ if (has_pending_exception) { \ call_depth_scope.Escape(); \ return value; \ } \ } while (false) #define RETURN_ON_FAILED_EXECUTION(T) \ EXCEPTION_BAILOUT_CHECK_SCOPED_DO_NOT_USE(isolate, MaybeLocal()) #define RETURN_ON_FAILED_EXECUTION_PRIMITIVE(T) \ EXCEPTION_BAILOUT_CHECK_SCOPED_DO_NOT_USE(isolate, Nothing()) #define RETURN_TO_LOCAL_UNCHECKED(maybe_local, T) \ return maybe_local.FromMaybe(Local()); #define RETURN_ESCAPED(value) return handle_scope.Escape(value); namespace { Local ContextFromNeverReadOnlySpaceObject( i::Handle obj) { return reinterpret_cast(obj->GetIsolate())->GetCurrentContext(); } class InternalEscapableScope : public v8::EscapableHandleScope { public: explicit inline InternalEscapableScope(i::Isolate* isolate) : v8::EscapableHandleScope(reinterpret_cast(isolate)) {} }; // TODO(jochen): This should be #ifdef DEBUG #ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY void CheckMicrotasksScopesConsistency(i::Isolate* isolate) { auto handle_scope_implementer = isolate->handle_scope_implementer(); if (handle_scope_implementer->microtasks_policy() == v8::MicrotasksPolicy::kScoped) { DCHECK(handle_scope_implementer->GetMicrotasksScopeDepth() || !handle_scope_implementer->DebugMicrotasksScopeDepthIsZero()); } } #endif template class CallDepthScope { public: explicit CallDepthScope(i::Isolate* isolate, Local context) : isolate_(isolate), context_(context), escaped_(false), safe_for_termination_(isolate->next_v8_call_is_safe_for_termination()), interrupts_scope_(isolate_, i::StackGuard::TERMINATE_EXECUTION, isolate_->only_terminate_in_safe_scope() ? (safe_for_termination_ ? i::InterruptsScope::kRunInterrupts : i::InterruptsScope::kPostponeInterrupts) : i::InterruptsScope::kNoop) { // TODO(dcarney): remove this when blink stops crashing. DCHECK(!isolate_->external_caught_exception()); isolate_->handle_scope_implementer()->IncrementCallDepth(); isolate_->set_next_v8_call_is_safe_for_termination(false); if (!context.IsEmpty()) { i::Handle env = Utils::OpenHandle(*context); i::HandleScopeImplementer* impl = isolate->handle_scope_implementer(); if (isolate->context() != nullptr && isolate->context()->native_context() == env->native_context()) { context_ = Local(); } else { impl->SaveContext(isolate->context()); isolate->set_context(*env); } } if (do_callback) isolate_->FireBeforeCallEnteredCallback(); } ~CallDepthScope() { if (!context_.IsEmpty()) { i::HandleScopeImplementer* impl = isolate_->handle_scope_implementer(); isolate_->set_context(impl->RestoreContext()); } if (!escaped_) isolate_->handle_scope_implementer()->DecrementCallDepth(); if (do_callback) isolate_->FireCallCompletedCallback(); // TODO(jochen): This should be #ifdef DEBUG #ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY if (do_callback) CheckMicrotasksScopesConsistency(isolate_); #endif isolate_->set_next_v8_call_is_safe_for_termination(safe_for_termination_); } void Escape() { DCHECK(!escaped_); escaped_ = true; auto handle_scope_implementer = isolate_->handle_scope_implementer(); handle_scope_implementer->DecrementCallDepth(); bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); isolate_->OptionalRescheduleException(call_depth_is_zero); } private: i::Isolate* const isolate_; Local context_; bool escaped_; bool do_callback_; bool safe_for_termination_; i::InterruptsScope interrupts_scope_; }; } // namespace static ScriptOrigin GetScriptOriginForScript(i::Isolate* isolate, i::Handle script) { i::Handle scriptName(script->GetNameOrSourceURL(), isolate); i::Handle source_map_url(script->source_mapping_url(), isolate); i::Handle host_defined_options(script->host_defined_options(), isolate); v8::Isolate* v8_isolate = reinterpret_cast(isolate); ScriptOriginOptions options(script->origin_options()); v8::ScriptOrigin origin( Utils::ToLocal(scriptName), v8::Integer::New(v8_isolate, script->line_offset()), v8::Integer::New(v8_isolate, script->column_offset()), v8::Boolean::New(v8_isolate, options.IsSharedCrossOrigin()), v8::Integer::New(v8_isolate, script->id()), Utils::ToLocal(source_map_url), v8::Boolean::New(v8_isolate, options.IsOpaque()), v8::Boolean::New(v8_isolate, script->type() == i::Script::TYPE_WASM), v8::Boolean::New(v8_isolate, 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 --- void i::FatalProcessOutOfMemory(i::Isolate* isolate, const char* location) { i::V8::FatalProcessOutOfMemory(isolate, location, false); } // 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* isolate, const char* location, bool is_heap_oom) { char last_few_messages[Heap::kTraceRingBufferSize + 1]; char js_stacktrace[Heap::kStacktraceBufferSize + 1]; i::HeapStats heap_stats; if (isolate == nullptr) { isolate = Isolate::Current(); } if (isolate == nullptr) { // On a background 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)); // Note that the embedder's oom handler won't be called in this case. We // just crash. FATAL( "API fatal error handler returned after process out of memory on the " "background thread"); 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 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 (isolate->heap()->HasBeenSetUp()) { // BUG(1718): Don't use the take_snapshot since we don't support // HeapIterator here without doing a special GC. isolate->heap()->RecordStats(&heap_stats, false); char* first_newline = strchr(last_few_messages, '\n'); if (first_newline == nullptr || first_newline[1] == '\0') first_newline = last_few_messages; PrintF("\n<--- Last few GCs --->\n%s\n", first_newline); PrintF("\n<--- JS stacktrace --->\n%s\n", js_stacktrace); } Utils::ReportOOMFailure(isolate, location, is_heap_oom); // If the fatal error handler returns, we stop execution. FATAL("API fatal error handler returned after process out of memory"); } void Utils::ReportApiFailure(const char* location, const char* message) { i::Isolate* isolate = i::Isolate::Current(); FatalErrorCallback callback = nullptr; if (isolate != nullptr) { callback = 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); } isolate->SignalFatalError(); } void Utils::ReportOOMFailure(i::Isolate* isolate, const char* location, bool is_heap_oom) { OOMErrorCallback oom_callback = isolate->oom_behavior(); if (oom_callback == nullptr) { // TODO(wfh): Remove this fallback once Blink is setting OOM handler. See // crbug.com/614440. FatalErrorCallback fatal_callback = isolate->exception_behavior(); if (fatal_callback == nullptr) { base::OS::PrintError("\n#\n# Fatal %s OOM in %s\n#\n\n", is_heap_oom ? "javascript" : "process", location); base::OS::Abort(); } else { fatal_callback(location, is_heap_oom ? "Allocation failed - JavaScript heap out of memory" : "Allocation failed - process out of memory"); } } else { oom_callback(location, is_heap_oom); } isolate->SignalFatalError(); } static inline bool IsExecutionTerminatingCheck(i::Isolate* isolate) { if (isolate->has_scheduled_exception()) { return isolate->scheduled_exception() == i::ReadOnlyRoots(isolate).termination_exception(); } return false; } void V8::SetNativesDataBlob(StartupData* natives_blob) { i::V8::SetNativesBlob(natives_blob); } void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) { i::V8::SetSnapshotBlob(snapshot_blob); } namespace { class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: void* Allocate(size_t length) override { #if V8_OS_AIX && _LINUX_SOURCE_COMPAT // Work around for GCC bug on AIX // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839 void* data = __linux_calloc(length, 1); #else void* data = calloc(length, 1); #endif return data; } void* AllocateUninitialized(size_t length) override { #if V8_OS_AIX && _LINUX_SOURCE_COMPAT // Work around for GCC bug on AIX // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839 void* data = __linux_malloc(length); #else void* data = malloc(length); #endif return data; } void Free(void* data, size_t) override { free(data); } }; struct SnapshotCreatorData { explicit SnapshotCreatorData(Isolate* isolate) : isolate_(isolate), default_context_(), contexts_(isolate), created_(false) {} static SnapshotCreatorData* cast(void* data) { return reinterpret_cast(data); } ArrayBufferAllocator allocator_; Isolate* isolate_; Persistent default_context_; SerializeInternalFieldsCallback default_embedder_fields_serializer_; PersistentValueVector contexts_; std::vector embedder_fields_serializers_; bool created_; }; } // namespace SnapshotCreator::SnapshotCreator(Isolate* isolate, const intptr_t* external_references, StartupData* existing_snapshot) { SnapshotCreatorData* data = new SnapshotCreatorData(isolate); data->isolate_ = isolate; i::Isolate* internal_isolate = reinterpret_cast(isolate); internal_isolate->set_array_buffer_allocator(&data->allocator_); internal_isolate->set_api_external_references(external_references); internal_isolate->enable_serializer(); isolate->Enter(); const StartupData* blob = existing_snapshot ? existing_snapshot : i::Snapshot::DefaultSnapshotBlob(); if (blob && blob->raw_size > 0) { internal_isolate->set_snapshot_blob(blob); i::Snapshot::Initialize(internal_isolate); } else { internal_isolate->Init(nullptr); } data_ = data; } SnapshotCreator::SnapshotCreator(const intptr_t* external_references, StartupData* existing_snapshot) : SnapshotCreator(reinterpret_cast(new i::Isolate()), external_references, existing_snapshot) {} SnapshotCreator::~SnapshotCreator() { SnapshotCreatorData* data = SnapshotCreatorData::cast(data_); DCHECK(data->created_); Isolate* isolate = data->isolate_; isolate->Exit(); isolate->Dispose(); delete data; } Isolate* SnapshotCreator::GetIsolate() { return SnapshotCreatorData::cast(data_)->isolate_; } void SnapshotCreator::SetDefaultContext( Local context, SerializeInternalFieldsCallback callback) { DCHECK(!context.IsEmpty()); SnapshotCreatorData* data = SnapshotCreatorData::cast(data_); DCHECK(!data->created_); DCHECK(data->default_context_.IsEmpty()); Isolate* isolate = data->isolate_; CHECK_EQ(isolate, context->GetIsolate()); data->default_context_.Reset(isolate, context); data->default_embedder_fields_serializer_ = callback; } size_t SnapshotCreator::AddContext(Local context, SerializeInternalFieldsCallback callback) { DCHECK(!context.IsEmpty()); SnapshotCreatorData* data = SnapshotCreatorData::cast(data_); DCHECK(!data->created_); Isolate* isolate = data->isolate_; CHECK_EQ(isolate, context->GetIsolate()); size_t index = data->contexts_.Size(); data->contexts_.Append(context); data->embedder_fields_serializers_.push_back(callback); return index; } size_t SnapshotCreator::AddTemplate(Local