| 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 |