// Copyright 2014 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/execution/execution.h" #include "src/api/api-inl.h" #include "src/compiler/wasm-compiler.h" // Only for static asserts. #include "src/execution/frames.h" #include "src/execution/isolate-inl.h" #include "src/execution/vm-state-inl.h" #include "src/logging/counters.h" namespace v8 { namespace internal { namespace { Handle NormalizeReceiver(Isolate* isolate, Handle receiver) { // Convert calls on global objects to be calls on the global // receiver instead to avoid having a 'this' pointer which refers // directly to a global object. if (receiver->IsJSGlobalObject()) { return handle(Handle::cast(receiver)->global_proxy(), isolate); } return receiver; } struct InvokeParams { static InvokeParams SetUpForNew(Isolate* isolate, Handle constructor, Handle new_target, int argc, Handle* argv); static InvokeParams SetUpForCall(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle* argv); static InvokeParams SetUpForTryCall( Isolate* isolate, Handle callable, Handle receiver, int argc, Handle* argv, Execution::MessageHandling message_handling, MaybeHandle* exception_out); static InvokeParams SetUpForRunMicrotasks(Isolate* isolate, MicrotaskQueue* microtask_queue, MaybeHandle* exception_out); Handle target; Handle receiver; int argc; Handle* argv; Handle new_target; MicrotaskQueue* microtask_queue; Execution::MessageHandling message_handling; MaybeHandle* exception_out; bool is_construct; Execution::Target execution_target; }; // static InvokeParams InvokeParams::SetUpForNew(Isolate* isolate, Handle constructor, Handle new_target, int argc, Handle* argv) { InvokeParams params; params.target = constructor; params.receiver = isolate->factory()->undefined_value(); params.argc = argc; params.argv = argv; params.new_target = new_target; params.microtask_queue = nullptr; params.message_handling = Execution::MessageHandling::kReport; params.exception_out = nullptr; params.is_construct = true; params.execution_target = Execution::Target::kCallable; return params; } // static InvokeParams InvokeParams::SetUpForCall(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle* argv) { InvokeParams params; params.target = callable; params.receiver = NormalizeReceiver(isolate, receiver); params.argc = argc; params.argv = argv; params.new_target = isolate->factory()->undefined_value(); params.microtask_queue = nullptr; params.message_handling = Execution::MessageHandling::kReport; params.exception_out = nullptr; params.is_construct = false; params.execution_target = Execution::Target::kCallable; return params; } // static InvokeParams InvokeParams::SetUpForTryCall( Isolate* isolate, Handle callable, Handle receiver, int argc, Handle* argv, Execution::MessageHandling message_handling, MaybeHandle* exception_out) { InvokeParams params; params.target = callable; params.receiver = NormalizeReceiver(isolate, receiver); params.argc = argc; params.argv = argv; params.new_target = isolate->factory()->undefined_value(); params.microtask_queue = nullptr; params.message_handling = message_handling; params.exception_out = exception_out; params.is_construct = false; params.execution_target = Execution::Target::kCallable; return params; } // static InvokeParams InvokeParams::SetUpForRunMicrotasks( Isolate* isolate, MicrotaskQueue* microtask_queue, MaybeHandle* exception_out) { auto undefined = isolate->factory()->undefined_value(); InvokeParams params; params.target = undefined; params.receiver = undefined; params.argc = 0; params.argv = nullptr; params.new_target = undefined; params.microtask_queue = microtask_queue; params.message_handling = Execution::MessageHandling::kReport; params.exception_out = exception_out; params.is_construct = false; params.execution_target = Execution::Target::kRunMicrotasks; return params; } Handle JSEntry(Isolate* isolate, Execution::Target execution_target, bool is_construct) { if (is_construct) { DCHECK_EQ(Execution::Target::kCallable, execution_target); return BUILTIN_CODE(isolate, JSConstructEntry); } else if (execution_target == Execution::Target::kCallable) { DCHECK(!is_construct); return BUILTIN_CODE(isolate, JSEntry); } else if (execution_target == Execution::Target::kRunMicrotasks) { DCHECK(!is_construct); return BUILTIN_CODE(isolate, JSRunMicrotasksEntry); } UNREACHABLE(); } V8_WARN_UNUSED_RESULT MaybeHandle Invoke(Isolate* isolate, const InvokeParams& params) { RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kInvoke); DCHECK(!params.receiver->IsJSGlobalObject()); DCHECK_LE(params.argc, FixedArray::kMaxLength); #ifdef USE_SIMULATOR // Simulators use separate stacks for C++ and JS. JS stack overflow checks // are performed whenever a JS function is called. However, it can be the case // that the C++ stack grows faster than the JS stack, resulting in an overflow // there. Add a check here to make that less likely. StackLimitCheck check(isolate); if (check.HasOverflowed()) { isolate->StackOverflow(); if (params.message_handling == Execution::MessageHandling::kReport) { isolate->ReportPendingMessages(); } return MaybeHandle(); } #endif // api callbacks can be called directly, unless we want to take the detour // through JS to set up a frame for break-at-entry. if (params.target->IsJSFunction()) { Handle function = Handle::cast(params.target); if ((!params.is_construct || function->IsConstructor()) && function->shared().IsApiFunction() && !function->shared().BreakAtEntry()) { SaveAndSwitchContext save(isolate, function->context()); DCHECK(function->context().global_object().IsJSGlobalObject()); Handle receiver = params.is_construct ? isolate->factory()->the_hole_value() : params.receiver; auto value = Builtins::InvokeApiFunction( isolate, params.is_construct, function, receiver, params.argc, params.argv, Handle::cast(params.new_target)); bool has_exception = value.is_null(); DCHECK(has_exception == isolate->has_pending_exception()); if (has_exception) { if (params.message_handling == Execution::MessageHandling::kReport) { isolate->ReportPendingMessages(); } return MaybeHandle(); } else { isolate->clear_pending_message(); } return value; } } // Entering JavaScript. VMState state(isolate); CHECK(AllowJavascriptExecution::IsAllowed(isolate)); if (!ThrowOnJavascriptExecution::IsAllowed(isolate)) { isolate->ThrowIllegalOperation(); if (params.message_handling == Execution::MessageHandling::kReport) { isolate->ReportPendingMessages(); } return MaybeHandle(); } if (!DumpOnJavascriptExecution::IsAllowed(isolate)) { V8::GetCurrentPlatform()->DumpWithoutCrashing(); return isolate->factory()->undefined_value(); } if (params.execution_target == Execution::Target::kCallable) { Handle context = isolate->native_context(); if (!context->script_execution_callback().IsUndefined(isolate)) { v8::Context::AbortScriptExecutionCallback callback = v8::ToCData( context->script_execution_callback()); v8::Isolate* api_isolate = reinterpret_cast(isolate); v8::Local api_context = v8::Utils::ToLocal(context); callback(api_isolate, api_context); DCHECK(!isolate->has_scheduled_exception()); // Always throw an exception to abort execution, if callback exists. isolate->ThrowIllegalOperation(); return MaybeHandle(); } } // Placeholder for return value. Object value; Handle code = JSEntry(isolate, params.execution_target, params.is_construct); { // Save and restore context around invocation and block the // allocation of handles without explicit handle scopes. SaveContext save(isolate); SealHandleScope shs(isolate); if (FLAG_clear_exceptions_on_js_entry) isolate->clear_pending_exception(); if (params.execution_target == Execution::Target::kCallable) { // clang-format off // {new_target}, {target}, {receiver}, return value: tagged pointers // {argv}: pointer to array of tagged pointers using JSEntryFunction = GeneratedCode; // clang-format on JSEntryFunction stub_entry = JSEntryFunction::FromAddress(isolate, code->InstructionStart()); Address orig_func = params.new_target->ptr(); Address func = params.target->ptr(); Address recv = params.receiver->ptr(); Address** argv = reinterpret_cast(params.argv); RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kJS_Execution); value = Object(stub_entry.Call(isolate->isolate_data()->isolate_root(), orig_func, func, recv, params.argc, argv)); } else { DCHECK_EQ(Execution::Target::kRunMicrotasks, params.execution_target); // clang-format off // return value: tagged pointers // {microtask_queue}: pointer to a C++ object using JSEntryFunction = GeneratedCode; // clang-format on JSEntryFunction stub_entry = JSEntryFunction::FromAddress(isolate, code->InstructionStart()); RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kJS_Execution); value = Object(stub_entry.Call(isolate->isolate_data()->isolate_root(), params.microtask_queue)); } } #ifdef VERIFY_HEAP if (FLAG_verify_heap) { value.ObjectVerify(isolate); } #endif // Update the pending exception flag and return the value. bool has_exception = value.IsException(isolate); DCHECK(has_exception == isolate->has_pending_exception()); if (has_exception) { if (params.message_handling == Execution::MessageHandling::kReport) { isolate->ReportPendingMessages(); } return MaybeHandle(); } else { isolate->clear_pending_message(); } return Handle(value, isolate); } MaybeHandle InvokeWithTryCatch(Isolate* isolate, const InvokeParams& params) { bool is_termination = false; MaybeHandle maybe_result; if (params.exception_out != nullptr) { *params.exception_out = MaybeHandle(); } DCHECK_IMPLIES( params.message_handling == Execution::MessageHandling::kKeepPending, params.exception_out == nullptr); // Enter a try-block while executing the JavaScript code. To avoid // duplicate error printing it must be non-verbose. Also, to avoid // creating message objects during stack overflow we shouldn't // capture messages. { v8::TryCatch catcher(reinterpret_cast(isolate)); catcher.SetVerbose(false); catcher.SetCaptureMessage(false); maybe_result = Invoke(isolate, params); if (maybe_result.is_null()) { DCHECK(isolate->has_pending_exception()); if (isolate->pending_exception() == ReadOnlyRoots(isolate).termination_exception()) { is_termination = true; } else { if (params.exception_out != nullptr) { DCHECK(catcher.HasCaught()); DCHECK(isolate->external_caught_exception()); *params.exception_out = v8::Utils::OpenHandle(*catcher.Exception()); } } if (params.message_handling == Execution::MessageHandling::kReport) { isolate->OptionalRescheduleException(true); } } } // Re-request terminate execution interrupt to trigger later. if (is_termination) isolate->stack_guard()->RequestTerminateExecution(); return maybe_result; } } // namespace // static MaybeHandle Execution::Call(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle argv[]) { return Invoke(isolate, InvokeParams::SetUpForCall(isolate, callable, receiver, argc, argv)); } MaybeHandle Execution::CallBuiltin(Isolate* isolate, Handle builtin, Handle receiver, int argc, Handle argv[]) { DCHECK(builtin->code().is_builtin()); DisableBreak no_break(isolate->debug()); return Invoke(isolate, InvokeParams::SetUpForCall(isolate, builtin, receiver, argc, argv)); } // static MaybeHandle Execution::New(Isolate* isolate, Handle constructor, int argc, Handle argv[]) { return New(isolate, constructor, constructor, argc, argv); } // static MaybeHandle Execution::New(Isolate* isolate, Handle constructor, Handle new_target, int argc, Handle argv[]) { return Invoke(isolate, InvokeParams::SetUpForNew(isolate, constructor, new_target, argc, argv)); } // static MaybeHandle Execution::TryCall(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle argv[], MessageHandling message_handling, MaybeHandle* exception_out) { return InvokeWithTryCatch( isolate, InvokeParams::SetUpForTryCall(isolate, callable, receiver, argc, argv, message_handling, exception_out)); } // static MaybeHandle Execution::TryRunMicrotasks( Isolate* isolate, MicrotaskQueue* microtask_queue, MaybeHandle* exception_out) { return InvokeWithTryCatch( isolate, InvokeParams::SetUpForRunMicrotasks(isolate, microtask_queue, exception_out)); } struct StackHandlerMarker { Address next; Address padding; }; STATIC_ASSERT(offsetof(StackHandlerMarker, next) == StackHandlerConstants::kNextOffset); STATIC_ASSERT(offsetof(StackHandlerMarker, padding) == StackHandlerConstants::kPaddingOffset); STATIC_ASSERT(sizeof(StackHandlerMarker) == StackHandlerConstants::kSize); void Execution::CallWasm(Isolate* isolate, Handle wrapper_code, Address wasm_call_target, Handle object_ref, Address packed_args) { using WasmEntryStub = GeneratedCode; WasmEntryStub stub_entry = WasmEntryStub::FromAddress(isolate, wrapper_code->InstructionStart()); // Save and restore context around invocation and block the // allocation of handles without explicit handle scopes. SaveContext save(isolate); SealHandleScope shs(isolate); Address saved_c_entry_fp = *isolate->c_entry_fp_address(); Address saved_js_entry_sp = *isolate->js_entry_sp_address(); if (saved_js_entry_sp == kNullAddress) { *isolate->js_entry_sp_address() = GetCurrentStackPosition(); } StackHandlerMarker stack_handler; stack_handler.next = isolate->thread_local_top()->handler_; #ifdef V8_USE_ADDRESS_SANITIZER stack_handler.padding = GetCurrentStackPosition(); #else stack_handler.padding = 0; #endif isolate->thread_local_top()->handler_ = reinterpret_cast
(&stack_handler); trap_handler::SetThreadInWasm(); { RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kJS_Execution); STATIC_ASSERT(compiler::CWasmEntryParameters::kCodeEntry == 0); STATIC_ASSERT(compiler::CWasmEntryParameters::kObjectRef == 1); STATIC_ASSERT(compiler::CWasmEntryParameters::kArgumentsBuffer == 2); STATIC_ASSERT(compiler::CWasmEntryParameters::kCEntryFp == 3); Address result = stub_entry.Call(wasm_call_target, object_ref->ptr(), packed_args, saved_c_entry_fp); if (result != kNullAddress) { isolate->set_pending_exception(Object(result)); } } // If there was an exception, then the thread-in-wasm flag is cleared // already. if (trap_handler::IsThreadInWasm()) { trap_handler::ClearThreadInWasm(); } isolate->thread_local_top()->handler_ = stack_handler.next; if (saved_js_entry_sp == kNullAddress) { *isolate->js_entry_sp_address() = saved_js_entry_sp; } *isolate->c_entry_fp_address() = saved_c_entry_fp; } } // namespace internal } // namespace v8