// 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. #ifdef V8_USE_ADDRESS_SANITIZER #include #endif // V8_USE_ADDRESS_SANITIZER #include // For isnan. #include #include #include "include/v8-debug.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/background-parsing-task.h" #include "src/base/functional.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.h" #include "src/deoptimizer.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/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/code-serializer.h" #include "src/snapshot/natives.h" #include "src/snapshot/snapshot.h" #include "src/startup-data-util.h" #include "src/tracing/trace-event.h" #include "src/trap-handler/trap-handler.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/wasm-module.h" #include "src/wasm/wasm-objects.h" #include "src/wasm/wasm-result.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::RuntimeCallStats::API_##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 ContextFromHeapObject(i::Handle obj) { return reinterpret_cast(i::HeapObject::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) { // TODO(dcarney): remove this when blink stops crashing. DCHECK(!isolate_->external_caught_exception()); isolate_->handle_scope_implementer()->IncrementCallDepth(); 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 } 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_; }; } // 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); v8::Isolate* v8_isolate = reinterpret_cast(script->GetIsolate()); 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())); return origin; } // --- E x c e p t i o n B e h a v i o r --- void i::FatalProcessOutOfMemory(const char* location) { i::V8::FatalProcessOutOfMemory(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(const char* location, bool is_heap_oom) { i::Isolate* isolate = i::Isolate::Current(); char last_few_messages[Heap::kTraceRingBufferSize + 1]; char js_stacktrace[Heap::kStacktraceBufferSize + 1]; i::HeapStats heap_stats; 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"); return; } 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 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 == NULL || 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(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 = 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(const char* location, bool is_heap_oom) { i::Isolate* isolate = i::Isolate::Current(); 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() == isolate->heap()->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); } void* v8::ArrayBuffer::Allocator::Reserve(size_t length) { UNIMPLEMENTED(); } void v8::ArrayBuffer::Allocator::Free(void* data, size_t length, AllocationMode mode) { switch (mode) { case AllocationMode::kNormal: { Free(data, length); return; } case AllocationMode::kReservation: { UNIMPLEMENTED(); return; } } } void v8::ArrayBuffer::Allocator::SetProtection( void* data, size_t length, v8::ArrayBuffer::Allocator::Protection protection) { UNIMPLEMENTED(); } namespace { class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: virtual void* Allocate(size_t length) { void* data = AllocateUninitialized(length); return data == NULL ? data : memset(data, 0, length); } virtual void* AllocateUninitialized(size_t length) { return malloc(length); } virtual void Free(void* data, size_t) { free(data); } virtual void* Reserve(size_t length) { return base::VirtualMemory::ReserveRegion(length, base::OS::GetRandomMmapAddr()); } virtual void Free(void* data, size_t length, v8::ArrayBuffer::Allocator::AllocationMode mode) { switch (mode) { case v8::ArrayBuffer::Allocator::AllocationMode::kNormal: { return Free(data, length); } case v8::ArrayBuffer::Allocator::AllocationMode::kReservation: { base::VirtualMemory::ReleaseRegion(data, length); return; } } } virtual void SetProtection( void* data, size_t length, v8::ArrayBuffer::Allocator::Protection protection) { switch (protection) { case v8::ArrayBuffer::Allocator::Protection::kNoAccess: { base::VirtualMemory::UncommitRegion(data, length); return; } case v8::ArrayBuffer::Allocator::Protection::kReadWrite: { const bool is_executable = false; base::VirtualMemory::CommitRegion(data, length, is_executable); return; } } } }; bool RunExtraCode(Isolate* isolate, Local context, const char* utf8_source, const char* name) { base::ElapsedTimer timer; timer.Start(); Context::Scope context_scope(context); TryCatch try_catch(isolate); Local source_string; if (!String::NewFromUtf8(isolate, utf8_source, NewStringType::kNormal) .ToLocal(&source_string)) { return false; } Local resource_name = String::NewFromUtf8(isolate, name, NewStringType::kNormal) .ToLocalChecked(); ScriptOrigin origin(resource_name); ScriptCompiler::Source source(source_string, origin); Local