• 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     RuntimeCallTimerScope timer(isolate, &RuntimeCallStats::JS_Execution);
98     value = CALL_GENERATED_CODE(isolate, stub_entry, orig_func, func, recv,
99                                 argc, argv);
100   }
101 
102 #ifdef VERIFY_HEAP
103   if (FLAG_verify_heap) {
104     value->ObjectVerify();
105   }
106 #endif
107 
108   // Update the pending exception flag and return the value.
109   bool has_exception = value->IsException(isolate);
110   DCHECK(has_exception == isolate->has_pending_exception());
111   if (has_exception) {
112     isolate->ReportPendingMessages();
113     return MaybeHandle<Object>();
114   } else {
115     isolate->clear_pending_message();
116   }
117 
118   return Handle<Object>(value, isolate);
119 }
120 
121 }  // namespace
122 
123 
124 // static
Call(Isolate * isolate,Handle<Object> callable,Handle<Object> receiver,int argc,Handle<Object> argv[])125 MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable,
126                                     Handle<Object> receiver, int argc,
127                                     Handle<Object> argv[]) {
128   // Convert calls on global objects to be calls on the global
129   // receiver instead to avoid having a 'this' pointer which refers
130   // directly to a global object.
131   if (receiver->IsJSGlobalObject()) {
132     receiver =
133         handle(Handle<JSGlobalObject>::cast(receiver)->global_proxy(), isolate);
134   }
135 
136   // api callbacks can be called directly.
137   if (callable->IsJSFunction() &&
138       Handle<JSFunction>::cast(callable)->shared()->IsApiFunction()) {
139     Handle<JSFunction> function = Handle<JSFunction>::cast(callable);
140     SaveContext save(isolate);
141     isolate->set_context(function->context());
142     DCHECK(function->context()->global_object()->IsJSGlobalObject());
143     auto value =
144         Builtins::InvokeApiFunction(isolate, function, receiver, argc, argv);
145     bool has_exception = value.is_null();
146     DCHECK(has_exception == isolate->has_pending_exception());
147     if (has_exception) {
148       isolate->ReportPendingMessages();
149       return MaybeHandle<Object>();
150     } else {
151       isolate->clear_pending_message();
152     }
153     return value;
154   }
155   return Invoke(isolate, false, callable, receiver, argc, argv,
156                 isolate->factory()->undefined_value());
157 }
158 
159 
160 // static
New(Handle<JSFunction> constructor,int argc,Handle<Object> argv[])161 MaybeHandle<Object> Execution::New(Handle<JSFunction> constructor, int argc,
162                                    Handle<Object> argv[]) {
163   return New(constructor->GetIsolate(), constructor, constructor, argc, argv);
164 }
165 
166 
167 // static
New(Isolate * isolate,Handle<Object> constructor,Handle<Object> new_target,int argc,Handle<Object> argv[])168 MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor,
169                                    Handle<Object> new_target, int argc,
170                                    Handle<Object> argv[]) {
171   return Invoke(isolate, true, constructor,
172                 isolate->factory()->undefined_value(), argc, argv, new_target);
173 }
174 
175 
TryCall(Isolate * isolate,Handle<Object> callable,Handle<Object> receiver,int argc,Handle<Object> args[],MaybeHandle<Object> * exception_out)176 MaybeHandle<Object> Execution::TryCall(Isolate* isolate,
177                                        Handle<Object> callable,
178                                        Handle<Object> receiver, int argc,
179                                        Handle<Object> args[],
180                                        MaybeHandle<Object>* exception_out) {
181   bool is_termination = false;
182   MaybeHandle<Object> maybe_result;
183   if (exception_out != NULL) *exception_out = MaybeHandle<Object>();
184   // Enter a try-block while executing the JavaScript code. To avoid
185   // duplicate error printing it must be non-verbose.  Also, to avoid
186   // creating message objects during stack overflow we shouldn't
187   // capture messages.
188   {
189     v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
190     catcher.SetVerbose(false);
191     catcher.SetCaptureMessage(false);
192 
193     maybe_result = Call(isolate, callable, receiver, argc, args);
194 
195     if (maybe_result.is_null()) {
196       DCHECK(catcher.HasCaught());
197       DCHECK(isolate->has_pending_exception());
198       DCHECK(isolate->external_caught_exception());
199       if (isolate->pending_exception() ==
200           isolate->heap()->termination_exception()) {
201         is_termination = true;
202       } else {
203         if (exception_out != NULL) {
204           *exception_out = v8::Utils::OpenHandle(*catcher.Exception());
205         }
206       }
207       isolate->OptionalRescheduleException(true);
208     }
209 
210     DCHECK(!isolate->has_pending_exception());
211   }
212 
213   // Re-request terminate execution interrupt to trigger later.
214   if (is_termination) isolate->stack_guard()->RequestTerminateExecution();
215 
216   return maybe_result;
217 }
218 
219 
SetStackLimit(uintptr_t limit)220 void StackGuard::SetStackLimit(uintptr_t limit) {
221   ExecutionAccess access(isolate_);
222   // If the current limits are special (e.g. due to a pending interrupt) then
223   // leave them alone.
224   uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit);
225   if (thread_local_.jslimit() == thread_local_.real_jslimit_) {
226     thread_local_.set_jslimit(jslimit);
227   }
228   if (thread_local_.climit() == thread_local_.real_climit_) {
229     thread_local_.set_climit(limit);
230   }
231   thread_local_.real_climit_ = limit;
232   thread_local_.real_jslimit_ = jslimit;
233 }
234 
235 
AdjustStackLimitForSimulator()236 void StackGuard::AdjustStackLimitForSimulator() {
237   ExecutionAccess access(isolate_);
238   uintptr_t climit = thread_local_.real_climit_;
239   // If the current limits are special (e.g. due to a pending interrupt) then
240   // leave them alone.
241   uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, climit);
242   if (thread_local_.jslimit() == thread_local_.real_jslimit_) {
243     thread_local_.set_jslimit(jslimit);
244     isolate_->heap()->SetStackLimits();
245   }
246 }
247 
248 
EnableInterrupts()249 void StackGuard::EnableInterrupts() {
250   ExecutionAccess access(isolate_);
251   if (has_pending_interrupts(access)) {
252     set_interrupt_limits(access);
253   }
254 }
255 
256 
DisableInterrupts()257 void StackGuard::DisableInterrupts() {
258   ExecutionAccess access(isolate_);
259   reset_limits(access);
260 }
261 
262 
PushPostponeInterruptsScope(PostponeInterruptsScope * scope)263 void StackGuard::PushPostponeInterruptsScope(PostponeInterruptsScope* scope) {
264   ExecutionAccess access(isolate_);
265   // Intercept already requested interrupts.
266   int intercepted = thread_local_.interrupt_flags_ & scope->intercept_mask_;
267   scope->intercepted_flags_ = intercepted;
268   thread_local_.interrupt_flags_ &= ~intercepted;
269   if (!has_pending_interrupts(access)) reset_limits(access);
270   // Add scope to the chain.
271   scope->prev_ = thread_local_.postpone_interrupts_;
272   thread_local_.postpone_interrupts_ = scope;
273 }
274 
275 
PopPostponeInterruptsScope()276 void StackGuard::PopPostponeInterruptsScope() {
277   ExecutionAccess access(isolate_);
278   PostponeInterruptsScope* top = thread_local_.postpone_interrupts_;
279   // Make intercepted interrupts active.
280   DCHECK((thread_local_.interrupt_flags_ & top->intercept_mask_) == 0);
281   thread_local_.interrupt_flags_ |= top->intercepted_flags_;
282   if (has_pending_interrupts(access)) set_interrupt_limits(access);
283   // Remove scope from chain.
284   thread_local_.postpone_interrupts_ = top->prev_;
285 }
286 
287 
CheckInterrupt(InterruptFlag flag)288 bool StackGuard::CheckInterrupt(InterruptFlag flag) {
289   ExecutionAccess access(isolate_);
290   return thread_local_.interrupt_flags_ & flag;
291 }
292 
293 
RequestInterrupt(InterruptFlag flag)294 void StackGuard::RequestInterrupt(InterruptFlag flag) {
295   ExecutionAccess access(isolate_);
296   // Check the chain of PostponeInterruptsScopes for interception.
297   if (thread_local_.postpone_interrupts_ &&
298       thread_local_.postpone_interrupts_->Intercept(flag)) {
299     return;
300   }
301 
302   // Not intercepted.  Set as active interrupt flag.
303   thread_local_.interrupt_flags_ |= flag;
304   set_interrupt_limits(access);
305 
306   // If this isolate is waiting in a futex, notify it to wake up.
307   isolate_->futex_wait_list_node()->NotifyWake();
308 }
309 
310 
ClearInterrupt(InterruptFlag flag)311 void StackGuard::ClearInterrupt(InterruptFlag flag) {
312   ExecutionAccess access(isolate_);
313   // Clear the interrupt flag from the chain of PostponeInterruptsScopes.
314   for (PostponeInterruptsScope* current = thread_local_.postpone_interrupts_;
315        current != NULL;
316        current = current->prev_) {
317     current->intercepted_flags_ &= ~flag;
318   }
319 
320   // Clear the interrupt flag from the active interrupt flags.
321   thread_local_.interrupt_flags_ &= ~flag;
322   if (!has_pending_interrupts(access)) reset_limits(access);
323 }
324 
325 
CheckAndClearInterrupt(InterruptFlag flag)326 bool StackGuard::CheckAndClearInterrupt(InterruptFlag flag) {
327   ExecutionAccess access(isolate_);
328   bool result = (thread_local_.interrupt_flags_ & flag);
329   thread_local_.interrupt_flags_ &= ~flag;
330   if (!has_pending_interrupts(access)) reset_limits(access);
331   return result;
332 }
333 
334 
ArchiveStackGuard(char * to)335 char* StackGuard::ArchiveStackGuard(char* to) {
336   ExecutionAccess access(isolate_);
337   MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
338   ThreadLocal blank;
339 
340   // Set the stack limits using the old thread_local_.
341   // TODO(isolates): This was the old semantics of constructing a ThreadLocal
342   //                 (as the ctor called SetStackLimits, which looked at the
343   //                 current thread_local_ from StackGuard)-- but is this
344   //                 really what was intended?
345   isolate_->heap()->SetStackLimits();
346   thread_local_ = blank;
347 
348   return to + sizeof(ThreadLocal);
349 }
350 
351 
RestoreStackGuard(char * from)352 char* StackGuard::RestoreStackGuard(char* from) {
353   ExecutionAccess access(isolate_);
354   MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
355   isolate_->heap()->SetStackLimits();
356   return from + sizeof(ThreadLocal);
357 }
358 
359 
FreeThreadResources()360 void StackGuard::FreeThreadResources() {
361   Isolate::PerIsolateThreadData* per_thread =
362       isolate_->FindOrAllocatePerThreadDataForThisThread();
363   per_thread->set_stack_limit(thread_local_.real_climit_);
364 }
365 
366 
Clear()367 void StackGuard::ThreadLocal::Clear() {
368   real_jslimit_ = kIllegalLimit;
369   set_jslimit(kIllegalLimit);
370   real_climit_ = kIllegalLimit;
371   set_climit(kIllegalLimit);
372   postpone_interrupts_ = NULL;
373   interrupt_flags_ = 0;
374 }
375 
376 
Initialize(Isolate * isolate)377 bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
378   bool should_set_stack_limits = false;
379   if (real_climit_ == kIllegalLimit) {
380     const uintptr_t kLimitSize = FLAG_stack_size * KB;
381     DCHECK(GetCurrentStackPosition() > kLimitSize);
382     uintptr_t limit = GetCurrentStackPosition() - kLimitSize;
383     real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
384     set_jslimit(SimulatorStack::JsLimitFromCLimit(isolate, limit));
385     real_climit_ = limit;
386     set_climit(limit);
387     should_set_stack_limits = true;
388   }
389   postpone_interrupts_ = NULL;
390   interrupt_flags_ = 0;
391   return should_set_stack_limits;
392 }
393 
394 
ClearThread(const ExecutionAccess & lock)395 void StackGuard::ClearThread(const ExecutionAccess& lock) {
396   thread_local_.Clear();
397   isolate_->heap()->SetStackLimits();
398 }
399 
400 
InitThread(const ExecutionAccess & lock)401 void StackGuard::InitThread(const ExecutionAccess& lock) {
402   if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits();
403   Isolate::PerIsolateThreadData* per_thread =
404       isolate_->FindOrAllocatePerThreadDataForThisThread();
405   uintptr_t stored_limit = per_thread->stack_limit();
406   // You should hold the ExecutionAccess lock when you call this.
407   if (stored_limit != 0) {
408     SetStackLimit(stored_limit);
409   }
410 }
411 
412 
413 // --- C a l l s   t o   n a t i v e s ---
414 
415 
GetStackTraceLine(Handle<Object> recv,Handle<JSFunction> fun,Handle<Object> pos,Handle<Object> is_global)416 Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
417                                             Handle<JSFunction> fun,
418                                             Handle<Object> pos,
419                                             Handle<Object> is_global) {
420   Isolate* isolate = fun->GetIsolate();
421   Handle<Object> args[] = { recv, fun, pos, is_global };
422   MaybeHandle<Object> maybe_result =
423       TryCall(isolate, isolate->get_stack_trace_line_fun(),
424               isolate->factory()->undefined_value(), arraysize(args), args);
425   Handle<Object> result;
426   if (!maybe_result.ToHandle(&result) || !result->IsString()) {
427     return isolate->factory()->empty_string();
428   }
429 
430   return Handle<String>::cast(result);
431 }
432 
433 
HandleGCInterrupt()434 void StackGuard::HandleGCInterrupt() {
435   if (CheckAndClearInterrupt(GC_REQUEST)) {
436     isolate_->heap()->HandleGCRequest();
437   }
438 }
439 
440 
HandleInterrupts()441 Object* StackGuard::HandleInterrupts() {
442   if (FLAG_verify_predictable) {
443     // Advance synthetic time by making a time request.
444     isolate_->heap()->MonotonicallyIncreasingTimeInMs();
445   }
446 
447   if (CheckAndClearInterrupt(GC_REQUEST)) {
448     isolate_->heap()->HandleGCRequest();
449   }
450 
451   if (CheckDebugBreak() || CheckDebugCommand()) {
452     isolate_->debug()->HandleDebugBreak();
453   }
454 
455   if (CheckAndClearInterrupt(TERMINATE_EXECUTION)) {
456     return isolate_->TerminateExecution();
457   }
458 
459   if (CheckAndClearInterrupt(DEOPT_MARKED_ALLOCATION_SITES)) {
460     isolate_->heap()->DeoptMarkedAllocationSites();
461   }
462 
463   if (CheckAndClearInterrupt(INSTALL_CODE)) {
464     DCHECK(isolate_->concurrent_recompilation_enabled());
465     isolate_->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
466   }
467 
468   if (CheckAndClearInterrupt(API_INTERRUPT)) {
469     // Callbacks must be invoked outside of ExecusionAccess lock.
470     isolate_->InvokeApiInterruptCallbacks();
471   }
472 
473   isolate_->counters()->stack_interrupts()->Increment();
474   isolate_->counters()->runtime_profiler_ticks()->Increment();
475   isolate_->runtime_profiler()->MarkCandidatesForOptimization();
476 
477   return isolate_->heap()->undefined_value();
478 }
479 
480 }  // namespace internal
481 }  // namespace v8
482