| Index: src/liveedit-debugger.js
|
| diff --git a/src/liveedit-debugger.js b/src/liveedit-debugger.js
|
| index c8c6f082c03297d28ca895c8b70cd73345db1197..506fb5b665319af775ed343f080735a724fe5d76 100644
|
| --- a/src/liveedit-debugger.js
|
| +++ b/src/liveedit-debugger.js
|
| @@ -138,77 +138,92 @@ Debug.LiveEdit = new function() {
|
|
|
| HarvestTodo(root_old_node);
|
|
|
| - // Collect shared infos for functions whose code need to be patched.
|
| - var replaced_function_infos = new Array();
|
| - for (var i = 0; i < replace_code_list.length; i++) {
|
| - var info_wrapper = replace_code_list[i].live_shared_info_wrapper;
|
| - if (info_wrapper) {
|
| - replaced_function_infos.push(info_wrapper);
|
| - }
|
| - }
|
| -
|
| + // Check whether the functions being patched are currently on stack.
|
| + var stack_info_wrapper =
|
| + CheckStackActivations(replace_code_list, change_log);
|
| + preview_description.stack_modified = !!stack_info_wrapper;
|
| +
|
| // We haven't changed anything before this line yet.
|
| // Committing all changes.
|
| -
|
| - // Check that function being patched is not currently on stack or drop them.
|
| - var dropped_functions_number =
|
| - CheckStackActivations(replaced_function_infos, change_log);
|
| -
|
| - preview_description.stack_modified = dropped_functions_number != 0;
|
| -
|
| +
|
| // Start with breakpoints. Convert their line/column positions and
|
| // temporary remove.
|
| var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
|
| -
|
| - var old_script;
|
| -
|
| - // Create an old script only if there are function that should be linked
|
| - // to old version.
|
| - if (link_to_old_script_list.length == 0) {
|
| - %LiveEditReplaceScript(script, new_source, null);
|
| - old_script = void 0;
|
| - } else {
|
| - var old_script_name = CreateNameForOldScript(script);
|
| -
|
| - // Update the script text and create a new script representing an old
|
| - // version of the script.
|
| - old_script = %LiveEditReplaceScript(script, new_source,
|
| - old_script_name);
|
| -
|
| - var link_to_old_script_report = new Array();
|
| - change_log.push( { linked_to_old_script: link_to_old_script_report } );
|
|
|
| - // We need to link to old script all former nested functions.
|
| - for (var i = 0; i < link_to_old_script_list.length; i++) {
|
| - LinkToOldScript(link_to_old_script_list[i], old_script,
|
| - link_to_old_script_report);
|
| + try {
|
| + var old_script;
|
| +
|
| + // Create an old script only if there are function that should be linked
|
| + // to old version.
|
| + if (link_to_old_script_list.length == 0) {
|
| + %LiveEditReplaceScript(script, new_source, null);
|
| + old_script = void 0;
|
| + } else {
|
| + var old_script_name = CreateNameForOldScript(script);
|
| +
|
| + // Update the script text and create a new script representing an old
|
| + // version of the script.
|
| + old_script = %LiveEditReplaceScript(script, new_source,
|
| + old_script_name);
|
| +
|
| + var link_to_old_script_report = new Array();
|
| + change_log.push( { linked_to_old_script: link_to_old_script_report } );
|
| +
|
| + // We need to link to old script all former nested functions.
|
| + for (var i = 0; i < link_to_old_script_list.length; i++) {
|
| + LinkToOldScript(link_to_old_script_list[i], old_script,
|
| + link_to_old_script_report);
|
| + }
|
| +
|
| + preview_description.created_script_name = old_script_name;
|
| }
|
|
|
| - preview_description.created_script_name = old_script_name;
|
| - }
|
| -
|
| - // Link to an actual script all the functions that we are going to use.
|
| - for (var i = 0; i < link_to_original_script_list.length; i++) {
|
| - %LiveEditFunctionSetScript(
|
| - link_to_original_script_list[i].info.shared_function_info, script);
|
| - }
|
| + // Link to an actual script all the functions that we are going to use.
|
| + for (var i = 0; i < link_to_original_script_list.length; i++) {
|
| + %LiveEditFunctionSetScript(
|
| + link_to_original_script_list[i].info.shared_function_info, script);
|
| + }
|
|
|
| - for (var i = 0; i < replace_code_list.length; i++) {
|
| - PatchFunctionCode(replace_code_list[i], change_log);
|
| - }
|
| -
|
| - var position_patch_report = new Array();
|
| - change_log.push( {position_patched: position_patch_report} );
|
| + // In the general case there are several functions to be patched
|
| + // and some of them are activated on the stack.
|
| + // We drop n topmost frames from the stack, while restaring the
|
| + // bottom frame. The bottom frame function always is in the patch
|
| + // list (otherwise there was no reason to touch it). We have to
|
| + // reset the stack and patch the function in one transaction.
|
| + // All other functions are patched later.
|
| + var bottom_function_index;
|
| + if (stack_info_wrapper) {
|
| + bottom_function_index = stack_info_wrapper.bottom_function_index;
|
| + // First we must patch the function that is being reset
|
| + // to start on stack.
|
| + PatchFunctionCode(replace_code_list[bottom_function_index],
|
| + stack_info_wrapper.drop_data, change_log);
|
| + } else {
|
| + bottom_function_index = -1;
|
| + }
|
| +
|
| +
|
| + for (var i = 0; i < replace_code_list.length; i++) {
|
| + if (i == bottom_function_index) {
|
| + // Skip the function that has already been patched.
|
| + continue;
|
| + }
|
| + PatchFunctionCode(replace_code_list[i], void 0, change_log);
|
| + }
|
|
|
| - for (var i = 0; i < update_positions_list.length; i++) {
|
| - // TODO(LiveEdit): take into account wether it's source_changed or
|
| - // unchanged and whether positions changed at all.
|
| - PatchPositions(update_positions_list[i], diff_array,
|
| - position_patch_report);
|
| + var position_patch_report = new Array();
|
| + change_log.push( {position_patched: position_patch_report} );
|
| +
|
| + for (var i = 0; i < update_positions_list.length; i++) {
|
| + // TODO(LiveEdit): take into account wether it's source_changed or
|
| + // unchanged and whether positions changed at all.
|
| + PatchPositions(update_positions_list[i], diff_array,
|
| + position_patch_report);
|
| + }
|
| + } finally {
|
| + break_points_restorer(pos_translator, old_script);
|
| }
|
|
|
| - break_points_restorer(pos_translator, old_script);
|
| -
|
| preview_description.updated = true;
|
| return preview_description;
|
| }
|
| @@ -292,13 +307,13 @@ Debug.LiveEdit = new function() {
|
| }
|
|
|
|
|
| - // Replaces function's Code.
|
| - function PatchFunctionCode(old_node, change_log) {
|
| + // Replaces function's Code and optionally resets the stack.
|
| + function PatchFunctionCode(old_node, stack_drop_data, change_log) {
|
| var new_info = old_node.corresponding_node.info;
|
| var shared_info_wrapper = old_node.live_shared_info_wrapper;
|
| if (shared_info_wrapper) {
|
| %LiveEditReplaceFunctionCode(new_info.raw_array,
|
| - shared_info_wrapper.raw_array);
|
| + shared_info_wrapper.raw_array, stack_drop_data);
|
|
|
| // The function got a new code. However, this new code brings all new
|
| // instances of SharedFunctionInfo for nested functions. However,
|
| @@ -814,6 +829,15 @@ Debug.LiveEdit = new function() {
|
| this.info = raw_array[3];
|
| this.raw_array = raw_array;
|
| }
|
| +
|
| + // Information about possible stack manipulations. Either
|
| + // success (drop_data is defined), failure (error_message is defined)
|
| + // or empty otherwise.
|
| + function StackInfoWrapper(raw_array) {
|
| + this.error_message = raw_array[0];
|
| + this.bottom_function_index = raw_array[1];
|
| + this.drop_data = raw_array[2];
|
| + }
|
|
|
| // Changes positions (including all statments) in function.
|
| function PatchPositions(old_info_node, diff_array, report_array) {
|
| @@ -869,67 +893,35 @@ Debug.LiveEdit = new function() {
|
| return;
|
| }
|
|
|
| - // Minifier forward declaration.
|
| - var FunctionPatchabilityStatus;
|
| -
|
| - // For array of wrapped shared function infos checks that none of them
|
| + // For an array of function nodes checks that none of them
|
| // have activations on stack (of any thread). Throws a Failure exception
|
| // if this proves to be false.
|
| - function CheckStackActivations(shared_wrapper_list, change_log) {
|
| + // Returns StackInfoWrapper for future stack manipulations or
|
| + // nothing if there is nothing to do with stack.
|
| + function CheckStackActivations(function_node_list, change_log) {
|
| var shared_list = new Array();
|
| - for (var i = 0; i < shared_wrapper_list.length; i++) {
|
| - shared_list[i] = shared_wrapper_list[i].info;
|
| - }
|
| - var result = %LiveEditCheckAndDropActivations(shared_list, true);
|
| - if (result[shared_list.length]) {
|
| - // Extra array element may contain error message.
|
| - throw new Failure(result[shared_list.length]);
|
| - }
|
| -
|
| - var problems = new Array();
|
| - var dropped = new Array();
|
| - for (var i = 0; i < shared_list.length; i++) {
|
| - var shared = shared_wrapper_list[i];
|
| - if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
|
| - dropped.push({ name: shared.function_name } );
|
| - } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
|
| - var description = {
|
| - name: shared.function_name,
|
| - start_pos: shared.start_position,
|
| - end_pos: shared.end_position,
|
| - replace_problem:
|
| - FunctionPatchabilityStatus.SymbolName(result[i])
|
| - };
|
| - problems.push(description);
|
| + for (var i = 0; i < function_node_list.length; i++) {
|
| + var info_wrapper = function_node_list[i].live_shared_info_wrapper;
|
| + if (info_wrapper) {
|
| + shared_list[i] = info_wrapper.info;
|
| }
|
| }
|
| - if (dropped.length > 0) {
|
| - change_log.push({ dropped_from_stack: dropped });
|
| + var raw_data_array = %LiveEditCheckActivations(shared_list);
|
| + var stack_info_wrapper = new StackInfoWrapper(raw_data_array);
|
| + var bottom_function_index = stack_info_wrapper.bottom_function_index;
|
| + if (stack_info_wrapper.error_message) {
|
| + if (IS_NUMBER(bottom_function_index)) {
|
| + throw new Failure(stack_info_wrapper.error_message +
|
| + "; function=" + shared_list[bottom_function_index].function_name);
|
| + } else {
|
| + throw new Failure(stack_info_wrapper.error_message + ".");
|
| + }
|
| }
|
| - if (problems.length > 0) {
|
| - change_log.push( { functions_on_stack: problems } );
|
| - throw new Failure("Blocked by functions on stack");
|
| + if (IS_NUMBER(bottom_function_index)) {
|
| + return stack_info_wrapper;
|
| + } else {
|
| + return;
|
| }
|
| -
|
| - return dropped.length;
|
| - }
|
| -
|
| - // A copy of the FunctionPatchabilityStatus enum from liveedit.h
|
| - var FunctionPatchabilityStatus = {
|
| - AVAILABLE_FOR_PATCH: 1,
|
| - BLOCKED_ON_ACTIVE_STACK: 2,
|
| - BLOCKED_ON_OTHER_STACK: 3,
|
| - BLOCKED_UNDER_NATIVE_CODE: 4,
|
| - REPLACED_ON_ACTIVE_STACK: 5
|
| - }
|
| -
|
| - FunctionPatchabilityStatus.SymbolName = function(code) {
|
| - var enum = FunctionPatchabilityStatus;
|
| - for (name in enum) {
|
| - if (enum[name] == code) {
|
| - return name;
|
| - }
|
| - }
|
| }
|
|
|
|
|
|
|