1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/debug/debug.h" 6 7#include <memory> 8#include <unordered_set> 9 10#include "src/api/api-inl.h" 11#include "src/api/api-natives.h" 12#include "src/base/platform/mutex.h" 13#include "src/builtins/builtins.h" 14#include "src/codegen/assembler-inl.h" 15#include "src/codegen/compilation-cache.h" 16#include "src/codegen/compiler.h" 17#include "src/common/assert-scope.h" 18#include "src/common/globals.h" 19#include "src/common/message-template.h" 20#include "src/debug/debug-evaluate.h" 21#include "src/debug/liveedit.h" 22#include "src/deoptimizer/deoptimizer.h" 23#include "src/execution/arguments.h" 24#include "src/execution/execution.h" 25#include "src/execution/frames-inl.h" 26#include "src/execution/isolate-inl.h" 27#include "src/execution/v8threads.h" 28#include "src/handles/global-handles-inl.h" 29#include "src/heap/heap-inl.h" // For NextDebuggingId. 30#include "src/init/bootstrapper.h" 31#include "src/interpreter/bytecode-array-iterator.h" 32#include "src/interpreter/interpreter.h" 33#include "src/logging/counters.h" 34#include "src/logging/runtime-call-stats-scope.h" 35#include "src/objects/api-callbacks-inl.h" 36#include "src/objects/debug-objects-inl.h" 37#include "src/objects/js-generator-inl.h" 38#include "src/objects/js-promise-inl.h" 39#include "src/objects/slots.h" 40#include "src/snapshot/embedded/embedded-data.h" 41#include "src/snapshot/snapshot.h" 42 43#if V8_ENABLE_WEBASSEMBLY 44#include "src/wasm/wasm-debug.h" 45#include "src/wasm/wasm-objects-inl.h" 46#endif // V8_ENABLE_WEBASSEMBLY 47 48namespace v8 { 49namespace internal { 50 51class Debug::TemporaryObjectsTracker : public HeapObjectAllocationTracker { 52 public: 53 TemporaryObjectsTracker() = default; 54 ~TemporaryObjectsTracker() override = default; 55 TemporaryObjectsTracker(const TemporaryObjectsTracker&) = delete; 56 TemporaryObjectsTracker& operator=(const TemporaryObjectsTracker&) = delete; 57 58 void AllocationEvent(Address addr, int) override { 59 if (disabled) return; 60 objects_.insert(addr); 61 } 62 63 void MoveEvent(Address from, Address to, int) override { 64 if (from == to) return; 65 base::MutexGuard guard(&mutex_); 66 auto it = objects_.find(from); 67 if (it == objects_.end()) { 68 // If temporary object was collected we can get MoveEvent which moves 69 // existing non temporary object to the address where we had temporary 70 // object. So we should mark new address as non temporary. 71 objects_.erase(to); 72 return; 73 } 74 objects_.erase(it); 75 objects_.insert(to); 76 } 77 78 bool HasObject(Handle<HeapObject> obj) const { 79 if (obj->IsJSObject() && 80 Handle<JSObject>::cast(obj)->GetEmbedderFieldCount()) { 81 // Embedder may store any pointers using embedder fields and implements 82 // non trivial logic, e.g. create wrappers lazily and store pointer to 83 // native object inside embedder field. We should consider all objects 84 // with embedder fields as non temporary. 85 return false; 86 } 87 return objects_.find(obj->address()) != objects_.end(); 88 } 89 90 bool disabled = false; 91 92 private: 93 std::unordered_set<Address> objects_; 94 base::Mutex mutex_; 95}; 96 97Debug::Debug(Isolate* isolate) 98 : is_active_(false), 99 hook_on_function_call_(false), 100 is_suppressed_(false), 101 break_disabled_(false), 102 break_points_active_(true), 103 break_on_exception_(false), 104 break_on_uncaught_exception_(false), 105 side_effect_check_failed_(false), 106 debug_info_list_(nullptr), 107 feature_tracker_(isolate), 108 isolate_(isolate) { 109 ThreadInit(); 110} 111 112Debug::~Debug() { DCHECK_NULL(debug_delegate_); } 113 114BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, 115 JavaScriptFrame* frame) { 116 if (debug_info->CanBreakAtEntry()) { 117 return BreakLocation(Debug::kBreakAtEntryPosition, DEBUG_BREAK_AT_ENTRY); 118 } 119 auto summary = FrameSummary::GetTop(frame).AsJavaScript(); 120 int offset = summary.code_offset(); 121 Handle<AbstractCode> abstract_code = summary.abstract_code(); 122 BreakIterator it(debug_info); 123 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset)); 124 return it.GetBreakLocation(); 125} 126 127MaybeHandle<FixedArray> Debug::CheckBreakPointsForLocations( 128 Handle<DebugInfo> debug_info, std::vector<BreakLocation>& break_locations, 129 bool* has_break_points) { 130 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray( 131 debug_info->GetBreakPointCount(isolate_)); 132 int break_points_hit_count = 0; 133 bool has_break_points_at_all = false; 134 for (size_t i = 0; i < break_locations.size(); i++) { 135 bool location_has_break_points; 136 MaybeHandle<FixedArray> check_result = CheckBreakPoints( 137 debug_info, &break_locations[i], &location_has_break_points); 138 has_break_points_at_all |= location_has_break_points; 139 if (!check_result.is_null()) { 140 Handle<FixedArray> break_points_current_hit = 141 check_result.ToHandleChecked(); 142 int num_objects = break_points_current_hit->length(); 143 for (int j = 0; j < num_objects; ++j) { 144 break_points_hit->set(break_points_hit_count++, 145 break_points_current_hit->get(j)); 146 } 147 } 148 } 149 *has_break_points = has_break_points_at_all; 150 if (break_points_hit_count == 0) return {}; 151 152 break_points_hit->Shrink(isolate_, break_points_hit_count); 153 return break_points_hit; 154} 155 156void BreakLocation::AllAtCurrentStatement( 157 Handle<DebugInfo> debug_info, JavaScriptFrame* frame, 158 std::vector<BreakLocation>* result_out) { 159 DCHECK(!debug_info->CanBreakAtEntry()); 160 auto summary = FrameSummary::GetTop(frame).AsJavaScript(); 161 int offset = summary.code_offset(); 162 Handle<AbstractCode> abstract_code = summary.abstract_code(); 163 if (abstract_code->IsCode()) offset = offset - 1; 164 int statement_position; 165 { 166 BreakIterator it(debug_info); 167 it.SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset)); 168 statement_position = it.statement_position(); 169 } 170 for (BreakIterator it(debug_info); !it.Done(); it.Next()) { 171 if (it.statement_position() == statement_position) { 172 result_out->push_back(it.GetBreakLocation()); 173 } 174 } 175} 176 177JSGeneratorObject BreakLocation::GetGeneratorObjectForSuspendedFrame( 178 JavaScriptFrame* frame) const { 179 DCHECK(IsSuspend()); 180 DCHECK_GE(generator_obj_reg_index_, 0); 181 182 Object generator_obj = UnoptimizedFrame::cast(frame)->ReadInterpreterRegister( 183 generator_obj_reg_index_); 184 185 return JSGeneratorObject::cast(generator_obj); 186} 187 188int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info, 189 Handle<AbstractCode> abstract_code, 190 int offset) { 191 // Run through all break points to locate the one closest to the address. 192 int closest_break = 0; 193 int distance = kMaxInt; 194 DCHECK(0 <= offset && offset < abstract_code->Size()); 195 for (BreakIterator it(debug_info); !it.Done(); it.Next()) { 196 // Check if this break point is closer that what was previously found. 197 if (it.code_offset() <= offset && offset - it.code_offset() < distance) { 198 closest_break = it.break_index(); 199 distance = offset - it.code_offset(); 200 // Check whether we can't get any closer. 201 if (distance == 0) break; 202 } 203 } 204 return closest_break; 205} 206 207bool BreakLocation::HasBreakPoint(Isolate* isolate, 208 Handle<DebugInfo> debug_info) const { 209 // First check whether there is a break point with the same source position. 210 if (!debug_info->HasBreakPoint(isolate, position_)) return false; 211 if (debug_info->CanBreakAtEntry()) { 212 DCHECK_EQ(Debug::kBreakAtEntryPosition, position_); 213 return debug_info->BreakAtEntry(); 214 } else { 215 // Then check whether a break point at that source position would have 216 // the same code offset. Otherwise it's just a break location that we can 217 // step to, but not actually a location where we can put a break point. 218 DCHECK(abstract_code_->IsBytecodeArray()); 219 BreakIterator it(debug_info); 220 it.SkipToPosition(position_); 221 return it.code_offset() == code_offset_; 222 } 223} 224 225debug::BreakLocationType BreakLocation::type() const { 226 switch (type_) { 227 case DEBUGGER_STATEMENT: 228 return debug::kDebuggerStatementBreakLocation; 229 case DEBUG_BREAK_SLOT_AT_CALL: 230 return debug::kCallBreakLocation; 231 case DEBUG_BREAK_SLOT_AT_RETURN: 232 return debug::kReturnBreakLocation; 233 234 // Externally, suspend breaks should look like normal breaks. 235 case DEBUG_BREAK_SLOT_AT_SUSPEND: 236 default: 237 return debug::kCommonBreakLocation; 238 } 239} 240 241BreakIterator::BreakIterator(Handle<DebugInfo> debug_info) 242 : debug_info_(debug_info), 243 break_index_(-1), 244 source_position_iterator_( 245 debug_info->DebugBytecodeArray().SourcePositionTable()) { 246 position_ = debug_info->shared().StartPosition(); 247 statement_position_ = position_; 248 // There is at least one break location. 249 DCHECK(!Done()); 250 Next(); 251} 252 253int BreakIterator::BreakIndexFromPosition(int source_position) { 254 for (; !Done(); Next()) { 255 if (source_position <= position()) { 256 int first_break = break_index(); 257 for (; !Done(); Next()) { 258 if (source_position == position()) return break_index(); 259 } 260 return first_break; 261 } 262 } 263 return break_index(); 264} 265 266void BreakIterator::Next() { 267 DisallowGarbageCollection no_gc; 268 DCHECK(!Done()); 269 bool first = break_index_ == -1; 270 while (!Done()) { 271 if (!first) source_position_iterator_.Advance(); 272 first = false; 273 if (Done()) return; 274 position_ = source_position_iterator_.source_position().ScriptOffset(); 275 if (source_position_iterator_.is_statement()) { 276 statement_position_ = position_; 277 } 278 DCHECK_LE(0, position_); 279 DCHECK_LE(0, statement_position_); 280 281 DebugBreakType type = GetDebugBreakType(); 282 if (type != NOT_DEBUG_BREAK) break; 283 } 284 break_index_++; 285} 286 287DebugBreakType BreakIterator::GetDebugBreakType() { 288 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray(); 289 interpreter::Bytecode bytecode = 290 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset())); 291 292 // Make sure we read the actual bytecode, not a prefix scaling bytecode. 293 if (interpreter::Bytecodes::IsPrefixScalingBytecode(bytecode)) { 294 bytecode = 295 interpreter::Bytecodes::FromByte(bytecode_array.get(code_offset() + 1)); 296 } 297 298 if (bytecode == interpreter::Bytecode::kDebugger) { 299 return DEBUGGER_STATEMENT; 300 } else if (bytecode == interpreter::Bytecode::kReturn) { 301 return DEBUG_BREAK_SLOT_AT_RETURN; 302 } else if (bytecode == interpreter::Bytecode::kSuspendGenerator) { 303 return DEBUG_BREAK_SLOT_AT_SUSPEND; 304 } else if (interpreter::Bytecodes::IsCallOrConstruct(bytecode)) { 305 return DEBUG_BREAK_SLOT_AT_CALL; 306 } else if (source_position_iterator_.is_statement()) { 307 return DEBUG_BREAK_SLOT; 308 } else { 309 return NOT_DEBUG_BREAK; 310 } 311} 312 313void BreakIterator::SkipToPosition(int position) { 314 BreakIterator it(debug_info_); 315 SkipTo(it.BreakIndexFromPosition(position)); 316} 317 318void BreakIterator::SetDebugBreak() { 319 DebugBreakType debug_break_type = GetDebugBreakType(); 320 if (debug_break_type == DEBUGGER_STATEMENT) return; 321 HandleScope scope(isolate()); 322 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); 323 Handle<BytecodeArray> bytecode_array(debug_info_->DebugBytecodeArray(), 324 isolate()); 325 interpreter::BytecodeArrayIterator(bytecode_array, code_offset()) 326 .ApplyDebugBreak(); 327} 328 329void BreakIterator::ClearDebugBreak() { 330 DebugBreakType debug_break_type = GetDebugBreakType(); 331 if (debug_break_type == DEBUGGER_STATEMENT) return; 332 DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); 333 BytecodeArray bytecode_array = debug_info_->DebugBytecodeArray(); 334 BytecodeArray original = debug_info_->OriginalBytecodeArray(); 335 bytecode_array.set(code_offset(), original.get(code_offset())); 336} 337 338BreakLocation BreakIterator::GetBreakLocation() { 339 Handle<AbstractCode> code( 340 AbstractCode::cast(debug_info_->DebugBytecodeArray()), isolate()); 341 DebugBreakType type = GetDebugBreakType(); 342 int generator_object_reg_index = -1; 343 int generator_suspend_id = -1; 344 if (type == DEBUG_BREAK_SLOT_AT_SUSPEND) { 345 // For suspend break, we'll need the generator object to be able to step 346 // over the suspend as if it didn't return. We get the interpreter register 347 // index that holds the generator object by reading it directly off the 348 // bytecode array, and we'll read the actual generator object off the 349 // interpreter stack frame in GetGeneratorObjectForSuspendedFrame. 350 BytecodeArray bytecode_array = debug_info_->OriginalBytecodeArray(); 351 interpreter::BytecodeArrayIterator iterator( 352 handle(bytecode_array, isolate()), code_offset()); 353 354 DCHECK_EQ(iterator.current_bytecode(), 355 interpreter::Bytecode::kSuspendGenerator); 356 interpreter::Register generator_obj_reg = iterator.GetRegisterOperand(0); 357 generator_object_reg_index = generator_obj_reg.index(); 358 359 // Also memorize the suspend ID, to be able to decide whether 360 // we are paused on the implicit initial yield later. 361 generator_suspend_id = iterator.GetUnsignedImmediateOperand(3); 362 } 363 return BreakLocation(code, type, code_offset(), position_, 364 generator_object_reg_index, generator_suspend_id); 365} 366 367Isolate* BreakIterator::isolate() { return debug_info_->GetIsolate(); } 368 369void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) { 370 uint32_t mask = 1 << feature; 371 // Only count one sample per feature and isolate. 372 if (bitfield_ & mask) return; 373 isolate_->counters()->debug_feature_usage()->AddSample(feature); 374 bitfield_ |= mask; 375} 376 377// Threading support. 378void Debug::ThreadInit() { 379 thread_local_.break_frame_id_ = StackFrameId::NO_ID; 380 thread_local_.last_step_action_ = StepNone; 381 thread_local_.last_statement_position_ = kNoSourcePosition; 382 thread_local_.last_frame_count_ = -1; 383 thread_local_.fast_forward_to_return_ = false; 384 thread_local_.ignore_step_into_function_ = Smi::zero(); 385 thread_local_.target_frame_count_ = -1; 386 thread_local_.return_value_ = Smi::zero(); 387 thread_local_.last_breakpoint_id_ = 0; 388 clear_suspended_generator(); 389 base::Relaxed_Store(&thread_local_.current_debug_scope_, 390 static_cast<base::AtomicWord>(0)); 391 thread_local_.break_on_next_function_call_ = false; 392 UpdateHookOnFunctionCall(); 393 thread_local_.promise_stack_ = Smi::zero(); 394} 395 396char* Debug::ArchiveDebug(char* storage) { 397 MemCopy(storage, reinterpret_cast<char*>(&thread_local_), 398 ArchiveSpacePerThread()); 399 return storage + ArchiveSpacePerThread(); 400} 401 402char* Debug::RestoreDebug(char* storage) { 403 MemCopy(reinterpret_cast<char*>(&thread_local_), storage, 404 ArchiveSpacePerThread()); 405 406 // Enter the debugger. 407 DebugScope debug_scope(this); 408 409 // Clear any one-shot breakpoints that may have been set by the other 410 // thread, and reapply breakpoints for this thread. 411 ClearOneShot(); 412 413 if (thread_local_.last_step_action_ != StepNone) { 414 int current_frame_count = CurrentFrameCount(); 415 int target_frame_count = thread_local_.target_frame_count_; 416 DCHECK(current_frame_count >= target_frame_count); 417 StackTraceFrameIterator frames_it(isolate_); 418 while (current_frame_count > target_frame_count) { 419 current_frame_count -= frames_it.FrameFunctionCount(); 420 frames_it.Advance(); 421 } 422 DCHECK(current_frame_count == target_frame_count); 423 // Set frame to what it was at Step break 424 thread_local_.break_frame_id_ = frames_it.frame()->id(); 425 426 // Reset the previous step action for this thread. 427 PrepareStep(thread_local_.last_step_action_); 428 } 429 430 return storage + ArchiveSpacePerThread(); 431} 432 433int Debug::ArchiveSpacePerThread() { return sizeof(ThreadLocal); } 434 435void Debug::Iterate(RootVisitor* v) { Iterate(v, &thread_local_); } 436 437char* Debug::Iterate(RootVisitor* v, char* thread_storage) { 438 ThreadLocal* thread_local_data = 439 reinterpret_cast<ThreadLocal*>(thread_storage); 440 Iterate(v, thread_local_data); 441 return thread_storage + ArchiveSpacePerThread(); 442} 443 444void Debug::Iterate(RootVisitor* v, ThreadLocal* thread_local_data) { 445 v->VisitRootPointer(Root::kDebug, nullptr, 446 FullObjectSlot(&thread_local_data->return_value_)); 447 v->VisitRootPointer(Root::kDebug, nullptr, 448 FullObjectSlot(&thread_local_data->suspended_generator_)); 449 v->VisitRootPointer( 450 Root::kDebug, nullptr, 451 FullObjectSlot(&thread_local_data->ignore_step_into_function_)); 452 v->VisitRootPointer(Root::kDebug, nullptr, 453 FullObjectSlot(&thread_local_data->promise_stack_)); 454} 455 456DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info) 457 : next_(nullptr) { 458 // Globalize the request debug info object and make it weak. 459 GlobalHandles* global_handles = isolate->global_handles(); 460 debug_info_ = global_handles->Create(debug_info).location(); 461} 462 463DebugInfoListNode::~DebugInfoListNode() { 464 if (debug_info_ == nullptr) return; 465 GlobalHandles::Destroy(debug_info_); 466 debug_info_ = nullptr; 467} 468 469void Debug::Unload() { 470 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 471 ClearAllBreakPoints(); 472 ClearStepping(); 473 RemoveAllCoverageInfos(); 474 ClearAllDebuggerHints(); 475 debug_delegate_ = nullptr; 476} 477 478void Debug::OnInstrumentationBreak() { 479 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 480 if (!debug_delegate_) return; 481 DCHECK(in_debug_scope()); 482 HandleScope scope(isolate_); 483 DisableBreak no_recursive_break(this); 484 485 Handle<Context> native_context(isolate_->native_context()); 486 debug_delegate_->BreakOnInstrumentation(v8::Utils::ToLocal(native_context), 487 kInstrumentationId); 488} 489 490void Debug::Break(JavaScriptFrame* frame, Handle<JSFunction> break_target) { 491 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 492 // Just continue if breaks are disabled or debugger cannot be loaded. 493 if (break_disabled()) return; 494 495 // Enter the debugger. 496 DebugScope debug_scope(this); 497 DisableBreak no_recursive_break(this); 498 499 // Return if we fail to retrieve debug info. 500 Handle<SharedFunctionInfo> shared(break_target->shared(), isolate_); 501 if (!EnsureBreakInfo(shared)) return; 502 PrepareFunctionForDebugExecution(shared); 503 504 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 505 506 // Find the break location where execution has stopped. 507 BreakLocation location = BreakLocation::FromFrame(debug_info, frame); 508 const bool hitInstrumentationBreak = 509 IsBreakOnInstrumentation(debug_info, location); 510 if (hitInstrumentationBreak) { 511 OnInstrumentationBreak(); 512 } 513 514 // Find actual break points, if any, and trigger debug break event. 515 bool has_break_points; 516 MaybeHandle<FixedArray> break_points_hit = 517 CheckBreakPoints(debug_info, &location, &has_break_points); 518 if (!break_points_hit.is_null() || break_on_next_function_call()) { 519 StepAction lastStepAction = last_step_action(); 520 // Clear all current stepping setup. 521 ClearStepping(); 522 // Notify the debug event listeners. 523 OnDebugBreak(!break_points_hit.is_null() 524 ? break_points_hit.ToHandleChecked() 525 : isolate_->factory()->empty_fixed_array(), 526 lastStepAction); 527 return; 528 } 529 530 // Debug break at function entry, do not worry about stepping. 531 if (location.IsDebugBreakAtEntry()) { 532 DCHECK(debug_info->BreakAtEntry()); 533 return; 534 } 535 536 DCHECK_NOT_NULL(frame); 537 538 // No break point. Check for stepping. 539 StepAction step_action = last_step_action(); 540 int current_frame_count = CurrentFrameCount(); 541 int target_frame_count = thread_local_.target_frame_count_; 542 int last_frame_count = thread_local_.last_frame_count_; 543 544 // StepOut at not return position was requested and return break locations 545 // were flooded with one shots. 546 if (thread_local_.fast_forward_to_return_) { 547 // We might hit an instrumentation breakpoint before running into a 548 // return/suspend location. 549 DCHECK(location.IsReturnOrSuspend() || hitInstrumentationBreak); 550 // We have to ignore recursive calls to function. 551 if (current_frame_count > target_frame_count) return; 552 ClearStepping(); 553 PrepareStep(StepOut); 554 return; 555 } 556 557 bool step_break = false; 558 switch (step_action) { 559 case StepNone: 560 return; 561 case StepOut: 562 // StepOut should not break in a deeper frame than target frame. 563 if (current_frame_count > target_frame_count) return; 564 step_break = true; 565 break; 566 case StepOver: 567 // StepOver should not break in a deeper frame than target frame. 568 if (current_frame_count > target_frame_count) return; 569 V8_FALLTHROUGH; 570 case StepInto: { 571 // Special case StepInto and StepOver for generators that are about to 572 // suspend, in which case we go into "generator stepping" mode. The 573 // exception here is the initial implicit yield in generators (which 574 // always has a suspend ID of 0), where we return to the caller first, 575 // instead of triggering "generator stepping" mode straight away. 576 if (location.IsSuspend() && (!IsGeneratorFunction(shared->kind()) || 577 location.generator_suspend_id() > 0)) { 578 DCHECK(!has_suspended_generator()); 579 thread_local_.suspended_generator_ = 580 location.GetGeneratorObjectForSuspendedFrame(frame); 581 ClearStepping(); 582 return; 583 } 584 585 FrameSummary summary = FrameSummary::GetTop(frame); 586 step_break = step_break || location.IsReturn() || 587 current_frame_count != last_frame_count || 588 thread_local_.last_statement_position_ != 589 summary.SourceStatementPosition(); 590 break; 591 } 592 } 593 594 StepAction lastStepAction = last_step_action(); 595 // Clear all current stepping setup. 596 ClearStepping(); 597 598 if (step_break) { 599 // Notify the debug event listeners. 600 OnDebugBreak(isolate_->factory()->empty_fixed_array(), lastStepAction); 601 } else { 602 // Re-prepare to continue. 603 PrepareStep(step_action); 604 } 605} 606 607bool Debug::IsBreakOnInstrumentation(Handle<DebugInfo> debug_info, 608 const BreakLocation& location) { 609 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 610 bool has_break_points_to_check = 611 break_points_active_ && location.HasBreakPoint(isolate_, debug_info); 612 if (!has_break_points_to_check) return {}; 613 614 Handle<Object> break_points = 615 debug_info->GetBreakPoints(isolate_, location.position()); 616 DCHECK(!break_points->IsUndefined(isolate_)); 617 if (!break_points->IsFixedArray()) { 618 const Handle<BreakPoint> break_point = 619 Handle<BreakPoint>::cast(break_points); 620 return break_point->id() == kInstrumentationId; 621 } 622 623 Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_); 624 for (int i = 0; i < array->length(); ++i) { 625 const Handle<BreakPoint> break_point = 626 Handle<BreakPoint>::cast(handle(array->get(i), isolate_)); 627 if (break_point->id() == kInstrumentationId) { 628 return true; 629 } 630 } 631 return false; 632} 633 634// Find break point objects for this location, if any, and evaluate them. 635// Return an array of break point objects that evaluated true, or an empty 636// handle if none evaluated true. 637// has_break_points will be true, if there is any (non-instrumentation) 638// breakpoint. 639MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info, 640 BreakLocation* location, 641 bool* has_break_points) { 642 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 643 bool has_break_points_to_check = 644 break_points_active_ && location->HasBreakPoint(isolate_, debug_info); 645 if (!has_break_points_to_check) { 646 *has_break_points = false; 647 return {}; 648 } 649 650 return Debug::GetHitBreakPoints(debug_info, location->position(), 651 has_break_points); 652} 653 654bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) { 655 // A break location is considered muted if break locations on the current 656 // statement have at least one break point, and all of these break points 657 // evaluate to false. Aside from not triggering a debug break event at the 658 // break location, we also do not trigger one for debugger statements, nor 659 // an exception event on exception at this location. 660 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 661 HandleScope scope(isolate_); 662 bool has_break_points; 663 MaybeHandle<FixedArray> checked = 664 GetHitBreakpointsAtCurrentStatement(frame, &has_break_points); 665 return has_break_points && checked.is_null(); 666} 667 668MaybeHandle<FixedArray> Debug::GetHitBreakpointsAtCurrentStatement( 669 JavaScriptFrame* frame, bool* has_break_points) { 670 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 671 FrameSummary summary = FrameSummary::GetTop(frame); 672 Handle<JSFunction> function = summary.AsJavaScript().function(); 673 if (!function->shared().HasBreakInfo()) { 674 *has_break_points = false; 675 return {}; 676 } 677 Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), isolate_); 678 // Enter the debugger. 679 DebugScope debug_scope(this); 680 std::vector<BreakLocation> break_locations; 681 BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations); 682 return CheckBreakPointsForLocations(debug_info, break_locations, 683 has_break_points); 684} 685 686// Check whether a single break point object is triggered. 687bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point, 688 bool is_break_at_entry) { 689 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 690 HandleScope scope(isolate_); 691 692 // Instrumentation breakpoints are handled separately. 693 if (break_point->id() == kInstrumentationId) { 694 return false; 695 } 696 697 if (!break_point->condition().length()) return true; 698 Handle<String> condition(break_point->condition(), isolate_); 699 MaybeHandle<Object> maybe_result; 700 Handle<Object> result; 701 702 if (is_break_at_entry) { 703 maybe_result = DebugEvaluate::WithTopmostArguments(isolate_, condition); 704 } else { 705 // Since we call CheckBreakpoint only for deoptimized frame on top of stack, 706 // we can use 0 as index of inlined frame. 707 const int inlined_jsframe_index = 0; 708 const bool throw_on_side_effect = false; 709 maybe_result = 710 DebugEvaluate::Local(isolate_, break_frame_id(), inlined_jsframe_index, 711 condition, throw_on_side_effect); 712 } 713 714 if (!maybe_result.ToHandle(&result)) { 715 if (isolate_->has_pending_exception()) { 716 isolate_->clear_pending_exception(); 717 } 718 return false; 719 } 720 return result->BooleanValue(isolate_); 721} 722 723bool Debug::SetBreakpoint(Handle<SharedFunctionInfo> shared, 724 Handle<BreakPoint> break_point, 725 int* source_position) { 726 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 727 HandleScope scope(isolate_); 728 729 // Make sure the function is compiled and has set up the debug info. 730 if (!EnsureBreakInfo(shared)) return false; 731 PrepareFunctionForDebugExecution(shared); 732 733 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 734 // Source positions starts with zero. 735 DCHECK_LE(0, *source_position); 736 737 // Find the break point and change it. 738 *source_position = FindBreakablePosition(debug_info, *source_position); 739 DebugInfo::SetBreakPoint(isolate_, debug_info, *source_position, break_point); 740 // At least one active break point now. 741 DCHECK_LT(0, debug_info->GetBreakPointCount(isolate_)); 742 743 ClearBreakPoints(debug_info); 744 ApplyBreakPoints(debug_info); 745 746 feature_tracker()->Track(DebugFeatureTracker::kBreakPoint); 747 return true; 748} 749 750bool Debug::SetBreakPointForScript(Handle<Script> script, 751 Handle<String> condition, 752 int* source_position, int* id) { 753 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 754 *id = ++thread_local_.last_breakpoint_id_; 755 Handle<BreakPoint> break_point = 756 isolate_->factory()->NewBreakPoint(*id, condition); 757#if V8_ENABLE_WEBASSEMBLY 758 if (script->type() == Script::TYPE_WASM) { 759 RecordWasmScriptWithBreakpoints(script); 760 return WasmScript::SetBreakPoint(script, source_position, break_point); 761 } 762#endif // V8_ENABLE_WEBASSEMBLY 763 764 HandleScope scope(isolate_); 765 766 // Obtain shared function info for the innermost function containing this 767 // position. 768 Handle<Object> result = 769 FindInnermostContainingFunctionInfo(script, *source_position); 770 if (result->IsUndefined(isolate_)) return false; 771 772 auto shared = Handle<SharedFunctionInfo>::cast(result); 773 if (!EnsureBreakInfo(shared)) return false; 774 PrepareFunctionForDebugExecution(shared); 775 776 // Find the nested shared function info that is closest to the position within 777 // the containing function. 778 shared = FindClosestSharedFunctionInfoFromPosition(*source_position, script, 779 shared); 780 781 // Set the breakpoint in the function. 782 return SetBreakpoint(shared, break_point, source_position); 783} 784 785int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info, 786 int source_position) { 787 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 788 if (debug_info->CanBreakAtEntry()) { 789 return kBreakAtEntryPosition; 790 } else { 791 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 792 BreakIterator it(debug_info); 793 it.SkipToPosition(source_position); 794 return it.position(); 795 } 796} 797 798void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { 799 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 800 DisallowGarbageCollection no_gc; 801 if (debug_info->CanBreakAtEntry()) { 802 debug_info->SetBreakAtEntry(); 803 } else { 804 if (!debug_info->HasInstrumentedBytecodeArray()) return; 805 FixedArray break_points = debug_info->break_points(); 806 for (int i = 0; i < break_points.length(); i++) { 807 if (break_points.get(i).IsUndefined(isolate_)) continue; 808 BreakPointInfo info = BreakPointInfo::cast(break_points.get(i)); 809 if (info.GetBreakPointCount(isolate_) == 0) continue; 810 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 811 BreakIterator it(debug_info); 812 it.SkipToPosition(info.source_position()); 813 it.SetDebugBreak(); 814 } 815 } 816 debug_info->SetDebugExecutionMode(DebugInfo::kBreakpoints); 817} 818 819void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) { 820 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 821 if (debug_info->CanBreakAtEntry()) { 822 debug_info->ClearBreakAtEntry(); 823 } else { 824 // If we attempt to clear breakpoints but none exist, simply return. This 825 // can happen e.g. CoverageInfos exist but no breakpoints are set. 826 if (!debug_info->HasInstrumentedBytecodeArray() || 827 !debug_info->HasBreakInfo()) { 828 return; 829 } 830 831 DisallowGarbageCollection no_gc; 832 for (BreakIterator it(debug_info); !it.Done(); it.Next()) { 833 it.ClearDebugBreak(); 834 } 835 } 836} 837 838void Debug::ClearBreakPoint(Handle<BreakPoint> break_point) { 839 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 840 HandleScope scope(isolate_); 841 842 for (DebugInfoListNode* node = debug_info_list_; node != nullptr; 843 node = node->next()) { 844 if (!node->debug_info()->HasBreakInfo()) continue; 845 Handle<Object> result = DebugInfo::FindBreakPointInfo( 846 isolate_, node->debug_info(), break_point); 847 if (result->IsUndefined(isolate_)) continue; 848 Handle<DebugInfo> debug_info = node->debug_info(); 849 if (DebugInfo::ClearBreakPoint(isolate_, debug_info, break_point)) { 850 ClearBreakPoints(debug_info); 851 if (debug_info->GetBreakPointCount(isolate_) == 0) { 852 RemoveBreakInfoAndMaybeFree(debug_info); 853 } else { 854 ApplyBreakPoints(debug_info); 855 } 856 return; 857 } 858 } 859} 860 861int Debug::GetFunctionDebuggingId(Handle<JSFunction> function) { 862 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 863 Handle<SharedFunctionInfo> shared = handle(function->shared(), isolate_); 864 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 865 int id = debug_info->debugging_id(); 866 if (id == DebugInfo::kNoDebuggingId) { 867 id = isolate_->heap()->NextDebuggingId(); 868 debug_info->set_debugging_id(id); 869 } 870 return id; 871} 872 873bool Debug::SetBreakpointForFunction(Handle<SharedFunctionInfo> shared, 874 Handle<String> condition, int* id, 875 BreakPointKind kind) { 876 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 877 if (kind == kInstrumentation) { 878 *id = kInstrumentationId; 879 } else { 880 *id = ++thread_local_.last_breakpoint_id_; 881 } 882 Handle<BreakPoint> breakpoint = 883 isolate_->factory()->NewBreakPoint(*id, condition); 884 int source_position = 0; 885#if V8_ENABLE_WEBASSEMBLY 886 // Handle wasm function. 887 if (shared->HasWasmExportedFunctionData()) { 888 int func_index = shared->wasm_exported_function_data().function_index(); 889 Handle<WasmInstanceObject> wasm_instance( 890 shared->wasm_exported_function_data().instance(), isolate_); 891 Handle<Script> script(Script::cast(wasm_instance->module_object().script()), 892 isolate_); 893 return WasmScript::SetBreakPointOnFirstBreakableForFunction( 894 script, func_index, breakpoint); 895 } 896#endif // V8_ENABLE_WEBASSEMBLY 897 return SetBreakpoint(shared, breakpoint, &source_position); 898} 899 900void Debug::RemoveBreakpoint(int id) { 901 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 902 Handle<BreakPoint> breakpoint = isolate_->factory()->NewBreakPoint( 903 id, isolate_->factory()->empty_string()); 904 ClearBreakPoint(breakpoint); 905} 906 907#if V8_ENABLE_WEBASSEMBLY 908void Debug::SetInstrumentationBreakpointForWasmScript(Handle<Script> script, 909 int* id) { 910 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 911 DCHECK_EQ(Script::TYPE_WASM, script->type()); 912 *id = kInstrumentationId; 913 914 Handle<BreakPoint> break_point = isolate_->factory()->NewBreakPoint( 915 *id, isolate_->factory()->empty_string()); 916 RecordWasmScriptWithBreakpoints(script); 917 WasmScript::SetInstrumentationBreakpoint(script, break_point); 918} 919 920void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) { 921 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 922 if (script->type() == Script::TYPE_WASM) { 923 WasmScript::ClearBreakPointById(script, id); 924 } 925} 926 927void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) { 928 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 929 if (wasm_scripts_with_break_points_.is_null()) { 930 Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4); 931 wasm_scripts_with_break_points_ = 932 isolate_->global_handles()->Create(*new_list); 933 } 934 { 935 DisallowGarbageCollection no_gc; 936 for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0; 937 --idx) { 938 HeapObject wasm_script; 939 if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject( 940 &wasm_script) && 941 wasm_script == *script) { 942 return; 943 } 944 } 945 } 946 Handle<WeakArrayList> new_list = WeakArrayList::Append( 947 isolate_, wasm_scripts_with_break_points_, MaybeObjectHandle{script}); 948 if (*new_list != *wasm_scripts_with_break_points_) { 949 isolate_->global_handles()->Destroy( 950 wasm_scripts_with_break_points_.location()); 951 wasm_scripts_with_break_points_ = 952 isolate_->global_handles()->Create(*new_list); 953 } 954} 955#endif // V8_ENABLE_WEBASSEMBLY 956 957// Clear out all the debug break code. 958void Debug::ClearAllBreakPoints() { 959 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 960 ClearAllDebugInfos([=](Handle<DebugInfo> info) { 961 ClearBreakPoints(info); 962 info->ClearBreakInfo(isolate_); 963 }); 964#if V8_ENABLE_WEBASSEMBLY 965 // Clear all wasm breakpoints. 966 if (!wasm_scripts_with_break_points_.is_null()) { 967 DisallowGarbageCollection no_gc; 968 for (int idx = wasm_scripts_with_break_points_->length() - 1; idx >= 0; 969 --idx) { 970 HeapObject raw_wasm_script; 971 if (wasm_scripts_with_break_points_->Get(idx).GetHeapObject( 972 &raw_wasm_script)) { 973 Script wasm_script = Script::cast(raw_wasm_script); 974 WasmScript::ClearAllBreakpoints(wasm_script); 975 wasm_script.wasm_native_module()->GetDebugInfo()->RemoveIsolate( 976 isolate_); 977 } 978 } 979 wasm_scripts_with_break_points_ = Handle<WeakArrayList>{}; 980 } 981#endif // V8_ENABLE_WEBASSEMBLY 982} 983 984void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared, 985 bool returns_only) { 986 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 987 if (IsBlackboxed(shared)) return; 988 // Make sure the function is compiled and has set up the debug info. 989 if (!EnsureBreakInfo(shared)) return; 990 PrepareFunctionForDebugExecution(shared); 991 992 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 993 // Flood the function with break points. 994 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 995 for (BreakIterator it(debug_info); !it.Done(); it.Next()) { 996 if (returns_only && !it.GetBreakLocation().IsReturnOrSuspend()) continue; 997 it.SetDebugBreak(); 998 } 999} 1000 1001void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) { 1002 if (type == BreakUncaughtException) { 1003 break_on_uncaught_exception_ = enable; 1004 } else { 1005 break_on_exception_ = enable; 1006 } 1007} 1008 1009bool Debug::IsBreakOnException(ExceptionBreakType type) { 1010 if (type == BreakUncaughtException) { 1011 return break_on_uncaught_exception_; 1012 } else { 1013 return break_on_exception_; 1014 } 1015} 1016 1017MaybeHandle<FixedArray> Debug::GetHitBreakPoints(Handle<DebugInfo> debug_info, 1018 int position, 1019 bool* has_break_points) { 1020 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1021 Handle<Object> break_points = debug_info->GetBreakPoints(isolate_, position); 1022 bool is_break_at_entry = debug_info->BreakAtEntry(); 1023 DCHECK(!break_points->IsUndefined(isolate_)); 1024 if (!break_points->IsFixedArray()) { 1025 const Handle<BreakPoint> break_point = 1026 Handle<BreakPoint>::cast(break_points); 1027 *has_break_points = break_point->id() != kInstrumentationId; 1028 if (!CheckBreakPoint(break_point, is_break_at_entry)) { 1029 return {}; 1030 } 1031 Handle<FixedArray> break_points_hit = isolate_->factory()->NewFixedArray(1); 1032 break_points_hit->set(0, *break_points); 1033 return break_points_hit; 1034 } 1035 1036 Handle<FixedArray> array(FixedArray::cast(*break_points), isolate_); 1037 int num_objects = array->length(); 1038 Handle<FixedArray> break_points_hit = 1039 isolate_->factory()->NewFixedArray(num_objects); 1040 int break_points_hit_count = 0; 1041 *has_break_points = false; 1042 for (int i = 0; i < num_objects; ++i) { 1043 Handle<BreakPoint> break_point = 1044 Handle<BreakPoint>::cast(handle(array->get(i), isolate_)); 1045 *has_break_points |= break_point->id() != kInstrumentationId; 1046 if (CheckBreakPoint(break_point, is_break_at_entry)) { 1047 break_points_hit->set(break_points_hit_count++, *break_point); 1048 } 1049 } 1050 if (break_points_hit_count == 0) return {}; 1051 break_points_hit->Shrink(isolate_, break_points_hit_count); 1052 return break_points_hit; 1053} 1054 1055void Debug::SetBreakOnNextFunctionCall() { 1056 // This method forces V8 to break on next function call regardless current 1057 // last_step_action_. If any break happens between SetBreakOnNextFunctionCall 1058 // and ClearBreakOnNextFunctionCall, we will clear this flag and stepping. If 1059 // break does not happen, e.g. all called functions are blackboxed or no 1060 // function is called, then we will clear this flag and let stepping continue 1061 // its normal business. 1062 thread_local_.break_on_next_function_call_ = true; 1063 UpdateHookOnFunctionCall(); 1064} 1065 1066void Debug::ClearBreakOnNextFunctionCall() { 1067 thread_local_.break_on_next_function_call_ = false; 1068 UpdateHookOnFunctionCall(); 1069} 1070 1071void Debug::PrepareStepIn(Handle<JSFunction> function) { 1072 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1073 CHECK(last_step_action() >= StepInto || break_on_next_function_call()); 1074 if (ignore_events()) return; 1075 if (in_debug_scope()) return; 1076 if (break_disabled()) return; 1077 Handle<SharedFunctionInfo> shared(function->shared(), isolate_); 1078 if (IsBlackboxed(shared)) return; 1079 if (*function == thread_local_.ignore_step_into_function_) return; 1080 thread_local_.ignore_step_into_function_ = Smi::zero(); 1081 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_)); 1082} 1083 1084void Debug::PrepareStepInSuspendedGenerator() { 1085 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1086 CHECK(has_suspended_generator()); 1087 if (ignore_events()) return; 1088 if (in_debug_scope()) return; 1089 if (break_disabled()) return; 1090 thread_local_.last_step_action_ = StepInto; 1091 UpdateHookOnFunctionCall(); 1092 Handle<JSFunction> function( 1093 JSGeneratorObject::cast(thread_local_.suspended_generator_).function(), 1094 isolate_); 1095 FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_)); 1096 clear_suspended_generator(); 1097} 1098 1099void Debug::PrepareStepOnThrow() { 1100 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1101 if (last_step_action() == StepNone) return; 1102 if (ignore_events()) return; 1103 if (in_debug_scope()) return; 1104 if (break_disabled()) return; 1105 1106 ClearOneShot(); 1107 1108 int current_frame_count = CurrentFrameCount(); 1109 1110 // Iterate through the JavaScript stack looking for handlers. 1111 JavaScriptFrameIterator it(isolate_); 1112 while (!it.done()) { 1113 JavaScriptFrame* frame = it.frame(); 1114 if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; 1115 std::vector<SharedFunctionInfo> infos; 1116 frame->GetFunctions(&infos); 1117 current_frame_count -= infos.size(); 1118 it.Advance(); 1119 } 1120 1121 // No handler found. Nothing to instrument. 1122 if (it.done()) return; 1123 1124 bool found_handler = false; 1125 // Iterate frames, including inlined frames. First, find the handler frame. 1126 // Then skip to the frame we want to break in, then instrument for stepping. 1127 for (; !it.done(); it.Advance()) { 1128 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame()); 1129 if (last_step_action() == StepInto) { 1130 // Deoptimize frame to ensure calls are checked for step-in. 1131 Deoptimizer::DeoptimizeFunction(frame->function()); 1132 } 1133 std::vector<FrameSummary> summaries; 1134 frame->Summarize(&summaries); 1135 for (size_t i = summaries.size(); i != 0; i--, current_frame_count--) { 1136 const FrameSummary& summary = summaries[i - 1]; 1137 if (!found_handler) { 1138 // We have yet to find the handler. If the frame inlines multiple 1139 // functions, we have to check each one for the handler. 1140 // If it only contains one function, we already found the handler. 1141 if (summaries.size() > 1) { 1142 Handle<AbstractCode> code = summary.AsJavaScript().abstract_code(); 1143 CHECK_EQ(CodeKind::INTERPRETED_FUNCTION, code->kind()); 1144 HandlerTable table(code->GetBytecodeArray()); 1145 int code_offset = summary.code_offset(); 1146 HandlerTable::CatchPrediction prediction; 1147 int index = table.LookupRange(code_offset, nullptr, &prediction); 1148 if (index > 0) found_handler = true; 1149 } else { 1150 found_handler = true; 1151 } 1152 } 1153 1154 if (found_handler) { 1155 // We found the handler. If we are stepping next or out, we need to 1156 // iterate until we found the suitable target frame to break in. 1157 if ((last_step_action() == StepOver || last_step_action() == StepOut) && 1158 current_frame_count > thread_local_.target_frame_count_) { 1159 continue; 1160 } 1161 Handle<SharedFunctionInfo> info( 1162 summary.AsJavaScript().function()->shared(), isolate_); 1163 if (IsBlackboxed(info)) continue; 1164 FloodWithOneShot(info); 1165 return; 1166 } 1167 } 1168 } 1169} 1170 1171void Debug::PrepareStep(StepAction step_action) { 1172 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1173 HandleScope scope(isolate_); 1174 1175 DCHECK(in_debug_scope()); 1176 1177 // Get the frame where the execution has stopped and skip the debug frame if 1178 // any. The debug frame will only be present if execution was stopped due to 1179 // hitting a break point. In other situations (e.g. unhandled exception) the 1180 // debug frame is not present. 1181 StackFrameId frame_id = break_frame_id(); 1182 // If there is no JavaScript stack don't do anything. 1183 if (frame_id == StackFrameId::NO_ID) return; 1184 1185 feature_tracker()->Track(DebugFeatureTracker::kStepping); 1186 1187 thread_local_.last_step_action_ = step_action; 1188 1189 StackTraceFrameIterator frames_it(isolate_, frame_id); 1190 CommonFrame* frame = frames_it.frame(); 1191 1192 BreakLocation location = BreakLocation::Invalid(); 1193 Handle<SharedFunctionInfo> shared; 1194 int current_frame_count = CurrentFrameCount(); 1195 1196 if (frame->is_java_script()) { 1197 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame); 1198 DCHECK(js_frame->function().IsJSFunction()); 1199 1200 // Get the debug info (create it if it does not exist). 1201 auto summary = FrameSummary::GetTop(frame).AsJavaScript(); 1202 Handle<JSFunction> function(summary.function()); 1203 shared = Handle<SharedFunctionInfo>(function->shared(), isolate_); 1204 if (!EnsureBreakInfo(shared)) return; 1205 PrepareFunctionForDebugExecution(shared); 1206 1207 // PrepareFunctionForDebugExecution can invalidate Baseline frames 1208 js_frame = JavaScriptFrame::cast(frames_it.Reframe()); 1209 1210 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 1211 location = BreakLocation::FromFrame(debug_info, js_frame); 1212 1213 // Any step at a return is a step-out, and a step-out at a suspend behaves 1214 // like a return. 1215 if (location.IsReturn() || 1216 (location.IsSuspend() && 1217 (step_action == StepOut || (IsGeneratorFunction(shared->kind()) && 1218 location.generator_suspend_id() == 0)))) { 1219 // On StepOut we'll ignore our further calls to current function in 1220 // PrepareStepIn callback. 1221 if (last_step_action() == StepOut) { 1222 thread_local_.ignore_step_into_function_ = *function; 1223 } 1224 step_action = StepOut; 1225 thread_local_.last_step_action_ = StepInto; 1226 } 1227 1228 // We need to schedule DebugOnFunction call callback 1229 UpdateHookOnFunctionCall(); 1230 1231 // A step-next in blackboxed function is a step-out. 1232 if (step_action == StepOver && IsBlackboxed(shared)) step_action = StepOut; 1233 1234 thread_local_.last_statement_position_ = 1235 summary.abstract_code()->SourceStatementPosition(summary.code_offset()); 1236 thread_local_.last_frame_count_ = current_frame_count; 1237 // No longer perform the current async step. 1238 clear_suspended_generator(); 1239#if V8_ENABLE_WEBASSEMBLY 1240 } else if (frame->is_wasm() && step_action != StepOut) { 1241 // Handle stepping in wasm. 1242 WasmFrame* wasm_frame = WasmFrame::cast(frame); 1243 auto* debug_info = wasm_frame->native_module()->GetDebugInfo(); 1244 if (debug_info->PrepareStep(wasm_frame)) { 1245 UpdateHookOnFunctionCall(); 1246 return; 1247 } 1248 // If the wasm code is not debuggable or will return after this step 1249 // (indicated by {PrepareStep} returning false), then step out of that frame 1250 // instead. 1251 step_action = StepOut; 1252 UpdateHookOnFunctionCall(); 1253#endif // V8_ENABLE_WEBASSEMBLY 1254 } 1255 1256 switch (step_action) { 1257 case StepNone: 1258 UNREACHABLE(); 1259 case StepOut: { 1260 // Clear last position info. For stepping out it does not matter. 1261 thread_local_.last_statement_position_ = kNoSourcePosition; 1262 thread_local_.last_frame_count_ = -1; 1263 if (!shared.is_null()) { 1264 if (!location.IsReturnOrSuspend() && !IsBlackboxed(shared)) { 1265 // At not return position we flood return positions with one shots and 1266 // will repeat StepOut automatically at next break. 1267 thread_local_.target_frame_count_ = current_frame_count; 1268 thread_local_.fast_forward_to_return_ = true; 1269 FloodWithOneShot(shared, true); 1270 return; 1271 } 1272 if (IsAsyncFunction(shared->kind())) { 1273 // Stepping out of an async function whose implicit promise is awaited 1274 // by some other async function, should resume the latter. The return 1275 // value here is either a JSPromise or a JSGeneratorObject (for the 1276 // initial yield of async generators). 1277 Handle<JSReceiver> return_value( 1278 JSReceiver::cast(thread_local_.return_value_), isolate_); 1279 Handle<Object> awaited_by = JSReceiver::GetDataProperty( 1280 isolate_, return_value, 1281 isolate_->factory()->promise_awaited_by_symbol()); 1282 if (awaited_by->IsJSGeneratorObject()) { 1283 DCHECK(!has_suspended_generator()); 1284 thread_local_.suspended_generator_ = *awaited_by; 1285 ClearStepping(); 1286 return; 1287 } 1288 } 1289 } 1290 // Skip the current frame, find the first frame we want to step out to 1291 // and deoptimize every frame along the way. 1292 bool in_current_frame = true; 1293 for (; !frames_it.done(); frames_it.Advance()) { 1294#if V8_ENABLE_WEBASSEMBLY 1295 if (frames_it.frame()->is_wasm()) { 1296 if (in_current_frame) { 1297 in_current_frame = false; 1298 continue; 1299 } 1300 // Handle stepping out into Wasm. 1301 WasmFrame* wasm_frame = WasmFrame::cast(frames_it.frame()); 1302 auto* debug_info = wasm_frame->native_module()->GetDebugInfo(); 1303 debug_info->PrepareStepOutTo(wasm_frame); 1304 return; 1305 } 1306#endif // V8_ENABLE_WEBASSEMBLY 1307 JavaScriptFrame* js_frame = JavaScriptFrame::cast(frames_it.frame()); 1308 if (last_step_action() == StepInto) { 1309 // Deoptimize frame to ensure calls are checked for step-in. 1310 Deoptimizer::DeoptimizeFunction(js_frame->function()); 1311 } 1312 HandleScope inner_scope(isolate_); 1313 std::vector<Handle<SharedFunctionInfo>> infos; 1314 js_frame->GetFunctions(&infos); 1315 for (; !infos.empty(); current_frame_count--) { 1316 Handle<SharedFunctionInfo> info = infos.back(); 1317 infos.pop_back(); 1318 if (in_current_frame) { 1319 // We want to step out, so skip the current frame. 1320 in_current_frame = false; 1321 continue; 1322 } 1323 if (IsBlackboxed(info)) continue; 1324 FloodWithOneShot(info); 1325 thread_local_.target_frame_count_ = current_frame_count; 1326 return; 1327 } 1328 } 1329 break; 1330 } 1331 case StepOver: 1332 thread_local_.target_frame_count_ = current_frame_count; 1333 V8_FALLTHROUGH; 1334 case StepInto: 1335 FloodWithOneShot(shared); 1336 break; 1337 } 1338} 1339 1340// Simple function for returning the source positions for active break points. 1341Handle<Object> Debug::GetSourceBreakLocations( 1342 Isolate* isolate, Handle<SharedFunctionInfo> shared) { 1343 RCS_SCOPE(isolate, RuntimeCallCounterId::kDebugger); 1344 if (!shared->HasBreakInfo()) { 1345 return isolate->factory()->undefined_value(); 1346 } 1347 1348 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate); 1349 if (debug_info->GetBreakPointCount(isolate) == 0) { 1350 return isolate->factory()->undefined_value(); 1351 } 1352 Handle<FixedArray> locations = isolate->factory()->NewFixedArray( 1353 debug_info->GetBreakPointCount(isolate)); 1354 int count = 0; 1355 for (int i = 0; i < debug_info->break_points().length(); ++i) { 1356 if (!debug_info->break_points().get(i).IsUndefined(isolate)) { 1357 BreakPointInfo break_point_info = 1358 BreakPointInfo::cast(debug_info->break_points().get(i)); 1359 int break_points = break_point_info.GetBreakPointCount(isolate); 1360 if (break_points == 0) continue; 1361 for (int j = 0; j < break_points; ++j) { 1362 locations->set(count++, 1363 Smi::FromInt(break_point_info.source_position())); 1364 } 1365 } 1366 } 1367 return locations; 1368} 1369 1370void Debug::ClearStepping() { 1371 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1372 // Clear the various stepping setup. 1373 ClearOneShot(); 1374 1375 thread_local_.last_step_action_ = StepNone; 1376 thread_local_.last_statement_position_ = kNoSourcePosition; 1377 thread_local_.ignore_step_into_function_ = Smi::zero(); 1378 thread_local_.fast_forward_to_return_ = false; 1379 thread_local_.last_frame_count_ = -1; 1380 thread_local_.target_frame_count_ = -1; 1381 thread_local_.break_on_next_function_call_ = false; 1382 UpdateHookOnFunctionCall(); 1383} 1384 1385// Clears all the one-shot break points that are currently set. Normally this 1386// function is called each time a break point is hit as one shot break points 1387// are used to support stepping. 1388void Debug::ClearOneShot() { 1389 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1390 // The current implementation just runs through all the breakpoints. When the 1391 // last break point for a function is removed that function is automatically 1392 // removed from the list. 1393 for (DebugInfoListNode* node = debug_info_list_; node != nullptr; 1394 node = node->next()) { 1395 Handle<DebugInfo> debug_info = node->debug_info(); 1396 ClearBreakPoints(debug_info); 1397 ApplyBreakPoints(debug_info); 1398 } 1399} 1400 1401namespace { 1402class DiscardBaselineCodeVisitor : public ThreadVisitor { 1403 public: 1404 explicit DiscardBaselineCodeVisitor(SharedFunctionInfo shared) 1405 : shared_(shared) {} 1406 DiscardBaselineCodeVisitor() : shared_(SharedFunctionInfo()) {} 1407 1408 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override { 1409 DisallowGarbageCollection diallow_gc; 1410 bool deopt_all = shared_ == SharedFunctionInfo(); 1411 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { 1412 if (!deopt_all && it.frame()->function().shared() != shared_) continue; 1413 if (it.frame()->type() == StackFrame::BASELINE) { 1414 BaselineFrame* frame = BaselineFrame::cast(it.frame()); 1415 int bytecode_offset = frame->GetBytecodeOffset(); 1416 Address* pc_addr = frame->pc_address(); 1417 Address advance = BUILTIN_CODE(isolate, InterpreterEnterAtNextBytecode) 1418 ->InstructionStart(); 1419 PointerAuthentication::ReplacePC(pc_addr, advance, kSystemPointerSize); 1420 InterpretedFrame::cast(it.Reframe()) 1421 ->PatchBytecodeOffset(bytecode_offset); 1422 } else if (it.frame()->type() == StackFrame::INTERPRETED) { 1423 // Check if the PC is a baseline entry trampoline. If it is, replace it 1424 // with the corresponding interpreter entry trampoline. 1425 // This is the case if a baseline function was inlined into a function 1426 // we deoptimized in the debugger and are stepping into it. 1427 JavaScriptFrame* frame = it.frame(); 1428 Address pc = frame->pc(); 1429 Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate, pc); 1430 if (builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode || 1431 builtin == Builtin::kBaselineOrInterpreterEnterAtNextBytecode) { 1432 Address* pc_addr = frame->pc_address(); 1433 Builtin advance = 1434 builtin == Builtin::kBaselineOrInterpreterEnterAtBytecode 1435 ? Builtin::kInterpreterEnterAtBytecode 1436 : Builtin::kInterpreterEnterAtNextBytecode; 1437 Address advance_pc = 1438 isolate->builtins()->code(advance).InstructionStart(); 1439 PointerAuthentication::ReplacePC(pc_addr, advance_pc, 1440 kSystemPointerSize); 1441 } 1442 } 1443 } 1444 } 1445 1446 private: 1447 SharedFunctionInfo shared_; 1448}; 1449} // namespace 1450 1451void Debug::DiscardBaselineCode(SharedFunctionInfo shared) { 1452 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1453 DCHECK(shared.HasBaselineCode()); 1454 Isolate* isolate = shared.GetIsolate(); 1455 DiscardBaselineCodeVisitor visitor(shared); 1456 visitor.VisitThread(isolate, isolate->thread_local_top()); 1457 isolate->thread_manager()->IterateArchivedThreads(&visitor); 1458 // TODO(v8:11429): Avoid this heap walk somehow. 1459 HeapObjectIterator iterator(isolate->heap()); 1460 auto trampoline = BUILTIN_CODE(isolate, InterpreterEntryTrampoline); 1461 shared.FlushBaselineCode(); 1462 for (HeapObject obj = iterator.Next(); !obj.is_null(); 1463 obj = iterator.Next()) { 1464 if (obj.IsJSFunction()) { 1465 JSFunction fun = JSFunction::cast(obj); 1466 if (fun.shared() == shared && fun.ActiveTierIsBaseline()) { 1467 fun.set_code(*trampoline); 1468 } 1469 } 1470 } 1471} 1472 1473void Debug::DiscardAllBaselineCode() { 1474 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1475 DiscardBaselineCodeVisitor visitor; 1476 visitor.VisitThread(isolate_, isolate_->thread_local_top()); 1477 HeapObjectIterator iterator(isolate_->heap()); 1478 auto trampoline = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline); 1479 isolate_->thread_manager()->IterateArchivedThreads(&visitor); 1480 for (HeapObject obj = iterator.Next(); !obj.is_null(); 1481 obj = iterator.Next()) { 1482 if (obj.IsJSFunction()) { 1483 JSFunction fun = JSFunction::cast(obj); 1484 if (fun.ActiveTierIsBaseline()) { 1485 fun.set_code(*trampoline); 1486 } 1487 } else if (obj.IsSharedFunctionInfo()) { 1488 SharedFunctionInfo shared = SharedFunctionInfo::cast(obj); 1489 if (shared.HasBaselineCode()) { 1490 shared.FlushBaselineCode(); 1491 } 1492 } 1493 } 1494} 1495 1496void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) { 1497 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1498 // Deoptimize all code compiled from this shared function info including 1499 // inlining. 1500 isolate_->AbortConcurrentOptimization(BlockingBehavior::kBlock); 1501 1502 if (shared->HasBaselineCode()) { 1503 DiscardBaselineCode(*shared); 1504 } 1505 1506 bool found_something = false; 1507 Code::OptimizedCodeIterator iterator(isolate_); 1508 do { 1509 Code code = iterator.Next(); 1510 if (code.is_null()) break; 1511 if (code.Inlines(*shared)) { 1512 code.set_marked_for_deoptimization(true); 1513 found_something = true; 1514 } 1515 } while (true); 1516 1517 if (found_something) { 1518 // Only go through with the deoptimization if something was found. 1519 Deoptimizer::DeoptimizeMarkedCode(isolate_); 1520 } 1521} 1522 1523void Debug::PrepareFunctionForDebugExecution( 1524 Handle<SharedFunctionInfo> shared) { 1525 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1526 // To prepare bytecode for debugging, we already need to have the debug 1527 // info (containing the debug copy) upfront, but since we do not recompile, 1528 // preparing for break points cannot fail. 1529 DCHECK(shared->is_compiled()); 1530 DCHECK(shared->HasDebugInfo()); 1531 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 1532 if (debug_info->flags(kRelaxedLoad) & DebugInfo::kPreparedForDebugExecution) { 1533 return; 1534 } 1535 1536 // Have to discard baseline code before installing debug bytecode, since the 1537 // bytecode array field on the baseline code object is immutable. 1538 if (debug_info->CanBreakAtEntry()) { 1539 // Deopt everything in case the function is inlined anywhere. 1540 Deoptimizer::DeoptimizeAll(isolate_); 1541 DiscardAllBaselineCode(); 1542 } else { 1543 DeoptimizeFunction(shared); 1544 } 1545 1546 if (shared->HasBytecodeArray()) { 1547 DCHECK(!shared->HasBaselineCode()); 1548 SharedFunctionInfo::InstallDebugBytecode(shared, isolate_); 1549 } 1550 1551 if (debug_info->CanBreakAtEntry()) { 1552 InstallDebugBreakTrampoline(); 1553 } else { 1554 // Update PCs on the stack to point to recompiled code. 1555 RedirectActiveFunctions redirect_visitor( 1556 *shared, RedirectActiveFunctions::Mode::kUseDebugBytecode); 1557 redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top()); 1558 isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor); 1559 } 1560 1561 debug_info->set_flags( 1562 debug_info->flags(kRelaxedLoad) | DebugInfo::kPreparedForDebugExecution, 1563 kRelaxedStore); 1564} 1565 1566void Debug::InstallDebugBreakTrampoline() { 1567 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1568 // Check the list of debug infos whether the debug break trampoline needs to 1569 // be installed. If that's the case, iterate the heap for functions to rewire 1570 // to the trampoline. 1571 HandleScope scope(isolate_); 1572 // If there is a breakpoint at function entry, we need to install trampoline. 1573 bool needs_to_use_trampoline = false; 1574 // If there we break at entry to an api callback, we need to clear ICs. 1575 bool needs_to_clear_ic = false; 1576 for (DebugInfoListNode* current = debug_info_list_; current != nullptr; 1577 current = current->next()) { 1578 if (current->debug_info()->CanBreakAtEntry()) { 1579 needs_to_use_trampoline = true; 1580 if (current->debug_info()->shared().IsApiFunction()) { 1581 needs_to_clear_ic = true; 1582 break; 1583 } 1584 } 1585 } 1586 1587 if (!needs_to_use_trampoline) return; 1588 1589 Handle<CodeT> trampoline = BUILTIN_CODE(isolate_, DebugBreakTrampoline); 1590 std::vector<Handle<JSFunction>> needs_compile; 1591 { 1592 HeapObjectIterator iterator(isolate_->heap()); 1593 for (HeapObject obj = iterator.Next(); !obj.is_null(); 1594 obj = iterator.Next()) { 1595 if (needs_to_clear_ic && obj.IsFeedbackVector()) { 1596 FeedbackVector::cast(obj).ClearSlots(isolate_); 1597 continue; 1598 } else if (obj.IsJSFunction()) { 1599 JSFunction fun = JSFunction::cast(obj); 1600 SharedFunctionInfo shared = fun.shared(); 1601 if (!shared.HasDebugInfo()) continue; 1602 if (!shared.GetDebugInfo().CanBreakAtEntry()) continue; 1603 if (!fun.is_compiled()) { 1604 needs_compile.push_back(handle(fun, isolate_)); 1605 } else { 1606 fun.set_code(*trampoline); 1607 } 1608 } 1609 } 1610 } 1611 1612 // By overwriting the function code with DebugBreakTrampoline, which tailcalls 1613 // to shared code, we bypass CompileLazy. Perform CompileLazy here instead. 1614 for (Handle<JSFunction> fun : needs_compile) { 1615 IsCompiledScope is_compiled_scope; 1616 Compiler::Compile(isolate_, fun, Compiler::CLEAR_EXCEPTION, 1617 &is_compiled_scope); 1618 DCHECK(is_compiled_scope.is_compiled()); 1619 fun->set_code(*trampoline); 1620 } 1621} 1622 1623namespace { 1624template <typename Iterator> 1625void GetBreakablePositions(Iterator* it, int start_position, int end_position, 1626 std::vector<BreakLocation>* locations) { 1627 while (!it->Done()) { 1628 if (it->position() >= start_position && it->position() < end_position) { 1629 locations->push_back(it->GetBreakLocation()); 1630 } 1631 it->Next(); 1632 } 1633} 1634 1635void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position, 1636 int end_position, 1637 std::vector<BreakLocation>* locations) { 1638 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 1639 BreakIterator it(debug_info); 1640 GetBreakablePositions(&it, start_position, end_position, locations); 1641} 1642 1643bool CompileTopLevel(Isolate* isolate, Handle<Script> script) { 1644 UnoptimizedCompileState compile_state; 1645 ReusableUnoptimizedCompileState reusable_state(isolate); 1646 UnoptimizedCompileFlags flags = 1647 UnoptimizedCompileFlags::ForScriptCompile(isolate, *script); 1648 ParseInfo parse_info(isolate, flags, &compile_state, &reusable_state); 1649 IsCompiledScope is_compiled_scope; 1650 const MaybeHandle<SharedFunctionInfo> maybe_result = 1651 Compiler::CompileToplevel(&parse_info, script, isolate, 1652 &is_compiled_scope); 1653 if (maybe_result.is_null()) { 1654 if (isolate->has_pending_exception()) { 1655 isolate->clear_pending_exception(); 1656 } 1657 return false; 1658 } 1659 return true; 1660} 1661} // namespace 1662 1663bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position, 1664 int end_position, bool restrict_to_function, 1665 std::vector<BreakLocation>* locations) { 1666 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1667 if (restrict_to_function) { 1668 Handle<Object> result = 1669 FindInnermostContainingFunctionInfo(script, start_position); 1670 if (result->IsUndefined(isolate_)) return false; 1671 1672 // Make sure the function has set up the debug info. 1673 Handle<SharedFunctionInfo> shared = 1674 Handle<SharedFunctionInfo>::cast(result); 1675 if (!EnsureBreakInfo(shared)) return false; 1676 PrepareFunctionForDebugExecution(shared); 1677 1678 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 1679 FindBreakablePositions(debug_info, start_position, end_position, locations); 1680 return true; 1681 } 1682 1683 HandleScope scope(isolate_); 1684 std::vector<Handle<SharedFunctionInfo>> candidates; 1685 if (!FindSharedFunctionInfosIntersectingRange(script, start_position, 1686 end_position, &candidates)) { 1687 return false; 1688 } 1689 for (const auto& candidate : candidates) { 1690 CHECK(candidate->HasBreakInfo()); 1691 Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_); 1692 FindBreakablePositions(debug_info, start_position, end_position, locations); 1693 } 1694 return true; 1695} 1696 1697class SharedFunctionInfoFinder { 1698 public: 1699 explicit SharedFunctionInfoFinder(int target_position) 1700 : current_start_position_(kNoSourcePosition), 1701 target_position_(target_position) {} 1702 1703 void NewCandidate(SharedFunctionInfo shared, 1704 JSFunction closure = JSFunction()) { 1705 if (!shared.IsSubjectToDebugging()) return; 1706 int start_position = shared.function_token_position(); 1707 if (start_position == kNoSourcePosition) { 1708 start_position = shared.StartPosition(); 1709 } 1710 1711 if (start_position > target_position_) return; 1712 if (target_position_ >= shared.EndPosition()) { 1713 // The SharedFunctionInfo::EndPosition() is generally exclusive, but there 1714 // are assumptions in various places in the debugger that for script level 1715 // (toplevel function) there's an end position that is technically outside 1716 // the script. It might be worth revisiting the overall design here at 1717 // some point in the future. 1718 if (!shared.is_toplevel() || target_position_ > shared.EndPosition()) { 1719 return; 1720 } 1721 } 1722 1723 if (!current_candidate_.is_null()) { 1724 if (current_start_position_ == start_position && 1725 shared.EndPosition() == current_candidate_.EndPosition()) { 1726 // If we already have a matching closure, do not throw it away. 1727 if (!current_candidate_closure_.is_null() && closure.is_null()) return; 1728 // If a top-level function contains only one function 1729 // declaration the source for the top-level and the function 1730 // is the same. In that case prefer the non top-level function. 1731 if (!current_candidate_.is_toplevel() && shared.is_toplevel()) return; 1732 } else if (start_position < current_start_position_ || 1733 current_candidate_.EndPosition() < shared.EndPosition()) { 1734 return; 1735 } 1736 } 1737 1738 current_start_position_ = start_position; 1739 current_candidate_ = shared; 1740 current_candidate_closure_ = closure; 1741 } 1742 1743 SharedFunctionInfo Result() { return current_candidate_; } 1744 1745 JSFunction ResultClosure() { return current_candidate_closure_; } 1746 1747 private: 1748 SharedFunctionInfo current_candidate_; 1749 JSFunction current_candidate_closure_; 1750 int current_start_position_; 1751 int target_position_; 1752 DISALLOW_GARBAGE_COLLECTION(no_gc_) 1753}; 1754 1755namespace { 1756SharedFunctionInfo FindSharedFunctionInfoCandidate(int position, 1757 Handle<Script> script, 1758 Isolate* isolate) { 1759 SharedFunctionInfoFinder finder(position); 1760 SharedFunctionInfo::ScriptIterator iterator(isolate, *script); 1761 for (SharedFunctionInfo info = iterator.Next(); !info.is_null(); 1762 info = iterator.Next()) { 1763 finder.NewCandidate(info); 1764 } 1765 return finder.Result(); 1766} 1767} // namespace 1768 1769Handle<SharedFunctionInfo> Debug::FindClosestSharedFunctionInfoFromPosition( 1770 int position, Handle<Script> script, 1771 Handle<SharedFunctionInfo> outer_shared) { 1772 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1773 CHECK(outer_shared->HasBreakInfo()); 1774 int closest_position = FindBreakablePosition( 1775 Handle<DebugInfo>(outer_shared->GetDebugInfo(), isolate_), position); 1776 Handle<SharedFunctionInfo> closest_candidate = outer_shared; 1777 if (closest_position == position) return outer_shared; 1778 1779 const int start_position = outer_shared->StartPosition(); 1780 const int end_position = outer_shared->EndPosition(); 1781 if (start_position == end_position) return outer_shared; 1782 1783 if (closest_position == 0) closest_position = end_position; 1784 std::vector<Handle<SharedFunctionInfo>> candidates; 1785 // Find all shared function infos of functions that are intersecting from 1786 // the requested position until the end of the enclosing function. 1787 if (!FindSharedFunctionInfosIntersectingRange( 1788 script, position, closest_position, &candidates)) { 1789 return outer_shared; 1790 } 1791 1792 for (auto candidate : candidates) { 1793 CHECK(candidate->HasBreakInfo()); 1794 Handle<DebugInfo> debug_info(candidate->GetDebugInfo(), isolate_); 1795 const int candidate_position = FindBreakablePosition(debug_info, position); 1796 if (candidate_position >= position && 1797 candidate_position < closest_position) { 1798 closest_position = candidate_position; 1799 closest_candidate = candidate; 1800 } 1801 if (closest_position == position) break; 1802 } 1803 return closest_candidate; 1804} 1805 1806bool Debug::FindSharedFunctionInfosIntersectingRange( 1807 Handle<Script> script, int start_position, int end_position, 1808 std::vector<Handle<SharedFunctionInfo>>* intersecting_shared) { 1809 bool candidateSubsumesRange = false; 1810 bool triedTopLevelCompile = false; 1811 1812 while (true) { 1813 std::vector<Handle<SharedFunctionInfo>> candidates; 1814 std::vector<IsCompiledScope> compiled_scopes; 1815 { 1816 DisallowGarbageCollection no_gc; 1817 SharedFunctionInfo::ScriptIterator iterator(isolate_, *script); 1818 for (SharedFunctionInfo info = iterator.Next(); !info.is_null(); 1819 info = iterator.Next()) { 1820 if (info.EndPosition() < start_position || 1821 info.StartPosition() >= end_position) { 1822 continue; 1823 } 1824 candidateSubsumesRange |= info.StartPosition() <= start_position && 1825 info.EndPosition() >= end_position; 1826 if (!info.IsSubjectToDebugging()) continue; 1827 if (!info.is_compiled() && !info.allows_lazy_compilation()) continue; 1828 candidates.push_back(i::handle(info, isolate_)); 1829 } 1830 } 1831 1832 if (!triedTopLevelCompile && !candidateSubsumesRange && 1833 script->shared_function_info_count() > 0) { 1834 DCHECK_LE(script->shared_function_info_count(), 1835 script->shared_function_infos().length()); 1836 MaybeObject maybeToplevel = script->shared_function_infos().Get(0); 1837 HeapObject heap_object; 1838 const bool topLevelInfoExists = 1839 maybeToplevel->GetHeapObject(&heap_object) && 1840 !heap_object.IsUndefined(); 1841 if (!topLevelInfoExists) { 1842 triedTopLevelCompile = true; 1843 const bool success = CompileTopLevel(isolate_, script); 1844 if (!success) return false; 1845 continue; 1846 } 1847 } 1848 1849 bool was_compiled = false; 1850 for (const auto& candidate : candidates) { 1851 IsCompiledScope is_compiled_scope(candidate->is_compiled_scope(isolate_)); 1852 if (!is_compiled_scope.is_compiled()) { 1853 // Code that cannot be compiled lazily are internal and not debuggable. 1854 DCHECK(candidate->allows_lazy_compilation()); 1855 if (!Compiler::Compile(isolate_, candidate, Compiler::CLEAR_EXCEPTION, 1856 &is_compiled_scope)) { 1857 return false; 1858 } else { 1859 was_compiled = true; 1860 } 1861 } 1862 DCHECK(is_compiled_scope.is_compiled()); 1863 compiled_scopes.push_back(is_compiled_scope); 1864 if (!EnsureBreakInfo(candidate)) return false; 1865 PrepareFunctionForDebugExecution(candidate); 1866 } 1867 if (was_compiled) continue; 1868 *intersecting_shared = std::move(candidates); 1869 return true; 1870 } 1871 UNREACHABLE(); 1872} 1873 1874// We need to find a SFI for a literal that may not yet have been compiled yet, 1875// and there may not be a JSFunction referencing it. Find the SFI closest to 1876// the given position, compile it to reveal possible inner SFIs and repeat. 1877// While we are at this, also ensure code with debug break slots so that we do 1878// not have to compile a SFI without JSFunction, which is paifu for those that 1879// cannot be compiled without context (need to find outer compilable SFI etc.) 1880Handle<Object> Debug::FindInnermostContainingFunctionInfo(Handle<Script> script, 1881 int position) { 1882 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1883 for (int iteration = 0;; iteration++) { 1884 // Go through all shared function infos associated with this script to 1885 // find the innermost function containing this position. 1886 // If there is no shared function info for this script at all, there is 1887 // no point in looking for it by walking the heap. 1888 1889 SharedFunctionInfo shared; 1890 IsCompiledScope is_compiled_scope; 1891 { 1892 shared = FindSharedFunctionInfoCandidate(position, script, isolate_); 1893 if (shared.is_null()) { 1894 if (iteration > 0) break; 1895 // It might be that the shared function info is not available as the 1896 // top level functions are removed due to the GC. Try to recompile 1897 // the top level functions. 1898 const bool success = CompileTopLevel(isolate_, script); 1899 if (!success) break; 1900 continue; 1901 } 1902 // We found it if it's already compiled. 1903 is_compiled_scope = shared.is_compiled_scope(isolate_); 1904 if (is_compiled_scope.is_compiled()) { 1905 Handle<SharedFunctionInfo> shared_handle(shared, isolate_); 1906 // If the iteration count is larger than 1, we had to compile the outer 1907 // function in order to create this shared function info. So there can 1908 // be no JSFunction referencing it. We can anticipate creating a debug 1909 // info while bypassing PrepareFunctionForDebugExecution. 1910 if (iteration > 1) { 1911 CreateBreakInfo(shared_handle); 1912 } 1913 return shared_handle; 1914 } 1915 } 1916 // If not, compile to reveal inner functions. 1917 HandleScope scope(isolate_); 1918 // Code that cannot be compiled lazily are internal and not debuggable. 1919 DCHECK(shared.allows_lazy_compilation()); 1920 if (!Compiler::Compile(isolate_, handle(shared, isolate_), 1921 Compiler::CLEAR_EXCEPTION, &is_compiled_scope)) { 1922 break; 1923 } 1924 } 1925 return isolate_->factory()->undefined_value(); 1926} 1927 1928// Ensures the debug information is present for shared. 1929bool Debug::EnsureBreakInfo(Handle<SharedFunctionInfo> shared) { 1930 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1931 // Return if we already have the break info for shared. 1932 if (shared->HasBreakInfo()) return true; 1933 if (!shared->IsSubjectToDebugging() && !CanBreakAtEntry(shared)) { 1934 return false; 1935 } 1936 IsCompiledScope is_compiled_scope = shared->is_compiled_scope(isolate_); 1937 if (!is_compiled_scope.is_compiled() && 1938 !Compiler::Compile(isolate_, shared, Compiler::CLEAR_EXCEPTION, 1939 &is_compiled_scope, CreateSourcePositions::kYes)) { 1940 return false; 1941 } 1942 CreateBreakInfo(shared); 1943 return true; 1944} 1945 1946void Debug::CreateBreakInfo(Handle<SharedFunctionInfo> shared) { 1947 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1948 HandleScope scope(isolate_); 1949 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 1950 1951 // Initialize with break information. 1952 1953 DCHECK(!debug_info->HasBreakInfo()); 1954 1955 Factory* factory = isolate_->factory(); 1956 Handle<FixedArray> break_points( 1957 factory->NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction)); 1958 1959 int flags = debug_info->flags(kRelaxedLoad); 1960 flags |= DebugInfo::kHasBreakInfo; 1961 if (CanBreakAtEntry(shared)) flags |= DebugInfo::kCanBreakAtEntry; 1962 debug_info->set_flags(flags, kRelaxedStore); 1963 debug_info->set_break_points(*break_points); 1964 1965 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared); 1966} 1967 1968Handle<DebugInfo> Debug::GetOrCreateDebugInfo( 1969 Handle<SharedFunctionInfo> shared) { 1970 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1971 if (shared->HasDebugInfo()) return handle(shared->GetDebugInfo(), isolate_); 1972 1973 // Create debug info and add it to the list. 1974 Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared); 1975 DebugInfoListNode* node = new DebugInfoListNode(isolate_, *debug_info); 1976 node->set_next(debug_info_list_); 1977 debug_info_list_ = node; 1978 1979 return debug_info; 1980} 1981 1982void Debug::InstallCoverageInfo(Handle<SharedFunctionInfo> shared, 1983 Handle<CoverageInfo> coverage_info) { 1984 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 1985 DCHECK(!coverage_info.is_null()); 1986 1987 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 1988 1989 DCHECK(!debug_info->HasCoverageInfo()); 1990 1991 debug_info->set_flags( 1992 debug_info->flags(kRelaxedLoad) | DebugInfo::kHasCoverageInfo, 1993 kRelaxedStore); 1994 debug_info->set_coverage_info(*coverage_info); 1995} 1996 1997void Debug::RemoveAllCoverageInfos() { 1998 ClearAllDebugInfos( 1999 [=](Handle<DebugInfo> info) { info->ClearCoverageInfo(isolate_); }); 2000} 2001 2002void Debug::ClearAllDebuggerHints() { 2003 ClearAllDebugInfos( 2004 [=](Handle<DebugInfo> info) { info->set_debugger_hints(0); }); 2005} 2006 2007void Debug::FindDebugInfo(Handle<DebugInfo> debug_info, 2008 DebugInfoListNode** prev, DebugInfoListNode** curr) { 2009 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2010 HandleScope scope(isolate_); 2011 *prev = nullptr; 2012 *curr = debug_info_list_; 2013 while (*curr != nullptr) { 2014 if ((*curr)->debug_info().is_identical_to(debug_info)) return; 2015 *prev = *curr; 2016 *curr = (*curr)->next(); 2017 } 2018 2019 UNREACHABLE(); 2020} 2021 2022void Debug::ClearAllDebugInfos(const DebugInfoClearFunction& clear_function) { 2023 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2024 DebugInfoListNode* prev = nullptr; 2025 DebugInfoListNode* current = debug_info_list_; 2026 while (current != nullptr) { 2027 DebugInfoListNode* next = current->next(); 2028 Handle<DebugInfo> debug_info = current->debug_info(); 2029 clear_function(debug_info); 2030 if (debug_info->IsEmpty()) { 2031 FreeDebugInfoListNode(prev, current); 2032 current = next; 2033 } else { 2034 prev = current; 2035 current = next; 2036 } 2037 } 2038} 2039 2040void Debug::RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info) { 2041 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2042 debug_info->ClearBreakInfo(isolate_); 2043 if (debug_info->IsEmpty()) { 2044 DebugInfoListNode* prev; 2045 DebugInfoListNode* node; 2046 FindDebugInfo(debug_info, &prev, &node); 2047 FreeDebugInfoListNode(prev, node); 2048 } 2049} 2050 2051void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev, 2052 DebugInfoListNode* node) { 2053 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2054 DCHECK(node->debug_info()->IsEmpty()); 2055 2056 // Unlink from list. If prev is nullptr we are looking at the first element. 2057 if (prev == nullptr) { 2058 debug_info_list_ = node->next(); 2059 } else { 2060 prev->set_next(node->next()); 2061 } 2062 2063 // Pack script back into the 2064 // SFI::script_or_debug_info field. 2065 Handle<DebugInfo> debug_info(node->debug_info()); 2066 debug_info->shared().set_script_or_debug_info(debug_info->script(), 2067 kReleaseStore); 2068 2069 delete node; 2070} 2071 2072bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { 2073 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2074 HandleScope scope(isolate_); 2075 2076 // Get the executing function in which the debug break occurred. 2077 Handle<SharedFunctionInfo> shared(frame->function().shared(), isolate_); 2078 2079 // With no debug info there are no break points, so we can't be at a return. 2080 if (!shared->HasBreakInfo()) return false; 2081 2082 DCHECK(!frame->is_optimized()); 2083 Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); 2084 BreakLocation location = BreakLocation::FromFrame(debug_info, frame); 2085 return location.IsReturn(); 2086} 2087 2088Handle<FixedArray> Debug::GetLoadedScripts() { 2089 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2090 isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, 2091 GarbageCollectionReason::kDebugger); 2092 Factory* factory = isolate_->factory(); 2093 if (!factory->script_list()->IsWeakArrayList()) { 2094 return factory->empty_fixed_array(); 2095 } 2096 Handle<WeakArrayList> array = 2097 Handle<WeakArrayList>::cast(factory->script_list()); 2098 Handle<FixedArray> results = factory->NewFixedArray(array->length()); 2099 int length = 0; 2100 { 2101 Script::Iterator iterator(isolate_); 2102 for (Script script = iterator.Next(); !script.is_null(); 2103 script = iterator.Next()) { 2104 if (script.HasValidSource()) results->set(length++, script); 2105 } 2106 } 2107 return FixedArray::ShrinkOrEmpty(isolate_, results, length); 2108} 2109 2110base::Optional<Object> Debug::OnThrow(Handle<Object> exception) { 2111 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2112 if (in_debug_scope() || ignore_events()) return {}; 2113 // Temporarily clear any scheduled_exception to allow evaluating 2114 // JavaScript from the debug event handler. 2115 HandleScope scope(isolate_); 2116 Handle<Object> scheduled_exception; 2117 if (isolate_->has_scheduled_exception()) { 2118 scheduled_exception = handle(isolate_->scheduled_exception(), isolate_); 2119 isolate_->clear_scheduled_exception(); 2120 } 2121 Handle<Object> maybe_promise = isolate_->GetPromiseOnStackOnThrow(); 2122 OnException(exception, maybe_promise, 2123 maybe_promise->IsJSPromise() ? v8::debug::kPromiseRejection 2124 : v8::debug::kException); 2125 if (!scheduled_exception.is_null()) { 2126 isolate_->set_scheduled_exception(*scheduled_exception); 2127 } 2128 PrepareStepOnThrow(); 2129 // If the OnException handler requested termination, then indicated this to 2130 // our caller Isolate::Throw so it can deal with it immediatelly instead of 2131 // throwing the original exception. 2132 if (isolate_->stack_guard()->CheckTerminateExecution()) { 2133 isolate_->stack_guard()->ClearTerminateExecution(); 2134 return isolate_->TerminateExecution(); 2135 } 2136 return {}; 2137} 2138 2139void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) { 2140 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2141 if (in_debug_scope() || ignore_events()) return; 2142 HandleScope scope(isolate_); 2143 // Check whether the promise has been marked as having triggered a message. 2144 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); 2145 if (!promise->IsJSObject() || 2146 JSReceiver::GetDataProperty(isolate_, Handle<JSObject>::cast(promise), 2147 key) 2148 ->IsUndefined(isolate_)) { 2149 OnException(value, promise, v8::debug::kPromiseRejection); 2150 } 2151} 2152 2153bool Debug::IsExceptionBlackboxed(bool uncaught) { 2154 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2155 // Uncaught exception is blackboxed if all current frames are blackboxed, 2156 // caught exception if top frame is blackboxed. 2157 StackTraceFrameIterator it(isolate_); 2158#if V8_ENABLE_WEBASSEMBLY 2159 while (!it.done() && it.is_wasm()) it.Advance(); 2160#endif // V8_ENABLE_WEBASSEMBLY 2161 bool is_top_frame_blackboxed = 2162 !it.done() ? IsFrameBlackboxed(it.javascript_frame()) : true; 2163 if (!uncaught || !is_top_frame_blackboxed) return is_top_frame_blackboxed; 2164 return AllFramesOnStackAreBlackboxed(); 2165} 2166 2167bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) { 2168 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2169 HandleScope scope(isolate_); 2170 std::vector<Handle<SharedFunctionInfo>> infos; 2171 frame->GetFunctions(&infos); 2172 for (const auto& info : infos) { 2173 if (!IsBlackboxed(info)) return false; 2174 } 2175 return true; 2176} 2177 2178void Debug::OnException(Handle<Object> exception, Handle<Object> promise, 2179 v8::debug::ExceptionType exception_type) { 2180 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2181 // Do not trigger exception event on stack overflow. We cannot perform 2182 // anything useful for debugging in that situation. 2183 StackLimitCheck stack_limit_check(isolate_); 2184 if (stack_limit_check.JsHasOverflowed()) return; 2185 2186 // Return if the event has nowhere to go. 2187 if (!debug_delegate_) return; 2188 2189 // Return if we are not interested in exception events. 2190 if (!break_on_exception_ && !break_on_uncaught_exception_) return; 2191 2192 Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher(); 2193 2194 bool uncaught = catch_type == Isolate::NOT_CAUGHT; 2195 if (promise->IsJSObject()) { 2196 Handle<JSObject> jsobject = Handle<JSObject>::cast(promise); 2197 // Mark the promise as already having triggered a message. 2198 Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); 2199 Object::SetProperty(isolate_, jsobject, key, key, StoreOrigin::kMaybeKeyed, 2200 Just(ShouldThrow::kThrowOnError)) 2201 .Assert(); 2202 // Check whether the promise reject is considered an uncaught exception. 2203 if (jsobject->IsJSPromise()) { 2204 Handle<JSPromise> jspromise = Handle<JSPromise>::cast(jsobject); 2205 2206 // Ignore the exception if the promise was marked as silent 2207 if (jspromise->is_silent()) return; 2208 2209 uncaught = !isolate_->PromiseHasUserDefinedRejectHandler(jspromise); 2210 } else { 2211 uncaught = true; 2212 } 2213 } 2214 2215 // Return if the exception is caught and we only care about uncaught 2216 // exceptions. 2217 if (!uncaught && !break_on_exception_) { 2218 DCHECK(break_on_uncaught_exception_); 2219 return; 2220 } 2221 2222 { 2223 JavaScriptFrameIterator it(isolate_); 2224 // Check whether the top frame is blackboxed or the break location is muted. 2225 if (!it.done() && (IsMutedAtCurrentLocation(it.frame()) || 2226 IsExceptionBlackboxed(uncaught))) { 2227 return; 2228 } 2229 if (it.done()) return; // Do not trigger an event with an empty stack. 2230 } 2231 2232 DebugScope debug_scope(this); 2233 HandleScope scope(isolate_); 2234 DisableBreak no_recursive_break(this); 2235 2236 { 2237 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); 2238 Handle<Context> native_context(isolate_->native_context()); 2239 debug_delegate_->ExceptionThrown( 2240 v8::Utils::ToLocal(native_context), v8::Utils::ToLocal(exception), 2241 v8::Utils::ToLocal(promise), uncaught, exception_type); 2242 } 2243} 2244 2245void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit, 2246 StepAction lastStepAction, 2247 v8::debug::BreakReasons break_reasons) { 2248 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2249 DCHECK(!break_points_hit.is_null()); 2250 // The caller provided for DebugScope. 2251 AssertDebugContext(); 2252 // Bail out if there is no listener for this event 2253 if (ignore_events()) return; 2254 2255#ifdef DEBUG 2256 PrintBreakLocation(); 2257#endif // DEBUG 2258 2259 if (!debug_delegate_) return; 2260 DCHECK(in_debug_scope()); 2261 HandleScope scope(isolate_); 2262 DisableBreak no_recursive_break(this); 2263 2264 if ((lastStepAction == StepAction::StepOver || 2265 lastStepAction == StepAction::StepInto) && 2266 ShouldBeSkipped()) { 2267 PrepareStep(lastStepAction); 2268 return; 2269 } 2270 2271 std::vector<int> inspector_break_points_hit; 2272 // This array contains breakpoints installed using JS debug API. 2273 for (int i = 0; i < break_points_hit->length(); ++i) { 2274 BreakPoint break_point = BreakPoint::cast(break_points_hit->get(i)); 2275 inspector_break_points_hit.push_back(break_point.id()); 2276 } 2277 { 2278 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); 2279 Handle<Context> native_context(isolate_->native_context()); 2280 if (lastStepAction != StepAction::StepNone) 2281 break_reasons.Add(debug::BreakReason::kStep); 2282 debug_delegate_->BreakProgramRequested(v8::Utils::ToLocal(native_context), 2283 inspector_break_points_hit, 2284 break_reasons); 2285 } 2286} 2287 2288namespace { 2289debug::Location GetDebugLocation(Handle<Script> script, int source_position) { 2290 Script::PositionInfo info; 2291 Script::GetPositionInfo(script, source_position, &info, Script::WITH_OFFSET); 2292 // V8 provides ScriptCompiler::CompileFunction method which takes 2293 // expression and compile it as anonymous function like (function() .. 2294 // expression ..). To produce correct locations for stmts inside of this 2295 // expression V8 compile this function with negative offset. Instead of stmt 2296 // position blackboxing use function start position which is negative in 2297 // described case. 2298 return debug::Location(std::max(info.line, 0), std::max(info.column, 0)); 2299} 2300} // namespace 2301 2302bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { 2303 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2304 if (!debug_delegate_) return !shared->IsSubjectToDebugging(); 2305 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 2306 if (!debug_info->computed_debug_is_blackboxed()) { 2307 bool is_blackboxed = 2308 !shared->IsSubjectToDebugging() || !shared->script().IsScript(); 2309 if (!is_blackboxed) { 2310 SuppressDebug while_processing(this); 2311 HandleScope handle_scope(isolate_); 2312 PostponeInterruptsScope no_interrupts(isolate_); 2313 DisableBreak no_recursive_break(this); 2314 DCHECK(shared->script().IsScript()); 2315 Handle<Script> script(Script::cast(shared->script()), isolate_); 2316 DCHECK(script->IsUserJavaScript()); 2317 debug::Location start = GetDebugLocation(script, shared->StartPosition()); 2318 debug::Location end = GetDebugLocation(script, shared->EndPosition()); 2319 { 2320 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); 2321 is_blackboxed = debug_delegate_->IsFunctionBlackboxed( 2322 ToApiHandle<debug::Script>(script), start, end); 2323 } 2324 } 2325 debug_info->set_debug_is_blackboxed(is_blackboxed); 2326 debug_info->set_computed_debug_is_blackboxed(true); 2327 } 2328 return debug_info->debug_is_blackboxed(); 2329} 2330 2331bool Debug::ShouldBeSkipped() { 2332 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2333 SuppressDebug while_processing(this); 2334 PostponeInterruptsScope no_interrupts(isolate_); 2335 DisableBreak no_recursive_break(this); 2336 2337 StackTraceFrameIterator iterator(isolate_); 2338 FrameSummary summary = iterator.GetTopValidFrame(); 2339 Handle<Object> script_obj = summary.script(); 2340 if (!script_obj->IsScript()) return false; 2341 2342 Handle<Script> script = Handle<Script>::cast(script_obj); 2343 summary.EnsureSourcePositionsAvailable(); 2344 int source_position = summary.SourcePosition(); 2345 int line = Script::GetLineNumber(script, source_position); 2346 int column = Script::GetColumnNumber(script, source_position); 2347 2348 { 2349 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); 2350 return debug_delegate_->ShouldBeSkipped(ToApiHandle<debug::Script>(script), 2351 line, column); 2352 } 2353} 2354 2355bool Debug::AllFramesOnStackAreBlackboxed() { 2356 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2357 HandleScope scope(isolate_); 2358 for (StackTraceFrameIterator it(isolate_); !it.done(); it.Advance()) { 2359 if (!it.is_javascript()) continue; 2360 if (!IsFrameBlackboxed(it.javascript_frame())) return false; 2361 } 2362 return true; 2363} 2364 2365bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) { 2366 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2367 // Allow break at entry for builtin functions. 2368 if (shared->native() || shared->IsApiFunction()) { 2369 // Functions that are subject to debugging can have regular breakpoints. 2370 DCHECK(!shared->IsSubjectToDebugging()); 2371 return true; 2372 } 2373 return false; 2374} 2375 2376bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source, 2377 bool preview, debug::LiveEditResult* result) { 2378 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2379 DebugScope debug_scope(this); 2380 feature_tracker()->Track(DebugFeatureTracker::kLiveEdit); 2381 running_live_edit_ = true; 2382 LiveEdit::PatchScript(isolate_, script, source, preview, result); 2383 running_live_edit_ = false; 2384 return result->status == debug::LiveEditResult::OK; 2385} 2386 2387void Debug::OnCompileError(Handle<Script> script) { 2388 ProcessCompileEvent(true, script); 2389} 2390 2391void Debug::OnAfterCompile(Handle<Script> script) { 2392 ProcessCompileEvent(false, script); 2393} 2394 2395void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) { 2396 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2397 // Ignore temporary scripts. 2398 if (script->id() == Script::kTemporaryScriptId) return; 2399 // TODO(kozyatinskiy): teach devtools to work with liveedit scripts better 2400 // first and then remove this fast return. 2401 if (running_live_edit_) return; 2402 // Attach the correct debug id to the script. The debug id is used by the 2403 // inspector to filter scripts by native context. 2404 script->set_context_data(isolate_->native_context()->debug_context_id()); 2405 if (ignore_events()) return; 2406#if V8_ENABLE_WEBASSEMBLY 2407 if (!script->IsUserJavaScript() && script->type() != i::Script::TYPE_WASM) { 2408 return; 2409 } 2410#else 2411 if (!script->IsUserJavaScript()) return; 2412#endif // V8_ENABLE_WEBASSEMBLY 2413 if (!debug_delegate_) return; 2414 SuppressDebug while_processing(this); 2415 DebugScope debug_scope(this); 2416 HandleScope scope(isolate_); 2417 DisableBreak no_recursive_break(this); 2418 AllowJavascriptExecution allow_script(isolate_); 2419 { 2420 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebuggerCallback); 2421 debug_delegate_->ScriptCompiled(ToApiHandle<debug::Script>(script), 2422 running_live_edit_, has_compile_error); 2423 } 2424} 2425 2426int Debug::CurrentFrameCount() { 2427 StackTraceFrameIterator it(isolate_); 2428 if (break_frame_id() != StackFrameId::NO_ID) { 2429 // Skip to break frame. 2430 DCHECK(in_debug_scope()); 2431 while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance(); 2432 } 2433 int counter = 0; 2434 for (; !it.done(); it.Advance()) { 2435 counter += it.FrameFunctionCount(); 2436 } 2437 return counter; 2438} 2439 2440void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) { 2441 debug_delegate_ = delegate; 2442 UpdateState(); 2443} 2444 2445void Debug::UpdateState() { 2446 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2447 bool is_active = debug_delegate_ != nullptr; 2448 if (is_active == is_active_) return; 2449 if (is_active) { 2450 // Note that the debug context could have already been loaded to 2451 // bootstrap test cases. 2452 isolate_->compilation_cache()->DisableScriptAndEval(); 2453 isolate_->CollectSourcePositionsForAllBytecodeArrays(); 2454 is_active = true; 2455 feature_tracker()->Track(DebugFeatureTracker::kActive); 2456 } else { 2457 isolate_->compilation_cache()->EnableScriptAndEval(); 2458 Unload(); 2459 } 2460 is_active_ = is_active; 2461 isolate_->PromiseHookStateUpdated(); 2462} 2463 2464void Debug::UpdateHookOnFunctionCall() { 2465 STATIC_ASSERT(LastStepAction == StepInto); 2466 hook_on_function_call_ = 2467 thread_local_.last_step_action_ == StepInto || 2468 isolate_->debug_execution_mode() == DebugInfo::kSideEffects || 2469 thread_local_.break_on_next_function_call_; 2470} 2471 2472void Debug::HandleDebugBreak(IgnoreBreakMode ignore_break_mode, 2473 v8::debug::BreakReasons break_reasons) { 2474 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2475 // Ignore debug break during bootstrapping. 2476 if (isolate_->bootstrapper()->IsActive()) return; 2477 // Just continue if breaks are disabled. 2478 if (break_disabled()) return; 2479 // Ignore debug break if debugger is not active. 2480 if (!is_active()) return; 2481 2482 StackLimitCheck check(isolate_); 2483 if (check.HasOverflowed()) return; 2484 2485 HandleScope scope(isolate_); 2486 MaybeHandle<FixedArray> break_points; 2487 { 2488 JavaScriptFrameIterator it(isolate_); 2489 DCHECK(!it.done()); 2490 Object fun = it.frame()->function(); 2491 if (fun.IsJSFunction()) { 2492 Handle<JSFunction> function(JSFunction::cast(fun), isolate_); 2493 // Don't stop in builtin and blackboxed functions. 2494 Handle<SharedFunctionInfo> shared(function->shared(), isolate_); 2495 bool ignore_break = ignore_break_mode == kIgnoreIfTopFrameBlackboxed 2496 ? IsBlackboxed(shared) 2497 : AllFramesOnStackAreBlackboxed(); 2498 if (ignore_break) return; 2499 if (function->shared().HasBreakInfo()) { 2500 Handle<DebugInfo> debug_info(function->shared().GetDebugInfo(), 2501 isolate_); 2502 // Enter the debugger. 2503 DebugScope debug_scope(this); 2504 2505 std::vector<BreakLocation> break_locations; 2506 BreakLocation::AllAtCurrentStatement(debug_info, it.frame(), 2507 &break_locations); 2508 2509 for (size_t i = 0; i < break_locations.size(); i++) { 2510 if (IsBreakOnInstrumentation(debug_info, break_locations[i])) { 2511 OnInstrumentationBreak(); 2512 break; 2513 } 2514 } 2515 2516 bool has_break_points; 2517 break_points = CheckBreakPointsForLocations(debug_info, break_locations, 2518 &has_break_points); 2519 bool is_muted = has_break_points && break_points.is_null(); 2520 // If we get to this point, a break was triggered because e.g. of a 2521 // debugger statement, an assert, .. . However, we do not stop if this 2522 // position "is muted", which happens if a conditional breakpoint at 2523 // this point evaluates to false. 2524 if (is_muted) return; 2525 } 2526 } 2527 } 2528 2529 StepAction lastStepAction = last_step_action(); 2530 2531 // Clear stepping to avoid duplicate breaks. 2532 ClearStepping(); 2533 2534 DebugScope debug_scope(this); 2535 OnDebugBreak(break_points.is_null() ? isolate_->factory()->empty_fixed_array() 2536 : break_points.ToHandleChecked(), 2537 lastStepAction, break_reasons); 2538} 2539 2540#ifdef DEBUG 2541void Debug::PrintBreakLocation() { 2542 if (!FLAG_print_break_location) return; 2543 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2544 HandleScope scope(isolate_); 2545 StackTraceFrameIterator iterator(isolate_); 2546 if (iterator.done()) return; 2547 CommonFrame* frame = iterator.frame(); 2548 std::vector<FrameSummary> frames; 2549 frame->Summarize(&frames); 2550 int inlined_frame_index = static_cast<int>(frames.size() - 1); 2551 FrameInspector inspector(frame, inlined_frame_index, isolate_); 2552 int source_position = inspector.GetSourcePosition(); 2553 Handle<Object> script_obj = inspector.GetScript(); 2554 PrintF("[debug] break in function '"); 2555 inspector.GetFunctionName()->PrintOn(stdout); 2556 PrintF("'.\n"); 2557 if (script_obj->IsScript()) { 2558 Handle<Script> script = Handle<Script>::cast(script_obj); 2559 Handle<String> source(String::cast(script->source()), isolate_); 2560 Script::InitLineEnds(isolate_, script); 2561 int line = 2562 Script::GetLineNumber(script, source_position) - script->line_offset(); 2563 int column = Script::GetColumnNumber(script, source_position) - 2564 (line == 0 ? script->column_offset() : 0); 2565 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()), 2566 isolate_); 2567 int line_start = line == 0 ? 0 : Smi::ToInt(line_ends->get(line - 1)) + 1; 2568 int line_end = Smi::ToInt(line_ends->get(line)); 2569 DisallowGarbageCollection no_gc; 2570 String::FlatContent content = source->GetFlatContent(no_gc); 2571 if (content.IsOneByte()) { 2572 PrintF("[debug] %.*s\n", line_end - line_start, 2573 content.ToOneByteVector().begin() + line_start); 2574 PrintF("[debug] "); 2575 for (int i = 0; i < column; i++) PrintF(" "); 2576 PrintF("^\n"); 2577 } else { 2578 PrintF("[debug] at line %d column %d\n", line, column); 2579 } 2580 } 2581} 2582#endif // DEBUG 2583 2584DebugScope::DebugScope(Debug* debug) 2585 : debug_(debug), 2586 prev_(reinterpret_cast<DebugScope*>( 2587 base::Relaxed_Load(&debug->thread_local_.current_debug_scope_))), 2588 no_interrupts_(debug_->isolate_) { 2589 // Link recursive debugger entry. 2590 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_, 2591 reinterpret_cast<base::AtomicWord>(this)); 2592 // Store the previous frame id and return value. 2593 break_frame_id_ = debug_->break_frame_id(); 2594 2595 // Create the new break info. If there is no proper frames there is no break 2596 // frame id. 2597 StackTraceFrameIterator it(isolate()); 2598 bool has_frames = !it.done(); 2599 debug_->thread_local_.break_frame_id_ = 2600 has_frames ? it.frame()->id() : StackFrameId::NO_ID; 2601 2602 debug_->UpdateState(); 2603} 2604 2605void DebugScope::set_terminate_on_resume() { terminate_on_resume_ = true; } 2606 2607DebugScope::~DebugScope() { 2608 // Terminate on resume must have been handled by retrieving it, if this is 2609 // the outer scope. 2610 if (terminate_on_resume_) { 2611 if (!prev_) { 2612 debug_->isolate_->stack_guard()->RequestTerminateExecution(); 2613 } else { 2614 prev_->set_terminate_on_resume(); 2615 } 2616 } 2617 // Leaving this debugger entry. 2618 base::Relaxed_Store(&debug_->thread_local_.current_debug_scope_, 2619 reinterpret_cast<base::AtomicWord>(prev_)); 2620 2621 // Restore to the previous break state. 2622 debug_->thread_local_.break_frame_id_ = break_frame_id_; 2623 2624 debug_->UpdateState(); 2625} 2626 2627ReturnValueScope::ReturnValueScope(Debug* debug) : debug_(debug) { 2628 return_value_ = debug_->return_value_handle(); 2629} 2630 2631ReturnValueScope::~ReturnValueScope() { 2632 debug_->set_return_value(*return_value_); 2633} 2634 2635void Debug::UpdateDebugInfosForExecutionMode() { 2636 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2637 // Walk all debug infos and update their execution mode if it is different 2638 // from the isolate execution mode. 2639 DebugInfoListNode* current = debug_info_list_; 2640 while (current != nullptr) { 2641 Handle<DebugInfo> debug_info = current->debug_info(); 2642 if (debug_info->HasInstrumentedBytecodeArray() && 2643 debug_info->DebugExecutionMode() != isolate_->debug_execution_mode()) { 2644 DCHECK(debug_info->shared().HasBytecodeArray()); 2645 if (isolate_->debug_execution_mode() == DebugInfo::kBreakpoints) { 2646 ClearSideEffectChecks(debug_info); 2647 ApplyBreakPoints(debug_info); 2648 } else { 2649 ClearBreakPoints(debug_info); 2650 ApplySideEffectChecks(debug_info); 2651 } 2652 } 2653 current = current->next(); 2654 } 2655} 2656 2657void Debug::SetTerminateOnResume() { 2658 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2659 DebugScope* scope = reinterpret_cast<DebugScope*>( 2660 base::Acquire_Load(&thread_local_.current_debug_scope_)); 2661 CHECK_NOT_NULL(scope); 2662 scope->set_terminate_on_resume(); 2663} 2664 2665void Debug::StartSideEffectCheckMode() { 2666 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2667 DCHECK(isolate_->debug_execution_mode() != DebugInfo::kSideEffects); 2668 isolate_->set_debug_execution_mode(DebugInfo::kSideEffects); 2669 UpdateHookOnFunctionCall(); 2670 side_effect_check_failed_ = false; 2671 2672 DCHECK(!temporary_objects_); 2673 temporary_objects_.reset(new TemporaryObjectsTracker()); 2674 isolate_->heap()->AddHeapObjectAllocationTracker(temporary_objects_.get()); 2675 Handle<FixedArray> array(isolate_->native_context()->regexp_last_match_info(), 2676 isolate_); 2677 regexp_match_info_ = 2678 Handle<RegExpMatchInfo>::cast(isolate_->factory()->CopyFixedArray(array)); 2679 2680 // Update debug infos to have correct execution mode. 2681 UpdateDebugInfosForExecutionMode(); 2682} 2683 2684void Debug::StopSideEffectCheckMode() { 2685 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2686 DCHECK(isolate_->debug_execution_mode() == DebugInfo::kSideEffects); 2687 if (side_effect_check_failed_) { 2688 DCHECK(isolate_->has_pending_exception()); 2689 DCHECK_EQ(ReadOnlyRoots(isolate_).termination_exception(), 2690 isolate_->pending_exception()); 2691 // Convert the termination exception into a regular exception. 2692 isolate_->CancelTerminateExecution(); 2693 isolate_->Throw(*isolate_->factory()->NewEvalError( 2694 MessageTemplate::kNoSideEffectDebugEvaluate)); 2695 } 2696 isolate_->set_debug_execution_mode(DebugInfo::kBreakpoints); 2697 UpdateHookOnFunctionCall(); 2698 side_effect_check_failed_ = false; 2699 2700 DCHECK(temporary_objects_); 2701 isolate_->heap()->RemoveHeapObjectAllocationTracker(temporary_objects_.get()); 2702 temporary_objects_.reset(); 2703 isolate_->native_context()->set_regexp_last_match_info(*regexp_match_info_); 2704 regexp_match_info_ = Handle<RegExpMatchInfo>::null(); 2705 2706 // Update debug infos to have correct execution mode. 2707 UpdateDebugInfosForExecutionMode(); 2708} 2709 2710void Debug::ApplySideEffectChecks(Handle<DebugInfo> debug_info) { 2711 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2712 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 2713 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(), 2714 isolate_); 2715 DebugEvaluate::ApplySideEffectChecks(debug_bytecode); 2716 debug_info->SetDebugExecutionMode(DebugInfo::kSideEffects); 2717} 2718 2719void Debug::ClearSideEffectChecks(Handle<DebugInfo> debug_info) { 2720 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2721 DCHECK(debug_info->HasInstrumentedBytecodeArray()); 2722 Handle<BytecodeArray> debug_bytecode(debug_info->DebugBytecodeArray(), 2723 isolate_); 2724 Handle<BytecodeArray> original(debug_info->OriginalBytecodeArray(), isolate_); 2725 for (interpreter::BytecodeArrayIterator it(debug_bytecode); !it.done(); 2726 it.Advance()) { 2727 // Restore from original. This may copy only the scaling prefix, which is 2728 // correct, since we patch scaling prefixes to debug breaks if exists. 2729 debug_bytecode->set(it.current_offset(), 2730 original->get(it.current_offset())); 2731 } 2732} 2733 2734bool Debug::PerformSideEffectCheck(Handle<JSFunction> function, 2735 Handle<Object> receiver) { 2736 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2737 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects); 2738 DisallowJavascriptExecution no_js(isolate_); 2739 IsCompiledScope is_compiled_scope( 2740 function->shared().is_compiled_scope(isolate_)); 2741 if (!function->is_compiled() && 2742 !Compiler::Compile(isolate_, function, Compiler::KEEP_EXCEPTION, 2743 &is_compiled_scope)) { 2744 return false; 2745 } 2746 DCHECK(is_compiled_scope.is_compiled()); 2747 Handle<SharedFunctionInfo> shared(function->shared(), isolate_); 2748 Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared); 2749 DebugInfo::SideEffectState side_effect_state = 2750 debug_info->GetSideEffectState(isolate_); 2751 switch (side_effect_state) { 2752 case DebugInfo::kHasSideEffects: 2753 if (FLAG_trace_side_effect_free_debug_evaluate) { 2754 PrintF("[debug-evaluate] Function %s failed side effect check.\n", 2755 function->shared().DebugNameCStr().get()); 2756 } 2757 side_effect_check_failed_ = true; 2758 // Throw an uncatchable termination exception. 2759 isolate_->TerminateExecution(); 2760 return false; 2761 case DebugInfo::kRequiresRuntimeChecks: { 2762 if (!shared->HasBytecodeArray()) { 2763 return PerformSideEffectCheckForObject(receiver); 2764 } 2765 // If function has bytecode array then prepare function for debug 2766 // execution to perform runtime side effect checks. 2767 DCHECK(shared->is_compiled()); 2768 PrepareFunctionForDebugExecution(shared); 2769 ApplySideEffectChecks(debug_info); 2770 return true; 2771 } 2772 case DebugInfo::kHasNoSideEffect: 2773 return true; 2774 case DebugInfo::kNotComputed: 2775 default: 2776 UNREACHABLE(); 2777 } 2778} 2779 2780Handle<Object> Debug::return_value_handle() { 2781 return handle(thread_local_.return_value_, isolate_); 2782} 2783 2784bool Debug::PerformSideEffectCheckForCallback( 2785 Handle<Object> callback_info, Handle<Object> receiver, 2786 Debug::AccessorKind accessor_kind) { 2787 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2788 DCHECK_EQ(!receiver.is_null(), callback_info->IsAccessorInfo()); 2789 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects); 2790 if (!callback_info.is_null() && callback_info->IsCallHandlerInfo() && 2791 i::CallHandlerInfo::cast(*callback_info).NextCallHasNoSideEffect()) { 2792 return true; 2793 } 2794 // TODO(7515): always pass a valid callback info object. 2795 if (!callback_info.is_null()) { 2796 if (callback_info->IsAccessorInfo()) { 2797 // List of allowlisted internal accessors can be found in accessors.h. 2798 AccessorInfo info = AccessorInfo::cast(*callback_info); 2799 DCHECK_NE(kNotAccessor, accessor_kind); 2800 switch (accessor_kind == kSetter ? info.setter_side_effect_type() 2801 : info.getter_side_effect_type()) { 2802 case SideEffectType::kHasNoSideEffect: 2803 // We do not support setter accessors with no side effects, since 2804 // calling set accessors go through a store bytecode. Store bytecodes 2805 // are considered to cause side effects (to non-temporary objects). 2806 DCHECK_NE(kSetter, accessor_kind); 2807 return true; 2808 case SideEffectType::kHasSideEffectToReceiver: 2809 DCHECK(!receiver.is_null()); 2810 if (PerformSideEffectCheckForObject(receiver)) return true; 2811 isolate_->OptionalRescheduleException(false); 2812 return false; 2813 case SideEffectType::kHasSideEffect: 2814 break; 2815 } 2816 if (FLAG_trace_side_effect_free_debug_evaluate) { 2817 PrintF("[debug-evaluate] API Callback '"); 2818 info.name().ShortPrint(); 2819 PrintF("' may cause side effect.\n"); 2820 } 2821 } else if (callback_info->IsInterceptorInfo()) { 2822 InterceptorInfo info = InterceptorInfo::cast(*callback_info); 2823 if (info.has_no_side_effect()) return true; 2824 if (FLAG_trace_side_effect_free_debug_evaluate) { 2825 PrintF("[debug-evaluate] API Interceptor may cause side effect.\n"); 2826 } 2827 } else if (callback_info->IsCallHandlerInfo()) { 2828 CallHandlerInfo info = CallHandlerInfo::cast(*callback_info); 2829 if (info.IsSideEffectFreeCallHandlerInfo()) return true; 2830 if (FLAG_trace_side_effect_free_debug_evaluate) { 2831 PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n"); 2832 } 2833 } 2834 } 2835 side_effect_check_failed_ = true; 2836 // Throw an uncatchable termination exception. 2837 isolate_->TerminateExecution(); 2838 isolate_->OptionalRescheduleException(false); 2839 return false; 2840} 2841 2842bool Debug::PerformSideEffectCheckAtBytecode(InterpretedFrame* frame) { 2843 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2844 using interpreter::Bytecode; 2845 2846 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects); 2847 SharedFunctionInfo shared = frame->function().shared(); 2848 BytecodeArray bytecode_array = shared.GetBytecodeArray(isolate_); 2849 int offset = frame->GetBytecodeOffset(); 2850 interpreter::BytecodeArrayIterator bytecode_iterator( 2851 handle(bytecode_array, isolate_), offset); 2852 2853 Bytecode bytecode = bytecode_iterator.current_bytecode(); 2854 if (interpreter::Bytecodes::IsCallRuntime(bytecode)) { 2855 auto id = (bytecode == Bytecode::kInvokeIntrinsic) 2856 ? bytecode_iterator.GetIntrinsicIdOperand(0) 2857 : bytecode_iterator.GetRuntimeIdOperand(0); 2858 if (DebugEvaluate::IsSideEffectFreeIntrinsic(id)) { 2859 return true; 2860 } 2861 side_effect_check_failed_ = true; 2862 // Throw an uncatchable termination exception. 2863 isolate_->TerminateExecution(); 2864 return false; 2865 } 2866 interpreter::Register reg; 2867 switch (bytecode) { 2868 case Bytecode::kStaCurrentContextSlot: 2869 reg = interpreter::Register::current_context(); 2870 break; 2871 default: 2872 reg = bytecode_iterator.GetRegisterOperand(0); 2873 break; 2874 } 2875 Handle<Object> object = 2876 handle(frame->ReadInterpreterRegister(reg.index()), isolate_); 2877 return PerformSideEffectCheckForObject(object); 2878} 2879 2880bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) { 2881 RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); 2882 DCHECK_EQ(isolate_->debug_execution_mode(), DebugInfo::kSideEffects); 2883 2884 // We expect no side-effects for primitives. 2885 if (object->IsNumber()) return true; 2886 if (object->IsName()) return true; 2887 2888 if (temporary_objects_->HasObject(Handle<HeapObject>::cast(object))) { 2889 return true; 2890 } 2891 2892 if (FLAG_trace_side_effect_free_debug_evaluate) { 2893 PrintF("[debug-evaluate] failed runtime side effect check.\n"); 2894 } 2895 side_effect_check_failed_ = true; 2896 // Throw an uncatchable termination exception. 2897 isolate_->TerminateExecution(); 2898 return false; 2899} 2900 2901void Debug::SetTemporaryObjectTrackingDisabled(bool disabled) { 2902 if (temporary_objects_) { 2903 temporary_objects_->disabled = disabled; 2904 } 2905} 2906 2907bool Debug::GetTemporaryObjectTrackingDisabled() const { 2908 if (temporary_objects_) { 2909 return temporary_objects_->disabled; 2910 } 2911 return false; 2912} 2913 2914} // namespace internal 2915} // namespace v8 2916