OLD | NEW |
| (Empty) |
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "api.h" | |
31 #include "bootstrapper.h" | |
32 #include "compiler.h" | |
33 #include "debug.h" | |
34 #include "execution.h" | |
35 #include "messages.h" | |
36 #include "platform.h" | |
37 #include "simulator.h" | |
38 #include "string-stream.h" | |
39 #include "vm-state-inl.h" | |
40 | |
41 | |
42 // TODO(isolates): move to isolate.cc. This stuff is kept here to | |
43 // simplify merging. | |
44 | |
45 namespace v8 { | |
46 namespace internal { | |
47 | |
48 ThreadLocalTop::ThreadLocalTop() { | |
49 InitializeInternal(); | |
50 } | |
51 | |
52 | |
53 void ThreadLocalTop::InitializeInternal() { | |
54 c_entry_fp_ = 0; | |
55 handler_ = 0; | |
56 #ifdef USE_SIMULATOR | |
57 simulator_ = NULL; | |
58 #endif | |
59 #ifdef ENABLE_LOGGING_AND_PROFILING | |
60 js_entry_sp_ = NULL; | |
61 external_callback_ = NULL; | |
62 #endif | |
63 #ifdef ENABLE_VMSTATE_TRACKING | |
64 current_vm_state_ = EXTERNAL; | |
65 #endif | |
66 try_catch_handler_address_ = NULL; | |
67 context_ = NULL; | |
68 thread_id_ = ThreadId::Invalid(); | |
69 external_caught_exception_ = false; | |
70 failed_access_check_callback_ = NULL; | |
71 save_context_ = NULL; | |
72 catcher_ = NULL; | |
73 } | |
74 | |
75 | |
76 void ThreadLocalTop::Initialize() { | |
77 InitializeInternal(); | |
78 #ifdef USE_SIMULATOR | |
79 #ifdef V8_TARGET_ARCH_ARM | |
80 simulator_ = Simulator::current(isolate_); | |
81 #elif V8_TARGET_ARCH_MIPS | |
82 simulator_ = Simulator::current(isolate_); | |
83 #endif | |
84 #endif | |
85 thread_id_ = ThreadId::Current(); | |
86 } | |
87 | |
88 | |
89 v8::TryCatch* ThreadLocalTop::TryCatchHandler() { | |
90 return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address()); | |
91 } | |
92 | |
93 | |
94 Address Isolate::get_address_from_id(Isolate::AddressId id) { | |
95 return isolate_addresses_[id]; | |
96 } | |
97 | |
98 | |
99 char* Isolate::Iterate(ObjectVisitor* v, char* thread_storage) { | |
100 ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage); | |
101 Iterate(v, thread); | |
102 return thread_storage + sizeof(ThreadLocalTop); | |
103 } | |
104 | |
105 | |
106 void Isolate::IterateThread(ThreadVisitor* v) { | |
107 v->VisitThread(this, thread_local_top()); | |
108 } | |
109 | |
110 | |
111 void Isolate::IterateThread(ThreadVisitor* v, char* t) { | |
112 ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(t); | |
113 v->VisitThread(this, thread); | |
114 } | |
115 | |
116 | |
117 void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) { | |
118 // Visit the roots from the top for a given thread. | |
119 Object* pending; | |
120 // The pending exception can sometimes be a failure. We can't show | |
121 // that to the GC, which only understands objects. | |
122 if (thread->pending_exception_->ToObject(&pending)) { | |
123 v->VisitPointer(&pending); | |
124 thread->pending_exception_ = pending; // In case GC updated it. | |
125 } | |
126 v->VisitPointer(&(thread->pending_message_obj_)); | |
127 v->VisitPointer(BitCast<Object**>(&(thread->pending_message_script_))); | |
128 v->VisitPointer(BitCast<Object**>(&(thread->context_))); | |
129 Object* scheduled; | |
130 if (thread->scheduled_exception_->ToObject(&scheduled)) { | |
131 v->VisitPointer(&scheduled); | |
132 thread->scheduled_exception_ = scheduled; | |
133 } | |
134 | |
135 for (v8::TryCatch* block = thread->TryCatchHandler(); | |
136 block != NULL; | |
137 block = TRY_CATCH_FROM_ADDRESS(block->next_)) { | |
138 v->VisitPointer(BitCast<Object**>(&(block->exception_))); | |
139 v->VisitPointer(BitCast<Object**>(&(block->message_))); | |
140 } | |
141 | |
142 // Iterate over pointers on native execution stack. | |
143 for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) { | |
144 it.frame()->Iterate(v); | |
145 } | |
146 } | |
147 | |
148 | |
149 void Isolate::Iterate(ObjectVisitor* v) { | |
150 ThreadLocalTop* current_t = thread_local_top(); | |
151 Iterate(v, current_t); | |
152 } | |
153 | |
154 | |
155 void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) { | |
156 // The ARM simulator has a separate JS stack. We therefore register | |
157 // the C++ try catch handler with the simulator and get back an | |
158 // address that can be used for comparisons with addresses into the | |
159 // JS stack. When running without the simulator, the address | |
160 // returned will be the address of the C++ try catch handler itself. | |
161 Address address = reinterpret_cast<Address>( | |
162 SimulatorStack::RegisterCTryCatch(reinterpret_cast<uintptr_t>(that))); | |
163 thread_local_top()->set_try_catch_handler_address(address); | |
164 } | |
165 | |
166 | |
167 void Isolate::UnregisterTryCatchHandler(v8::TryCatch* that) { | |
168 ASSERT(thread_local_top()->TryCatchHandler() == that); | |
169 thread_local_top()->set_try_catch_handler_address( | |
170 reinterpret_cast<Address>(that->next_)); | |
171 thread_local_top()->catcher_ = NULL; | |
172 SimulatorStack::UnregisterCTryCatch(); | |
173 } | |
174 | |
175 | |
176 Handle<String> Isolate::StackTraceString() { | |
177 if (stack_trace_nesting_level_ == 0) { | |
178 stack_trace_nesting_level_++; | |
179 HeapStringAllocator allocator; | |
180 StringStream::ClearMentionedObjectCache(); | |
181 StringStream accumulator(&allocator); | |
182 incomplete_message_ = &accumulator; | |
183 PrintStack(&accumulator); | |
184 Handle<String> stack_trace = accumulator.ToString(); | |
185 incomplete_message_ = NULL; | |
186 stack_trace_nesting_level_ = 0; | |
187 return stack_trace; | |
188 } else if (stack_trace_nesting_level_ == 1) { | |
189 stack_trace_nesting_level_++; | |
190 OS::PrintError( | |
191 "\n\nAttempt to print stack while printing stack (double fault)\n"); | |
192 OS::PrintError( | |
193 "If you are lucky you may find a partial stack dump on stdout.\n\n"); | |
194 incomplete_message_->OutputToStdOut(); | |
195 return factory()->empty_symbol(); | |
196 } else { | |
197 OS::Abort(); | |
198 // Unreachable | |
199 return factory()->empty_symbol(); | |
200 } | |
201 } | |
202 | |
203 | |
204 Handle<JSArray> Isolate::CaptureCurrentStackTrace( | |
205 int frame_limit, StackTrace::StackTraceOptions options) { | |
206 // Ensure no negative values. | |
207 int limit = Max(frame_limit, 0); | |
208 Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit); | |
209 | |
210 Handle<String> column_key = factory()->LookupAsciiSymbol("column"); | |
211 Handle<String> line_key = factory()->LookupAsciiSymbol("lineNumber"); | |
212 Handle<String> script_key = factory()->LookupAsciiSymbol("scriptName"); | |
213 Handle<String> name_or_source_url_key = | |
214 factory()->LookupAsciiSymbol("nameOrSourceURL"); | |
215 Handle<String> script_name_or_source_url_key = | |
216 factory()->LookupAsciiSymbol("scriptNameOrSourceURL"); | |
217 Handle<String> function_key = factory()->LookupAsciiSymbol("functionName"); | |
218 Handle<String> eval_key = factory()->LookupAsciiSymbol("isEval"); | |
219 Handle<String> constructor_key = | |
220 factory()->LookupAsciiSymbol("isConstructor"); | |
221 | |
222 StackTraceFrameIterator it(this); | |
223 int frames_seen = 0; | |
224 while (!it.done() && (frames_seen < limit)) { | |
225 JavaScriptFrame* frame = it.frame(); | |
226 // Set initial size to the maximum inlining level + 1 for the outermost | |
227 // function. | |
228 List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1); | |
229 frame->Summarize(&frames); | |
230 for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) { | |
231 // Create a JSObject to hold the information for the StackFrame. | |
232 Handle<JSObject> stackFrame = factory()->NewJSObject(object_function()); | |
233 | |
234 Handle<JSFunction> fun = frames[i].function(); | |
235 Handle<Script> script(Script::cast(fun->shared()->script())); | |
236 | |
237 if (options & StackTrace::kLineNumber) { | |
238 int script_line_offset = script->line_offset()->value(); | |
239 int position = frames[i].code()->SourcePosition(frames[i].pc()); | |
240 int line_number = GetScriptLineNumber(script, position); | |
241 // line_number is already shifted by the script_line_offset. | |
242 int relative_line_number = line_number - script_line_offset; | |
243 if (options & StackTrace::kColumnOffset && relative_line_number >= 0) { | |
244 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); | |
245 int start = (relative_line_number == 0) ? 0 : | |
246 Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1; | |
247 int column_offset = position - start; | |
248 if (relative_line_number == 0) { | |
249 // For the case where the code is on the same line as the script | |
250 // tag. | |
251 column_offset += script->column_offset()->value(); | |
252 } | |
253 SetLocalPropertyNoThrow(stackFrame, column_key, | |
254 Handle<Smi>(Smi::FromInt(column_offset + 1))); | |
255 } | |
256 SetLocalPropertyNoThrow(stackFrame, line_key, | |
257 Handle<Smi>(Smi::FromInt(line_number + 1))); | |
258 } | |
259 | |
260 if (options & StackTrace::kScriptName) { | |
261 Handle<Object> script_name(script->name(), this); | |
262 SetLocalPropertyNoThrow(stackFrame, script_key, script_name); | |
263 } | |
264 | |
265 if (options & StackTrace::kScriptNameOrSourceURL) { | |
266 Handle<Object> script_name(script->name(), this); | |
267 Handle<JSValue> script_wrapper = GetScriptWrapper(script); | |
268 Handle<Object> property = GetProperty(script_wrapper, | |
269 name_or_source_url_key); | |
270 ASSERT(property->IsJSFunction()); | |
271 Handle<JSFunction> method = Handle<JSFunction>::cast(property); | |
272 bool caught_exception; | |
273 Handle<Object> result = Execution::TryCall(method, script_wrapper, 0, | |
274 NULL, &caught_exception); | |
275 if (caught_exception) { | |
276 result = factory()->undefined_value(); | |
277 } | |
278 SetLocalPropertyNoThrow(stackFrame, script_name_or_source_url_key, | |
279 result); | |
280 } | |
281 | |
282 if (options & StackTrace::kFunctionName) { | |
283 Handle<Object> fun_name(fun->shared()->name(), this); | |
284 if (fun_name->ToBoolean()->IsFalse()) { | |
285 fun_name = Handle<Object>(fun->shared()->inferred_name(), this); | |
286 } | |
287 SetLocalPropertyNoThrow(stackFrame, function_key, fun_name); | |
288 } | |
289 | |
290 if (options & StackTrace::kIsEval) { | |
291 int type = Smi::cast(script->compilation_type())->value(); | |
292 Handle<Object> is_eval = (type == Script::COMPILATION_TYPE_EVAL) ? | |
293 factory()->true_value() : factory()->false_value(); | |
294 SetLocalPropertyNoThrow(stackFrame, eval_key, is_eval); | |
295 } | |
296 | |
297 if (options & StackTrace::kIsConstructor) { | |
298 Handle<Object> is_constructor = (frames[i].is_constructor()) ? | |
299 factory()->true_value() : factory()->false_value(); | |
300 SetLocalPropertyNoThrow(stackFrame, constructor_key, is_constructor); | |
301 } | |
302 | |
303 FixedArray::cast(stack_trace->elements())->set(frames_seen, *stackFrame); | |
304 frames_seen++; | |
305 } | |
306 it.Advance(); | |
307 } | |
308 | |
309 stack_trace->set_length(Smi::FromInt(frames_seen)); | |
310 return stack_trace; | |
311 } | |
312 | |
313 | |
314 void Isolate::PrintStack() { | |
315 if (stack_trace_nesting_level_ == 0) { | |
316 stack_trace_nesting_level_++; | |
317 | |
318 StringAllocator* allocator; | |
319 if (preallocated_message_space_ == NULL) { | |
320 allocator = new HeapStringAllocator(); | |
321 } else { | |
322 allocator = preallocated_message_space_; | |
323 } | |
324 | |
325 StringStream::ClearMentionedObjectCache(); | |
326 StringStream accumulator(allocator); | |
327 incomplete_message_ = &accumulator; | |
328 PrintStack(&accumulator); | |
329 accumulator.OutputToStdOut(); | |
330 accumulator.Log(); | |
331 incomplete_message_ = NULL; | |
332 stack_trace_nesting_level_ = 0; | |
333 if (preallocated_message_space_ == NULL) { | |
334 // Remove the HeapStringAllocator created above. | |
335 delete allocator; | |
336 } | |
337 } else if (stack_trace_nesting_level_ == 1) { | |
338 stack_trace_nesting_level_++; | |
339 OS::PrintError( | |
340 "\n\nAttempt to print stack while printing stack (double fault)\n"); | |
341 OS::PrintError( | |
342 "If you are lucky you may find a partial stack dump on stdout.\n\n"); | |
343 incomplete_message_->OutputToStdOut(); | |
344 } | |
345 } | |
346 | |
347 | |
348 static void PrintFrames(StringStream* accumulator, | |
349 StackFrame::PrintMode mode) { | |
350 StackFrameIterator it; | |
351 for (int i = 0; !it.done(); it.Advance()) { | |
352 it.frame()->Print(accumulator, mode, i++); | |
353 } | |
354 } | |
355 | |
356 | |
357 void Isolate::PrintStack(StringStream* accumulator) { | |
358 if (!IsInitialized()) { | |
359 accumulator->Add( | |
360 "\n==== Stack trace is not available ==========================\n\n"); | |
361 accumulator->Add( | |
362 "\n==== Isolate for the thread is not initialized =============\n\n"); | |
363 return; | |
364 } | |
365 // The MentionedObjectCache is not GC-proof at the moment. | |
366 AssertNoAllocation nogc; | |
367 ASSERT(StringStream::IsMentionedObjectCacheClear()); | |
368 | |
369 // Avoid printing anything if there are no frames. | |
370 if (c_entry_fp(thread_local_top()) == 0) return; | |
371 | |
372 accumulator->Add( | |
373 "\n==== Stack trace ============================================\n\n"); | |
374 PrintFrames(accumulator, StackFrame::OVERVIEW); | |
375 | |
376 accumulator->Add( | |
377 "\n==== Details ================================================\n\n"); | |
378 PrintFrames(accumulator, StackFrame::DETAILS); | |
379 | |
380 accumulator->PrintMentionedObjectCache(); | |
381 accumulator->Add("=====================\n\n"); | |
382 } | |
383 | |
384 | |
385 void Isolate::SetFailedAccessCheckCallback( | |
386 v8::FailedAccessCheckCallback callback) { | |
387 thread_local_top()->failed_access_check_callback_ = callback; | |
388 } | |
389 | |
390 | |
391 void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) { | |
392 if (!thread_local_top()->failed_access_check_callback_) return; | |
393 | |
394 ASSERT(receiver->IsAccessCheckNeeded()); | |
395 ASSERT(context()); | |
396 | |
397 // Get the data object from access check info. | |
398 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor()); | |
399 if (!constructor->shared()->IsApiFunction()) return; | |
400 Object* data_obj = | |
401 constructor->shared()->get_api_func_data()->access_check_info(); | |
402 if (data_obj == heap_.undefined_value()) return; | |
403 | |
404 HandleScope scope; | |
405 Handle<JSObject> receiver_handle(receiver); | |
406 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data()); | |
407 thread_local_top()->failed_access_check_callback_( | |
408 v8::Utils::ToLocal(receiver_handle), | |
409 type, | |
410 v8::Utils::ToLocal(data)); | |
411 } | |
412 | |
413 | |
414 enum MayAccessDecision { | |
415 YES, NO, UNKNOWN | |
416 }; | |
417 | |
418 | |
419 static MayAccessDecision MayAccessPreCheck(Isolate* isolate, | |
420 JSObject* receiver, | |
421 v8::AccessType type) { | |
422 // During bootstrapping, callback functions are not enabled yet. | |
423 if (isolate->bootstrapper()->IsActive()) return YES; | |
424 | |
425 if (receiver->IsJSGlobalProxy()) { | |
426 Object* receiver_context = JSGlobalProxy::cast(receiver)->context(); | |
427 if (!receiver_context->IsContext()) return NO; | |
428 | |
429 // Get the global context of current top context. | |
430 // avoid using Isolate::global_context() because it uses Handle. | |
431 Context* global_context = isolate->context()->global()->global_context(); | |
432 if (receiver_context == global_context) return YES; | |
433 | |
434 if (Context::cast(receiver_context)->security_token() == | |
435 global_context->security_token()) | |
436 return YES; | |
437 } | |
438 | |
439 return UNKNOWN; | |
440 } | |
441 | |
442 | |
443 bool Isolate::MayNamedAccess(JSObject* receiver, Object* key, | |
444 v8::AccessType type) { | |
445 ASSERT(receiver->IsAccessCheckNeeded()); | |
446 | |
447 // The callers of this method are not expecting a GC. | |
448 AssertNoAllocation no_gc; | |
449 | |
450 // Skip checks for hidden properties access. Note, we do not | |
451 // require existence of a context in this case. | |
452 if (key == heap_.hidden_symbol()) return true; | |
453 | |
454 // Check for compatibility between the security tokens in the | |
455 // current lexical context and the accessed object. | |
456 ASSERT(context()); | |
457 | |
458 MayAccessDecision decision = MayAccessPreCheck(this, receiver, type); | |
459 if (decision != UNKNOWN) return decision == YES; | |
460 | |
461 // Get named access check callback | |
462 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor()); | |
463 if (!constructor->shared()->IsApiFunction()) return false; | |
464 | |
465 Object* data_obj = | |
466 constructor->shared()->get_api_func_data()->access_check_info(); | |
467 if (data_obj == heap_.undefined_value()) return false; | |
468 | |
469 Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback(); | |
470 v8::NamedSecurityCallback callback = | |
471 v8::ToCData<v8::NamedSecurityCallback>(fun_obj); | |
472 | |
473 if (!callback) return false; | |
474 | |
475 HandleScope scope(this); | |
476 Handle<JSObject> receiver_handle(receiver, this); | |
477 Handle<Object> key_handle(key, this); | |
478 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this); | |
479 LOG(this, ApiNamedSecurityCheck(key)); | |
480 bool result = false; | |
481 { | |
482 // Leaving JavaScript. | |
483 VMState state(this, EXTERNAL); | |
484 result = callback(v8::Utils::ToLocal(receiver_handle), | |
485 v8::Utils::ToLocal(key_handle), | |
486 type, | |
487 v8::Utils::ToLocal(data)); | |
488 } | |
489 return result; | |
490 } | |
491 | |
492 | |
493 bool Isolate::MayIndexedAccess(JSObject* receiver, | |
494 uint32_t index, | |
495 v8::AccessType type) { | |
496 ASSERT(receiver->IsAccessCheckNeeded()); | |
497 // Check for compatibility between the security tokens in the | |
498 // current lexical context and the accessed object. | |
499 ASSERT(context()); | |
500 | |
501 MayAccessDecision decision = MayAccessPreCheck(this, receiver, type); | |
502 if (decision != UNKNOWN) return decision == YES; | |
503 | |
504 // Get indexed access check callback | |
505 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor()); | |
506 if (!constructor->shared()->IsApiFunction()) return false; | |
507 | |
508 Object* data_obj = | |
509 constructor->shared()->get_api_func_data()->access_check_info(); | |
510 if (data_obj == heap_.undefined_value()) return false; | |
511 | |
512 Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback(); | |
513 v8::IndexedSecurityCallback callback = | |
514 v8::ToCData<v8::IndexedSecurityCallback>(fun_obj); | |
515 | |
516 if (!callback) return false; | |
517 | |
518 HandleScope scope(this); | |
519 Handle<JSObject> receiver_handle(receiver, this); | |
520 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this); | |
521 LOG(this, ApiIndexedSecurityCheck(index)); | |
522 bool result = false; | |
523 { | |
524 // Leaving JavaScript. | |
525 VMState state(this, EXTERNAL); | |
526 result = callback(v8::Utils::ToLocal(receiver_handle), | |
527 index, | |
528 type, | |
529 v8::Utils::ToLocal(data)); | |
530 } | |
531 return result; | |
532 } | |
533 | |
534 | |
535 const char* const Isolate::kStackOverflowMessage = | |
536 "Uncaught RangeError: Maximum call stack size exceeded"; | |
537 | |
538 | |
539 Failure* Isolate::StackOverflow() { | |
540 HandleScope scope; | |
541 Handle<String> key = factory()->stack_overflow_symbol(); | |
542 Handle<JSObject> boilerplate = | |
543 Handle<JSObject>::cast(GetProperty(js_builtins_object(), key)); | |
544 Handle<Object> exception = Copy(boilerplate); | |
545 // TODO(1240995): To avoid having to call JavaScript code to compute | |
546 // the message for stack overflow exceptions which is very likely to | |
547 // double fault with another stack overflow exception, we use a | |
548 // precomputed message. | |
549 DoThrow(*exception, NULL); | |
550 return Failure::Exception(); | |
551 } | |
552 | |
553 | |
554 Failure* Isolate::TerminateExecution() { | |
555 DoThrow(heap_.termination_exception(), NULL); | |
556 return Failure::Exception(); | |
557 } | |
558 | |
559 | |
560 Failure* Isolate::Throw(Object* exception, MessageLocation* location) { | |
561 DoThrow(exception, location); | |
562 return Failure::Exception(); | |
563 } | |
564 | |
565 | |
566 Failure* Isolate::ReThrow(MaybeObject* exception, MessageLocation* location) { | |
567 bool can_be_caught_externally = false; | |
568 ShouldReportException(&can_be_caught_externally, | |
569 is_catchable_by_javascript(exception)); | |
570 thread_local_top()->catcher_ = can_be_caught_externally ? | |
571 try_catch_handler() : NULL; | |
572 | |
573 // Set the exception being re-thrown. | |
574 set_pending_exception(exception); | |
575 return Failure::Exception(); | |
576 } | |
577 | |
578 | |
579 Failure* Isolate::ThrowIllegalOperation() { | |
580 return Throw(heap_.illegal_access_symbol()); | |
581 } | |
582 | |
583 | |
584 void Isolate::ScheduleThrow(Object* exception) { | |
585 // When scheduling a throw we first throw the exception to get the | |
586 // error reporting if it is uncaught before rescheduling it. | |
587 Throw(exception); | |
588 thread_local_top()->scheduled_exception_ = pending_exception(); | |
589 thread_local_top()->external_caught_exception_ = false; | |
590 clear_pending_exception(); | |
591 } | |
592 | |
593 | |
594 Failure* Isolate::PromoteScheduledException() { | |
595 MaybeObject* thrown = scheduled_exception(); | |
596 clear_scheduled_exception(); | |
597 // Re-throw the exception to avoid getting repeated error reporting. | |
598 return ReThrow(thrown); | |
599 } | |
600 | |
601 | |
602 void Isolate::PrintCurrentStackTrace(FILE* out) { | |
603 StackTraceFrameIterator it(this); | |
604 while (!it.done()) { | |
605 HandleScope scope; | |
606 // Find code position if recorded in relocation info. | |
607 JavaScriptFrame* frame = it.frame(); | |
608 int pos = frame->LookupCode()->SourcePosition(frame->pc()); | |
609 Handle<Object> pos_obj(Smi::FromInt(pos)); | |
610 // Fetch function and receiver. | |
611 Handle<JSFunction> fun(JSFunction::cast(frame->function())); | |
612 Handle<Object> recv(frame->receiver()); | |
613 // Advance to the next JavaScript frame and determine if the | |
614 // current frame is the top-level frame. | |
615 it.Advance(); | |
616 Handle<Object> is_top_level = it.done() | |
617 ? factory()->true_value() | |
618 : factory()->false_value(); | |
619 // Generate and print stack trace line. | |
620 Handle<String> line = | |
621 Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level); | |
622 if (line->length() > 0) { | |
623 line->PrintOn(out); | |
624 fprintf(out, "\n"); | |
625 } | |
626 } | |
627 } | |
628 | |
629 | |
630 void Isolate::ComputeLocation(MessageLocation* target) { | |
631 *target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1); | |
632 StackTraceFrameIterator it(this); | |
633 if (!it.done()) { | |
634 JavaScriptFrame* frame = it.frame(); | |
635 JSFunction* fun = JSFunction::cast(frame->function()); | |
636 Object* script = fun->shared()->script(); | |
637 if (script->IsScript() && | |
638 !(Script::cast(script)->source()->IsUndefined())) { | |
639 int pos = frame->LookupCode()->SourcePosition(frame->pc()); | |
640 // Compute the location from the function and the reloc info. | |
641 Handle<Script> casted_script(Script::cast(script)); | |
642 *target = MessageLocation(casted_script, pos, pos + 1); | |
643 } | |
644 } | |
645 } | |
646 | |
647 | |
648 bool Isolate::ShouldReportException(bool* can_be_caught_externally, | |
649 bool catchable_by_javascript) { | |
650 // Find the top-most try-catch handler. | |
651 StackHandler* handler = | |
652 StackHandler::FromAddress(Isolate::handler(thread_local_top())); | |
653 while (handler != NULL && !handler->is_try_catch()) { | |
654 handler = handler->next(); | |
655 } | |
656 | |
657 // Get the address of the external handler so we can compare the address to | |
658 // determine which one is closer to the top of the stack. | |
659 Address external_handler_address = | |
660 thread_local_top()->try_catch_handler_address(); | |
661 | |
662 // The exception has been externally caught if and only if there is | |
663 // an external handler which is on top of the top-most try-catch | |
664 // handler. | |
665 *can_be_caught_externally = external_handler_address != NULL && | |
666 (handler == NULL || handler->address() > external_handler_address || | |
667 !catchable_by_javascript); | |
668 | |
669 if (*can_be_caught_externally) { | |
670 // Only report the exception if the external handler is verbose. | |
671 return try_catch_handler()->is_verbose_; | |
672 } else { | |
673 // Report the exception if it isn't caught by JavaScript code. | |
674 return handler == NULL; | |
675 } | |
676 } | |
677 | |
678 | |
679 void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) { | |
680 ASSERT(!has_pending_exception()); | |
681 | |
682 HandleScope scope; | |
683 Object* exception_object = Smi::FromInt(0); | |
684 bool is_object = exception->ToObject(&exception_object); | |
685 Handle<Object> exception_handle(exception_object); | |
686 | |
687 // Determine reporting and whether the exception is caught externally. | |
688 bool catchable_by_javascript = is_catchable_by_javascript(exception); | |
689 // Only real objects can be caught by JS. | |
690 ASSERT(!catchable_by_javascript || is_object); | |
691 bool can_be_caught_externally = false; | |
692 bool should_report_exception = | |
693 ShouldReportException(&can_be_caught_externally, catchable_by_javascript); | |
694 bool report_exception = catchable_by_javascript && should_report_exception; | |
695 | |
696 #ifdef ENABLE_DEBUGGER_SUPPORT | |
697 // Notify debugger of exception. | |
698 if (catchable_by_javascript) { | |
699 debugger_->OnException(exception_handle, report_exception); | |
700 } | |
701 #endif | |
702 | |
703 // Generate the message. | |
704 Handle<Object> message_obj; | |
705 MessageLocation potential_computed_location; | |
706 bool try_catch_needs_message = | |
707 can_be_caught_externally && | |
708 try_catch_handler()->capture_message_; | |
709 if (report_exception || try_catch_needs_message) { | |
710 if (location == NULL) { | |
711 // If no location was specified we use a computed one instead | |
712 ComputeLocation(&potential_computed_location); | |
713 location = &potential_computed_location; | |
714 } | |
715 if (!bootstrapper()->IsActive()) { | |
716 // It's not safe to try to make message objects or collect stack | |
717 // traces while the bootstrapper is active since the infrastructure | |
718 // may not have been properly initialized. | |
719 Handle<String> stack_trace; | |
720 if (FLAG_trace_exception) stack_trace = StackTraceString(); | |
721 Handle<JSArray> stack_trace_object; | |
722 if (report_exception && capture_stack_trace_for_uncaught_exceptions_) { | |
723 stack_trace_object = CaptureCurrentStackTrace( | |
724 stack_trace_for_uncaught_exceptions_frame_limit_, | |
725 stack_trace_for_uncaught_exceptions_options_); | |
726 } | |
727 ASSERT(is_object); // Can't use the handle unless there's a real object. | |
728 message_obj = MessageHandler::MakeMessageObject("uncaught_exception", | |
729 location, HandleVector<Object>(&exception_handle, 1), stack_trace, | |
730 stack_trace_object); | |
731 } | |
732 } | |
733 | |
734 // Save the message for reporting if the the exception remains uncaught. | |
735 thread_local_top()->has_pending_message_ = report_exception; | |
736 if (!message_obj.is_null()) { | |
737 thread_local_top()->pending_message_obj_ = *message_obj; | |
738 if (location != NULL) { | |
739 thread_local_top()->pending_message_script_ = *location->script(); | |
740 thread_local_top()->pending_message_start_pos_ = location->start_pos(); | |
741 thread_local_top()->pending_message_end_pos_ = location->end_pos(); | |
742 } | |
743 } | |
744 | |
745 // Do not forget to clean catcher_ if currently thrown exception cannot | |
746 // be caught. If necessary, ReThrow will update the catcher. | |
747 thread_local_top()->catcher_ = can_be_caught_externally ? | |
748 try_catch_handler() : NULL; | |
749 | |
750 // NOTE: Notifying the debugger or generating the message | |
751 // may have caused new exceptions. For now, we just ignore | |
752 // that and set the pending exception to the original one. | |
753 if (is_object) { | |
754 set_pending_exception(*exception_handle); | |
755 } else { | |
756 // Failures are not on the heap so they neither need nor work with handles. | |
757 ASSERT(exception_handle->IsFailure()); | |
758 set_pending_exception(exception); | |
759 } | |
760 } | |
761 | |
762 | |
763 bool Isolate::IsExternallyCaught() { | |
764 ASSERT(has_pending_exception()); | |
765 | |
766 if ((thread_local_top()->catcher_ == NULL) || | |
767 (try_catch_handler() != thread_local_top()->catcher_)) { | |
768 // When throwing the exception, we found no v8::TryCatch | |
769 // which should care about this exception. | |
770 return false; | |
771 } | |
772 | |
773 if (!is_catchable_by_javascript(pending_exception())) { | |
774 return true; | |
775 } | |
776 | |
777 // Get the address of the external handler so we can compare the address to | |
778 // determine which one is closer to the top of the stack. | |
779 Address external_handler_address = | |
780 thread_local_top()->try_catch_handler_address(); | |
781 ASSERT(external_handler_address != NULL); | |
782 | |
783 // The exception has been externally caught if and only if there is | |
784 // an external handler which is on top of the top-most try-finally | |
785 // handler. | |
786 // There should be no try-catch blocks as they would prohibit us from | |
787 // finding external catcher in the first place (see catcher_ check above). | |
788 // | |
789 // Note, that finally clause would rethrow an exception unless it's | |
790 // aborted by jumps in control flow like return, break, etc. and we'll | |
791 // have another chances to set proper v8::TryCatch. | |
792 StackHandler* handler = | |
793 StackHandler::FromAddress(Isolate::handler(thread_local_top())); | |
794 while (handler != NULL && handler->address() < external_handler_address) { | |
795 ASSERT(!handler->is_try_catch()); | |
796 if (handler->is_try_finally()) return false; | |
797 | |
798 handler = handler->next(); | |
799 } | |
800 | |
801 return true; | |
802 } | |
803 | |
804 | |
805 void Isolate::ReportPendingMessages() { | |
806 ASSERT(has_pending_exception()); | |
807 PropagatePendingExceptionToExternalTryCatch(); | |
808 | |
809 // If the pending exception is OutOfMemoryException set out_of_memory in | |
810 // the global context. Note: We have to mark the global context here | |
811 // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to | |
812 // set it. | |
813 HandleScope scope; | |
814 if (thread_local_top_.pending_exception_ == Failure::OutOfMemoryException()) { | |
815 context()->mark_out_of_memory(); | |
816 } else if (thread_local_top_.pending_exception_ == | |
817 heap()->termination_exception()) { | |
818 // Do nothing: if needed, the exception has been already propagated to | |
819 // v8::TryCatch. | |
820 } else { | |
821 if (thread_local_top_.has_pending_message_) { | |
822 thread_local_top_.has_pending_message_ = false; | |
823 if (!thread_local_top_.pending_message_obj_->IsTheHole()) { | |
824 HandleScope scope; | |
825 Handle<Object> message_obj(thread_local_top_.pending_message_obj_); | |
826 if (thread_local_top_.pending_message_script_ != NULL) { | |
827 Handle<Script> script(thread_local_top_.pending_message_script_); | |
828 int start_pos = thread_local_top_.pending_message_start_pos_; | |
829 int end_pos = thread_local_top_.pending_message_end_pos_; | |
830 MessageLocation location(script, start_pos, end_pos); | |
831 MessageHandler::ReportMessage(this, &location, message_obj); | |
832 } else { | |
833 MessageHandler::ReportMessage(this, NULL, message_obj); | |
834 } | |
835 } | |
836 } | |
837 } | |
838 clear_pending_message(); | |
839 } | |
840 | |
841 | |
842 void Isolate::TraceException(bool flag) { | |
843 FLAG_trace_exception = flag; // TODO(isolates): This is an unfortunate use. | |
844 } | |
845 | |
846 | |
847 bool Isolate::OptionalRescheduleException(bool is_bottom_call) { | |
848 ASSERT(has_pending_exception()); | |
849 PropagatePendingExceptionToExternalTryCatch(); | |
850 | |
851 // Allways reschedule out of memory exceptions. | |
852 if (!is_out_of_memory()) { | |
853 bool is_termination_exception = | |
854 pending_exception() == heap_.termination_exception(); | |
855 | |
856 // Do not reschedule the exception if this is the bottom call. | |
857 bool clear_exception = is_bottom_call; | |
858 | |
859 if (is_termination_exception) { | |
860 if (is_bottom_call) { | |
861 thread_local_top()->external_caught_exception_ = false; | |
862 clear_pending_exception(); | |
863 return false; | |
864 } | |
865 } else if (thread_local_top()->external_caught_exception_) { | |
866 // If the exception is externally caught, clear it if there are no | |
867 // JavaScript frames on the way to the C++ frame that has the | |
868 // external handler. | |
869 ASSERT(thread_local_top()->try_catch_handler_address() != NULL); | |
870 Address external_handler_address = | |
871 thread_local_top()->try_catch_handler_address(); | |
872 JavaScriptFrameIterator it; | |
873 if (it.done() || (it.frame()->sp() > external_handler_address)) { | |
874 clear_exception = true; | |
875 } | |
876 } | |
877 | |
878 // Clear the exception if needed. | |
879 if (clear_exception) { | |
880 thread_local_top()->external_caught_exception_ = false; | |
881 clear_pending_exception(); | |
882 return false; | |
883 } | |
884 } | |
885 | |
886 // Reschedule the exception. | |
887 thread_local_top()->scheduled_exception_ = pending_exception(); | |
888 clear_pending_exception(); | |
889 return true; | |
890 } | |
891 | |
892 | |
893 void Isolate::SetCaptureStackTraceForUncaughtExceptions( | |
894 bool capture, | |
895 int frame_limit, | |
896 StackTrace::StackTraceOptions options) { | |
897 capture_stack_trace_for_uncaught_exceptions_ = capture; | |
898 stack_trace_for_uncaught_exceptions_frame_limit_ = frame_limit; | |
899 stack_trace_for_uncaught_exceptions_options_ = options; | |
900 } | |
901 | |
902 | |
903 bool Isolate::is_out_of_memory() { | |
904 if (has_pending_exception()) { | |
905 MaybeObject* e = pending_exception(); | |
906 if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) { | |
907 return true; | |
908 } | |
909 } | |
910 if (has_scheduled_exception()) { | |
911 MaybeObject* e = scheduled_exception(); | |
912 if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) { | |
913 return true; | |
914 } | |
915 } | |
916 return false; | |
917 } | |
918 | |
919 | |
920 Handle<Context> Isolate::global_context() { | |
921 GlobalObject* global = thread_local_top()->context_->global(); | |
922 return Handle<Context>(global->global_context()); | |
923 } | |
924 | |
925 | |
926 Handle<Context> Isolate::GetCallingGlobalContext() { | |
927 JavaScriptFrameIterator it; | |
928 #ifdef ENABLE_DEBUGGER_SUPPORT | |
929 if (debug_->InDebugger()) { | |
930 while (!it.done()) { | |
931 JavaScriptFrame* frame = it.frame(); | |
932 Context* context = Context::cast(frame->context()); | |
933 if (context->global_context() == *debug_->debug_context()) { | |
934 it.Advance(); | |
935 } else { | |
936 break; | |
937 } | |
938 } | |
939 } | |
940 #endif // ENABLE_DEBUGGER_SUPPORT | |
941 if (it.done()) return Handle<Context>::null(); | |
942 JavaScriptFrame* frame = it.frame(); | |
943 Context* context = Context::cast(frame->context()); | |
944 return Handle<Context>(context->global_context()); | |
945 } | |
946 | |
947 | |
948 char* Isolate::ArchiveThread(char* to) { | |
949 if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) { | |
950 RuntimeProfiler::IsolateExitedJS(this); | |
951 } | |
952 memcpy(to, reinterpret_cast<char*>(thread_local_top()), | |
953 sizeof(ThreadLocalTop)); | |
954 InitializeThreadLocal(); | |
955 return to + sizeof(ThreadLocalTop); | |
956 } | |
957 | |
958 | |
959 char* Isolate::RestoreThread(char* from) { | |
960 memcpy(reinterpret_cast<char*>(thread_local_top()), from, | |
961 sizeof(ThreadLocalTop)); | |
962 // This might be just paranoia, but it seems to be needed in case a | |
963 // thread_local_top_ is restored on a separate OS thread. | |
964 #ifdef USE_SIMULATOR | |
965 #ifdef V8_TARGET_ARCH_ARM | |
966 thread_local_top()->simulator_ = Simulator::current(this); | |
967 #elif V8_TARGET_ARCH_MIPS | |
968 thread_local_top()->simulator_ = Simulator::current(this); | |
969 #endif | |
970 #endif | |
971 if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) { | |
972 RuntimeProfiler::IsolateEnteredJS(this); | |
973 } | |
974 return from + sizeof(ThreadLocalTop); | |
975 } | |
976 | |
977 } } // namespace v8::internal | |
OLD | NEW |