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/api-inl.h" |
8 | #include "src/bootstrapper.h" |
9 | #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" |
10 | #include "src/debug/debug.h" |
11 | #include "src/isolate-inl.h" |
12 | #include "src/runtime-profiler.h" |
13 | #include "src/vm-state-inl.h" |
14 | |
15 | namespace v8 { |
16 | namespace internal { |
17 | |
18 | void StackGuard::set_interrupt_limits(const ExecutionAccess& lock) { |
19 | DCHECK_NOT_NULL(isolate_); |
20 | thread_local_.set_jslimit(kInterruptLimit); |
21 | thread_local_.set_climit(kInterruptLimit); |
22 | isolate_->heap()->SetStackLimits(); |
23 | } |
24 | |
25 | void StackGuard::reset_limits(const ExecutionAccess& lock) { |
26 | DCHECK_NOT_NULL(isolate_); |
27 | thread_local_.set_jslimit(thread_local_.real_jslimit_); |
28 | thread_local_.set_climit(thread_local_.real_climit_); |
29 | isolate_->heap()->SetStackLimits(); |
30 | } |
31 | |
32 | namespace { |
33 | |
34 | Handle<Object> NormalizeReceiver(Isolate* isolate, Handle<Object> receiver) { |
35 | // Convert calls on global objects to be calls on the global |
36 | // receiver instead to avoid having a 'this' pointer which refers |
37 | // directly to a global object. |
38 | if (receiver->IsJSGlobalObject()) { |
39 | return handle(Handle<JSGlobalObject>::cast(receiver)->global_proxy(), |
40 | isolate); |
41 | } |
42 | return receiver; |
43 | } |
44 | |
45 | struct InvokeParams { |
46 | static InvokeParams SetUpForNew(Isolate* isolate, Handle<Object> constructor, |
47 | Handle<Object> new_target, int argc, |
48 | Handle<Object>* argv); |
49 | |
50 | static InvokeParams SetUpForCall(Isolate* isolate, Handle<Object> callable, |
51 | Handle<Object> receiver, int argc, |
52 | Handle<Object>* argv); |
53 | |
54 | static InvokeParams SetUpForTryCall( |
55 | Isolate* isolate, Handle<Object> callable, Handle<Object> receiver, |
56 | int argc, Handle<Object>* argv, |
57 | Execution::MessageHandling message_handling, |
58 | MaybeHandle<Object>* exception_out); |
59 | |
60 | static InvokeParams SetUpForRunMicrotasks(Isolate* isolate, |
61 | MicrotaskQueue* microtask_queue, |
62 | MaybeHandle<Object>* exception_out); |
63 | |
64 | Handle<Object> target; |
65 | Handle<Object> receiver; |
66 | int argc; |
67 | Handle<Object>* argv; |
68 | Handle<Object> new_target; |
69 | |
70 | MicrotaskQueue* microtask_queue; |
71 | |
72 | Execution::MessageHandling message_handling; |
73 | MaybeHandle<Object>* exception_out; |
74 | |
75 | bool is_construct; |
76 | Execution::Target execution_target; |
77 | }; |
78 | |
79 | // static |
80 | InvokeParams InvokeParams::SetUpForNew(Isolate* isolate, |
81 | Handle<Object> constructor, |
82 | Handle<Object> new_target, int argc, |
83 | Handle<Object>* argv) { |
84 | InvokeParams params; |
85 | params.target = constructor; |
86 | params.receiver = isolate->factory()->undefined_value(); |
87 | params.argc = argc; |
88 | params.argv = argv; |
89 | params.new_target = new_target; |
90 | params.microtask_queue = nullptr; |
91 | params.message_handling = Execution::MessageHandling::kReport; |
92 | params.exception_out = nullptr; |
93 | params.is_construct = true; |
94 | params.execution_target = Execution::Target::kCallable; |
95 | return params; |
96 | } |
97 | |
98 | // static |
99 | InvokeParams InvokeParams::SetUpForCall(Isolate* isolate, |
100 | Handle<Object> callable, |
101 | Handle<Object> receiver, int argc, |
102 | Handle<Object>* argv) { |
103 | InvokeParams params; |
104 | params.target = callable; |
105 | params.receiver = NormalizeReceiver(isolate, receiver); |
106 | params.argc = argc; |
107 | params.argv = argv; |
108 | params.new_target = isolate->factory()->undefined_value(); |
109 | params.microtask_queue = nullptr; |
110 | params.message_handling = Execution::MessageHandling::kReport; |
111 | params.exception_out = nullptr; |
112 | params.is_construct = false; |
113 | params.execution_target = Execution::Target::kCallable; |
114 | return params; |
115 | } |
116 | |
117 | // static |
118 | InvokeParams InvokeParams::SetUpForTryCall( |
119 | Isolate* isolate, Handle<Object> callable, Handle<Object> receiver, |
120 | int argc, Handle<Object>* argv, Execution::MessageHandling message_handling, |
121 | MaybeHandle<Object>* exception_out) { |
122 | InvokeParams params; |
123 | params.target = callable; |
124 | params.receiver = NormalizeReceiver(isolate, receiver); |
125 | params.argc = argc; |
126 | params.argv = argv; |
127 | params.new_target = isolate->factory()->undefined_value(); |
128 | params.microtask_queue = nullptr; |
129 | params.message_handling = message_handling; |
130 | params.exception_out = exception_out; |
131 | params.is_construct = false; |
132 | params.execution_target = Execution::Target::kCallable; |
133 | return params; |
134 | } |
135 | |
136 | // static |
137 | InvokeParams InvokeParams::SetUpForRunMicrotasks( |
138 | Isolate* isolate, MicrotaskQueue* microtask_queue, |
139 | MaybeHandle<Object>* exception_out) { |
140 | auto undefined = isolate->factory()->undefined_value(); |
141 | InvokeParams params; |
142 | params.target = undefined; |
143 | params.receiver = undefined; |
144 | params.argc = 0; |
145 | params.argv = nullptr; |
146 | params.new_target = undefined; |
147 | params.microtask_queue = microtask_queue; |
148 | params.message_handling = Execution::MessageHandling::kReport; |
149 | params.exception_out = exception_out; |
150 | params.is_construct = false; |
151 | params.execution_target = Execution::Target::kRunMicrotasks; |
152 | return params; |
153 | } |
154 | |
155 | Handle<Code> JSEntry(Isolate* isolate, Execution::Target execution_target, |
156 | bool is_construct) { |
157 | if (is_construct) { |
158 | DCHECK_EQ(Execution::Target::kCallable, execution_target); |
159 | return BUILTIN_CODE(isolate, JSConstructEntry); |
160 | } else if (execution_target == Execution::Target::kCallable) { |
161 | DCHECK(!is_construct); |
162 | return BUILTIN_CODE(isolate, JSEntry); |
163 | } else if (execution_target == Execution::Target::kRunMicrotasks) { |
164 | DCHECK(!is_construct); |
165 | return BUILTIN_CODE(isolate, JSRunMicrotasksEntry); |
166 | } |
167 | UNREACHABLE(); |
168 | } |
169 | |
170 | V8_WARN_UNUSED_RESULT MaybeHandle<Object> Invoke(Isolate* isolate, |
171 | const InvokeParams& params) { |
172 | RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kInvoke); |
173 | DCHECK(!params.receiver->IsJSGlobalObject()); |
174 | DCHECK_LE(params.argc, FixedArray::kMaxLength); |
175 | |
176 | #ifdef USE_SIMULATOR |
177 | // Simulators use separate stacks for C++ and JS. JS stack overflow checks |
178 | // are performed whenever a JS function is called. However, it can be the case |
179 | // that the C++ stack grows faster than the JS stack, resulting in an overflow |
180 | // there. Add a check here to make that less likely. |
181 | StackLimitCheck check(isolate); |
182 | if (check.HasOverflowed()) { |
183 | isolate->StackOverflow(); |
184 | if (params.message_handling == Execution::MessageHandling::kReport) { |
185 | isolate->ReportPendingMessages(); |
186 | } |
187 | return MaybeHandle<Object>(); |
188 | } |
189 | #endif |
190 | |
191 | // api callbacks can be called directly, unless we want to take the detour |
192 | // through JS to set up a frame for break-at-entry. |
193 | if (params.target->IsJSFunction()) { |
194 | Handle<JSFunction> function = Handle<JSFunction>::cast(params.target); |
195 | if ((!params.is_construct || function->IsConstructor()) && |
196 | function->shared()->IsApiFunction() && |
197 | !function->shared()->BreakAtEntry()) { |
198 | SaveAndSwitchContext save(isolate, function->context()); |
199 | DCHECK(function->context()->global_object()->IsJSGlobalObject()); |
200 | |
201 | Handle<Object> receiver = params.is_construct |
202 | ? isolate->factory()->the_hole_value() |
203 | : params.receiver; |
204 | auto value = Builtins::InvokeApiFunction( |
205 | isolate, params.is_construct, function, receiver, params.argc, |
206 | params.argv, Handle<HeapObject>::cast(params.new_target)); |
207 | bool has_exception = value.is_null(); |
208 | DCHECK(has_exception == isolate->has_pending_exception()); |
209 | if (has_exception) { |
210 | if (params.message_handling == Execution::MessageHandling::kReport) { |
211 | isolate->ReportPendingMessages(); |
212 | } |
213 | return MaybeHandle<Object>(); |
214 | } else { |
215 | isolate->clear_pending_message(); |
216 | } |
217 | return value; |
218 | } |
219 | } |
220 | |
221 | // Entering JavaScript. |
222 | VMState<JS> state(isolate); |
223 | CHECK(AllowJavascriptExecution::IsAllowed(isolate)); |
224 | if (!ThrowOnJavascriptExecution::IsAllowed(isolate)) { |
225 | isolate->ThrowIllegalOperation(); |
226 | if (params.message_handling == Execution::MessageHandling::kReport) { |
227 | isolate->ReportPendingMessages(); |
228 | } |
229 | return MaybeHandle<Object>(); |
230 | } |
231 | if (!DumpOnJavascriptExecution::IsAllowed(isolate)) { |
232 | V8::GetCurrentPlatform()->DumpWithoutCrashing(); |
233 | return isolate->factory()->undefined_value(); |
234 | } |
235 | |
236 | // Placeholder for return value. |
237 | Object value; |
238 | |
239 | Handle<Code> code = |
240 | JSEntry(isolate, params.execution_target, params.is_construct); |
241 | { |
242 | // Save and restore context around invocation and block the |
243 | // allocation of handles without explicit handle scopes. |
244 | SaveContext save(isolate); |
245 | SealHandleScope shs(isolate); |
246 | |
247 | if (FLAG_clear_exceptions_on_js_entry) isolate->clear_pending_exception(); |
248 | |
249 | if (params.execution_target == Execution::Target::kCallable) { |
250 | // clang-format off |
251 | // {new_target}, {target}, {receiver}, return value: tagged pointers |
252 | // {argv}: pointer to array of tagged pointers |
253 | using JSEntryFunction = GeneratedCode<Address( |
254 | Address root_register_value, Address new_target, Address target, |
255 | Address receiver, intptr_t argc, Address** argv)>; |
256 | // clang-format on |
257 | JSEntryFunction stub_entry = |
258 | JSEntryFunction::FromAddress(isolate, code->InstructionStart()); |
259 | |
260 | Address orig_func = params.new_target->ptr(); |
261 | Address func = params.target->ptr(); |
262 | Address recv = params.receiver->ptr(); |
263 | Address** argv = reinterpret_cast<Address**>(params.argv); |
264 | RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kJS_Execution); |
265 | value = Object(stub_entry.Call(isolate->isolate_data()->isolate_root(), |
266 | orig_func, func, recv, params.argc, argv)); |
267 | } else { |
268 | DCHECK_EQ(Execution::Target::kRunMicrotasks, params.execution_target); |
269 | |
270 | // clang-format off |
271 | // return value: tagged pointers |
272 | // {microtask_queue}: pointer to a C++ object |
273 | using JSEntryFunction = GeneratedCode<Address( |
274 | Address root_register_value, MicrotaskQueue* microtask_queue)>; |
275 | // clang-format on |
276 | JSEntryFunction stub_entry = |
277 | JSEntryFunction::FromAddress(isolate, code->InstructionStart()); |
278 | |
279 | RuntimeCallTimerScope timer(isolate, RuntimeCallCounterId::kJS_Execution); |
280 | value = Object(stub_entry.Call(isolate->isolate_data()->isolate_root(), |
281 | params.microtask_queue)); |
282 | } |
283 | } |
284 | |
285 | #ifdef VERIFY_HEAP |
286 | if (FLAG_verify_heap) { |
287 | value->ObjectVerify(isolate); |
288 | } |
289 | #endif |
290 | |
291 | // Update the pending exception flag and return the value. |
292 | bool has_exception = value->IsException(isolate); |
293 | DCHECK(has_exception == isolate->has_pending_exception()); |
294 | if (has_exception) { |
295 | if (params.message_handling == Execution::MessageHandling::kReport) { |
296 | isolate->ReportPendingMessages(); |
297 | } |
298 | return MaybeHandle<Object>(); |
299 | } else { |
300 | isolate->clear_pending_message(); |
301 | } |
302 | |
303 | return Handle<Object>(value, isolate); |
304 | } |
305 | |
306 | MaybeHandle<Object> InvokeWithTryCatch(Isolate* isolate, |
307 | const InvokeParams& params) { |
308 | bool is_termination = false; |
309 | MaybeHandle<Object> maybe_result; |
310 | if (params.exception_out != nullptr) { |
311 | *params.exception_out = MaybeHandle<Object>(); |
312 | } |
313 | DCHECK_IMPLIES( |
314 | params.message_handling == Execution::MessageHandling::kKeepPending, |
315 | params.exception_out == nullptr); |
316 | // Enter a try-block while executing the JavaScript code. To avoid |
317 | // duplicate error printing it must be non-verbose. Also, to avoid |
318 | // creating message objects during stack overflow we shouldn't |
319 | // capture messages. |
320 | { |
321 | v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate)); |
322 | catcher.SetVerbose(false); |
323 | catcher.SetCaptureMessage(false); |
324 | |
325 | maybe_result = Invoke(isolate, params); |
326 | |
327 | if (maybe_result.is_null()) { |
328 | DCHECK(isolate->has_pending_exception()); |
329 | if (isolate->pending_exception() == |
330 | ReadOnlyRoots(isolate).termination_exception()) { |
331 | is_termination = true; |
332 | } else { |
333 | if (params.exception_out != nullptr) { |
334 | DCHECK(catcher.HasCaught()); |
335 | DCHECK(isolate->external_caught_exception()); |
336 | *params.exception_out = v8::Utils::OpenHandle(*catcher.Exception()); |
337 | } |
338 | } |
339 | if (params.message_handling == Execution::MessageHandling::kReport) { |
340 | isolate->OptionalRescheduleException(true); |
341 | } |
342 | } |
343 | } |
344 | |
345 | // Re-request terminate execution interrupt to trigger later. |
346 | if (is_termination) isolate->stack_guard()->RequestTerminateExecution(); |
347 | |
348 | return maybe_result; |
349 | } |
350 | |
351 | } // namespace |
352 | |
353 | // static |
354 | MaybeHandle<Object> Execution::Call(Isolate* isolate, Handle<Object> callable, |
355 | Handle<Object> receiver, int argc, |
356 | Handle<Object> argv[]) { |
357 | return Invoke(isolate, InvokeParams::SetUpForCall(isolate, callable, receiver, |
358 | argc, argv)); |
359 | } |
360 | |
361 | // static |
362 | MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor, |
363 | int argc, Handle<Object> argv[]) { |
364 | return New(isolate, constructor, constructor, argc, argv); |
365 | } |
366 | |
367 | // static |
368 | MaybeHandle<Object> Execution::New(Isolate* isolate, Handle<Object> constructor, |
369 | Handle<Object> new_target, int argc, |
370 | Handle<Object> argv[]) { |
371 | return Invoke(isolate, InvokeParams::SetUpForNew(isolate, constructor, |
372 | new_target, argc, argv)); |
373 | } |
374 | |
375 | // static |
376 | MaybeHandle<Object> Execution::TryCall(Isolate* isolate, |
377 | Handle<Object> callable, |
378 | Handle<Object> receiver, int argc, |
379 | Handle<Object> argv[], |
380 | MessageHandling message_handling, |
381 | MaybeHandle<Object>* exception_out) { |
382 | return InvokeWithTryCatch( |
383 | isolate, |
384 | InvokeParams::SetUpForTryCall(isolate, callable, receiver, argc, argv, |
385 | message_handling, exception_out)); |
386 | } |
387 | |
388 | // static |
389 | MaybeHandle<Object> Execution::TryRunMicrotasks( |
390 | Isolate* isolate, MicrotaskQueue* microtask_queue, |
391 | MaybeHandle<Object>* exception_out) { |
392 | return InvokeWithTryCatch( |
393 | isolate, InvokeParams::SetUpForRunMicrotasks(isolate, microtask_queue, |
394 | exception_out)); |
395 | } |
396 | |
397 | void StackGuard::SetStackLimit(uintptr_t limit) { |
398 | ExecutionAccess access(isolate_); |
399 | // If the current limits are special (e.g. due to a pending interrupt) then |
400 | // leave them alone. |
401 | uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit); |
402 | if (thread_local_.jslimit() == thread_local_.real_jslimit_) { |
403 | thread_local_.set_jslimit(jslimit); |
404 | } |
405 | if (thread_local_.climit() == thread_local_.real_climit_) { |
406 | thread_local_.set_climit(limit); |
407 | } |
408 | thread_local_.real_climit_ = limit; |
409 | thread_local_.real_jslimit_ = jslimit; |
410 | } |
411 | |
412 | |
413 | void StackGuard::AdjustStackLimitForSimulator() { |
414 | ExecutionAccess access(isolate_); |
415 | uintptr_t climit = thread_local_.real_climit_; |
416 | // If the current limits are special (e.g. due to a pending interrupt) then |
417 | // leave them alone. |
418 | uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, climit); |
419 | if (thread_local_.jslimit() == thread_local_.real_jslimit_) { |
420 | thread_local_.set_jslimit(jslimit); |
421 | isolate_->heap()->SetStackLimits(); |
422 | } |
423 | } |
424 | |
425 | |
426 | void StackGuard::EnableInterrupts() { |
427 | ExecutionAccess access(isolate_); |
428 | if (has_pending_interrupts(access)) { |
429 | set_interrupt_limits(access); |
430 | } |
431 | } |
432 | |
433 | |
434 | void StackGuard::DisableInterrupts() { |
435 | ExecutionAccess access(isolate_); |
436 | reset_limits(access); |
437 | } |
438 | |
439 | void StackGuard::PushInterruptsScope(InterruptsScope* scope) { |
440 | ExecutionAccess access(isolate_); |
441 | DCHECK_NE(scope->mode_, InterruptsScope::kNoop); |
442 | if (scope->mode_ == InterruptsScope::kPostponeInterrupts) { |
443 | // Intercept already requested interrupts. |
444 | int intercepted = thread_local_.interrupt_flags_ & scope->intercept_mask_; |
445 | scope->intercepted_flags_ = intercepted; |
446 | thread_local_.interrupt_flags_ &= ~intercepted; |
447 | } else { |
448 | DCHECK_EQ(scope->mode_, InterruptsScope::kRunInterrupts); |
449 | // Restore postponed interrupts. |
450 | int restored_flags = 0; |
451 | for (InterruptsScope* current = thread_local_.interrupt_scopes_; |
452 | current != nullptr; current = current->prev_) { |
453 | restored_flags |= (current->intercepted_flags_ & scope->intercept_mask_); |
454 | current->intercepted_flags_ &= ~scope->intercept_mask_; |
455 | } |
456 | thread_local_.interrupt_flags_ |= restored_flags; |
457 | } |
458 | if (!has_pending_interrupts(access)) reset_limits(access); |
459 | // Add scope to the chain. |
460 | scope->prev_ = thread_local_.interrupt_scopes_; |
461 | thread_local_.interrupt_scopes_ = scope; |
462 | } |
463 | |
464 | void StackGuard::PopInterruptsScope() { |
465 | ExecutionAccess access(isolate_); |
466 | InterruptsScope* top = thread_local_.interrupt_scopes_; |
467 | DCHECK_NE(top->mode_, InterruptsScope::kNoop); |
468 | if (top->mode_ == InterruptsScope::kPostponeInterrupts) { |
469 | // Make intercepted interrupts active. |
470 | DCHECK_EQ(thread_local_.interrupt_flags_ & top->intercept_mask_, 0); |
471 | thread_local_.interrupt_flags_ |= top->intercepted_flags_; |
472 | } else { |
473 | DCHECK_EQ(top->mode_, InterruptsScope::kRunInterrupts); |
474 | // Postpone existing interupts if needed. |
475 | if (top->prev_) { |
476 | for (int interrupt = 1; interrupt < ALL_INTERRUPTS; |
477 | interrupt = interrupt << 1) { |
478 | InterruptFlag flag = static_cast<InterruptFlag>(interrupt); |
479 | if ((thread_local_.interrupt_flags_ & flag) && |
480 | top->prev_->Intercept(flag)) { |
481 | thread_local_.interrupt_flags_ &= ~flag; |
482 | } |
483 | } |
484 | } |
485 | } |
486 | if (has_pending_interrupts(access)) set_interrupt_limits(access); |
487 | // Remove scope from chain. |
488 | thread_local_.interrupt_scopes_ = top->prev_; |
489 | } |
490 | |
491 | |
492 | bool StackGuard::CheckInterrupt(InterruptFlag flag) { |
493 | ExecutionAccess access(isolate_); |
494 | return thread_local_.interrupt_flags_ & flag; |
495 | } |
496 | |
497 | |
498 | void StackGuard::RequestInterrupt(InterruptFlag flag) { |
499 | ExecutionAccess access(isolate_); |
500 | // Check the chain of InterruptsScope for interception. |
501 | if (thread_local_.interrupt_scopes_ && |
502 | thread_local_.interrupt_scopes_->Intercept(flag)) { |
503 | return; |
504 | } |
505 | |
506 | // Not intercepted. Set as active interrupt flag. |
507 | thread_local_.interrupt_flags_ |= flag; |
508 | set_interrupt_limits(access); |
509 | |
510 | // If this isolate is waiting in a futex, notify it to wake up. |
511 | isolate_->futex_wait_list_node()->NotifyWake(); |
512 | } |
513 | |
514 | |
515 | void StackGuard::ClearInterrupt(InterruptFlag flag) { |
516 | ExecutionAccess access(isolate_); |
517 | // Clear the interrupt flag from the chain of InterruptsScope. |
518 | for (InterruptsScope* current = thread_local_.interrupt_scopes_; |
519 | current != nullptr; current = current->prev_) { |
520 | current->intercepted_flags_ &= ~flag; |
521 | } |
522 | |
523 | // Clear the interrupt flag from the active interrupt flags. |
524 | thread_local_.interrupt_flags_ &= ~flag; |
525 | if (!has_pending_interrupts(access)) reset_limits(access); |
526 | } |
527 | |
528 | |
529 | bool StackGuard::CheckAndClearInterrupt(InterruptFlag flag) { |
530 | ExecutionAccess access(isolate_); |
531 | bool result = (thread_local_.interrupt_flags_ & flag); |
532 | thread_local_.interrupt_flags_ &= ~flag; |
533 | if (!has_pending_interrupts(access)) reset_limits(access); |
534 | return result; |
535 | } |
536 | |
537 | |
538 | char* StackGuard::ArchiveStackGuard(char* to) { |
539 | ExecutionAccess access(isolate_); |
540 | MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal)); |
541 | ThreadLocal blank; |
542 | |
543 | // Set the stack limits using the old thread_local_. |
544 | // TODO(isolates): This was the old semantics of constructing a ThreadLocal |
545 | // (as the ctor called SetStackLimits, which looked at the |
546 | // current thread_local_ from StackGuard)-- but is this |
547 | // really what was intended? |
548 | isolate_->heap()->SetStackLimits(); |
549 | thread_local_ = blank; |
550 | |
551 | return to + sizeof(ThreadLocal); |
552 | } |
553 | |
554 | |
555 | char* StackGuard::RestoreStackGuard(char* from) { |
556 | ExecutionAccess access(isolate_); |
557 | MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal)); |
558 | isolate_->heap()->SetStackLimits(); |
559 | return from + sizeof(ThreadLocal); |
560 | } |
561 | |
562 | |
563 | void StackGuard::FreeThreadResources() { |
564 | Isolate::PerIsolateThreadData* per_thread = |
565 | isolate_->FindOrAllocatePerThreadDataForThisThread(); |
566 | per_thread->set_stack_limit(thread_local_.real_climit_); |
567 | } |
568 | |
569 | |
570 | void StackGuard::ThreadLocal::Clear() { |
571 | real_jslimit_ = kIllegalLimit; |
572 | set_jslimit(kIllegalLimit); |
573 | real_climit_ = kIllegalLimit; |
574 | set_climit(kIllegalLimit); |
575 | interrupt_scopes_ = nullptr; |
576 | interrupt_flags_ = 0; |
577 | } |
578 | |
579 | |
580 | bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) { |
581 | bool should_set_stack_limits = false; |
582 | if (real_climit_ == kIllegalLimit) { |
583 | const uintptr_t kLimitSize = FLAG_stack_size * KB; |
584 | DCHECK_GT(GetCurrentStackPosition(), kLimitSize); |
585 | uintptr_t limit = GetCurrentStackPosition() - kLimitSize; |
586 | real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit); |
587 | set_jslimit(SimulatorStack::JsLimitFromCLimit(isolate, limit)); |
588 | real_climit_ = limit; |
589 | set_climit(limit); |
590 | should_set_stack_limits = true; |
591 | } |
592 | interrupt_scopes_ = nullptr; |
593 | interrupt_flags_ = 0; |
594 | return should_set_stack_limits; |
595 | } |
596 | |
597 | |
598 | void StackGuard::ClearThread(const ExecutionAccess& lock) { |
599 | thread_local_.Clear(); |
600 | isolate_->heap()->SetStackLimits(); |
601 | } |
602 | |
603 | |
604 | void StackGuard::InitThread(const ExecutionAccess& lock) { |
605 | if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits(); |
606 | Isolate::PerIsolateThreadData* per_thread = |
607 | isolate_->FindOrAllocatePerThreadDataForThisThread(); |
608 | uintptr_t stored_limit = per_thread->stack_limit(); |
609 | // You should hold the ExecutionAccess lock when you call this. |
610 | if (stored_limit != 0) { |
611 | SetStackLimit(stored_limit); |
612 | } |
613 | } |
614 | |
615 | |
616 | // --- C a l l s t o n a t i v e s --- |
617 | |
618 | Object StackGuard::HandleInterrupts() { |
619 | TRACE_EVENT0("v8.execute" , "V8.HandleInterrupts" ); |
620 | |
621 | if (FLAG_verify_predictable) { |
622 | // Advance synthetic time by making a time request. |
623 | isolate_->heap()->MonotonicallyIncreasingTimeInMs(); |
624 | } |
625 | |
626 | if (CheckAndClearInterrupt(GC_REQUEST)) { |
627 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc" ), "V8.GCHandleGCRequest" ); |
628 | isolate_->heap()->HandleGCRequest(); |
629 | } |
630 | |
631 | if (CheckAndClearInterrupt(GROW_SHARED_MEMORY)) { |
632 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm" ), |
633 | "V8.WasmGrowSharedMemory" ); |
634 | isolate_->wasm_engine()->memory_tracker()->UpdateSharedMemoryInstances( |
635 | isolate_); |
636 | } |
637 | |
638 | if (CheckAndClearInterrupt(TERMINATE_EXECUTION)) { |
639 | TRACE_EVENT0("v8.execute" , "V8.TerminateExecution" ); |
640 | return isolate_->TerminateExecution(); |
641 | } |
642 | |
643 | if (CheckAndClearInterrupt(DEOPT_MARKED_ALLOCATION_SITES)) { |
644 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc" ), |
645 | "V8.GCDeoptMarkedAllocationSites" ); |
646 | isolate_->heap()->DeoptMarkedAllocationSites(); |
647 | } |
648 | |
649 | if (CheckAndClearInterrupt(INSTALL_CODE)) { |
650 | TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile" ), |
651 | "V8.InstallOptimizedFunctions" ); |
652 | DCHECK(isolate_->concurrent_recompilation_enabled()); |
653 | isolate_->optimizing_compile_dispatcher()->InstallOptimizedFunctions(); |
654 | } |
655 | |
656 | if (CheckAndClearInterrupt(API_INTERRUPT)) { |
657 | TRACE_EVENT0("v8.execute" , "V8.InvokeApiInterruptCallbacks" ); |
658 | // Callbacks must be invoked outside of ExecusionAccess lock. |
659 | isolate_->InvokeApiInterruptCallbacks(); |
660 | } |
661 | |
662 | isolate_->counters()->stack_interrupts()->Increment(); |
663 | isolate_->counters()->runtime_profiler_ticks()->Increment(); |
664 | isolate_->runtime_profiler()->MarkCandidatesForOptimization(); |
665 | |
666 | return ReadOnlyRoots(isolate_).undefined_value(); |
667 | } |
668 | |
669 | } // namespace internal |
670 | } // namespace v8 |
671 | |