| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/debug/liveedit.h" | 5 #include "src/debug/liveedit.h" |
| 6 | 6 |
| 7 #include "src/ast/scopes.h" | 7 #include "src/ast/scopes.h" |
| 8 #include "src/code-stubs.h" | 8 #include "src/code-stubs.h" |
| 9 #include "src/compilation-cache.h" | 9 #include "src/compilation-cache.h" |
| 10 #include "src/compiler.h" | 10 #include "src/compiler.h" |
| (...skipping 634 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 | 645 |
| 646 | 646 |
| 647 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() { | 647 Handle<SharedFunctionInfo> SharedInfoWrapper::GetInfo() { |
| 648 Handle<Object> element = this->GetField(kSharedInfoOffset_); | 648 Handle<Object> element = this->GetField(kSharedInfoOffset_); |
| 649 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element); | 649 Handle<JSValue> value_wrapper = Handle<JSValue>::cast(element); |
| 650 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper); | 650 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper); |
| 651 } | 651 } |
| 652 | 652 |
| 653 | 653 |
| 654 void LiveEdit::InitializeThreadLocal(Debug* debug) { | 654 void LiveEdit::InitializeThreadLocal(Debug* debug) { |
| 655 debug->thread_local_.frame_drop_mode_ = LIVE_EDIT_FRAMES_UNTOUCHED; | 655 debug->thread_local_.restart_fp_ = 0; |
| 656 } | 656 } |
| 657 | 657 |
| 658 | 658 |
| 659 bool LiveEdit::SetAfterBreakTarget(Debug* debug) { | |
| 660 Code* code = NULL; | |
| 661 Isolate* isolate = debug->isolate_; | |
| 662 switch (debug->thread_local_.frame_drop_mode_) { | |
| 663 case LIVE_EDIT_FRAMES_UNTOUCHED: | |
| 664 return false; | |
| 665 case LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL: | |
| 666 // Debug break slot stub does not return normally, instead it manually | |
| 667 // cleans the stack and jumps. We should patch the jump address. | |
| 668 code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit); | |
| 669 break; | |
| 670 case LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL: | |
| 671 // Nothing to do, after_break_target is not used here. | |
| 672 return true; | |
| 673 case LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL: | |
| 674 code = isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit); | |
| 675 break; | |
| 676 case LIVE_EDIT_CURRENTLY_SET_MODE: | |
| 677 UNREACHABLE(); | |
| 678 break; | |
| 679 } | |
| 680 debug->after_break_target_ = code->entry(); | |
| 681 return true; | |
| 682 } | |
| 683 | |
| 684 | |
| 685 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script, | 659 MaybeHandle<JSArray> LiveEdit::GatherCompileInfo(Handle<Script> script, |
| 686 Handle<String> source) { | 660 Handle<String> source) { |
| 687 Isolate* isolate = script->GetIsolate(); | 661 Isolate* isolate = script->GetIsolate(); |
| 688 | 662 |
| 689 MaybeHandle<JSArray> infos; | 663 MaybeHandle<JSArray> infos; |
| 690 Handle<Object> original_source = | 664 Handle<Object> original_source = |
| 691 Handle<Object>(script->source(), isolate); | 665 Handle<Object>(script->source(), isolate); |
| 692 script->set_source(*source); | 666 script->set_source(*source); |
| 693 | 667 |
| 694 { | 668 { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 738 // A logical 'finally' section. | 712 // A logical 'finally' section. |
| 739 script->set_source(*original_source); | 713 script->set_source(*original_source); |
| 740 | 714 |
| 741 if (rethrow_exception.is_null()) { | 715 if (rethrow_exception.is_null()) { |
| 742 return infos.ToHandleChecked(); | 716 return infos.ToHandleChecked(); |
| 743 } else { | 717 } else { |
| 744 return isolate->Throw<JSArray>(rethrow_exception); | 718 return isolate->Throw<JSArray>(rethrow_exception); |
| 745 } | 719 } |
| 746 } | 720 } |
| 747 | 721 |
| 748 | |
| 749 // Visitor that finds all references to a particular code object, | |
| 750 // including "CODE_TARGET" references in other code objects and replaces | |
| 751 // them on the fly. | |
| 752 class ReplacingVisitor : public ObjectVisitor { | |
| 753 public: | |
| 754 explicit ReplacingVisitor(Code* original, Code* substitution) | |
| 755 : original_(original), substitution_(substitution) { | |
| 756 } | |
| 757 | |
| 758 void VisitPointers(Object** start, Object** end) override { | |
| 759 for (Object** p = start; p < end; p++) { | |
| 760 if (*p == original_) { | |
| 761 *p = substitution_; | |
| 762 } | |
| 763 } | |
| 764 } | |
| 765 | |
| 766 void VisitCodeEntry(Address entry) override { | |
| 767 if (Code::GetObjectFromEntryAddress(entry) == original_) { | |
| 768 Address substitution_entry = substitution_->instruction_start(); | |
| 769 Memory::Address_at(entry) = substitution_entry; | |
| 770 } | |
| 771 } | |
| 772 | |
| 773 void VisitCodeTarget(RelocInfo* rinfo) override { | |
| 774 if (RelocInfo::IsCodeTarget(rinfo->rmode()) && | |
| 775 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) { | |
| 776 Address substitution_entry = substitution_->instruction_start(); | |
| 777 rinfo->set_target_address(substitution_entry); | |
| 778 } | |
| 779 } | |
| 780 | |
| 781 void VisitDebugTarget(RelocInfo* rinfo) override { VisitCodeTarget(rinfo); } | |
| 782 | |
| 783 private: | |
| 784 Code* original_; | |
| 785 Code* substitution_; | |
| 786 }; | |
| 787 | |
| 788 | |
| 789 // Finds all references to original and replaces them with substitution. | 722 // Finds all references to original and replaces them with substitution. |
| 790 static void ReplaceCodeObject(Handle<Code> original, | 723 static void ReplaceCodeObject(Handle<Code> original, |
| 791 Handle<Code> substitution) { | 724 Handle<Code> substitution) { |
| 792 // Perform a full GC in order to ensure that we are not in the middle of an | 725 // Perform a full GC in order to ensure that we are not in the middle of an |
| 793 // incremental marking phase when we are replacing the code object. | 726 // incremental marking phase when we are replacing the code object. |
| 794 // Since we are not in an incremental marking phase we can write pointers | 727 // Since we are not in an incremental marking phase we can write pointers |
| 795 // to code objects (that are never in new space) without worrying about | 728 // to code objects (that are never in new space) without worrying about |
| 796 // write barriers. | 729 // write barriers. |
| 797 Heap* heap = original->GetHeap(); | 730 Heap* heap = original->GetHeap(); |
| 798 HeapIterator iterator(heap); | 731 HeapIterator iterator(heap); |
| 799 | |
| 800 DCHECK(!heap->InNewSpace(*substitution)); | |
| 801 | |
| 802 ReplacingVisitor visitor(*original, *substitution); | |
| 803 | |
| 804 // Iterate over all roots. Stack frames may have pointer into original code, | |
| 805 // so temporary replace the pointers with offset numbers | |
| 806 // in prologue/epilogue. | |
| 807 heap->IterateRoots(&visitor, VISIT_ALL); | |
| 808 | |
| 809 // Now iterate over all pointers of all objects, including code_target | 732 // Now iterate over all pointers of all objects, including code_target |
| 810 // implicit pointers. | 733 // implicit pointers. |
| 811 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { | 734 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { |
| 812 obj->Iterate(&visitor); | 735 if (obj->IsJSFunction()) { |
| 736 JSFunction* fun = JSFunction::cast(obj); |
| 737 if (fun->code() == *original) fun->ReplaceCode(*substitution); |
| 738 } else if (obj->IsSharedFunctionInfo()) { |
| 739 SharedFunctionInfo* info = SharedFunctionInfo::cast(obj); |
| 740 if (info->code() == *original) info->set_code(*substitution); |
| 741 } |
| 813 } | 742 } |
| 814 } | 743 } |
| 815 | 744 |
| 816 | 745 |
| 817 // Patch function literals. | 746 // Patch function literals. |
| 818 // Name 'literals' is a misnomer. Rather it's a cache for complex object | 747 // Name 'literals' is a misnomer. Rather it's a cache for complex object |
| 819 // boilerplates and for a native context. We must clean cached values. | 748 // boilerplates and for a native context. We must clean cached values. |
| 820 // Additionally we may need to allocate a new array if number of literals | 749 // Additionally we may need to allocate a new array if number of literals |
| 821 // changed. | 750 // changed. |
| 822 class LiteralFixer { | 751 class LiteralFixer { |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1270 UnwrapSharedFunctionInfoFromJSValue(jsvalue); | 1199 UnwrapSharedFunctionInfoFromJSValue(jsvalue); |
| 1271 | 1200 |
| 1272 if (function->Inlines(*shared)) { | 1201 if (function->Inlines(*shared)) { |
| 1273 SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate)); | 1202 SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate)); |
| 1274 return true; | 1203 return true; |
| 1275 } | 1204 } |
| 1276 } | 1205 } |
| 1277 return false; | 1206 return false; |
| 1278 } | 1207 } |
| 1279 | 1208 |
| 1280 | |
| 1281 // Iterates over handler chain and removes all elements that are inside | |
| 1282 // frames being dropped. | |
| 1283 static bool FixTryCatchHandler(StackFrame* top_frame, | |
| 1284 StackFrame* bottom_frame) { | |
| 1285 Address* pointer_address = | |
| 1286 &Memory::Address_at(top_frame->isolate()->get_address_from_id( | |
| 1287 Isolate::kHandlerAddress)); | |
| 1288 | |
| 1289 while (*pointer_address < top_frame->sp()) { | |
| 1290 pointer_address = &Memory::Address_at(*pointer_address); | |
| 1291 } | |
| 1292 Address* above_frame_address = pointer_address; | |
| 1293 while (*pointer_address < bottom_frame->fp()) { | |
| 1294 pointer_address = &Memory::Address_at(*pointer_address); | |
| 1295 } | |
| 1296 bool change = *above_frame_address != *pointer_address; | |
| 1297 *above_frame_address = *pointer_address; | |
| 1298 return change; | |
| 1299 } | |
| 1300 | |
| 1301 | |
| 1302 // Initializes an artificial stack frame. The data it contains is used for: | |
| 1303 // a. successful work of frame dropper code which eventually gets control, | |
| 1304 // b. being compatible with a typed frame structure for various stack | |
| 1305 // iterators. | |
| 1306 // Frame structure (conforms to InternalFrame structure): | |
| 1307 // -- function | |
| 1308 // -- code | |
| 1309 // -- SMI marker | |
| 1310 // -- frame base | |
| 1311 static void SetUpFrameDropperFrame(StackFrame* bottom_js_frame, | |
| 1312 Handle<Code> code) { | |
| 1313 DCHECK(bottom_js_frame->is_java_script()); | |
| 1314 Address fp = bottom_js_frame->fp(); | |
| 1315 Memory::Object_at(fp + FrameDropperFrameConstants::kFunctionOffset) = | |
| 1316 Memory::Object_at(fp + StandardFrameConstants::kFunctionOffset); | |
| 1317 Memory::Object_at(fp + FrameDropperFrameConstants::kFrameTypeOffset) = | |
| 1318 Smi::FromInt(StackFrame::INTERNAL); | |
| 1319 Memory::Object_at(fp + FrameDropperFrameConstants::kCodeOffset) = *code; | |
| 1320 } | |
| 1321 | |
| 1322 | |
| 1323 // Removes specified range of frames from stack. There may be 1 or more | |
| 1324 // frames in range. Anyway the bottom frame is restarted rather than dropped, | |
| 1325 // and therefore has to be a JavaScript frame. | |
| 1326 // Returns error message or NULL. | |
| 1327 static const char* DropFrames(Vector<StackFrame*> frames, int top_frame_index, | |
| 1328 int bottom_js_frame_index, | |
| 1329 LiveEditFrameDropMode* mode) { | |
| 1330 if (!LiveEdit::kFrameDropperSupported) { | |
| 1331 return "Stack manipulations are not supported in this architecture."; | |
| 1332 } | |
| 1333 | |
| 1334 StackFrame* pre_top_frame = frames[top_frame_index - 1]; | |
| 1335 StackFrame* top_frame = frames[top_frame_index]; | |
| 1336 StackFrame* bottom_js_frame = frames[bottom_js_frame_index]; | |
| 1337 | |
| 1338 DCHECK(bottom_js_frame->is_java_script()); | |
| 1339 | |
| 1340 // Check the nature of the top frame. | |
| 1341 Isolate* isolate = bottom_js_frame->isolate(); | |
| 1342 Code* pre_top_frame_code = pre_top_frame->LookupCode(); | |
| 1343 bool frame_has_padding = true; | |
| 1344 if (pre_top_frame_code == | |
| 1345 isolate->builtins()->builtin(Builtins::kSlot_DebugBreak)) { | |
| 1346 // OK, we can drop debug break slot. | |
| 1347 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DEBUG_SLOT_CALL; | |
| 1348 } else if (pre_top_frame_code == | |
| 1349 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)) { | |
| 1350 // OK, we can drop our own code. | |
| 1351 pre_top_frame = frames[top_frame_index - 2]; | |
| 1352 top_frame = frames[top_frame_index - 1]; | |
| 1353 *mode = LIVE_EDIT_CURRENTLY_SET_MODE; | |
| 1354 frame_has_padding = false; | |
| 1355 } else if (pre_top_frame_code == | |
| 1356 isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) { | |
| 1357 *mode = LIVE_EDIT_FRAME_DROPPED_IN_RETURN_CALL; | |
| 1358 } else if (pre_top_frame_code->kind() == Code::STUB && | |
| 1359 CodeStub::GetMajorKey(pre_top_frame_code) == CodeStub::CEntry) { | |
| 1360 // Entry from our unit tests on 'debugger' statement. | |
| 1361 // It's fine, we support this case. | |
| 1362 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL; | |
| 1363 // We don't have a padding from 'debugger' statement call. | |
| 1364 // Here the stub is CEntry, it's not debug-only and can't be padded. | |
| 1365 // If anyone would complain, a proxy padded stub could be added. | |
| 1366 frame_has_padding = false; | |
| 1367 } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) { | |
| 1368 // This must be adaptor that remain from the frame dropping that | |
| 1369 // is still on stack. A frame dropper frame must be above it. | |
| 1370 DCHECK(frames[top_frame_index - 2]->LookupCode() == | |
| 1371 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)); | |
| 1372 pre_top_frame = frames[top_frame_index - 3]; | |
| 1373 top_frame = frames[top_frame_index - 2]; | |
| 1374 *mode = LIVE_EDIT_CURRENTLY_SET_MODE; | |
| 1375 frame_has_padding = false; | |
| 1376 } else if (pre_top_frame_code->kind() == Code::BYTECODE_HANDLER) { | |
| 1377 // Interpreted bytecode takes up two stack frames, one for the bytecode | |
| 1378 // handler and one for the interpreter entry trampoline. Therefore we shift | |
| 1379 // up by one frame. | |
| 1380 *mode = LIVE_EDIT_FRAME_DROPPED_IN_DIRECT_CALL; | |
| 1381 pre_top_frame = frames[top_frame_index - 2]; | |
| 1382 top_frame = frames[top_frame_index - 1]; | |
| 1383 } else { | |
| 1384 return "Unknown structure of stack above changing function"; | |
| 1385 } | |
| 1386 | |
| 1387 Address unused_stack_top = top_frame->sp(); | |
| 1388 Address unused_stack_bottom = | |
| 1389 bottom_js_frame->fp() - FrameDropperFrameConstants::kFixedFrameSize + | |
| 1390 2 * kPointerSize; // Bigger address end is exclusive. | |
| 1391 | |
| 1392 Address* top_frame_pc_address = top_frame->pc_address(); | |
| 1393 | |
| 1394 // top_frame may be damaged below this point. Do not used it. | |
| 1395 DCHECK(!(top_frame = NULL)); | |
| 1396 | |
| 1397 if (unused_stack_top > unused_stack_bottom) { | |
| 1398 if (frame_has_padding) { | |
| 1399 int shortage_bytes = | |
| 1400 static_cast<int>(unused_stack_top - unused_stack_bottom); | |
| 1401 | |
| 1402 Address padding_start = | |
| 1403 pre_top_frame->fp() - | |
| 1404 (FrameDropperFrameConstants::kFixedFrameSize - kPointerSize); | |
| 1405 | |
| 1406 Address padding_pointer = padding_start; | |
| 1407 Smi* padding_object = Smi::FromInt(LiveEdit::kFramePaddingValue); | |
| 1408 while (Memory::Object_at(padding_pointer) == padding_object) { | |
| 1409 padding_pointer -= kPointerSize; | |
| 1410 } | |
| 1411 int padding_counter = | |
| 1412 Smi::cast(Memory::Object_at(padding_pointer))->value(); | |
| 1413 if (padding_counter * kPointerSize < shortage_bytes) { | |
| 1414 return "Not enough space for frame dropper frame " | |
| 1415 "(even with padding frame)"; | |
| 1416 } | |
| 1417 Memory::Object_at(padding_pointer) = | |
| 1418 Smi::FromInt(padding_counter - shortage_bytes / kPointerSize); | |
| 1419 | |
| 1420 StackFrame* pre_pre_frame = frames[top_frame_index - 2]; | |
| 1421 | |
| 1422 MemMove(padding_start + kPointerSize - shortage_bytes, | |
| 1423 padding_start + kPointerSize, | |
| 1424 FrameDropperFrameConstants::kFixedFrameSize - kPointerSize); | |
| 1425 | |
| 1426 pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes); | |
| 1427 pre_pre_frame->SetCallerFp(pre_top_frame->fp()); | |
| 1428 unused_stack_top -= shortage_bytes; | |
| 1429 | |
| 1430 STATIC_ASSERT(sizeof(Address) == kPointerSize); | |
| 1431 top_frame_pc_address -= shortage_bytes / kPointerSize; | |
| 1432 } else { | |
| 1433 return "Not enough space for frame dropper frame"; | |
| 1434 } | |
| 1435 } | |
| 1436 | |
| 1437 // Committing now. After this point we should return only NULL value. | |
| 1438 | |
| 1439 FixTryCatchHandler(pre_top_frame, bottom_js_frame); | |
| 1440 // Make sure FixTryCatchHandler is idempotent. | |
| 1441 DCHECK(!FixTryCatchHandler(pre_top_frame, bottom_js_frame)); | |
| 1442 | |
| 1443 Handle<Code> code = isolate->builtins()->FrameDropper_LiveEdit(); | |
| 1444 *top_frame_pc_address = code->entry(); | |
| 1445 pre_top_frame->SetCallerFp(bottom_js_frame->fp()); | |
| 1446 | |
| 1447 SetUpFrameDropperFrame(bottom_js_frame, code); | |
| 1448 | |
| 1449 for (Address a = unused_stack_top; | |
| 1450 a < unused_stack_bottom; | |
| 1451 a += kPointerSize) { | |
| 1452 Memory::Object_at(a) = Smi::kZero; | |
| 1453 } | |
| 1454 | |
| 1455 return NULL; | |
| 1456 } | |
| 1457 | |
| 1458 | |
| 1459 // Describes a set of call frames that execute any of listed functions. | 1209 // Describes a set of call frames that execute any of listed functions. |
| 1460 // Finding no such frames does not mean error. | 1210 // Finding no such frames does not mean error. |
| 1461 class MultipleFunctionTarget { | 1211 class MultipleFunctionTarget { |
| 1462 public: | 1212 public: |
| 1463 MultipleFunctionTarget(Handle<JSArray> old_shared_array, | 1213 MultipleFunctionTarget(Handle<JSArray> old_shared_array, |
| 1464 Handle<JSArray> new_shared_array, | 1214 Handle<JSArray> new_shared_array, |
| 1465 Handle<JSArray> result) | 1215 Handle<JSArray> result) |
| 1466 : old_shared_array_(old_shared_array), | 1216 : old_shared_array_(old_shared_array), |
| 1467 new_shared_array_(new_shared_array), | 1217 new_shared_array_(new_shared_array), |
| 1468 result_(result) {} | 1218 result_(result) {} |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1536 | 1286 |
| 1537 // Drops all call frame matched by target and all frames above them. | 1287 // Drops all call frame matched by target and all frames above them. |
| 1538 template <typename TARGET> | 1288 template <typename TARGET> |
| 1539 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, | 1289 static const char* DropActivationsInActiveThreadImpl(Isolate* isolate, |
| 1540 TARGET& target, // NOLINT | 1290 TARGET& target, // NOLINT |
| 1541 bool do_drop) { | 1291 bool do_drop) { |
| 1542 Debug* debug = isolate->debug(); | 1292 Debug* debug = isolate->debug(); |
| 1543 Zone zone(isolate->allocator(), ZONE_NAME); | 1293 Zone zone(isolate->allocator(), ZONE_NAME); |
| 1544 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone); | 1294 Vector<StackFrame*> frames = CreateStackMap(isolate, &zone); |
| 1545 | 1295 |
| 1546 | |
| 1547 int top_frame_index = -1; | 1296 int top_frame_index = -1; |
| 1548 int frame_index = 0; | 1297 int frame_index = 0; |
| 1549 for (; frame_index < frames.length(); frame_index++) { | 1298 for (; frame_index < frames.length(); frame_index++) { |
| 1550 StackFrame* frame = frames[frame_index]; | 1299 StackFrame* frame = frames[frame_index]; |
| 1551 if (frame->id() == debug->break_frame_id()) { | 1300 if (frame->id() == debug->break_frame_id()) { |
| 1552 top_frame_index = frame_index; | 1301 top_frame_index = frame_index; |
| 1553 break; | 1302 break; |
| 1554 } | 1303 } |
| 1555 if (target.MatchActivation( | 1304 if (target.MatchActivation( |
| 1556 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { | 1305 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1621 if (!do_drop) { | 1370 if (!do_drop) { |
| 1622 // We are in check-only mode. | 1371 // We are in check-only mode. |
| 1623 return NULL; | 1372 return NULL; |
| 1624 } | 1373 } |
| 1625 | 1374 |
| 1626 if (!target_frame_found) { | 1375 if (!target_frame_found) { |
| 1627 // Nothing to drop. | 1376 // Nothing to drop. |
| 1628 return target.GetNotFoundMessage(); | 1377 return target.GetNotFoundMessage(); |
| 1629 } | 1378 } |
| 1630 | 1379 |
| 1631 LiveEditFrameDropMode drop_mode = LIVE_EDIT_FRAMES_UNTOUCHED; | 1380 if (!LiveEdit::kFrameDropperSupported) { |
| 1632 const char* error_message = | 1381 return "Stack manipulations are not supported in this architecture."; |
| 1633 DropFrames(frames, top_frame_index, bottom_js_frame_index, &drop_mode); | |
| 1634 | |
| 1635 if (error_message != NULL) { | |
| 1636 return error_message; | |
| 1637 } | 1382 } |
| 1638 | 1383 |
| 1639 // Adjust break_frame after some frames has been dropped. | 1384 debug->ScheduleFrameRestart(frames[bottom_js_frame_index]); |
| 1640 StackFrame::Id new_id = StackFrame::NO_ID; | |
| 1641 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) { | |
| 1642 if (frames[i]->type() == StackFrame::JAVA_SCRIPT || | |
| 1643 frames[i]->type() == StackFrame::INTERPRETED) { | |
| 1644 new_id = frames[i]->id(); | |
| 1645 break; | |
| 1646 } | |
| 1647 } | |
| 1648 debug->FramesHaveBeenDropped(new_id, drop_mode); | |
| 1649 return NULL; | 1385 return NULL; |
| 1650 } | 1386 } |
| 1651 | 1387 |
| 1652 | 1388 |
| 1653 // Fills result array with statuses of functions. Modifies the stack | 1389 // Fills result array with statuses of functions. Modifies the stack |
| 1654 // removing all listed function if possible and if do_drop is true. | 1390 // removing all listed function if possible and if do_drop is true. |
| 1655 static const char* DropActivationsInActiveThread( | 1391 static const char* DropActivationsInActiveThread( |
| 1656 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, | 1392 Handle<JSArray> old_shared_array, Handle<JSArray> new_shared_array, |
| 1657 Handle<JSArray> result, bool do_drop) { | 1393 Handle<JSArray> result, bool do_drop) { |
| 1658 MultipleFunctionTarget target(old_shared_array, new_shared_array, result); | 1394 MultipleFunctionTarget target(old_shared_array, new_shared_array, result); |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1935 scope_info_length++; | 1671 scope_info_length++; |
| 1936 | 1672 |
| 1937 current_scope = current_scope->outer_scope(); | 1673 current_scope = current_scope->outer_scope(); |
| 1938 } | 1674 } |
| 1939 | 1675 |
| 1940 return scope_info_list; | 1676 return scope_info_list; |
| 1941 } | 1677 } |
| 1942 | 1678 |
| 1943 } // namespace internal | 1679 } // namespace internal |
| 1944 } // namespace v8 | 1680 } // namespace v8 |
| OLD | NEW |