| OLD | NEW |
| 1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 // LiveEdit namespace is declared inside a single function constructor. | 44 // LiveEdit namespace is declared inside a single function constructor. |
| 45 Debug.LiveEdit = new function() { | 45 Debug.LiveEdit = new function() { |
| 46 | 46 |
| 47 // Forward declaration for minifier. | 47 // Forward declaration for minifier. |
| 48 var FunctionStatus; | 48 var FunctionStatus; |
| 49 | 49 |
| 50 | 50 |
| 51 // Applies the change to the script. | 51 // Applies the change to the script. |
| 52 // The change is in form of list of chunks encoded in a single array as | 52 // The change is in form of list of chunks encoded in a single array as |
| 53 // a series of triplets (pos1_start, pos1_end, pos2_end) | 53 // a series of triplets (pos1_start, pos1_end, pos2_end) |
| 54 function ApplyPatchMultiChunk(script, diff_array, new_source, change_log) { | 54 function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only, |
| 55 change_log) { |
| 55 | 56 |
| 56 var old_source = script.source; | 57 var old_source = script.source; |
| 57 | 58 |
| 58 // Gather compile information about old version of script. | 59 // Gather compile information about old version of script. |
| 59 var old_compile_info = GatherCompileInfo(old_source, script); | 60 var old_compile_info = GatherCompileInfo(old_source, script); |
| 60 | 61 |
| 61 // Build tree structures for old and new versions of the script. | 62 // Build tree structures for old and new versions of the script. |
| 62 var root_old_node = BuildCodeInfoTree(old_compile_info); | 63 var root_old_node = BuildCodeInfoTree(old_compile_info); |
| 63 | 64 |
| 64 var pos_translator = new PosTranslator(diff_array); | 65 var pos_translator = new PosTranslator(diff_array); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 89 | 90 |
| 90 function HarvestTodo(old_node) { | 91 function HarvestTodo(old_node) { |
| 91 function CollectDamaged(node) { | 92 function CollectDamaged(node) { |
| 92 link_to_old_script_list.push(node); | 93 link_to_old_script_list.push(node); |
| 93 for (var i = 0; i < node.children.length; i++) { | 94 for (var i = 0; i < node.children.length; i++) { |
| 94 CollectDamaged(node.children[i]); | 95 CollectDamaged(node.children[i]); |
| 95 } | 96 } |
| 96 } | 97 } |
| 97 | 98 |
| 98 // Recursively collects all newly compiled functions that are going into | 99 // Recursively collects all newly compiled functions that are going into |
| 99 // business and should be have link to the actual script updated. | 100 // business and should have link to the actual script updated. |
| 100 function CollectNew(node_list) { | 101 function CollectNew(node_list) { |
| 101 for (var i = 0; i < node_list.length; i++) { | 102 for (var i = 0; i < node_list.length; i++) { |
| 102 link_to_original_script_list.push(node_list[i]); | 103 link_to_original_script_list.push(node_list[i]); |
| 103 CollectNew(node_list[i].children); | 104 CollectNew(node_list[i].children); |
| 104 } | 105 } |
| 105 } | 106 } |
| 106 | 107 |
| 107 if (old_node.status == FunctionStatus.DAMAGED) { | 108 if (old_node.status == FunctionStatus.DAMAGED) { |
| 108 CollectDamaged(old_node); | 109 CollectDamaged(old_node); |
| 109 return; | 110 return; |
| 110 } | 111 } |
| 111 if (old_node.status == FunctionStatus.UNCHANGED) { | 112 if (old_node.status == FunctionStatus.UNCHANGED) { |
| 112 update_positions_list.push(old_node); | 113 update_positions_list.push(old_node); |
| 113 } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) { | 114 } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) { |
| 114 update_positions_list.push(old_node); | 115 update_positions_list.push(old_node); |
| 115 } else if (old_node.status == FunctionStatus.CHANGED) { | 116 } else if (old_node.status == FunctionStatus.CHANGED) { |
| 116 replace_code_list.push(old_node); | 117 replace_code_list.push(old_node); |
| 117 CollectNew(old_node.unmatched_new_nodes); | 118 CollectNew(old_node.unmatched_new_nodes); |
| 118 } | 119 } |
| 119 for (var i = 0; i < old_node.children.length; i++) { | 120 for (var i = 0; i < old_node.children.length; i++) { |
| 120 HarvestTodo(old_node.children[i]); | 121 HarvestTodo(old_node.children[i]); |
| 121 } | 122 } |
| 122 } | 123 } |
| 123 | 124 |
| 125 var preview_description = { |
| 126 change_tree: DescribeChangeTree(root_old_node), |
| 127 textual_diff: { |
| 128 old_len: old_source.length, |
| 129 new_len: new_source.length, |
| 130 chunks: diff_array |
| 131 }, |
| 132 updated: false |
| 133 }; |
| 134 |
| 135 if (preview_only) { |
| 136 return preview_description; |
| 137 } |
| 138 |
| 124 HarvestTodo(root_old_node); | 139 HarvestTodo(root_old_node); |
| 125 | 140 |
| 126 // Collect shared infos for functions whose code need to be patched. | 141 // Collect shared infos for functions whose code need to be patched. |
| 127 var replaced_function_infos = new Array(); | 142 var replaced_function_infos = new Array(); |
| 128 for (var i = 0; i < replace_code_list.length; i++) { | 143 for (var i = 0; i < replace_code_list.length; i++) { |
| 129 var info_wrapper = replace_code_list[i].live_shared_info_wrapper; | 144 var info_wrapper = replace_code_list[i].live_shared_info_wrapper; |
| 130 if (info_wrapper) { | 145 if (info_wrapper) { |
| 131 replaced_function_infos.push(info_wrapper); | 146 replaced_function_infos.push(info_wrapper); |
| 132 } | 147 } |
| 133 } | 148 } |
| 134 | 149 |
| 135 // Check that function being patched is not currently on stack. | |
| 136 CheckStackActivations(replaced_function_infos, change_log); | |
| 137 | |
| 138 | |
| 139 // We haven't changed anything before this line yet. | 150 // We haven't changed anything before this line yet. |
| 140 // Committing all changes. | 151 // Committing all changes. |
| 141 | 152 |
| 153 // Check that function being patched is not currently on stack or drop them. |
| 154 var dropped_functions_number = |
| 155 CheckStackActivations(replaced_function_infos, change_log); |
| 156 |
| 157 preview_description.stack_modified = dropped_functions_number != 0; |
| 158 |
| 142 // Start with breakpoints. Convert their line/column positions and | 159 // Start with breakpoints. Convert their line/column positions and |
| 143 // temporary remove. | 160 // temporary remove. |
| 144 var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log); | 161 var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log); |
| 145 | 162 |
| 146 var old_script; | 163 var old_script; |
| 147 | 164 |
| 148 // Create an old script only if there are function that should be linked | 165 // Create an old script only if there are function that should be linked |
| 149 // to old version. | 166 // to old version. |
| 150 if (link_to_old_script_list.length == 0) { | 167 if (link_to_old_script_list.length == 0) { |
| 151 %LiveEditReplaceScript(script, new_source, null); | 168 %LiveEditReplaceScript(script, new_source, null); |
| 152 old_script = void 0; | 169 old_script = void 0; |
| 153 } else { | 170 } else { |
| 154 var old_script_name = CreateNameForOldScript(script); | 171 var old_script_name = CreateNameForOldScript(script); |
| 155 | 172 |
| 156 // Update the script text and create a new script representing an old | 173 // Update the script text and create a new script representing an old |
| 157 // version of the script. | 174 // version of the script. |
| 158 old_script = %LiveEditReplaceScript(script, new_source, | 175 old_script = %LiveEditReplaceScript(script, new_source, |
| 159 old_script_name); | 176 old_script_name); |
| 160 | 177 |
| 161 var link_to_old_script_report = new Array(); | 178 var link_to_old_script_report = new Array(); |
| 162 change_log.push( { linked_to_old_script: link_to_old_script_report } ); | 179 change_log.push( { linked_to_old_script: link_to_old_script_report } ); |
| 163 | 180 |
| 164 // We need to link to old script all former nested functions. | 181 // We need to link to old script all former nested functions. |
| 165 for (var i = 0; i < link_to_old_script_list.length; i++) { | 182 for (var i = 0; i < link_to_old_script_list.length; i++) { |
| 166 LinkToOldScript(link_to_old_script_list[i], old_script, | 183 LinkToOldScript(link_to_old_script_list[i], old_script, |
| 167 link_to_old_script_report); | 184 link_to_old_script_report); |
| 168 } | 185 } |
| 186 |
| 187 preview_description.created_script_name = old_script_name; |
| 169 } | 188 } |
| 170 | 189 |
| 171 // Link to an actual script all the functions that we are going to use. | 190 // Link to an actual script all the functions that we are going to use. |
| 172 for (var i = 0; i < link_to_original_script_list.length; i++) { | 191 for (var i = 0; i < link_to_original_script_list.length; i++) { |
| 173 %LiveEditFunctionSetScript( | 192 %LiveEditFunctionSetScript( |
| 174 link_to_original_script_list[i].info.shared_function_info, script); | 193 link_to_original_script_list[i].info.shared_function_info, script); |
| 175 } | 194 } |
| 176 | 195 |
| 177 for (var i = 0; i < replace_code_list.length; i++) { | 196 for (var i = 0; i < replace_code_list.length; i++) { |
| 178 PatchFunctionCode(replace_code_list[i], change_log); | 197 PatchFunctionCode(replace_code_list[i], change_log); |
| 179 } | 198 } |
| 180 | 199 |
| 181 var position_patch_report = new Array(); | 200 var position_patch_report = new Array(); |
| 182 change_log.push( {position_patched: position_patch_report} ); | 201 change_log.push( {position_patched: position_patch_report} ); |
| 183 | 202 |
| 184 for (var i = 0; i < update_positions_list.length; i++) { | 203 for (var i = 0; i < update_positions_list.length; i++) { |
| 185 // TODO(LiveEdit): take into account wether it's source_changed or | 204 // TODO(LiveEdit): take into account wether it's source_changed or |
| 186 // unchanged and whether positions changed at all. | 205 // unchanged and whether positions changed at all. |
| 187 PatchPositions(update_positions_list[i], diff_array, | 206 PatchPositions(update_positions_list[i], diff_array, |
| 188 position_patch_report); | 207 position_patch_report); |
| 189 } | 208 } |
| 190 | 209 |
| 191 break_points_restorer(pos_translator, old_script); | 210 break_points_restorer(pos_translator, old_script); |
| 211 |
| 212 preview_description.updated = true; |
| 213 return preview_description; |
| 192 } | 214 } |
| 193 // Function is public. | 215 // Function is public. |
| 194 this.ApplyPatchMultiChunk = ApplyPatchMultiChunk; | 216 this.ApplyPatchMultiChunk = ApplyPatchMultiChunk; |
| 195 | 217 |
| 196 | 218 |
| 197 // Fully compiles source string as a script. Returns Array of | 219 // Fully compiles source string as a script. Returns Array of |
| 198 // FunctionCompileInfo -- a descriptions of all functions of the script. | 220 // FunctionCompileInfo -- a descriptions of all functions of the script. |
| 199 // Elements of array are ordered by start positions of functions (from top | 221 // Elements of array are ordered by start positions of functions (from top |
| 200 // to bottom) in the source. Fields outer_index and next_sibling_index help | 222 // to bottom) in the source. Fields outer_index and next_sibling_index help |
| 201 // to navigate the nesting structure of functions. | 223 // to navigate the nesting structure of functions. |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 this.parent = void 0; | 509 this.parent = void 0; |
| 488 | 510 |
| 489 this.status = FunctionStatus.UNCHANGED; | 511 this.status = FunctionStatus.UNCHANGED; |
| 490 // Status explanation is used for debugging purposes and will be shown | 512 // Status explanation is used for debugging purposes and will be shown |
| 491 // in user UI if some explanations are needed. | 513 // in user UI if some explanations are needed. |
| 492 this.status_explanation = void 0; | 514 this.status_explanation = void 0; |
| 493 this.new_start_pos = void 0; | 515 this.new_start_pos = void 0; |
| 494 this.new_end_pos = void 0; | 516 this.new_end_pos = void 0; |
| 495 this.corresponding_node = void 0; | 517 this.corresponding_node = void 0; |
| 496 this.unmatched_new_nodes = void 0; | 518 this.unmatched_new_nodes = void 0; |
| 519 |
| 520 // 'Textual' correspondence/matching is weaker than 'pure' |
| 521 // correspondence/matching. We need 'textual' level for visual presentation |
| 522 // in UI, we use 'pure' level for actual code manipulation. |
| 523 // Sometimes only function body is changed (functions in old and new script |
| 524 // textually correspond), but we cannot patch the code, so we see them |
| 525 // as an old function deleted and new function created. |
| 526 this.textual_corresponding_node = void 0; |
| 527 this.textually_unmatched_new_nodes = void 0; |
| 528 |
| 497 this.live_shared_info_wrapper = void 0; | 529 this.live_shared_info_wrapper = void 0; |
| 498 } | 530 } |
| 499 | 531 |
| 500 // From array of function infos that is implicitly a tree creates | 532 // From array of function infos that is implicitly a tree creates |
| 501 // an actual tree of functions in script. | 533 // an actual tree of functions in script. |
| 502 function BuildCodeInfoTree(code_info_array) { | 534 function BuildCodeInfoTree(code_info_array) { |
| 503 // Throughtout all function we iterate over input array. | 535 // Throughtout all function we iterate over input array. |
| 504 var index = 0; | 536 var index = 0; |
| 505 | 537 |
| 506 // Recursive function that builds a branch of tree. | 538 // Recursive function that builds a branch of tree. |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 // splits a function into 2 functions. | 665 // splits a function into 2 functions. |
| 634 function FindCorrespondingFunctions(old_code_tree, new_code_tree) { | 666 function FindCorrespondingFunctions(old_code_tree, new_code_tree) { |
| 635 | 667 |
| 636 // A recursive function that tries to find a correspondence for all | 668 // A recursive function that tries to find a correspondence for all |
| 637 // child functions and for their inner functions. | 669 // child functions and for their inner functions. |
| 638 function ProcessChildren(old_node, new_node) { | 670 function ProcessChildren(old_node, new_node) { |
| 639 var old_children = old_node.children; | 671 var old_children = old_node.children; |
| 640 var new_children = new_node.children; | 672 var new_children = new_node.children; |
| 641 | 673 |
| 642 var unmatched_new_nodes_list = []; | 674 var unmatched_new_nodes_list = []; |
| 675 var textually_unmatched_new_nodes_list = []; |
| 643 | 676 |
| 644 var old_index = 0; | 677 var old_index = 0; |
| 645 var new_index = 0; | 678 var new_index = 0; |
| 646 while (old_index < old_children.length) { | 679 while (old_index < old_children.length) { |
| 647 if (old_children[old_index].status == FunctionStatus.DAMAGED) { | 680 if (old_children[old_index].status == FunctionStatus.DAMAGED) { |
| 648 old_index++; | 681 old_index++; |
| 649 } else if (new_index < new_children.length) { | 682 } else if (new_index < new_children.length) { |
| 650 if (new_children[new_index].info.start_position < | 683 if (new_children[new_index].info.start_position < |
| 651 old_children[old_index].new_start_pos) { | 684 old_children[old_index].new_start_pos) { |
| 652 unmatched_new_nodes_list.push(new_children[new_index]); | 685 unmatched_new_nodes_list.push(new_children[new_index]); |
| 686 textually_unmatched_new_nodes_list.push(new_children[new_index]); |
| 653 new_index++; | 687 new_index++; |
| 654 } else if (new_children[new_index].info.start_position == | 688 } else if (new_children[new_index].info.start_position == |
| 655 old_children[old_index].new_start_pos) { | 689 old_children[old_index].new_start_pos) { |
| 656 if (new_children[new_index].info.end_position == | 690 if (new_children[new_index].info.end_position == |
| 657 old_children[old_index].new_end_pos) { | 691 old_children[old_index].new_end_pos) { |
| 658 old_children[old_index].corresponding_node = | 692 old_children[old_index].corresponding_node = |
| 659 new_children[new_index]; | 693 new_children[new_index]; |
| 694 old_children[old_index].textual_corresponding_node = |
| 695 new_children[new_index]; |
| 660 if (old_children[old_index].status != FunctionStatus.UNCHANGED) { | 696 if (old_children[old_index].status != FunctionStatus.UNCHANGED) { |
| 661 ProcessChildren(old_children[old_index], | 697 ProcessChildren(old_children[old_index], |
| 662 new_children[new_index]); | 698 new_children[new_index]); |
| 663 if (old_children[old_index].status == FunctionStatus.DAMAGED) { | 699 if (old_children[old_index].status == FunctionStatus.DAMAGED) { |
| 664 unmatched_new_nodes_list.push( | 700 unmatched_new_nodes_list.push( |
| 665 old_children[old_index].corresponding_node); | 701 old_children[old_index].corresponding_node); |
| 666 old_children[old_index].corresponding_node = void 0; | 702 old_children[old_index].corresponding_node = void 0; |
| 667 old_node.status = FunctionStatus.CHANGED; | 703 old_node.status = FunctionStatus.CHANGED; |
| 668 } | 704 } |
| 669 } | 705 } |
| 670 } else { | 706 } else { |
| 671 old_children[old_index].status = FunctionStatus.DAMAGED; | 707 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 672 old_children[old_index].status_explanation = | 708 old_children[old_index].status_explanation = |
| 673 "No corresponding function in new script found"; | 709 "No corresponding function in new script found"; |
| 674 old_node.status = FunctionStatus.CHANGED; | 710 old_node.status = FunctionStatus.CHANGED; |
| 675 unmatched_new_nodes_list.push(new_children[new_index]); | 711 unmatched_new_nodes_list.push(new_children[new_index]); |
| 712 textually_unmatched_new_nodes_list.push(new_children[new_index]); |
| 676 } | 713 } |
| 677 new_index++; | 714 new_index++; |
| 678 old_index++; | 715 old_index++; |
| 679 } else { | 716 } else { |
| 680 old_children[old_index].status = FunctionStatus.DAMAGED; | 717 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 681 old_children[old_index].status_explanation = | 718 old_children[old_index].status_explanation = |
| 682 "No corresponding function in new script found"; | 719 "No corresponding function in new script found"; |
| 683 old_node.status = FunctionStatus.CHANGED; | 720 old_node.status = FunctionStatus.CHANGED; |
| 684 old_index++; | 721 old_index++; |
| 685 } | 722 } |
| 686 } else { | 723 } else { |
| 687 old_children[old_index].status = FunctionStatus.DAMAGED; | 724 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 688 old_children[old_index].status_explanation = | 725 old_children[old_index].status_explanation = |
| 689 "No corresponding function in new script found"; | 726 "No corresponding function in new script found"; |
| 690 old_node.status = FunctionStatus.CHANGED; | 727 old_node.status = FunctionStatus.CHANGED; |
| 691 old_index++; | 728 old_index++; |
| 692 } | 729 } |
| 693 } | 730 } |
| 694 | 731 |
| 695 while (new_index < new_children.length) { | 732 while (new_index < new_children.length) { |
| 696 unmatched_new_nodes_list.push(new_children[new_index]); | 733 unmatched_new_nodes_list.push(new_children[new_index]); |
| 734 textually_unmatched_new_nodes_list.push(new_children[new_index]); |
| 697 new_index++; | 735 new_index++; |
| 698 } | 736 } |
| 699 | 737 |
| 700 if (old_node.status == FunctionStatus.CHANGED) { | 738 if (old_node.status == FunctionStatus.CHANGED) { |
| 701 if (!CompareFunctionExpectations(old_node.info, new_node.info)) { | 739 var why_wrong_expectations = |
| 740 WhyFunctionExpectationsDiffer(old_node.info, new_node.info); |
| 741 if (why_wrong_expectations) { |
| 702 old_node.status = FunctionStatus.DAMAGED; | 742 old_node.status = FunctionStatus.DAMAGED; |
| 703 old_node.status_explanation = "Changed code expectations"; | 743 old_node.status_explanation = why_wrong_expectations; |
| 704 } | 744 } |
| 705 } | 745 } |
| 706 old_node.unmatched_new_nodes = unmatched_new_nodes_list; | 746 old_node.unmatched_new_nodes = unmatched_new_nodes_list; |
| 747 old_node.textually_unmatched_new_nodes = |
| 748 textually_unmatched_new_nodes_list; |
| 707 } | 749 } |
| 708 | 750 |
| 709 ProcessChildren(old_code_tree, new_code_tree); | 751 ProcessChildren(old_code_tree, new_code_tree); |
| 710 | 752 |
| 711 old_code_tree.corresponding_node = new_code_tree; | 753 old_code_tree.corresponding_node = new_code_tree; |
| 754 old_code_tree.textual_corresponding_node = new_code_tree; |
| 755 |
| 712 Assert(old_code_tree.status != FunctionStatus.DAMAGED, | 756 Assert(old_code_tree.status != FunctionStatus.DAMAGED, |
| 713 "Script became damaged"); | 757 "Script became damaged"); |
| 714 } | 758 } |
| 715 | 759 |
| 716 function FindLiveSharedInfos(old_code_tree, script) { | 760 function FindLiveSharedInfos(old_code_tree, script) { |
| 717 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script); | 761 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script); |
| 718 | 762 |
| 719 var shared_infos = new Array(); | 763 var shared_infos = new Array(); |
| 720 | 764 |
| 721 for (var i = 0; i < shared_raw_list.length; i++) { | 765 for (var i = 0; i < shared_raw_list.length; i++) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 785 report_array.push( { name: old_info_node.info.function_name } ); | 829 report_array.push( { name: old_info_node.info.function_name } ); |
| 786 } | 830 } |
| 787 | 831 |
| 788 // Adds a suffix to script name to mark that it is old version. | 832 // Adds a suffix to script name to mark that it is old version. |
| 789 function CreateNameForOldScript(script) { | 833 function CreateNameForOldScript(script) { |
| 790 // TODO(635): try better than this; support several changes. | 834 // TODO(635): try better than this; support several changes. |
| 791 return script.name + " (old)"; | 835 return script.name + " (old)"; |
| 792 } | 836 } |
| 793 | 837 |
| 794 // Compares a function interface old and new version, whether it | 838 // Compares a function interface old and new version, whether it |
| 795 // changed or not. | 839 // changed or not. Returns explanation if they differ. |
| 796 function CompareFunctionExpectations(function_info1, function_info2) { | 840 function WhyFunctionExpectationsDiffer(function_info1, function_info2) { |
| 797 // Check that function has the same number of parameters (there may exist | 841 // Check that function has the same number of parameters (there may exist |
| 798 // an adapter, that won't survive function parameter number change). | 842 // an adapter, that won't survive function parameter number change). |
| 799 if (function_info1.param_num != function_info2.param_num) { | 843 if (function_info1.param_num != function_info2.param_num) { |
| 800 return false; | 844 return "Changed parameter number: " + function_info1.param_num + |
| 845 " and " + function_info2.param_num; |
| 801 } | 846 } |
| 802 var scope_info1 = function_info1.scope_info; | 847 var scope_info1 = function_info1.scope_info; |
| 803 var scope_info2 = function_info2.scope_info; | 848 var scope_info2 = function_info2.scope_info; |
| 804 | 849 |
| 805 if (!scope_info1) { | 850 var scope_info1_text; |
| 806 return !scope_info2; | 851 var scope_info2_text; |
| 852 |
| 853 if (scope_info1) { |
| 854 scope_info1_text = scope_info1.toString(); |
| 855 } else { |
| 856 scope_info1_text = ""; |
| 807 } | 857 } |
| 808 | 858 if (scope_info2) { |
| 809 if (scope_info1.length != scope_info2.length) { | 859 scope_info2_text = scope_info2.toString(); |
| 810 return false; | 860 } else { |
| 861 scope_info2_text = ""; |
| 811 } | 862 } |
| 812 | 863 |
| 813 // Check that outer scope structure is not changed. Otherwise the function | 864 if (scope_info1_text != scope_info2_text) { |
| 814 // will not properly work with existing scopes. | 865 return "Incompatible variable maps: [" + scope_info1_text + |
| 815 return scope_info1.toString() == scope_info2.toString(); | 866 "] and [" + scope_info2_text + "]"; |
| 867 } |
| 868 // No differences. Return undefined. |
| 869 return; |
| 816 } | 870 } |
| 817 | 871 |
| 818 // Minifier forward declaration. | 872 // Minifier forward declaration. |
| 819 var FunctionPatchabilityStatus; | 873 var FunctionPatchabilityStatus; |
| 820 | 874 |
| 821 // For array of wrapped shared function infos checks that none of them | 875 // For array of wrapped shared function infos checks that none of them |
| 822 // have activations on stack (of any thread). Throws a Failure exception | 876 // have activations on stack (of any thread). Throws a Failure exception |
| 823 // if this proves to be false. | 877 // if this proves to be false. |
| 824 function CheckStackActivations(shared_wrapper_list, change_log) { | 878 function CheckStackActivations(shared_wrapper_list, change_log) { |
| 825 var shared_list = new Array(); | 879 var shared_list = new Array(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 849 problems.push(description); | 903 problems.push(description); |
| 850 } | 904 } |
| 851 } | 905 } |
| 852 if (dropped.length > 0) { | 906 if (dropped.length > 0) { |
| 853 change_log.push({ dropped_from_stack: dropped }); | 907 change_log.push({ dropped_from_stack: dropped }); |
| 854 } | 908 } |
| 855 if (problems.length > 0) { | 909 if (problems.length > 0) { |
| 856 change_log.push( { functions_on_stack: problems } ); | 910 change_log.push( { functions_on_stack: problems } ); |
| 857 throw new Failure("Blocked by functions on stack"); | 911 throw new Failure("Blocked by functions on stack"); |
| 858 } | 912 } |
| 913 |
| 914 return dropped.length; |
| 859 } | 915 } |
| 860 | 916 |
| 861 // A copy of the FunctionPatchabilityStatus enum from liveedit.h | 917 // A copy of the FunctionPatchabilityStatus enum from liveedit.h |
| 862 var FunctionPatchabilityStatus = { | 918 var FunctionPatchabilityStatus = { |
| 863 AVAILABLE_FOR_PATCH: 1, | 919 AVAILABLE_FOR_PATCH: 1, |
| 864 BLOCKED_ON_ACTIVE_STACK: 2, | 920 BLOCKED_ON_ACTIVE_STACK: 2, |
| 865 BLOCKED_ON_OTHER_STACK: 3, | 921 BLOCKED_ON_OTHER_STACK: 3, |
| 866 BLOCKED_UNDER_NATIVE_CODE: 4, | 922 BLOCKED_UNDER_NATIVE_CODE: 4, |
| 867 REPLACED_ON_ACTIVE_STACK: 5 | 923 REPLACED_ON_ACTIVE_STACK: 5 |
| 868 } | 924 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 890 } | 946 } |
| 891 | 947 |
| 892 // A testing entry. | 948 // A testing entry. |
| 893 function GetPcFromSourcePos(func, source_pos) { | 949 function GetPcFromSourcePos(func, source_pos) { |
| 894 return %GetFunctionCodePositionFromSource(func, source_pos); | 950 return %GetFunctionCodePositionFromSource(func, source_pos); |
| 895 } | 951 } |
| 896 // Function is public. | 952 // Function is public. |
| 897 this.GetPcFromSourcePos = GetPcFromSourcePos; | 953 this.GetPcFromSourcePos = GetPcFromSourcePos; |
| 898 | 954 |
| 899 // LiveEdit main entry point: changes a script text to a new string. | 955 // LiveEdit main entry point: changes a script text to a new string. |
| 900 function SetScriptSource(script, new_source, change_log) { | 956 function SetScriptSource(script, new_source, preview_only, change_log) { |
| 901 var old_source = script.source; | 957 var old_source = script.source; |
| 902 var diff = CompareStringsLinewise(old_source, new_source); | 958 var diff = CompareStringsLinewise(old_source, new_source); |
| 903 if (diff.length == 0) { | 959 return ApplyPatchMultiChunk(script, diff, new_source, preview_only, |
| 904 change_log.push( { empty_diff: true } ); | 960 change_log); |
| 905 return; | |
| 906 } | |
| 907 ApplyPatchMultiChunk(script, diff, new_source, change_log); | |
| 908 } | 961 } |
| 909 // Function is public. | 962 // Function is public. |
| 910 this.SetScriptSource = SetScriptSource; | 963 this.SetScriptSource = SetScriptSource; |
| 911 | 964 |
| 912 function CompareStringsLinewise(s1, s2) { | 965 function CompareStringsLinewise(s1, s2) { |
| 913 return %LiveEditCompareStringsLinewise(s1, s2); | 966 return %LiveEditCompareStringsLinewise(s1, s2); |
| 914 } | 967 } |
| 915 | 968 |
| 916 // Applies the change to the script. | 969 // Applies the change to the script. |
| 917 // The change is always a substring (change_pos, change_pos + change_len) | 970 // The change is always a substring (change_pos, change_pos + change_len) |
| 918 // being replaced with a completely different string new_str. | 971 // being replaced with a completely different string new_str. |
| 919 // This API is a legacy and is obsolete. | 972 // This API is a legacy and is obsolete. |
| 920 // | 973 // |
| 921 // @param {Script} script that is being changed | 974 // @param {Script} script that is being changed |
| 922 // @param {Array} change_log a list that collects engineer-readable | 975 // @param {Array} change_log a list that collects engineer-readable |
| 923 // description of what happened. | 976 // description of what happened. |
| 924 function ApplySingleChunkPatch(script, change_pos, change_len, new_str, | 977 function ApplySingleChunkPatch(script, change_pos, change_len, new_str, |
| 925 change_log) { | 978 change_log) { |
| 926 var old_source = script.source; | 979 var old_source = script.source; |
| 927 | 980 |
| 928 // Prepare new source string. | 981 // Prepare new source string. |
| 929 var new_source = old_source.substring(0, change_pos) + | 982 var new_source = old_source.substring(0, change_pos) + |
| 930 new_str + old_source.substring(change_pos + change_len); | 983 new_str + old_source.substring(change_pos + change_len); |
| 931 | 984 |
| 932 return ApplyPatchMultiChunk(script, | 985 return ApplyPatchMultiChunk(script, |
| 933 [ change_pos, change_pos + change_len, change_pos + new_str.length], | 986 [ change_pos, change_pos + change_len, change_pos + new_str.length], |
| 934 new_source, change_log); | 987 new_source, false, change_log); |
| 988 } |
| 989 |
| 990 // Creates JSON description for a change tree. |
| 991 function DescribeChangeTree(old_code_tree) { |
| 992 |
| 993 function ProcessOldNode(node) { |
| 994 var child_infos = []; |
| 995 for (var i = 0; i < node.children.length; i++) { |
| 996 var child = node.children[i]; |
| 997 if (child.status != FunctionStatus.UNCHANGED) { |
| 998 child_infos.push(ProcessOldNode(child)); |
| 999 } |
| 1000 } |
| 1001 var new_child_infos = []; |
| 1002 if (node.textually_unmatched_new_nodes) { |
| 1003 for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) { |
| 1004 var child = node.textually_unmatched_new_nodes[i]; |
| 1005 new_child_infos.push(ProcessNewNode(child)); |
| 1006 } |
| 1007 } |
| 1008 var res = { |
| 1009 name: node.info.function_name, |
| 1010 positions: DescribePositions(node), |
| 1011 status: node.status, |
| 1012 children: child_infos, |
| 1013 new_children: new_child_infos |
| 1014 }; |
| 1015 if (node.status_explanation) { |
| 1016 res.status_explanation = node.status_explanation; |
| 1017 } |
| 1018 if (node.textual_corresponding_node) { |
| 1019 res.new_positions = DescribePositions(node.textual_corresponding_node); |
| 1020 } |
| 1021 return res; |
| 1022 } |
| 1023 |
| 1024 function ProcessNewNode(node) { |
| 1025 var child_infos = []; |
| 1026 // Do not list ancestors. |
| 1027 if (false) { |
| 1028 for (var i = 0; i < node.children.length; i++) { |
| 1029 child_infos.push(ProcessNewNode(node.children[i])); |
| 1030 } |
| 1031 } |
| 1032 var res = { |
| 1033 name: node.info.function_name, |
| 1034 positions: DescribePositions(node), |
| 1035 children: child_infos, |
| 1036 }; |
| 1037 return res; |
| 1038 } |
| 1039 |
| 1040 function DescribePositions(node) { |
| 1041 return { |
| 1042 start_position: node.info.start_position, |
| 1043 end_position: node.info.end_position |
| 1044 }; |
| 1045 } |
| 1046 |
| 1047 return ProcessOldNode(old_code_tree); |
| 935 } | 1048 } |
| 936 | 1049 |
| 937 | 1050 |
| 938 // Functions are public for tests. | 1051 // Functions are public for tests. |
| 939 this.TestApi = { | 1052 this.TestApi = { |
| 940 PosTranslator: PosTranslator, | 1053 PosTranslator: PosTranslator, |
| 941 CompareStringsLinewise: CompareStringsLinewise, | 1054 CompareStringsLinewise: CompareStringsLinewise, |
| 942 ApplySingleChunkPatch: ApplySingleChunkPatch | 1055 ApplySingleChunkPatch: ApplySingleChunkPatch |
| 943 } | 1056 } |
| 944 } | 1057 } |
| OLD | NEW |