• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/execution.h"
6 
7 #include "src/bootstrapper.h"
8 #include "src/codegen.h"
9 #include "src/isolate-inl.h"
10 #include "src/messages.h"
11 #include "src/vm-state-inl.h"
12 
13 namespace v8 {
14 namespace internal {
15 
StackGuard()16 StackGuard::StackGuard()
17     : isolate_(NULL) {
18 }
19 
20 
set_interrupt_limits(const ExecutionAccess & lock)21 void StackGuard::set_interrupt_limits(const ExecutionAccess& lock) {
22   DCHECK(isolate_ != NULL);
23   thread_local_.set_jslimit(kInterruptLimit);
24   thread_local_.set_climit(kInterruptLimit);
25   isolate_->heap()->SetStackLimits();
26 }
27 
28 
reset_limits(const ExecutionAccess & lock)29 void StackGuard::reset_limits(const ExecutionAccess& lock) {
30   DCHECK(isolate_ != NULL);
31   thread_local_.set_jslimit(thread_local_.real_jslimit_);
32   thread_local_.set_climit(thread_local_.real_climit_);
33   isolate_->heap()->SetStackLimits();
34 }
35 
36 
PrintDeserializedCodeInfo(Handle<JSFunction> function)37 static void PrintDeserializedCodeInfo(Handle<JSFunction> function) {
38   if (function->code() == function->shared()->code() &&
39       function->shared()->deserialized()) {
40     PrintF("[Running deserialized script");
41     Object* script = function->shared()->script();
42     if (script->IsScript()) {
43       Object* name = Script::cast(script)->name();
44       if (name->IsString()) {
45         PrintF(": %s", String::cast(name)->ToCString().get());
46       }
47     }
48     PrintF("]\n");
49   }
50 }
51 
52 
53 namespace {
54 
Invoke(Isolate * isolate,bool is_construct,Handle<Object> target,Handle<Object> receiver,int argc,Handle<Object> args[],Handle<Object> new_target)55 MUST_USE_RESULT MaybeHandle<Object> Invoke(Isolate* isolate, bool is_construct,
56                                            Handle<Object> target,
57                                            Handle<Object> receiver, int argc,
58                                            Handle<Object> args[],
59                                            Handle<Object> new_target) {
60   DCHECK(!receiver->IsJSGlobalObject());
61 
62   // Entering JavaScript.
63   VMState<JS> state(isolate);
64   CHECK(AllowJavascriptExecution::IsAllowed(isolate));
65   if (!ThrowOnJavascriptExecution::IsAllowed(isolate)) {
66     isolate->ThrowIllegalOperation();
67     isolate->ReportPendingMessages();
68     return MaybeHandle<Object>();
69   }
70 
71   // Placeholder for return value.
72   Object* value = NULL;
73 
74   typedef Object* (*JSEntryFunction)(Object* new_target, Object* target,
75                                      Object* receiver, int argc,
76                                      Object*** args);
77 
78   Handle<Code> code = is_construct
79       ? isolate->factory()->js_construct_entry_code()
80       : isolate->factory()->js_entry_code();
81 
82   {
83     // Save and restore context around invocation and block the
84     // allocation of handles without explicit handle scopes.
85     SaveContext save(isolate);
86     SealHandleScope shs(isolate);
87     JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
88 
89     // Call the function through the right JS entry stub.
90     Object* orig_func = *new_target;
91     Object* func = *target;
92     Object* recv = *receiver;
93     Object*** argv = reinterpret_cast<Object***>(args);
94     if (FLAG_profile_deserialization && target->IsJSFunction()) {
95       PrintDeserializedCodeInfo(Handle<JSFunction>::cast(target));
96     }
97     value = CALL_GENERATED_CODE(isolate, stub_entry, orig_func, func, recv,
98                                 argc, argv);
99   }
100 
101 #ifdef VERIFY_HEAP
102   if (FLAG_verify_heap) {
103     value->ObjectVerify();
104   }
105 #endif
106 
107   // Update the pending exception flag and return the value.
108   bool has_exception = value->IsException();
109   DCHECK(has_exception == isolate->has_pending_exception());
110   if (has_exception) {
111     isolate->ReportPendingMessages();
112     return MaybeHandle<Object>();
113   } else {
114     isolate->clear_pending_message();
115   }
116 
117   return Handle<Object>(value, isolate);
118 }
119 
120 }  // namespace
121 
122 
123 // static
Call(Isolate * isolate,Handle<Object> callable,Handle<Object> receiver,int argc,Handle<Object> argv[])124 MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable,
125                                     Handle<Object> receiver, int argc,
126                                     Handle<Object> argv[]) {
127   // Convert calls on global objects to be calls on the global
128   // receiver instead to avoid having a 'this' pointer which refers
129   // directly to a global object.
130   if (receiver->IsJSGlobalObject()) {
131     receiver =
132         handle(Handle<JSGlobalObject>::cast(receiver)->global_proxy(), isolate);
133   }
134 
135   // api callbacks can be called directly.
136   if (callable->IsJSFunction() &&
137       Handle<JSFunction>::cast(callable)->shared()->IsApiFunction()) {
138     Handle<JSFunction> function = Handle<JSFunction>::cast(callable);
139     SaveContext save(isolate);
140     isolate->set_context(function->context());
141     // Do proper receiver conversion for non-strict mode api functions.
142     if (!receiver->IsJSReceiver() &&
143         is_sloppy(function->shared()->language_mode())) {
144       if (receiver->IsUndefined() || receiver->IsNull()) {
145         receiver = handle(function->global_proxy(), isolate);
146       } else {
147         ASSIGN_RETURN_ON_EXCEPTION(
148             isolate, receiver, Execution::ToObject(isolate, receiver), Object);
149       }
150     }
151     DCHECK(function->context()->global_object()->IsJSGlobalObject());
152     auto value = Builtins::InvokeApiFunction(function, receiver, argc, argv);
153     bool has_exception = value.is_null();
154     DCHECK(has_exception == isolate->has_pending_exception());
155     if (has_exception) {
156       isolate->ReportPendingMessages();
157       return MaybeHandle<Object>();
158     } else {
159       isolate->clear_pending_message();
160     }
161     return value;
162   }
163   return Invoke(isolate, false, callable, receiver, argc, argv,
164                 isolate->factory()->undefined_value());
165 }
166 
167 
168 // static
New(Handle<JSFunction> constructor,int argc,Handle<Object> argv[])169 MaybeHandle<Object> Execution::New(Handle<JSFunction> constructor, int argc,
170                                    Handle<Object> argv[]) {
171   return New(constructor->GetIsolate(), constructor, constructor, argc, argv);
172 }
173 
174 
175 // static
New(Isolate * isolate,Handle<Object> constructor,Handle<Object> new_target,int argc,Handle<Object> argv[])176 MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor,
177                                    Handle<Object> new_target, int argc,
178                                    Handle<Object> argv[]) {
179   return Invoke(isolate, true, constructor,
180                 isolate->factory()->undefined_value(), argc, argv, new_target);
181 }
182 
183 
TryCall(Isolate * isolate,Handle<Object> callable,Handle<Object> receiver,int argc,Handle<Object> args[],MaybeHandle<Object> * exception_out)184 MaybeHandle<Object> Execution::TryCall(Isolate* isolate,
185                                        Handle<Object> callable,
186                                        Handle<Object> receiver, int argc,
187                                        Handle<Object> args[],
188                                        MaybeHandle<Object>* exception_out) {
189   bool is_termination = false;
190   MaybeHandle<Object> maybe_result;
191   if (exception_out != NULL) *exception_out = MaybeHandle<Object>();
192   // Enter a try-block while executing the JavaScript code. To avoid
193   // duplicate error printing it must be non-verbose.  Also, to avoid
194   // creating message objects during stack overflow we shouldn't
195   // capture messages.
196   {
197     v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
198     catcher.SetVerbose(false);
199     catcher.SetCaptureMessage(false);
200 
201     maybe_result = Call(isolate, callable, receiver, argc, args);
202 
203     if (maybe_result.is_null()) {
204       DCHECK(catcher.HasCaught());
205       DCHECK(isolate->has_pending_exception());
206       DCHECK(isolate->external_caught_exception());
207       if (isolate->pending_exception() ==
208           isolate->heap()->termination_exception()) {
209         is_termination = true;
210       } else {
211         if (exception_out != NULL) {
212           *exception_out = v8::Utils::OpenHandle(*catcher.Exception());
213         }
214       }
215       isolate->OptionalRescheduleException(true);
216     }
217 
218     DCHECK(!isolate->has_pending_exception());
219   }
220 
221   // Re-request terminate execution interrupt to trigger later.
222   if (is_termination) isolate->stack_guard()->RequestTerminateExecution();
223 
224   return maybe_result;
225 }
226 
227 
SetStackLimit(uintptr_t limit)228 void StackGuard::SetStackLimit(uintptr_t limit) {
229   ExecutionAccess access(isolate_);
230   // If the current limits are special (e.g. due to a pending interrupt) then
231   // leave them alone.
232   uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit);
233   if (thread_local_.jslimit() == thread_local_.real_jslimit_) {
234     thread_local_.set_jslimit(jslimit);
235   }
236   if (thread_local_.climit() == thread_local_.real_climit_) {
237     thread_local_.set_climit(limit);
238   }
239   thread_local_.real_climit_ = limit;
240   thread_local_.real_jslimit_ = jslimit;
241 }
242 
243 
AdjustStackLimitForSimulator()244 void StackGuard::AdjustStackLimitForSimulator() {
245   ExecutionAccess access(isolate_);
246   uintptr_t climit = thread_local_.real_climit_;
247   // If the current limits are special (e.g. due to a pending interrupt) then
248   // leave them alone.
249   uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, climit);
250   if (thread_local_.jslimit() == thread_local_.real_jslimit_) {
251     thread_local_.set_jslimit(jslimit);
252     isolate_->heap()->SetStackLimits();
253   }
254 }
255 
256 
EnableInterrupts()257 void StackGuard::EnableInterrupts() {
258   ExecutionAccess access(isolate_);
259   if (has_pending_interrupts(access)) {
260     set_interrupt_limits(access);
261   }
262 }
263 
264 
DisableInterrupts()265 void StackGuard::DisableInterrupts() {
266   ExecutionAccess access(isolate_);
267   reset_limits(access);
268 }
269 
270 
PushPostponeInterruptsScope(PostponeInterruptsScope * scope)271 void StackGuard::PushPostponeInterruptsScope(PostponeInterruptsScope* scope) {
272   ExecutionAccess access(isolate_);
273   // Intercept already requested interrupts.
274   int intercepted = thread_local_.interrupt_flags_ & scope->intercept_mask_;
275   scope->intercepted_flags_ = intercepted;
276   thread_local_.interrupt_flags_ &= ~intercepted;
277   if (!has_pending_interrupts(access)) reset_limits(access);
278   // Add scope to the chain.
279   scope->prev_ = thread_local_.postpone_interrupts_;
280   thread_local_.postpone_interrupts_ = scope;
281 }
282 
283 
PopPostponeInterruptsScope()284 void StackGuard::PopPostponeInterruptsScope() {
285   ExecutionAccess access(isolate_);
286   PostponeInterruptsScope* top = thread_local_.postpone_interrupts_;
287   // Make intercepted interrupts active.
288   DCHECK((thread_local_.interrupt_flags_ & top->intercept_mask_) == 0);
289   thread_local_.interrupt_flags_ |= top->intercepted_flags_;
290   if (has_pending_interrupts(access)) set_interrupt_limits(access);
291   // Remove scope from chain.
292   thread_local_.postpone_interrupts_ = top->prev_;
293 }
294 
295 
CheckInterrupt(InterruptFlag flag)296 bool StackGuard::CheckInterrupt(InterruptFlag flag) {
297   ExecutionAccess access(isolate_);
298   return thread_local_.interrupt_flags_ & flag;
299 }
300 
301 
RequestInterrupt(InterruptFlag flag)302 void StackGuard::RequestInterrupt(InterruptFlag flag) {
303   ExecutionAccess access(isolate_);
304   // Check the chain of PostponeInterruptsScopes for interception.
305   if (thread_local_.postpone_interrupts_ &&
306       thread_local_.postpone_interrupts_->Intercept(flag)) {
307     return;
308   }
309 
310   // Not intercepted.  Set as active interrupt flag.
311   thread_local_.interrupt_flags_ |= flag;
312   set_interrupt_limits(access);
313 
314   // If this isolate is waiting in a futex, notify it to wake up.
315   isolate_->futex_wait_list_node()->NotifyWake();
316 }
317 
318 
ClearInterrupt(InterruptFlag flag)319 void StackGuard::ClearInterrupt(InterruptFlag flag) {
320   ExecutionAccess access(isolate_);
321   // Clear the interrupt flag from the chain of PostponeInterruptsScopes.
322   for (PostponeInterruptsScope* current = thread_local_.postpone_interrupts_;
323        current != NULL;
324        current = current->prev_) {
325     current->intercepted_flags_ &= ~flag;
326   }
327 
328   // Clear the interrupt flag from the active interrupt flags.
329   thread_local_.interrupt_flags_ &= ~flag;
330   if (!has_pending_interrupts(access)) reset_limits(access);
331 }
332 
333 
CheckAndClearInterrupt(InterruptFlag flag)334 bool StackGuard::CheckAndClearInterrupt(InterruptFlag flag) {
335   ExecutionAccess access(isolate_);
336   bool result = (thread_local_.interrupt_flags_ & flag);
337   thread_local_.interrupt_flags_ &= ~flag;
338   if (!has_pending_interrupts(access)) reset_limits(access);
339   return result;
340 }
341 
342 
ArchiveStackGuard(char * to)343 char* StackGuard::ArchiveStackGuard(char* to) {
344   ExecutionAccess access(isolate_);
345   MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
346   ThreadLocal blank;
347 
348   // Set the stack limits using the old thread_local_.
349   // TODO(isolates): This was the old semantics of constructing a ThreadLocal
350   //                 (as the ctor called SetStackLimits, which looked at the
351   //                 current thread_local_ from StackGuard)-- but is this
352   //                 really what was intended?
353   isolate_->heap()->SetStackLimits();
354   thread_local_ = blank;
355 
356   return to + sizeof(ThreadLocal);
357 }
358 
359 
RestoreStackGuard(char * from)360 char* StackGuard::RestoreStackGuard(char* from) {
361   ExecutionAccess access(isolate_);
362   MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
363   isolate_->heap()->SetStackLimits();
364   return from + sizeof(ThreadLocal);
365 }
366 
367 
FreeThreadResources()368 void StackGuard::FreeThreadResources() {
369   Isolate::PerIsolateThreadData* per_thread =
370       isolate_->FindOrAllocatePerThreadDataForThisThread();
371   per_thread->set_stack_limit(thread_local_.real_climit_);
372 }
373 
374 
Clear()375 void StackGuard::ThreadLocal::Clear() {
376   real_jslimit_ = kIllegalLimit;
377   set_jslimit(kIllegalLimit);
378   real_climit_ = kIllegalLimit;
379   set_climit(kIllegalLimit);
380   postpone_interrupts_ = NULL;
381   interrupt_flags_ = 0;
382 }
383 
384 
Initialize(Isolate * isolate)385 bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
386   bool should_set_stack_limits = false;
387   if (real_climit_ == kIllegalLimit) {
388     const uintptr_t kLimitSize = FLAG_stack_size * KB;
389     DCHECK(GetCurrentStackPosition() > kLimitSize);
390     uintptr_t limit = GetCurrentStackPosition() - kLimitSize;
391     real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
392     set_jslimit(SimulatorStack::JsLimitFromCLimit(isolate, limit));
393     real_climit_ = limit;
394     set_climit(limit);
395     should_set_stack_limits = true;
396   }
397   postpone_interrupts_ = NULL;
398   interrupt_flags_ = 0;
399   return should_set_stack_limits;
400 }
401 
402 
ClearThread(const ExecutionAccess & lock)403 void StackGuard::ClearThread(const ExecutionAccess& lock) {
404   thread_local_.Clear();
405   isolate_->heap()->SetStackLimits();
406 }
407 
408 
InitThread(const ExecutionAccess & lock)409 void StackGuard::InitThread(const ExecutionAccess& lock) {
410   if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits();
411   Isolate::PerIsolateThreadData* per_thread =
412       isolate_->FindOrAllocatePerThreadDataForThisThread();
413   uintptr_t stored_limit = per_thread->stack_limit();
414   // You should hold the ExecutionAccess lock when you call this.
415   if (stored_limit != 0) {
416     SetStackLimit(stored_limit);
417   }
418 }
419 
420 
421 // --- C a l l s   t o   n a t i v e s ---
422 
423 
ToObject(Isolate * isolate,Handle<Object> obj)424 MaybeHandle<JSReceiver> Execution::ToObject(Isolate* isolate,
425                                             Handle<Object> obj) {
426   Handle<JSReceiver> receiver;
427   if (JSReceiver::ToObject(isolate, obj).ToHandle(&receiver)) {
428     return receiver;
429   }
430   THROW_NEW_ERROR(isolate,
431                   NewTypeError(MessageTemplate::kUndefinedOrNullToObject),
432                   JSReceiver);
433 }
434 
435 
GetStackTraceLine(Handle<Object> recv,Handle<JSFunction> fun,Handle<Object> pos,Handle<Object> is_global)436 Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
437                                             Handle<JSFunction> fun,
438                                             Handle<Object> pos,
439                                             Handle<Object> is_global) {
440   Isolate* isolate = fun->GetIsolate();
441   Handle<Object> args[] = { recv, fun, pos, is_global };
442   MaybeHandle<Object> maybe_result =
443       TryCall(isolate, isolate->get_stack_trace_line_fun(),
444               isolate->factory()->undefined_value(), arraysize(args), args);
445   Handle<Object> result;
446   if (!maybe_result.ToHandle(&result) || !result->IsString()) {
447     return isolate->factory()->empty_string();
448   }
449 
450   return Handle<String>::cast(result);
451 }
452 
453 
HandleGCInterrupt()454 void StackGuard::HandleGCInterrupt() {
455   if (CheckAndClearInterrupt(GC_REQUEST)) {
456     isolate_->heap()->HandleGCRequest();
457   }
458 }
459 
460 
HandleInterrupts()461 Object* StackGuard::HandleInterrupts() {
462   if (FLAG_verify_predictable) {
463     // Advance synthetic time by making a time request.
464     isolate_->heap()->MonotonicallyIncreasingTimeInMs();
465   }
466 
467   if (CheckAndClearInterrupt(GC_REQUEST)) {
468     isolate_->heap()->HandleGCRequest();
469   }
470 
471   if (CheckDebugBreak() || CheckDebugCommand()) {
472     isolate_->debug()->HandleDebugBreak();
473   }
474 
475   if (CheckAndClearInterrupt(TERMINATE_EXECUTION)) {
476     return isolate_->TerminateExecution();
477   }
478 
479   if (CheckAndClearInterrupt(DEOPT_MARKED_ALLOCATION_SITES)) {
480     isolate_->heap()->DeoptMarkedAllocationSites();
481   }
482 
483   if (CheckAndClearInterrupt(INSTALL_CODE)) {
484     DCHECK(isolate_->concurrent_recompilation_enabled());
485     isolate_->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
486   }
487 
488   if (CheckAndClearInterrupt(API_INTERRUPT)) {
489     // Callbacks must be invoked outside of ExecusionAccess lock.
490     isolate_->InvokeApiInterruptCallbacks();
491   }
492 
493   isolate_->counters()->stack_interrupts()->Increment();
494   isolate_->counters()->runtime_profiler_ticks()->Increment();
495   isolate_->runtime_profiler()->OptimizeNow();
496 
497   return isolate_->heap()->undefined_value();
498 }
499 
500 }  // namespace internal
501 }  // namespace v8
502