// 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 "execution.h" #include "bootstrapper.h" #include "codegen.h" #include "deoptimizer.h" #include "isolate-inl.h" #include "vm-state-inl.h" namespace v8 { namespace internal { StackGuard::StackGuard() : isolate_(NULL) { } void StackGuard::set_interrupt_limits(const ExecutionAccess& lock) { ASSERT(isolate_ != NULL); // Ignore attempts to interrupt when interrupts are postponed. if (should_postpone_interrupts(lock)) return; thread_local_.jslimit_ = kInterruptLimit; thread_local_.climit_ = kInterruptLimit; isolate_->heap()->SetStackLimits(); } void StackGuard::reset_limits(const ExecutionAccess& lock) { ASSERT(isolate_ != NULL); thread_local_.jslimit_ = thread_local_.real_jslimit_; thread_local_.climit_ = thread_local_.real_climit_; isolate_->heap()->SetStackLimits(); } MUST_USE_RESULT static MaybeHandle Invoke( bool is_construct, Handle function, Handle receiver, int argc, Handle args[]) { Isolate* isolate = function->GetIsolate(); // Entering JavaScript. VMState state(isolate); CHECK(AllowJavascriptExecution::IsAllowed(isolate)); if (!ThrowOnJavascriptExecution::IsAllowed(isolate)) { isolate->ThrowIllegalOperation(); isolate->ReportPendingMessages(); return MaybeHandle(); } // Placeholder for return value. Object* value = NULL; typedef Object* (*JSEntryFunction)(byte* entry, Object* function, Object* receiver, int argc, Object*** args); Handle code = is_construct ? isolate->factory()->js_construct_entry_code() : isolate->factory()->js_entry_code(); // 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->IsGlobalObject()) { Handle global = Handle::cast(receiver); receiver = Handle(global->global_receiver()); } // Make sure that the global object of the context we're about to // make the current one is indeed a global object. ASSERT(function->context()->global_object()->IsGlobalObject()); { // Save and restore context around invocation and block the // allocation of handles without explicit handle scopes. SaveContext save(isolate); SealHandleScope shs(isolate); JSEntryFunction stub_entry = FUNCTION_CAST(code->entry()); // Call the function through the right JS entry stub. byte* function_entry = function->code()->entry(); JSFunction* func = *function; Object* recv = *receiver; Object*** argv = reinterpret_cast(args); value = CALL_GENERATED_CODE(stub_entry, function_entry, func, recv, argc, argv); } #ifdef VERIFY_HEAP value->ObjectVerify(); #endif // Update the pending exception flag and return the value. bool has_exception = value->IsException(); ASSERT(has_exception == isolate->has_pending_exception()); if (has_exception) { isolate->ReportPendingMessages(); // Reset stepping state when script exits with uncaught exception. if (isolate->debugger()->IsDebuggerActive()) { isolate->debug()->ClearStepping(); } return MaybeHandle(); } else { isolate->clear_pending_message(); } return Handle(value, isolate); } MaybeHandle Execution::Call(Isolate* isolate, Handle callable, Handle receiver, int argc, Handle argv[], bool convert_receiver) { if (!callable->IsJSFunction()) { ASSIGN_RETURN_ON_EXCEPTION( isolate, callable, TryGetFunctionDelegate(isolate, callable), Object); } Handle func = Handle::cast(callable); // In sloppy mode, convert receiver. if (convert_receiver && !receiver->IsJSReceiver() && !func->shared()->native() && func->shared()->strict_mode() == SLOPPY) { if (receiver->IsUndefined() || receiver->IsNull()) { Object* global = func->context()->global_object()->global_receiver(); // Under some circumstances, 'global' can be the JSBuiltinsObject // In that case, don't rewrite. (FWIW, the same holds for // GetIsolate()->global_object()->global_receiver().) if (!global->IsJSBuiltinsObject()) { receiver = Handle(global, func->GetIsolate()); } } else { ASSIGN_RETURN_ON_EXCEPTION( isolate, receiver, ToObject(isolate, receiver), Object); } } return Invoke(false, func, receiver, argc, argv); } MaybeHandle Execution::New(Handle func, int argc, Handle argv[]) { return Invoke(true, func, func->GetIsolate()->global_object(), argc, argv); } MaybeHandle Execution::TryCall(Handle func, Handle receiver, int argc, Handle args[], Handle* exception_out) { // 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; catcher.SetVerbose(false); catcher.SetCaptureMessage(false); // Get isolate now, because handle might be persistent // and get destroyed in the next call. Isolate* isolate = func->GetIsolate(); MaybeHandle maybe_result = Invoke(false, func, receiver, argc, args); if (maybe_result.is_null()) { ASSERT(catcher.HasCaught()); ASSERT(isolate->has_pending_exception()); ASSERT(isolate->external_caught_exception()); if (exception_out != NULL) { if (isolate->pending_exception() == isolate->heap()->termination_exception()) { *exception_out = isolate->factory()->termination_exception(); } else { *exception_out = v8::Utils::OpenHandle(*catcher.Exception()); } } isolate->OptionalRescheduleException(true); } ASSERT(!isolate->has_pending_exception()); ASSERT(!isolate->external_caught_exception()); return maybe_result; } Handle Execution::GetFunctionDelegate(Isolate* isolate, Handle object) { ASSERT(!object->IsJSFunction()); Factory* factory = isolate->factory(); // If you return a function from here, it will be called when an // attempt is made to call the given object as a function. // If object is a function proxy, get its handler. Iterate if necessary. Object* fun = *object; while (fun->IsJSFunctionProxy()) { fun = JSFunctionProxy::cast(fun)->call_trap(); } if (fun->IsJSFunction()) return Handle(fun, isolate); // Objects created through the API can have an instance-call handler // that should be used when calling the object as a function. if (fun->IsHeapObject() && HeapObject::cast(fun)->map()->has_instance_call_handler()) { return Handle( isolate->native_context()->call_as_function_delegate()); } return factory->undefined_value(); } MaybeHandle Execution::TryGetFunctionDelegate(Isolate* isolate, Handle object) { ASSERT(!object->IsJSFunction()); // If object is a function proxy, get its handler. Iterate if necessary. Object* fun = *object; while (fun->IsJSFunctionProxy()) { fun = JSFunctionProxy::cast(fun)->call_trap(); } if (fun->IsJSFunction()) return Handle(fun, isolate); // Objects created through the API can have an instance-call handler // that should be used when calling the object as a function. if (fun->IsHeapObject() && HeapObject::cast(fun)->map()->has_instance_call_handler()) { return Handle( isolate->native_context()->call_as_function_delegate()); } // If the Object doesn't have an instance-call handler we should // throw a non-callable exception. i::Handle error_obj = isolate->factory()->NewTypeError( "called_non_callable", i::HandleVector(&object, 1)); return isolate->Throw(error_obj); } Handle Execution::GetConstructorDelegate(Isolate* isolate, Handle object) { ASSERT(!object->IsJSFunction()); // If you return a function from here, it will be called when an // attempt is made to call the given object as a constructor. // If object is a function proxies, get its handler. Iterate if necessary. Object* fun = *object; while (fun->IsJSFunctionProxy()) { fun = JSFunctionProxy::cast(fun)->call_trap(); } if (fun->IsJSFunction()) return Handle(fun, isolate); // Objects created through the API can have an instance-call handler // that should be used when calling the object as a function. if (fun->IsHeapObject() && HeapObject::cast(fun)->map()->has_instance_call_handler()) { return Handle( isolate->native_context()->call_as_constructor_delegate()); } return isolate->factory()->undefined_value(); } MaybeHandle Execution::TryGetConstructorDelegate( Isolate* isolate, Handle object) { ASSERT(!object->IsJSFunction()); // If you return a function from here, it will be called when an // attempt is made to call the given object as a constructor. // If object is a function proxies, get its handler. Iterate if necessary. Object* fun = *object; while (fun->IsJSFunctionProxy()) { fun = JSFunctionProxy::cast(fun)->call_trap(); } if (fun->IsJSFunction()) return Handle(fun, isolate); // Objects created through the API can have an instance-call handler // that should be used when calling the object as a function. if (fun->IsHeapObject() && HeapObject::cast(fun)->map()->has_instance_call_handler()) { return Handle( isolate->native_context()->call_as_constructor_delegate()); } // If the Object doesn't have an instance-call handler we should // throw a non-callable exception. i::Handle error_obj = isolate->factory()->NewTypeError( "called_non_callable", i::HandleVector(&object, 1)); return isolate->Throw(error_obj); } void Execution::RunMicrotasks(Isolate* isolate) { ASSERT(isolate->microtask_pending()); Execution::Call( isolate, isolate->run_microtasks(), isolate->factory()->undefined_value(), 0, NULL).Check(); } void Execution::EnqueueMicrotask(Isolate* isolate, Handle microtask) { Handle args[] = { microtask }; Execution::Call( isolate, isolate->enqueue_microtask(), isolate->factory()->undefined_value(), 1, args).Check(); } bool StackGuard::IsStackOverflow() { ExecutionAccess access(isolate_); return (thread_local_.jslimit_ != kInterruptLimit && thread_local_.climit_ != kInterruptLimit); } void StackGuard::EnableInterrupts() { ExecutionAccess access(isolate_); if (has_pending_interrupts(access)) { set_interrupt_limits(access); } } void StackGuard::SetStackLimit(uintptr_t limit) { ExecutionAccess access(isolate_); // If the current limits are special (e.g. due to a pending interrupt) then // leave them alone. uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit); if (thread_local_.jslimit_ == thread_local_.real_jslimit_) { thread_local_.jslimit_ = jslimit; } if (thread_local_.climit_ == thread_local_.real_climit_) { thread_local_.climit_ = limit; } thread_local_.real_climit_ = limit; thread_local_.real_jslimit_ = jslimit; } void StackGuard::DisableInterrupts() { ExecutionAccess access(isolate_); reset_limits(access); } bool StackGuard::CheckInterrupt(int flagbit) { ExecutionAccess access(isolate_); return thread_local_.interrupt_flags_ & flagbit; } void StackGuard::RequestInterrupt(int flagbit) { ExecutionAccess access(isolate_); thread_local_.interrupt_flags_ |= flagbit; set_interrupt_limits(access); } void StackGuard::ClearInterrupt(int flagbit) { ExecutionAccess access(isolate_); thread_local_.interrupt_flags_ &= ~flagbit; if (!should_postpone_interrupts(access) && !has_pending_interrupts(access)) { reset_limits(access); } } char* StackGuard::ArchiveStackGuard(char* to) { ExecutionAccess access(isolate_); OS::MemCopy(to, reinterpret_cast(&thread_local_), sizeof(ThreadLocal)); ThreadLocal blank; // Set the stack limits using the old thread_local_. // TODO(isolates): This was the old semantics of constructing a ThreadLocal // (as the ctor called SetStackLimits, which looked at the // current thread_local_ from StackGuard)-- but is this // really what was intended? isolate_->heap()->SetStackLimits(); thread_local_ = blank; return to + sizeof(ThreadLocal); } char* StackGuard::RestoreStackGuard(char* from) { ExecutionAccess access(isolate_); OS::MemCopy( reinterpret_cast(&thread_local_), from, sizeof(ThreadLocal)); isolate_->heap()->SetStackLimits(); return from + sizeof(ThreadLocal); } void StackGuard::FreeThreadResources() { Isolate::PerIsolateThreadData* per_thread = isolate_->FindOrAllocatePerThreadDataForThisThread(); per_thread->set_stack_limit(thread_local_.real_climit_); } void StackGuard::ThreadLocal::Clear() { real_jslimit_ = kIllegalLimit; jslimit_ = kIllegalLimit; real_climit_ = kIllegalLimit; climit_ = kIllegalLimit; nesting_ = 0; postpone_interrupts_nesting_ = 0; interrupt_flags_ = 0; } bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) { bool should_set_stack_limits = false; if (real_climit_ == kIllegalLimit) { // Takes the address of the limit variable in order to find out where // the top of stack is right now. const uintptr_t kLimitSize = FLAG_stack_size * KB; uintptr_t limit = reinterpret_cast(&limit) - kLimitSize; ASSERT(reinterpret_cast(&limit) > kLimitSize); real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit); jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit); real_climit_ = limit; climit_ = limit; should_set_stack_limits = true; } nesting_ = 0; postpone_interrupts_nesting_ = 0; interrupt_flags_ = 0; return should_set_stack_limits; } void StackGuard::ClearThread(const ExecutionAccess& lock) { thread_local_.Clear(); isolate_->heap()->SetStackLimits(); } void StackGuard::InitThread(const ExecutionAccess& lock) { if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits(); Isolate::PerIsolateThreadData* per_thread = isolate_->FindOrAllocatePerThreadDataForThisThread(); uintptr_t stored_limit = per_thread->stack_limit(); // You should hold the ExecutionAccess lock when you call this. if (stored_limit != 0) { SetStackLimit(stored_limit); } } // --- C a l l s t o n a t i v e s --- #define RETURN_NATIVE_CALL(name, args) \ do { \ Handle argv[] = args; \ return Call(isolate, \ isolate->name##_fun(), \ isolate->js_builtins_object(), \ ARRAY_SIZE(argv), argv); \ } while (false) MaybeHandle Execution::ToNumber( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_number, { obj }); } MaybeHandle Execution::ToString( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_string, { obj }); } MaybeHandle Execution::ToDetailString( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_detail_string, { obj }); } MaybeHandle Execution::ToObject( Isolate* isolate, Handle obj) { if (obj->IsSpecObject()) return obj; RETURN_NATIVE_CALL(to_object, { obj }); } MaybeHandle Execution::ToInteger( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_integer, { obj }); } MaybeHandle Execution::ToUint32( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_uint32, { obj }); } MaybeHandle Execution::ToInt32( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_int32, { obj }); } MaybeHandle Execution::NewDate(Isolate* isolate, double time) { Handle time_obj = isolate->factory()->NewNumber(time); RETURN_NATIVE_CALL(create_date, { time_obj }); } #undef RETURN_NATIVE_CALL MaybeHandle Execution::NewJSRegExp(Handle pattern, Handle flags) { Isolate* isolate = pattern->GetIsolate(); Handle function = Handle( isolate->native_context()->regexp_function()); Handle re_obj; ASSIGN_RETURN_ON_EXCEPTION( isolate, re_obj, RegExpImpl::CreateRegExpLiteral(function, pattern, flags), JSRegExp); return Handle::cast(re_obj); } Handle Execution::CharAt(Handle string, uint32_t index) { Isolate* isolate = string->GetIsolate(); Factory* factory = isolate->factory(); int int_index = static_cast(index); if (int_index < 0 || int_index >= string->length()) { return factory->undefined_value(); } Handle char_at = Object::GetProperty( isolate->js_builtins_object(), factory->char_at_string()).ToHandleChecked(); if (!char_at->IsJSFunction()) { return factory->undefined_value(); } Handle index_object = factory->NewNumberFromInt(int_index); Handle index_arg[] = { index_object }; Handle result; if (!TryCall(Handle::cast(char_at), string, ARRAY_SIZE(index_arg), index_arg).ToHandle(&result)) { return factory->undefined_value(); } return result; } MaybeHandle Execution::InstantiateFunction( Handle data) { Isolate* isolate = data->GetIsolate(); if (!data->do_not_cache()) { // Fast case: see if the function has already been instantiated int serial_number = Smi::cast(data->serial_number())->value(); Handle cache(isolate->native_context()->function_cache()); Handle elm = Object::GetElement(isolate, cache, serial_number).ToHandleChecked(); if (elm->IsJSFunction()) return Handle::cast(elm); } // The function has not yet been instantiated in this context; do it. Handle args[] = { data }; Handle result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Call(isolate, isolate->instantiate_fun(), isolate->js_builtins_object(), ARRAY_SIZE(args), args), JSFunction); return Handle::cast(result); } MaybeHandle Execution::InstantiateObject( Handle data) { Isolate* isolate = data->GetIsolate(); Handle result; if (data->property_list()->IsUndefined() && !data->constructor()->IsUndefined()) { Handle cons_template = Handle( FunctionTemplateInfo::cast(data->constructor())); Handle cons; ASSIGN_RETURN_ON_EXCEPTION( isolate, cons, InstantiateFunction(cons_template), JSObject); ASSIGN_RETURN_ON_EXCEPTION(isolate, result, New(cons, 0, NULL), JSObject); } else { Handle args[] = { data }; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Call(isolate, isolate->instantiate_fun(), isolate->js_builtins_object(), ARRAY_SIZE(args), args), JSObject); } return Handle::cast(result); } MaybeHandle Execution::ConfigureInstance( Isolate* isolate, Handle instance, Handle instance_template) { Handle args[] = { instance, instance_template }; return Execution::Call(isolate, isolate->configure_instance_fun(), isolate->js_builtins_object(), ARRAY_SIZE(args), args); } Handle Execution::GetStackTraceLine(Handle recv, Handle fun, Handle pos, Handle is_global) { Isolate* isolate = fun->GetIsolate(); Handle args[] = { recv, fun, pos, is_global }; MaybeHandle maybe_result = TryCall(isolate->get_stack_trace_line_fun(), isolate->js_builtins_object(), ARRAY_SIZE(args), args); Handle result; if (!maybe_result.ToHandle(&result) || !result->IsString()) { return isolate->factory()->empty_string(); } return Handle::cast(result); } void Execution::DebugBreakHelper(Isolate* isolate) { // Just continue if breaks are disabled. if (isolate->debug()->disable_break()) return; // Ignore debug break during bootstrapping. if (isolate->bootstrapper()->IsActive()) return; // Ignore debug break if debugger is not active. if (!isolate->debugger()->IsDebuggerActive()) return; StackLimitCheck check(isolate); if (check.HasOverflowed()) return; { JavaScriptFrameIterator it(isolate); ASSERT(!it.done()); Object* fun = it.frame()->function(); if (fun && fun->IsJSFunction()) { // Don't stop in builtin functions. if (JSFunction::cast(fun)->IsBuiltin()) return; GlobalObject* global = JSFunction::cast(fun)->context()->global_object(); // Don't stop in debugger functions. if (isolate->debug()->IsDebugGlobal(global)) return; } } // Collect the break state before clearing the flags. bool debug_command_only = isolate->stack_guard()->CheckDebugCommand() && !isolate->stack_guard()->CheckDebugBreak(); isolate->stack_guard()->ClearDebugBreak(); Execution::ProcessDebugMessages(isolate, debug_command_only); } void Execution::ProcessDebugMessages(Isolate* isolate, bool debug_command_only) { isolate->stack_guard()->ClearDebugCommand(); StackLimitCheck check(isolate); if (check.HasOverflowed()) return; HandleScope scope(isolate); // Enter the debugger. Just continue if we fail to enter the debugger. EnterDebugger debugger(isolate); if (debugger.FailedToEnter()) return; // Notify the debug event listeners. Indicate auto continue if the break was // a debug command break. isolate->debugger()->OnDebugBreak(isolate->factory()->undefined_value(), debug_command_only); } Object* StackGuard::HandleInterrupts() { { ExecutionAccess access(isolate_); if (should_postpone_interrupts(access)) { return isolate_->heap()->undefined_value(); } } if (CheckApiInterrupt()) { ClearApiInterrupt(); isolate_->InvokeApiInterruptCallback(); } if (CheckGC()) { isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, "GC interrupt"); ClearGC(); } if (CheckDebugBreak() || CheckDebugCommand()) { Execution::DebugBreakHelper(isolate_); } if (CheckTerminateExecution()) { ClearTerminateExecution(); return isolate_->TerminateExecution(); } if (CheckFullDeopt()) { ClearFullDeopt(); Deoptimizer::DeoptimizeAll(isolate_); } if (CheckDeoptMarkedAllocationSites()) { ClearDeoptMarkedAllocationSites(); isolate_->heap()->DeoptMarkedAllocationSites(); } if (CheckInstallCode()) { ASSERT(isolate_->concurrent_recompilation_enabled()); ClearInstallCode(); isolate_->optimizing_compiler_thread()->InstallOptimizedFunctions(); } isolate_->counters()->stack_interrupts()->Increment(); isolate_->counters()->runtime_profiler_ticks()->Increment(); isolate_->runtime_profiler()->OptimizeNow(); return isolate_->heap()->undefined_value(); } } } // namespace v8::internal