blob: 7e239ec83fd2233655f0d92c412a0f0a95d3e388 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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 "debug.h"
33#include "execution.h"
34#include "string-stream.h"
35#include "platform.h"
36
37namespace v8 { namespace internal {
38
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039ThreadLocalTop Top::thread_local_;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000040Mutex* Top::break_access_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000041StackFrame::Id Top::break_frame_id_;
42int Top::break_count_;
43int Top::break_id_;
44
kasper.lundaf4734f2008-07-28 12:50:18 +000045NoAllocationStringAllocator* preallocated_message_space = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000046
47Address top_addresses[] = {
48#define C(name) reinterpret_cast<Address>(Top::name()),
49 TOP_ADDRESS_LIST(C)
50#undef C
51 NULL
52};
53
54Address Top::get_address_from_id(Top::AddressId id) {
55 return top_addresses[id];
56}
57
58char* Top::Iterate(ObjectVisitor* v, char* thread_storage) {
59 ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage);
60 Iterate(v, thread);
61 return thread_storage + sizeof(ThreadLocalTop);
62}
63
64
65#define VISIT(field) v->VisitPointer(reinterpret_cast<Object**>(&(field)));
66
67void Top::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000068 v->VisitPointer(&(thread->pending_exception_));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000069 v->VisitPointer(bit_cast<Object**, Context**>(&(thread->context_)));
70 v->VisitPointer(&(thread->scheduled_exception_));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000071
72 for (v8::TryCatch* block = thread->try_catch_handler_;
73 block != NULL;
74 block = block->next_) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000075 v->VisitPointer(bit_cast<Object**, void**>(&(block->exception_)));
76 v->VisitPointer(bit_cast<Object**, void**>(&(block->message_)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000077 }
78
79 // Iterate over pointers on native execution stack.
80 for (StackFrameIterator it(thread); !it.done(); it.Advance()) {
81 it.frame()->Iterate(v);
82 }
83}
84#undef VISIT
85
86
87void Top::Iterate(ObjectVisitor* v) {
88 ThreadLocalTop* current_t = &thread_local_;
89 Iterate(v, current_t);
90}
91
92
93void Top::InitializeThreadLocal() {
94 thread_local_.c_entry_fp_ = 0;
95 thread_local_.handler_ = 0;
96 thread_local_.stack_is_cooked_ = false;
97 thread_local_.try_catch_handler_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098 thread_local_.context_ = NULL;
99 thread_local_.external_caught_exception_ = false;
100 thread_local_.failed_access_check_callback_ = NULL;
101 clear_pending_exception();
102 clear_scheduled_exception();
103 thread_local_.save_context_ = NULL;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000104 thread_local_.catcher_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105}
106
107
kasper.lundaf4734f2008-07-28 12:50:18 +0000108// Create a dummy thread that will wait forever on a semaphore. The only
109// purpose for this thread is to have some stack area to save essential data
110// into for use by a stacks only core dump (aka minidump).
111class PreallocatedMemoryThread: public Thread {
112 public:
113 PreallocatedMemoryThread() : keep_running_(true) {
114 wait_for_ever_semaphore_ = OS::CreateSemaphore(0);
115 data_ready_semaphore_ = OS::CreateSemaphore(0);
116 }
117
118 // When the thread starts running it will allocate a fixed number of bytes
119 // on the stack and publish the location of this memory for others to use.
120 void Run() {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000121 EmbeddedVector<char, 32 * 1024> local_buffer;
kasper.lundaf4734f2008-07-28 12:50:18 +0000122
123 // Initialize the buffer with a known good value.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000124 OS::StrNCpy(local_buffer, "Trace data was not generated.\n",
125 local_buffer.length());
kasper.lundaf4734f2008-07-28 12:50:18 +0000126
127 // Publish the local buffer and signal its availability.
128 data_ = &local_buffer[0];
129 length_ = sizeof(local_buffer);
130 data_ready_semaphore_->Signal();
131
132 while (keep_running_) {
133 // This thread will wait here until the end of time.
134 wait_for_ever_semaphore_->Wait();
135 }
136
137 // Make sure we access the buffer after the wait to remove all possibility
138 // of it being optimized away.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000139 OS::StrNCpy(local_buffer, "PreallocatedMemoryThread shutting down.\n",
140 local_buffer.length());
kasper.lundaf4734f2008-07-28 12:50:18 +0000141 }
142
143 static char* data() {
144 if (data_ready_semaphore_ != NULL) {
145 // Initial access is guarded until the data has been published.
146 data_ready_semaphore_->Wait();
147 delete data_ready_semaphore_;
148 data_ready_semaphore_ = NULL;
149 }
150 return data_;
151 }
152
153 static unsigned length() {
154 if (data_ready_semaphore_ != NULL) {
155 // Initial access is guarded until the data has been published.
156 data_ready_semaphore_->Wait();
157 delete data_ready_semaphore_;
158 data_ready_semaphore_ = NULL;
159 }
160 return length_;
161 }
162
163 static void StartThread() {
164 if (the_thread_ != NULL) return;
165
166 the_thread_ = new PreallocatedMemoryThread();
167 the_thread_->Start();
168 }
169
170 // Stop the PreallocatedMemoryThread and release its resources.
171 static void StopThread() {
172 if (the_thread_ == NULL) return;
173
174 the_thread_->keep_running_ = false;
175 wait_for_ever_semaphore_->Signal();
176
177 // Wait for the thread to terminate.
178 the_thread_->Join();
179
180 if (data_ready_semaphore_ != NULL) {
181 delete data_ready_semaphore_;
182 data_ready_semaphore_ = NULL;
183 }
184
185 delete wait_for_ever_semaphore_;
186 wait_for_ever_semaphore_ = NULL;
187
188 // Done with the thread entirely.
189 delete the_thread_;
190 the_thread_ = NULL;
191 }
192
193 private:
194 // Used to make sure that the thread keeps looping even for spurious wakeups.
195 bool keep_running_;
196
197 // The preallocated memory thread singleton.
198 static PreallocatedMemoryThread* the_thread_;
199 // This semaphore is used by the PreallocatedMemoryThread to wait for ever.
200 static Semaphore* wait_for_ever_semaphore_;
201 // Semaphore to signal that the data has been initialized.
202 static Semaphore* data_ready_semaphore_;
203
204 // Location and size of the preallocated memory block.
205 static char* data_;
206 static unsigned length_;
207
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000208 DISALLOW_COPY_AND_ASSIGN(PreallocatedMemoryThread);
kasper.lundaf4734f2008-07-28 12:50:18 +0000209};
210
211PreallocatedMemoryThread* PreallocatedMemoryThread::the_thread_ = NULL;
212Semaphore* PreallocatedMemoryThread::wait_for_ever_semaphore_ = NULL;
213Semaphore* PreallocatedMemoryThread::data_ready_semaphore_ = NULL;
214char* PreallocatedMemoryThread::data_ = NULL;
215unsigned PreallocatedMemoryThread::length_ = 0;
216
217static bool initialized = false;
218
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219void Top::Initialize() {
kasper.lundaf4734f2008-07-28 12:50:18 +0000220 CHECK(!initialized);
221
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000222 InitializeThreadLocal();
223
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000224 break_access_ = OS::CreateMutex();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000225 break_frame_id_ = StackFrame::NO_ID;
226 break_count_ = 0;
227 break_id_ = 0;
228
kasper.lundaf4734f2008-07-28 12:50:18 +0000229 // Only preallocate on the first initialization.
230 if (FLAG_preallocate_message_memory && (preallocated_message_space == NULL)) {
231 // Start the thread which will set aside some memory.
232 PreallocatedMemoryThread::StartThread();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000233 preallocated_message_space =
kasper.lundaf4734f2008-07-28 12:50:18 +0000234 new NoAllocationStringAllocator(PreallocatedMemoryThread::data(),
235 PreallocatedMemoryThread::length());
236 PreallocatedStorage::Init(PreallocatedMemoryThread::length() / 4);
237 }
238 initialized = true;
239}
240
241
242void Top::TearDown() {
243 if (initialized) {
244 // Remove the external reference to the preallocated stack memory.
245 if (preallocated_message_space != NULL) {
246 delete preallocated_message_space;
247 preallocated_message_space = NULL;
248 }
249
250 PreallocatedMemoryThread::StopThread();
251 initialized = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000252 }
253}
254
255
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000256// There are cases where the C stack is separated from JS stack (ARM simulator).
257// To figure out the order of top-most JS try-catch handler and the top-most C
258// try-catch handler, the C try-catch handler keeps a reference to the top-most
259// JS try_catch handler when it was created.
260//
261// Here is a picture to explain the idea:
262// Top::thread_local_.handler_ Top::thread_local_.try_catch_handler_
263//
264// | |
265// v v
266//
267// | JS handler | | C try_catch handler |
268// | next |--+ +-------- | js_handler_ |
269// | | | next_ |--+
270// | | |
271// | JS handler |--+ <---------+ |
272// | next |
273//
274// If the top-most JS try-catch handler is not equal to
275// Top::thread_local_.try_catch_handler_.js_handler_, it means the JS handler
276// is on the top. Otherwise, it means the C try-catch handler is on the top.
277//
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000278void Top::RegisterTryCatchHandler(v8::TryCatch* that) {
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000279 that->js_handler_ = thread_local_.handler_; // casted to void*
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000280 thread_local_.try_catch_handler_ = that;
281}
282
283
284void Top::UnregisterTryCatchHandler(v8::TryCatch* that) {
285 ASSERT(thread_local_.try_catch_handler_ == that);
286 thread_local_.try_catch_handler_ = that->next_;
287}
288
289
290void Top::new_break(StackFrame::Id break_frame_id) {
291 ExecutionAccess access;
292 break_frame_id_ = break_frame_id;
293 break_id_ = ++break_count_;
294}
295
296
297void Top::set_break(StackFrame::Id break_frame_id, int break_id) {
298 ExecutionAccess access;
299 break_frame_id_ = break_frame_id;
300 break_id_ = break_id;
301}
302
303
304bool Top::check_break(int break_id) {
305 ExecutionAccess access;
306 return break_id == break_id_;
307}
308
309
310bool Top::is_break() {
311 ExecutionAccess access;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 return break_id_ != 0;
313}
314
kasper.lund7276f142008-07-30 08:49:36 +0000315
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316StackFrame::Id Top::break_frame_id() {
317 ExecutionAccess access;
318 return break_frame_id_;
319}
320
321
322int Top::break_id() {
323 ExecutionAccess access;
324 return break_id_;
325}
326
327
328void Top::MarkCompactPrologue() {
329 MarkCompactPrologue(&thread_local_);
330}
331
332
333void Top::MarkCompactPrologue(char* data) {
334 MarkCompactPrologue(reinterpret_cast<ThreadLocalTop*>(data));
335}
336
337
338void Top::MarkCompactPrologue(ThreadLocalTop* thread) {
339 StackFrame::CookFramesForThread(thread);
340}
341
342
343void Top::MarkCompactEpilogue(char* data) {
344 MarkCompactEpilogue(reinterpret_cast<ThreadLocalTop*>(data));
345}
346
347
348void Top::MarkCompactEpilogue() {
349 MarkCompactEpilogue(&thread_local_);
350}
351
352
353void Top::MarkCompactEpilogue(ThreadLocalTop* thread) {
354 StackFrame::UncookFramesForThread(thread);
355}
356
357
358static int stack_trace_nesting_level = 0;
359static StringStream* incomplete_message = NULL;
360
361
362Handle<String> Top::StackTrace() {
363 if (stack_trace_nesting_level == 0) {
364 stack_trace_nesting_level++;
365 HeapStringAllocator allocator;
366 StringStream::ClearMentionedObjectCache();
367 StringStream accumulator(&allocator);
368 incomplete_message = &accumulator;
369 PrintStack(&accumulator);
370 Handle<String> stack_trace = accumulator.ToString();
371 incomplete_message = NULL;
372 stack_trace_nesting_level = 0;
373 return stack_trace;
374 } else if (stack_trace_nesting_level == 1) {
375 stack_trace_nesting_level++;
376 OS::PrintError(
377 "\n\nAttempt to print stack while printing stack (double fault)\n");
378 OS::PrintError(
379 "If you are lucky you may find a partial stack dump on stdout.\n\n");
380 incomplete_message->OutputToStdOut();
381 return Factory::empty_symbol();
382 } else {
383 OS::Abort();
384 // Unreachable
385 return Factory::empty_symbol();
386 }
387}
388
389
390void Top::PrintStack() {
391 if (stack_trace_nesting_level == 0) {
392 stack_trace_nesting_level++;
393
394 StringAllocator* allocator;
kasper.lundaf4734f2008-07-28 12:50:18 +0000395 if (preallocated_message_space == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396 allocator = new HeapStringAllocator();
397 } else {
398 allocator = preallocated_message_space;
399 }
400
401 NativeAllocationChecker allocation_checker(
kasper.lundaf4734f2008-07-28 12:50:18 +0000402 !FLAG_preallocate_message_memory ?
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000403 NativeAllocationChecker::ALLOW :
404 NativeAllocationChecker::DISALLOW);
405
406 StringStream::ClearMentionedObjectCache();
407 StringStream accumulator(allocator);
408 incomplete_message = &accumulator;
409 PrintStack(&accumulator);
410 accumulator.OutputToStdOut();
411 accumulator.Log();
412 incomplete_message = NULL;
413 stack_trace_nesting_level = 0;
kasper.lundaf4734f2008-07-28 12:50:18 +0000414 if (preallocated_message_space == NULL) {
415 // Remove the HeapStringAllocator created above.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416 delete allocator;
417 }
418 } else if (stack_trace_nesting_level == 1) {
419 stack_trace_nesting_level++;
420 OS::PrintError(
421 "\n\nAttempt to print stack while printing stack (double fault)\n");
422 OS::PrintError(
423 "If you are lucky you may find a partial stack dump on stdout.\n\n");
424 incomplete_message->OutputToStdOut();
425 }
426}
427
428
429static void PrintFrames(StringStream* accumulator,
430 StackFrame::PrintMode mode) {
431 StackFrameIterator it;
432 for (int i = 0; !it.done(); it.Advance()) {
433 it.frame()->Print(accumulator, mode, i++);
434 }
435}
436
437
438void Top::PrintStack(StringStream* accumulator) {
439 // The MentionedObjectCache is not GC-proof at the moment.
440 AssertNoAllocation nogc;
441 ASSERT(StringStream::IsMentionedObjectCacheClear());
442
443 // Avoid printing anything if there are no frames.
444 if (c_entry_fp(GetCurrentThread()) == 0) return;
445
446 accumulator->Add(
447 "\n==== Stack trace ============================================\n\n");
448 PrintFrames(accumulator, StackFrame::OVERVIEW);
449
450 accumulator->Add(
451 "\n==== Details ================================================\n\n");
452 PrintFrames(accumulator, StackFrame::DETAILS);
453
454 accumulator->PrintMentionedObjectCache();
455 accumulator->Add("=====================\n\n");
456}
457
458
459void Top::SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback) {
460 ASSERT(thread_local_.failed_access_check_callback_ == NULL);
461 thread_local_.failed_access_check_callback_ = callback;
462}
463
464
465void Top::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
466 if (!thread_local_.failed_access_check_callback_) return;
467
468 ASSERT(receiver->IsAccessCheckNeeded());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000469 ASSERT(Top::context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000470 // The callers of this method are not expecting a GC.
471 AssertNoAllocation no_gc;
472
473 // Get the data object from access check info.
474 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
475 Object* info = constructor->shared()->function_data();
476 if (info == Heap::undefined_value()) return;
477
478 Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
479 if (data_obj == Heap::undefined_value()) return;
480
481 HandleScope scope;
482 Handle<JSObject> receiver_handle(receiver);
483 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data());
484 thread_local_.failed_access_check_callback_(
485 v8::Utils::ToLocal(receiver_handle),
486 type,
487 v8::Utils::ToLocal(data));
488}
489
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000490
491enum MayAccessDecision {
492 YES, NO, UNKNOWN
493};
494
495
496static MayAccessDecision MayAccessPreCheck(JSObject* receiver,
497 v8::AccessType type) {
498 // During bootstrapping, callback functions are not enabled yet.
499 if (Bootstrapper::IsActive()) return YES;
500
501 if (receiver->IsJSGlobalProxy()) {
502 Object* receiver_context = JSGlobalProxy::cast(receiver)->context();
503 if (!receiver_context->IsContext()) return NO;
504
505 // Get the global context of current top context.
506 // avoid using Top::global_context() because it uses Handle.
507 Context* global_context = Top::context()->global()->global_context();
508 if (receiver_context == global_context) return YES;
509
510 if (Context::cast(receiver_context)->security_token() ==
511 global_context->security_token())
512 return YES;
513 }
514
515 return UNKNOWN;
516}
517
518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519bool Top::MayNamedAccess(JSObject* receiver, Object* key, v8::AccessType type) {
520 ASSERT(receiver->IsAccessCheckNeeded());
521 // Check for compatibility between the security tokens in the
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000522 // current lexical context and the accessed object.
523 ASSERT(Top::context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000524 // The callers of this method are not expecting a GC.
525 AssertNoAllocation no_gc;
526
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000527 MayAccessDecision decision = MayAccessPreCheck(receiver, type);
528 if (decision != UNKNOWN) return decision == YES;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000529
530 // Get named access check callback
531 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
532 Object* info = constructor->shared()->function_data();
533 if (info == Heap::undefined_value()) return false;
534
535 Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
536 if (data_obj == Heap::undefined_value()) return false;
537
538 Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
539 v8::NamedSecurityCallback callback =
540 v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
541
542 if (!callback) return false;
543
544 HandleScope scope;
545 Handle<JSObject> receiver_handle(receiver);
546 Handle<Object> key_handle(key);
547 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data());
548 LOG(ApiNamedSecurityCheck(key));
549 bool result = false;
550 {
551 // Leaving JavaScript.
552 VMState state(OTHER);
553 result = callback(v8::Utils::ToLocal(receiver_handle),
554 v8::Utils::ToLocal(key_handle),
555 type,
556 v8::Utils::ToLocal(data));
557 }
558 return result;
559}
560
561
562bool Top::MayIndexedAccess(JSObject* receiver,
563 uint32_t index,
564 v8::AccessType type) {
565 ASSERT(receiver->IsAccessCheckNeeded());
566 // Check for compatibility between the security tokens in the
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000567 // current lexical context and the accessed object.
568 ASSERT(Top::context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000569 // The callers of this method are not expecting a GC.
570 AssertNoAllocation no_gc;
571
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000572 MayAccessDecision decision = MayAccessPreCheck(receiver, type);
573 if (decision != UNKNOWN) return decision == YES;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574
575 // Get indexed access check callback
576 JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
577 Object* info = constructor->shared()->function_data();
578 if (info == Heap::undefined_value()) return false;
579
580 Object* data_obj = FunctionTemplateInfo::cast(info)->access_check_info();
581 if (data_obj == Heap::undefined_value()) return false;
582
583 Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
584 v8::IndexedSecurityCallback callback =
585 v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
586
587 if (!callback) return false;
588
589 HandleScope scope;
590 Handle<JSObject> receiver_handle(receiver);
591 Handle<Object> data(AccessCheckInfo::cast(data_obj)->data());
592 LOG(ApiIndexedSecurityCheck(index));
593 bool result = false;
594 {
595 // Leaving JavaScript.
596 VMState state(OTHER);
597 result = callback(v8::Utils::ToLocal(receiver_handle),
598 index,
599 type,
600 v8::Utils::ToLocal(data));
601 }
602 return result;
603}
604
605
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000606const char* Top::kStackOverflowMessage =
607 "Uncaught RangeError: Maximum call stack size exceeded";
608
609
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000610Failure* Top::StackOverflow() {
611 HandleScope scope;
612 Handle<String> key = Factory::stack_overflow_symbol();
613 Handle<JSObject> boilerplate =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000614 Handle<JSObject>::cast(GetProperty(Top::builtins(), key));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000615 Handle<Object> exception = Copy(boilerplate);
616 // TODO(1240995): To avoid having to call JavaScript code to compute
617 // the message for stack overflow exceptions which is very likely to
618 // double fault with another stack overflow exception, we use a
619 // precomputed message. This is somewhat problematic in that it
620 // doesn't use ReportUncaughtException to determine the location
621 // from where the exception occurred. It should probably be
622 // reworked.
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000623 DoThrow(*exception, NULL, kStackOverflowMessage);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000624 return Failure::Exception();
625}
626
627
628Failure* Top::Throw(Object* exception, MessageLocation* location) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000629 DoThrow(exception, location, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000630 return Failure::Exception();
631}
632
633
634Failure* Top::ReThrow(Object* exception, MessageLocation* location) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000635 // Set the exception beeing re-thrown.
636 set_pending_exception(exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000637 return Failure::Exception();
638}
639
640
641void Top::ScheduleThrow(Object* exception) {
642 // When scheduling a throw we first throw the exception to get the
643 // error reporting if it is uncaught before rescheduling it.
644 Throw(exception);
645 thread_local_.scheduled_exception_ = pending_exception();
646 thread_local_.external_caught_exception_ = false;
647 clear_pending_exception();
648}
649
650
651Object* Top::PromoteScheduledException() {
652 Object* thrown = scheduled_exception();
653 clear_scheduled_exception();
654 // Re-throw the exception to avoid getting repeated error reporting.
655 return ReThrow(thrown);
656}
657
658
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000659// NOTE: The stack trace frame iterator is an iterator that only
660// traverse proper JavaScript frames; that is JavaScript frames that
661// have proper JavaScript functions. This excludes the problematic
662// functions in runtime.js.
663class StackTraceFrameIterator: public JavaScriptFrameIterator {
664 public:
665 StackTraceFrameIterator() {
666 if (!done() && !frame()->function()->IsJSFunction()) Advance();
667 }
668
669 void Advance() {
670 while (true) {
671 JavaScriptFrameIterator::Advance();
672 if (done()) return;
673 if (frame()->function()->IsJSFunction()) return;
674 }
675 }
676};
677
678
679void Top::PrintCurrentStackTrace(FILE* out) {
680 StackTraceFrameIterator it;
681 while (!it.done()) {
682 HandleScope scope;
683 // Find code position if recorded in relocation info.
684 JavaScriptFrame* frame = it.frame();
685 int pos = frame->FindCode()->SourcePosition(frame->pc());
686 Handle<Object> pos_obj(Smi::FromInt(pos));
687 // Fetch function and receiver.
688 Handle<JSFunction> fun(JSFunction::cast(frame->function()));
689 Handle<Object> recv(frame->receiver());
690 // Advance to the next JavaScript frame and determine if the
691 // current frame is the top-level frame.
692 it.Advance();
693 Handle<Object> is_top_level = it.done()
694 ? Factory::true_value()
695 : Factory::false_value();
696 // Generate and print strack trace line.
697 Handle<String> line =
698 Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level);
699 if (line->length() > 0) {
700 line->PrintOn(out);
701 fprintf(out, "\n");
702 }
703 }
704}
705
706
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000707void Top::ComputeLocation(MessageLocation* target) {
708 *target = MessageLocation(empty_script(), -1, -1);
709 StackTraceFrameIterator it;
710 if (!it.done()) {
711 JavaScriptFrame* frame = it.frame();
712 JSFunction* fun = JSFunction::cast(frame->function());
713 Object* script = fun->shared()->script();
714 if (script->IsScript() &&
715 !(Script::cast(script)->source()->IsUndefined())) {
716 int pos = frame->FindCode()->SourcePosition(frame->pc());
717 // Compute the location from the function and the reloc info.
718 Handle<Script> casted_script(Script::cast(script));
719 *target = MessageLocation(casted_script, pos, pos + 1);
720 }
721 }
722}
723
724
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725void Top::ReportUncaughtException(Handle<Object> exception,
726 MessageLocation* location,
727 Handle<String> stack_trace) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000728 Handle<Object> message =
729 MessageHandler::MakeMessageObject("uncaught_exception",
730 location,
731 HandleVector<Object>(&exception, 1),
732 stack_trace);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733
734 // Report the uncaught exception.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000735 MessageHandler::ReportMessage(location, message);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736}
737
738
739bool Top::ShouldReportException(bool* is_caught_externally) {
740 StackHandler* handler =
741 StackHandler::FromAddress(Top::handler(Top::GetCurrentThread()));
742
743 // Determine if we have an external exception handler and get the
744 // address of the external handler so we can compare the address to
745 // determine which one is closer to the top of the stack.
746 bool has_external_handler = (thread_local_.try_catch_handler_ != NULL);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000747 v8::TryCatch* try_catch = thread_local_.try_catch_handler_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000748
749 // NOTE: The stack is assumed to grown towards lower addresses. If
750 // the handler is at a higher address than the external address it
751 // means that it is below it on the stack.
752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753 // Find the top-most try-catch handler.
754 while (handler != NULL && !handler->is_try_catch()) {
755 handler = handler->next();
756 }
757
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000758 // The exception has been externally caught if and only if there is
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000759 // an external handler which is on top of the top-most try-catch
760 // handler.
761 //
762 // See comments in RegisterTryCatchHandler for details.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000763 *is_caught_externally = has_external_handler &&
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000764 (handler == NULL || handler == try_catch->js_handler_);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000765
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000766 // If we have a try-catch handler then the exception is caught in
767 // JavaScript code.
768 bool is_uncaught_by_js = (handler == NULL);
769
770 // If there is no external try-catch handler, we report the
771 // exception if it isn't caught by JavaScript code.
772 if (!has_external_handler) return is_uncaught_by_js;
773
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000774 if (is_uncaught_by_js || handler == try_catch->js_handler_) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775 // Only report the exception if the external handler is verbose.
776 return thread_local_.try_catch_handler_->is_verbose_;
777 } else {
778 // Report the exception if it isn't caught by JavaScript code.
779 return is_uncaught_by_js;
780 }
781}
782
783
784void Top::DoThrow(Object* exception,
785 MessageLocation* location,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000786 const char* message) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000787 ASSERT(!has_pending_exception());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000788
789 HandleScope scope;
790 Handle<Object> exception_handle(exception);
791
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000792 // Determine reporting and whether the exception is caught externally.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 bool is_caught_externally = false;
794 bool report_exception = (exception != Failure::OutOfMemoryException()) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000795 ShouldReportException(&is_caught_externally);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000796
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000797 // Generate the message.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000798 Handle<Object> message_obj;
799 MessageLocation potential_computed_location;
800 bool try_catch_needs_message =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000801 is_caught_externally &&
802 thread_local_.try_catch_handler_->capture_message_;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000803 if (report_exception || try_catch_needs_message) {
804 if (location == NULL) {
805 // If no location was specified we use a computed one instead
806 ComputeLocation(&potential_computed_location);
807 location = &potential_computed_location;
808 }
809 Handle<String> stack_trace;
810 if (FLAG_trace_exception) stack_trace = StackTrace();
811 message_obj = MessageHandler::MakeMessageObject("uncaught_exception",
812 location, HandleVector<Object>(&exception_handle, 1), stack_trace);
813 }
814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000815 // If the exception is caught externally, we store it in the
816 // try/catch handler. The C code can find it later and process it if
817 // necessary.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000818 thread_local_.catcher_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000819 if (is_caught_externally) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000820 thread_local_.catcher_ = thread_local_.try_catch_handler_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000821 thread_local_.try_catch_handler_->exception_ =
822 reinterpret_cast<void*>(*exception_handle);
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000823 if (!message_obj.is_null()) {
824 thread_local_.try_catch_handler_->message_ =
825 reinterpret_cast<void*>(*message_obj);
826 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827 }
828
829 // Notify debugger of exception.
830 Debugger::OnException(exception_handle, report_exception);
831
832 if (report_exception) {
833 if (message != NULL) {
834 MessageHandler::ReportMessage(message);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000835 } else if (!message_obj.is_null()) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000836 MessageHandler::ReportMessage(location, message_obj);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000837 }
838 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000839
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840 // NOTE: Notifying the debugger or reporting the exception may have caused
841 // new exceptions. For now, we just ignore that and set the pending exception
842 // to the original one.
843 set_pending_exception(*exception_handle);
844}
845
846
847void Top::TraceException(bool flag) {
848 FLAG_trace_exception = flag;
849}
850
851
852bool Top::optional_reschedule_exception(bool is_bottom_call) {
853 if (!is_out_of_memory() &&
854 (thread_local_.external_caught_exception_ || is_bottom_call)) {
855 thread_local_.external_caught_exception_ = false;
856 clear_pending_exception();
857 return false;
858 } else {
859 thread_local_.scheduled_exception_ = pending_exception();
860 clear_pending_exception();
861 return true;
862 }
863}
864
865
866bool Top::is_out_of_memory() {
867 if (has_pending_exception()) {
868 Object* e = pending_exception();
869 if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
870 return true;
871 }
872 }
873 if (has_scheduled_exception()) {
874 Object* e = scheduled_exception();
875 if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
876 return true;
877 }
878 }
879 return false;
880}
881
882
883Handle<Context> Top::global_context() {
884 GlobalObject* global = thread_local_.context_->global();
885 return Handle<Context>(global->global_context());
886}
887
888
889Object* Top::LookupSpecialFunction(JSObject* receiver,
890 JSObject* prototype,
891 JSFunction* function) {
892 if (receiver->IsJSArray()) {
893 FixedArray* table = context()->global_context()->special_function_table();
894 for (int index = 0; index < table->length(); index +=3) {
895 if ((prototype == table->get(index)) &&
896 (function == table->get(index+1))) {
897 return table->get(index+2);
898 }
899 }
900 }
901 return Heap::undefined_value();
902}
903
904
905char* Top::ArchiveThread(char* to) {
906 memcpy(to, reinterpret_cast<char*>(&thread_local_), sizeof(thread_local_));
kasper.lundaf4734f2008-07-28 12:50:18 +0000907 InitializeThreadLocal();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000908 return to + sizeof(thread_local_);
909}
910
911
912char* Top::RestoreThread(char* from) {
913 memcpy(reinterpret_cast<char*>(&thread_local_), from, sizeof(thread_local_));
914 return from + sizeof(thread_local_);
915}
916
917
918ExecutionAccess::ExecutionAccess() {
919 Top::break_access_->Lock();
920}
921
922
923ExecutionAccess::~ExecutionAccess() {
924 Top::break_access_->Unlock();
925}
926
927
928} } // namespace v8::internal