| 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 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 function ApplyPatchMultiChunk(script, diff_array, new_source, change_log) { | 76 function ApplyPatchMultiChunk(script, diff_array, new_source, change_log) { |
| 77 | 77 |
| 78 // Fully compiles source string as a script. Returns Array of | 78 // Fully compiles source string as a script. Returns Array of |
| 79 // FunctionCompileInfo -- a descriptions of all functions of the script. | 79 // FunctionCompileInfo -- a descriptions of all functions of the script. |
| 80 // Elements of array are ordered by start positions of functions (from top | 80 // Elements of array are ordered by start positions of functions (from top |
| 81 // to bottom) in the source. Fields outer_index and next_sibling_index help | 81 // to bottom) in the source. Fields outer_index and next_sibling_index help |
| 82 // to navigate the nesting structure of functions. | 82 // to navigate the nesting structure of functions. |
| 83 // | 83 // |
| 84 // The script is used for compilation, because it produces code that | 84 // The script is used for compilation, because it produces code that |
| 85 // needs to be linked with some particular script (for nested functions). | 85 // needs to be linked with some particular script (for nested functions). |
| 86 function DebugGatherCompileInfo(source) { | 86 function GatherCompileInfo(source) { |
| 87 // Get function info, elements are partially sorted (it is a tree of | 87 // Get function info, elements are partially sorted (it is a tree of |
| 88 // nested functions serialized as parent followed by serialized children. | 88 // nested functions serialized as parent followed by serialized children. |
| 89 var raw_compile_info = %LiveEditGatherCompileInfo(script, source); | 89 var raw_compile_info = %LiveEditGatherCompileInfo(script, source); |
| 90 | 90 |
| 91 // Sort function infos by start position field. | 91 // Sort function infos by start position field. |
| 92 var compile_info = new Array(); | 92 var compile_info = new Array(); |
| 93 var old_index_map = new Array(); | 93 var old_index_map = new Array(); |
| 94 for (var i = 0; i < raw_compile_info.length; i++) { | 94 for (var i = 0; i < raw_compile_info.length; i++) { |
| 95 compile_info.push(new FunctionCompileInfo(raw_compile_info[i])); | 95 var info = new FunctionCompileInfo(raw_compile_info[i]); |
| 96 old_index_map.push(i); | 96 // Remove all links to the actual script. Breakpoints system and |
| 97 // LiveEdit itself believe that any function in heap that points to a |
| 98 // particular script is a regular function. |
| 99 // For some functions we will restore this link later. |
| 100 %LiveEditFunctionSetScript(info.shared_function_info, void 0); |
| 101 compile_info.push(info); |
| 102 old_index_map.push(i); |
| 97 } | 103 } |
| 98 | 104 |
| 99 for (var i = 0; i < compile_info.length; i++) { | 105 for (var i = 0; i < compile_info.length; i++) { |
| 100 var k = i; | 106 var k = i; |
| 101 for (var j = i + 1; j < compile_info.length; j++) { | 107 for (var j = i + 1; j < compile_info.length; j++) { |
| 102 if (compile_info[k].start_position > compile_info[j].start_position) { | 108 if (compile_info[k].start_position > compile_info[j].start_position) { |
| 103 k = j; | 109 k = j; |
| 104 } | 110 } |
| 105 } | 111 } |
| 106 if (k != i) { | 112 if (k != i) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 Assert(current_index == compile_info.length); | 147 Assert(current_index == compile_info.length); |
| 142 | 148 |
| 143 return compile_info; | 149 return compile_info; |
| 144 } | 150 } |
| 145 | 151 |
| 146 // Variable forward declarations. Preprocessor "Minifier" needs them. | 152 // Variable forward declarations. Preprocessor "Minifier" needs them. |
| 147 var old_compile_info; | 153 var old_compile_info; |
| 148 var shared_infos; | 154 var shared_infos; |
| 149 // Finds SharedFunctionInfo that corresponds compile info with index | 155 // Finds SharedFunctionInfo that corresponds compile info with index |
| 150 // in old version of the script. | 156 // in old version of the script. |
| 157 // TODO(LiveEdit): Redesign this part. Find live SharedFunctionInfo |
| 158 // about the time that FindCorrespondingFunctions is being run. |
| 151 function FindFunctionInfo(index) { | 159 function FindFunctionInfo(index) { |
| 152 var old_info = old_compile_info[index]; | 160 var old_info = old_compile_info[index]; |
| 153 for (var i = 0; i < shared_infos.length; i++) { | 161 for (var i = 0; i < shared_infos.length; i++) { |
| 154 var info = shared_infos[i]; | 162 var info = shared_infos[i]; |
| 155 if (info.start_position == old_info.start_position && | 163 if (info.start_position == old_info.start_position && |
| 156 info.end_position == old_info.end_position) { | 164 info.end_position == old_info.end_position) { |
| 157 return info; | 165 return info; |
| 158 } | 166 } |
| 159 } | 167 } |
| 160 } | 168 } |
| 161 | 169 |
| 162 // Replaces function's Code. | 170 // Replaces function's Code. |
| 163 function PatchCode(new_info, shared_info) { | 171 function PatchCode(old_node) { |
| 172 var new_info = old_node.corresponding_node.info; |
| 173 var shared_info = FindFunctionInfo(old_node.array_index); |
| 164 if (shared_info) { | 174 if (shared_info) { |
| 165 %LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array); | 175 %LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array); |
| 176 |
| 177 // The function got a new code. However, this new code brings all new |
| 178 // instances of SharedFunctionInfo for nested functions. However, |
| 179 // we want the original instances to be used wherever possible. |
| 180 // (This is because old instances and new instances will be both |
| 181 // linked to a script and breakpoints subsystem does not really |
| 182 // expects this; neither does LiveEdit subsystem on next call). |
| 183 for (var i = 0; i < old_node.children.length; i++) { |
| 184 if (old_node.children[i].corresponding_node) { |
| 185 var corresponding_child = old_node.children[i].corresponding_node; |
| 186 var child_shared_info = |
| 187 FindFunctionInfo(old_node.children[i].array_index); |
| 188 if (child_shared_info) { |
| 189 %LiveEditReplaceRefToNestedFunction(shared_info.info, |
| 190 corresponding_child.info.shared_function_info, |
| 191 child_shared_info.info); |
| 192 } |
| 193 } |
| 194 } |
| 195 |
| 166 change_log.push( {function_patched: new_info.function_name} ); | 196 change_log.push( {function_patched: new_info.function_name} ); |
| 167 } else { | 197 } else { |
| 168 change_log.push( {function_patched: new_info.function_name, | 198 change_log.push( {function_patched: new_info.function_name, |
| 169 function_info_not_found: true} ); | 199 function_info_not_found: true} ); |
| 170 } | 200 } |
| 171 | |
| 172 } | 201 } |
| 173 | 202 |
| 174 | 203 |
| 175 var position_patch_report; | 204 var position_patch_report; |
| 176 function PatchPositions(old_info, shared_info) { | 205 function PatchPositions(old_info, shared_info) { |
| 177 if (!shared_info) { | 206 if (!shared_info) { |
| 178 // TODO(LiveEdit): function is not compiled yet or is already collected. | 207 // TODO(LiveEdit): function is not compiled yet or is already collected. |
| 179 position_patch_report.push( | 208 position_patch_report.push( |
| 180 { name: old_info.function_name, info_not_found: true } ); | 209 { name: old_info.function_name, info_not_found: true } ); |
| 181 return; | 210 return; |
| 182 } | 211 } |
| 183 var breakpoint_position_update = %LiveEditPatchFunctionPositions( | 212 %LiveEditPatchFunctionPositions( |
| 184 shared_info.raw_array, diff_array); | 213 shared_info.raw_array, diff_array); |
| 185 for (var i = 0; i < breakpoint_position_update.length; i += 2) { | |
| 186 var new_pos = breakpoint_position_update[i]; | |
| 187 var break_point_object = breakpoint_position_update[i + 1]; | |
| 188 change_log.push( { breakpoint_position_update: | |
| 189 { from: break_point_object.source_position(), to: new_pos } } ); | |
| 190 break_point_object.updateSourcePosition(new_pos, script); | |
| 191 } | |
| 192 position_patch_report.push( { name: old_info.function_name } ); | 214 position_patch_report.push( { name: old_info.function_name } ); |
| 193 } | 215 } |
| 194 | 216 |
| 195 var link_to_old_script_report; | 217 var link_to_old_script_report; |
| 196 var old_script; | 218 var old_script; |
| 197 // Makes a function associated with another instance of a script (the | 219 // Makes a function associated with another instance of a script (the |
| 198 // one representing its old version). This way the function still | 220 // one representing its old version). This way the function still |
| 199 // may access its own text. | 221 // may access its own text. |
| 200 function LinkToOldScript(shared_info, old_info_node) { | 222 function LinkToOldScript(shared_info, old_info_node) { |
| 201 if (shared_info) { | 223 if (shared_info) { |
| 202 %LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script); | 224 %LiveEditFunctionSetScript(shared_info.info, old_script); |
| 203 link_to_old_script_report.push( { name: shared_info.function_name } ); | 225 link_to_old_script_report.push( { name: shared_info.function_name } ); |
| 204 } else { | 226 } else { |
| 205 link_to_old_script_report.push( | 227 link_to_old_script_report.push( |
| 206 { name: old_info_node.info.function_name, not_found: true } ); | 228 { name: old_info_node.info.function_name, not_found: true } ); |
| 207 } | 229 } |
| 208 } | 230 } |
| 209 | 231 |
| 210 | 232 |
| 211 var old_source = script.source; | 233 var old_source = script.source; |
| 212 | 234 |
| 213 // Find all SharedFunctionInfo's that are compiled from this script. | 235 // Find all SharedFunctionInfo's that are compiled from this script. |
| 214 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script); | 236 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script); |
| 215 | 237 |
| 216 var shared_infos = new Array(); | 238 var shared_infos = new Array(); |
| 217 | 239 |
| 218 for (var i = 0; i < shared_raw_list.length; i++) { | 240 for (var i = 0; i < shared_raw_list.length; i++) { |
| 219 shared_infos.push(new SharedInfoWrapper(shared_raw_list[i])); | 241 shared_infos.push(new SharedInfoWrapper(shared_raw_list[i])); |
| 220 } | 242 } |
| 221 | 243 |
| 222 // Gather compile information about old version of script. | 244 // Gather compile information about old version of script. |
| 223 var old_compile_info = DebugGatherCompileInfo(old_source); | 245 var old_compile_info = GatherCompileInfo(old_source); |
| 224 | 246 |
| 225 // Gather compile information about new version of script. | 247 // Gather compile information about new version of script. |
| 226 var new_compile_info; | 248 var new_compile_info; |
| 227 try { | 249 try { |
| 228 new_compile_info = DebugGatherCompileInfo(new_source); | 250 new_compile_info = GatherCompileInfo(new_source); |
| 229 } catch (e) { | 251 } catch (e) { |
| 230 throw new Failure("Failed to compile new version of script: " + e); | 252 throw new Failure("Failed to compile new version of script: " + e); |
| 231 } | 253 } |
| 232 | 254 |
| 233 var pos_translator = new PosTranslator(diff_array); | 255 var pos_translator = new PosTranslator(diff_array); |
| 234 | 256 |
| 235 // Build tree structures for old and new versions of the script. | 257 // Build tree structures for old and new versions of the script. |
| 236 var root_old_node = BuildCodeInfoTree(old_compile_info); | 258 var root_old_node = BuildCodeInfoTree(old_compile_info); |
| 237 var root_new_node = BuildCodeInfoTree(new_compile_info); | 259 var root_new_node = BuildCodeInfoTree(new_compile_info); |
| 238 | 260 |
| 239 // Analyze changes. | 261 // Analyze changes. |
| 240 MarkChangedFunctions(root_old_node, pos_translator.GetChunks()); | 262 MarkChangedFunctions(root_old_node, pos_translator.GetChunks()); |
| 241 FindCorrespondingFunctions(root_old_node, root_new_node); | 263 FindCorrespondingFunctions(root_old_node, root_new_node); |
| 242 | 264 |
| 243 // Prepare to-do lists. | 265 // Prepare to-do lists. |
| 244 var replace_code_list = new Array(); | 266 var replace_code_list = new Array(); |
| 245 var link_to_old_script_list = new Array(); | 267 var link_to_old_script_list = new Array(); |
| 268 var link_to_original_script_list = new Array(); |
| 246 var update_positions_list = new Array(); | 269 var update_positions_list = new Array(); |
| 247 | 270 |
| 248 function HarvestTodo(old_node) { | 271 function HarvestTodo(old_node) { |
| 249 function CollectDamaged(node) { | 272 function CollectDamaged(node) { |
| 250 link_to_old_script_list.push(node); | 273 link_to_old_script_list.push(node); |
| 251 for (var i = 0; i < node.children.length; i++) { | 274 for (var i = 0; i < node.children.length; i++) { |
| 252 CollectDamaged(node.children[i]); | 275 CollectDamaged(node.children[i]); |
| 253 } | 276 } |
| 254 } | 277 } |
| 278 |
| 279 // Recursively collects all newly compiled functions that are going into |
| 280 // business and should be have link to the actual script updated. |
| 281 function CollectNew(node_list) { |
| 282 for (var i = 0; i < node_list.length; i++) { |
| 283 link_to_original_script_list.push(node_list[i]); |
| 284 CollectNew(node_list[i].children); |
| 285 } |
| 286 } |
| 255 | 287 |
| 256 if (old_node.status == FunctionStatus.DAMAGED) { | 288 if (old_node.status == FunctionStatus.DAMAGED) { |
| 257 CollectDamaged(old_node); | 289 CollectDamaged(old_node); |
| 258 return; | 290 return; |
| 259 } | 291 } |
| 260 if (old_node.status == FunctionStatus.UNCHANGED) { | 292 if (old_node.status == FunctionStatus.UNCHANGED) { |
| 261 update_positions_list.push(old_node); | 293 update_positions_list.push(old_node); |
| 262 } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) { | 294 } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) { |
| 263 update_positions_list.push(old_node); | 295 update_positions_list.push(old_node); |
| 264 } else if (old_node.status == FunctionStatus.CHANGED) { | 296 } else if (old_node.status == FunctionStatus.CHANGED) { |
| 265 replace_code_list.push(old_node); | 297 replace_code_list.push(old_node); |
| 298 CollectNew(old_node.unmatched_new_nodes); |
| 266 } | 299 } |
| 267 for (var i = 0; i < old_node.children.length; i++) { | 300 for (var i = 0; i < old_node.children.length; i++) { |
| 268 HarvestTodo(old_node.children[i]); | 301 HarvestTodo(old_node.children[i]); |
| 269 } | 302 } |
| 270 } | 303 } |
| 271 | 304 |
| 272 HarvestTodo(root_old_node); | 305 HarvestTodo(root_old_node); |
| 273 | 306 |
| 274 // Collect shared infos for functions whose code need to be patched. | 307 // Collect shared infos for functions whose code need to be patched. |
| 275 var replaced_function_infos = new Array(); | 308 var replaced_function_infos = new Array(); |
| 276 for (var i = 0; i < replace_code_list.length; i++) { | 309 for (var i = 0; i < replace_code_list.length; i++) { |
| 277 var info = FindFunctionInfo(replace_code_list[i].array_index); | 310 var info = FindFunctionInfo(replace_code_list[i].array_index); |
| 278 if (info) { | 311 if (info) { |
| 279 replaced_function_infos.push(info); | 312 replaced_function_infos.push(info); |
| 280 } | 313 } |
| 281 } | 314 } |
| 282 | 315 |
| 283 // Check that function being patched is not currently on stack. | 316 // Check that function being patched is not currently on stack. |
| 284 CheckStackActivations(replaced_function_infos, change_log); | 317 CheckStackActivations(replaced_function_infos, change_log); |
| 285 | 318 |
| 286 | 319 |
| 287 // We haven't changed anything before this line yet. | 320 // We haven't changed anything before this line yet. |
| 288 // Committing all changes. | 321 // Committing all changes. |
| 322 |
| 323 // Start with breakpoints. Convert their line/column positions and |
| 324 // temporary remove. |
| 325 var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log); |
| 289 | 326 |
| 290 // Create old script if there are function linked to old version. | 327 var old_script; |
| 291 if (link_to_old_script_list.length > 0) { | 328 |
| 329 // Create an old script only if there are function that should be linked |
| 330 // to old version. |
| 331 if (link_to_old_script_list.length == 0) { |
| 332 %LiveEditReplaceScript(script, new_source, null); |
| 333 old_script = void 0; |
| 334 } else { |
| 292 var old_script_name = CreateNameForOldScript(script); | 335 var old_script_name = CreateNameForOldScript(script); |
| 293 | 336 |
| 294 // Update the script text and create a new script representing an old | 337 // Update the script text and create a new script representing an old |
| 295 // version of the script. | 338 // version of the script. |
| 296 var old_script = %LiveEditReplaceScript(script, new_source, | 339 old_script = %LiveEditReplaceScript(script, new_source, |
| 297 old_script_name); | 340 old_script_name); |
| 298 | 341 |
| 299 var link_to_old_script_report = new Array(); | 342 var link_to_old_script_report = new Array(); |
| 300 change_log.push( { linked_to_old_script: link_to_old_script_report } ); | 343 change_log.push( { linked_to_old_script: link_to_old_script_report } ); |
| 301 | 344 |
| 302 // We need to link to old script all former nested functions. | 345 // We need to link to old script all former nested functions. |
| 303 for (var i = 0; i < link_to_old_script_list.length; i++) { | 346 for (var i = 0; i < link_to_old_script_list.length; i++) { |
| 304 LinkToOldScript( | 347 LinkToOldScript( |
| 305 FindFunctionInfo(link_to_old_script_list[i].array_index), | 348 FindFunctionInfo(link_to_old_script_list[i].array_index), |
| 306 link_to_old_script_list[i]); | 349 link_to_old_script_list[i]); |
| 307 } | 350 } |
| 308 } | 351 } |
| 309 | 352 |
| 353 // Link to an actual script all the functions that we are going to use. |
| 354 for (var i = 0; i < link_to_original_script_list.length; i++) { |
| 355 %LiveEditFunctionSetScript( |
| 356 link_to_original_script_list[i].info.shared_function_info, script); |
| 357 } |
| 310 | 358 |
| 311 for (var i = 0; i < replace_code_list.length; i++) { | 359 for (var i = 0; i < replace_code_list.length; i++) { |
| 312 PatchCode(replace_code_list[i].corresponding_node.info, | 360 PatchCode(replace_code_list[i]); |
| 313 FindFunctionInfo(replace_code_list[i].array_index)); | |
| 314 } | 361 } |
| 315 | 362 |
| 316 var position_patch_report = new Array(); | 363 var position_patch_report = new Array(); |
| 317 change_log.push( {position_patched: position_patch_report} ); | 364 change_log.push( {position_patched: position_patch_report} ); |
| 318 | 365 |
| 319 for (var i = 0; i < update_positions_list.length; i++) { | 366 for (var i = 0; i < update_positions_list.length; i++) { |
| 320 // TODO(LiveEdit): take into account wether it's source_changed or | 367 // TODO(LiveEdit): take into account wether it's source_changed or |
| 321 // unchanged and whether positions changed at all. | 368 // unchanged and whether positions changed at all. |
| 322 PatchPositions(update_positions_list[i].info, | 369 PatchPositions(update_positions_list[i].info, |
| 323 FindFunctionInfo(update_positions_list[i].array_index)); | 370 FindFunctionInfo(update_positions_list[i].array_index)); |
| 324 } | 371 } |
| 372 |
| 373 break_points_restorer(pos_translator, old_script); |
| 325 } | 374 } |
| 326 // Function is public. | 375 // Function is public. |
| 327 this.ApplyPatchMultiChunk = ApplyPatchMultiChunk; | 376 this.ApplyPatchMultiChunk = ApplyPatchMultiChunk; |
| 328 | 377 |
| 378 |
| 379 // Returns function that restores breakpoints. |
| 380 function TemporaryRemoveBreakPoints(original_script, change_log) { |
| 381 var script_break_points = GetScriptBreakPoints(original_script); |
| 382 |
| 383 var break_points_update_report = []; |
| 384 change_log.push( { break_points_update: break_points_update_report } ); |
| 385 |
| 386 var break_point_old_positions = []; |
| 387 for (var i = 0; i < script_break_points.length; i++) { |
| 388 var break_point = script_break_points[i]; |
| 389 |
| 390 break_point.clear(); |
| 391 |
| 392 // TODO(LiveEdit): be careful with resource offset here. |
| 393 var break_point_position = Debug.findScriptSourcePosition(original_script, |
| 394 break_point.line(), break_point.column()); |
| 395 |
| 396 var old_position_description = { |
| 397 position: break_point_position, |
| 398 line: break_point.line(), |
| 399 column: break_point.column() |
| 400 } |
| 401 break_point_old_positions.push(old_position_description); |
| 402 } |
| 403 |
| 404 |
| 405 // Restores breakpoints and creates their copies in the "old" copy of |
| 406 // the script. |
| 407 return function (pos_translator, old_script_copy_opt) { |
| 408 // Update breakpoints (change positions and restore them in old version |
| 409 // of script. |
| 410 for (var i = 0; i < script_break_points.length; i++) { |
| 411 var break_point = script_break_points[i]; |
| 412 if (old_script_copy_opt) { |
| 413 var clone = break_point.cloneForOtherScript(old_script_copy_opt); |
| 414 clone.set(old_script_copy_opt); |
| 415 |
| 416 break_points_update_report.push( { |
| 417 type: "copied_to_old", |
| 418 id: break_point.number(), |
| 419 new_id: clone.number(), |
| 420 positions: break_point_old_positions[i] |
| 421 } ); |
| 422 } |
| 423 |
| 424 var updated_position = pos_translator.Translate( |
| 425 break_point_old_positions[i].position, |
| 426 PosTranslator.ShiftWithTopInsideChunkHandler); |
| 427 |
| 428 var new_location = |
| 429 original_script.locationFromPosition(updated_position, false); |
| 430 |
| 431 break_point.update_positions(new_location.line, new_location.column); |
| 432 |
| 433 var new_position_description = { |
| 434 position: updated_position, |
| 435 line: new_location.line, |
| 436 column: new_location.column |
| 437 } |
| 438 |
| 439 break_point.set(original_script); |
| 440 |
| 441 break_points_update_report.push( { type: "position_changed", |
| 442 id: break_point.number(), |
| 443 old_positions: break_point_old_positions[i], |
| 444 new_positions: new_position_description |
| 445 } ); |
| 446 } |
| 447 } |
| 448 } |
| 449 |
| 329 | 450 |
| 330 function Assert(condition, message) { | 451 function Assert(condition, message) { |
| 331 if (!condition) { | 452 if (!condition) { |
| 332 if (message) { | 453 if (message) { |
| 333 throw "Assert " + message; | 454 throw "Assert " + message; |
| 334 } else { | 455 } else { |
| 335 throw "Assert"; | 456 throw "Assert"; |
| 336 } | 457 } |
| 337 } | 458 } |
| 338 } | 459 } |
| 339 | 460 |
| 340 function DiffChunk(pos1, pos2, len1, len2) { | 461 function DiffChunk(pos1, pos2, len1, len2) { |
| 341 this.pos1 = pos1; | 462 this.pos1 = pos1; |
| 342 this.pos2 = pos2; | 463 this.pos2 = pos2; |
| 343 this.len1 = len1; | 464 this.len1 = len1; |
| 344 this.len2 = len2; | 465 this.len2 = len2; |
| 345 } | 466 } |
| 346 | 467 |
| 347 function PosTranslator(diff_array) { | 468 function PosTranslator(diff_array) { |
| 348 var chunks = new Array(); | 469 var chunks = new Array(); |
| 349 var pos1 = 0; | 470 var current_diff = 0; |
| 350 var pos2 = 0; | |
| 351 for (var i = 0; i < diff_array.length; i += 3) { | 471 for (var i = 0; i < diff_array.length; i += 3) { |
| 352 pos2 += diff_array[i] - pos1 + pos2; | 472 var pos1_begin = diff_array[i]; |
| 353 pos1 = diff_array[i]; | 473 var pos2_begin = pos1_begin + current_diff; |
| 354 chunks.push(new DiffChunk(pos1, pos2, diff_array[i + 1] - pos1, | 474 var pos1_end = diff_array[i + 1]; |
| 355 diff_array[i + 2] - pos2)); | 475 var pos2_end = diff_array[i + 2]; |
| 356 pos1 = diff_array[i + 1]; | 476 chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin, |
| 357 pos2 = diff_array[i + 2]; | 477 pos2_end - pos2_begin)); |
| 478 current_diff = pos2_end - pos1_end; |
| 358 } | 479 } |
| 359 this.chunks = chunks; | 480 this.chunks = chunks; |
| 360 } | 481 } |
| 361 PosTranslator.prototype.GetChunks = function() { | 482 PosTranslator.prototype.GetChunks = function() { |
| 362 return this.chunks; | 483 return this.chunks; |
| 363 } | 484 } |
| 364 | 485 |
| 365 PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) { | 486 PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) { |
| 366 var array = this.chunks; | 487 var array = this.chunks; |
| 367 if (array.length == 0 || pos < array[0]) { | 488 if (array.length == 0 || pos < array[0].pos1) { |
| 368 return pos; | 489 return pos; |
| 369 } | 490 } |
| 370 var chunk_index1 = 0; | 491 var chunk_index1 = 0; |
| 371 var chunk_index2 = array.length - 1; | 492 var chunk_index2 = array.length - 1; |
| 372 | 493 |
| 373 while (chunk_index1 < chunk_index2) { | 494 while (chunk_index1 < chunk_index2) { |
| 374 var middle_index = (chunk_index1 + chunk_index2) / 2; | 495 var middle_index = Math.floor((chunk_index1 + chunk_index2) / 2); |
| 375 if (pos < array[middle_index + 1].pos1) { | 496 if (pos < array[middle_index + 1].pos1) { |
| 376 chunk_index2 = middle_index; | 497 chunk_index2 = middle_index; |
| 377 } else { | 498 } else { |
| 378 chunk_index1 = middle_index + 1; | 499 chunk_index1 = middle_index + 1; |
| 379 } | 500 } |
| 380 } | 501 } |
| 381 var chunk = array[chunk_index1]; | 502 var chunk = array[chunk_index1]; |
| 382 if (pos >= chunk.pos1 + chunk.len1) { | 503 if (pos >= chunk.pos1 + chunk.len1) { |
| 383 return pos += chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1; | 504 return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1; |
| 384 } | 505 } |
| 385 | 506 |
| 386 if (!inside_chunk_handler) { | 507 if (!inside_chunk_handler) { |
| 387 inside_chunk_handler = PosTranslator.default_inside_chunk_handler; | 508 inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler; |
| 388 } | 509 } |
| 389 inside_chunk_handler(pos, chunk); | 510 return inside_chunk_handler(pos, chunk); |
| 390 } | 511 } |
| 391 | 512 |
| 392 PosTranslator.default_inside_chunk_handler = function() { | 513 PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) { |
| 393 Assert(false, "Cannot translate position in chaged area"); | 514 Assert(false, "Cannot translate position in changed area"); |
| 515 } |
| 516 |
| 517 PosTranslator.ShiftWithTopInsideChunkHandler = |
| 518 function(pos, diff_chunk) { |
| 519 // We carelessly do not check whether we stay inside the chunk after |
| 520 // translation. |
| 521 return pos - diff_chunk.pos1 + diff_chunk.pos2; |
| 394 } | 522 } |
| 395 | 523 |
| 396 var FunctionStatus = { | 524 var FunctionStatus = { |
| 397 // No change to function or its inner functions; however its positions | 525 // No change to function or its inner functions; however its positions |
| 398 // in script may have been shifted. | 526 // in script may have been shifted. |
| 399 UNCHANGED: "unchanged", | 527 UNCHANGED: "unchanged", |
| 400 // The code of a function remains unchanged, but something happened inside | 528 // The code of a function remains unchanged, but something happened inside |
| 401 // some inner functions. | 529 // some inner functions. |
| 402 SOURCE_CHANGED: "source changed", | 530 SOURCE_CHANGED: "source changed", |
| 403 // The code of a function is changed or some nested function cannot be | 531 // The code of a function is changed or some nested function cannot be |
| 404 // properly patched so this function must be recompiled. | 532 // properly patched so this function must be recompiled. |
| 405 CHANGED: "changed", | 533 CHANGED: "changed", |
| 406 // Function is changed but cannot be patched. | 534 // Function is changed but cannot be patched. |
| 407 DAMAGED: "damaged" | 535 DAMAGED: "damaged" |
| 408 } | 536 } |
| 409 | 537 |
| 410 function CodeInfoTreeNode(code_info, children, array_index) { | 538 function CodeInfoTreeNode(code_info, children, array_index) { |
| 411 this.info = code_info; | 539 this.info = code_info; |
| 412 this.children = children; | 540 this.children = children; |
| 413 // an index in array of compile_info | 541 // an index in array of compile_info |
| 414 this.array_index = array_index; | 542 this.array_index = array_index; |
| 415 this.parent = void(0); | 543 this.parent = void 0; |
| 416 | 544 |
| 417 this.status = FunctionStatus.UNCHANGED; | 545 this.status = FunctionStatus.UNCHANGED; |
| 418 // Status explanation is used for debugging purposes and will be shown | 546 // Status explanation is used for debugging purposes and will be shown |
| 419 // in user UI if some explanations are needed. | 547 // in user UI if some explanations are needed. |
| 420 this.status_explanation = void(0); | 548 this.status_explanation = void 0; |
| 421 this.new_start_pos = void(0); | 549 this.new_start_pos = void 0; |
| 422 this.new_end_pos = void(0); | 550 this.new_end_pos = void 0; |
| 423 this.corresponding_node = void(0); | 551 this.corresponding_node = void 0; |
| 552 this.unmatched_new_nodes = void 0; |
| 424 } | 553 } |
| 425 | 554 |
| 426 // From array of function infos that is implicitly a tree creates | 555 // From array of function infos that is implicitly a tree creates |
| 427 // an actual tree of functions in script. | 556 // an actual tree of functions in script. |
| 428 function BuildCodeInfoTree(code_info_array) { | 557 function BuildCodeInfoTree(code_info_array) { |
| 429 // Throughtout all function we iterate over input array. | 558 // Throughtout all function we iterate over input array. |
| 430 var index = 0; | 559 var index = 0; |
| 431 | 560 |
| 432 // Recursive function that builds a branch of tree. | 561 // Recursive function that builds a branch of tree. |
| 433 function BuildNode() { | 562 function BuildNode() { |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 // in new script may become enclosed into other function; the innocent change | 686 // in new script may become enclosed into other function; the innocent change |
| 558 // inside function body may in fact be something like "} function B() {" that | 687 // inside function body may in fact be something like "} function B() {" that |
| 559 // splits a function into 2 functions. | 688 // splits a function into 2 functions. |
| 560 function FindCorrespondingFunctions(old_code_tree, new_code_tree) { | 689 function FindCorrespondingFunctions(old_code_tree, new_code_tree) { |
| 561 | 690 |
| 562 // A recursive function that tries to find a correspondence for all | 691 // A recursive function that tries to find a correspondence for all |
| 563 // child functions and for their inner functions. | 692 // child functions and for their inner functions. |
| 564 function ProcessChildren(old_node, new_node) { | 693 function ProcessChildren(old_node, new_node) { |
| 565 var old_children = old_node.children; | 694 var old_children = old_node.children; |
| 566 var new_children = new_node.children; | 695 var new_children = new_node.children; |
| 696 |
| 697 var unmatched_new_nodes_list = []; |
| 567 | 698 |
| 568 var old_index = 0; | 699 var old_index = 0; |
| 569 var new_index = 0; | 700 var new_index = 0; |
| 570 while (old_index < old_children.length) { | 701 while (old_index < old_children.length) { |
| 571 if (old_children[old_index].status == FunctionStatus.DAMAGED) { | 702 if (old_children[old_index].status == FunctionStatus.DAMAGED) { |
| 572 old_index++; | 703 old_index++; |
| 573 } else if (new_index < new_children.length) { | 704 } else if (new_index < new_children.length) { |
| 574 if (new_children[new_index].info.start_position < | 705 if (new_children[new_index].info.start_position < |
| 575 old_children[old_index].new_start_pos) { | 706 old_children[old_index].new_start_pos) { |
| 707 unmatched_new_nodes_list.push(new_children[new_index]); |
| 576 new_index++; | 708 new_index++; |
| 577 } else if (new_children[new_index].info.start_position == | 709 } else if (new_children[new_index].info.start_position == |
| 578 old_children[old_index].new_start_pos) { | 710 old_children[old_index].new_start_pos) { |
| 579 if (new_children[new_index].info.end_position == | 711 if (new_children[new_index].info.end_position == |
| 580 old_children[old_index].new_end_pos) { | 712 old_children[old_index].new_end_pos) { |
| 581 old_children[old_index].corresponding_node = | 713 old_children[old_index].corresponding_node = |
| 582 new_children[new_index]; | 714 new_children[new_index]; |
| 583 if (old_children[old_index].status != FunctionStatus.UNCHANGED) { | 715 if (old_children[old_index].status != FunctionStatus.UNCHANGED) { |
| 584 ProcessChildren(old_children[old_index], | 716 ProcessChildren(old_children[old_index], |
| 585 new_children[new_index]); | 717 new_children[new_index]); |
| 586 if (old_children[old_index].status == FunctionStatus.DAMAGED) { | 718 if (old_children[old_index].status == FunctionStatus.DAMAGED) { |
| 719 unmatched_new_nodes_list.push( |
| 720 old_children[old_index].corresponding_node); |
| 721 old_children[old_index].corresponding_node = void 0; |
| 587 old_node.status = FunctionStatus.CHANGED; | 722 old_node.status = FunctionStatus.CHANGED; |
| 588 } | 723 } |
| 589 } | 724 } |
| 590 } else { | 725 } else { |
| 591 old_children[old_index].status = FunctionStatus.DAMAGED; | 726 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 592 old_children[old_index].status_explanation = | 727 old_children[old_index].status_explanation = |
| 593 "No corresponding function in new script found"; | 728 "No corresponding function in new script found"; |
| 594 old_node.status = FunctionStatus.CHANGED; | 729 old_node.status = FunctionStatus.CHANGED; |
| 730 unmatched_new_nodes_list.push(new_children[new_index]); |
| 595 } | 731 } |
| 596 new_index++; | 732 new_index++; |
| 597 old_index++; | 733 old_index++; |
| 598 } else { | 734 } else { |
| 599 old_children[old_index].status = FunctionStatus.DAMAGED; | 735 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 600 old_children[old_index].status_explanation = | 736 old_children[old_index].status_explanation = |
| 601 "No corresponding function in new script found"; | 737 "No corresponding function in new script found"; |
| 602 old_node.status = FunctionStatus.CHANGED; | 738 old_node.status = FunctionStatus.CHANGED; |
| 603 old_index++; | 739 old_index++; |
| 604 } | 740 } |
| 605 } else { | 741 } else { |
| 606 old_children[old_index].status = FunctionStatus.DAMAGED; | 742 old_children[old_index].status = FunctionStatus.DAMAGED; |
| 607 old_children[old_index].status_explanation = | 743 old_children[old_index].status_explanation = |
| 608 "No corresponding function in new script found"; | 744 "No corresponding function in new script found"; |
| 609 old_node.status = FunctionStatus.CHANGED; | 745 old_node.status = FunctionStatus.CHANGED; |
| 610 old_index++; | 746 old_index++; |
| 611 } | 747 } |
| 612 } | 748 } |
| 613 | 749 |
| 750 while (new_index < new_children.length) { |
| 751 unmatched_new_nodes_list.push(new_children[new_index]); |
| 752 new_index++; |
| 753 } |
| 754 |
| 614 if (old_node.status == FunctionStatus.CHANGED) { | 755 if (old_node.status == FunctionStatus.CHANGED) { |
| 615 if (!CompareFunctionExpectations(old_node.info, new_node.info)) { | 756 if (!CompareFunctionExpectations(old_node.info, new_node.info)) { |
| 616 old_node.status = FunctionStatus.DAMAGED; | 757 old_node.status = FunctionStatus.DAMAGED; |
| 617 old_node.status_explanation = "Changed code expectations"; | 758 old_node.status_explanation = "Changed code expectations"; |
| 618 } | 759 } |
| 619 } | 760 } |
| 761 old_node.unmatched_new_nodes = unmatched_new_nodes_list; |
| 620 } | 762 } |
| 621 | 763 |
| 622 ProcessChildren(old_code_tree, new_code_tree); | 764 ProcessChildren(old_code_tree, new_code_tree); |
| 623 | 765 |
| 624 old_code_tree.corresponding_node = new_code_tree; | 766 old_code_tree.corresponding_node = new_code_tree; |
| 625 Assert(old_code_tree.status != FunctionStatus.DAMAGED, | 767 Assert(old_code_tree.status != FunctionStatus.DAMAGED, |
| 626 "Script became damaged"); | 768 "Script became damaged"); |
| 627 } | 769 } |
| 628 | 770 |
| 629 | 771 |
| 630 // An object describing function compilation details. Its index fields | 772 // An object describing function compilation details. Its index fields |
| 631 // apply to indexes inside array that stores these objects. | 773 // apply to indexes inside array that stores these objects. |
| 632 function FunctionCompileInfo(raw_array) { | 774 function FunctionCompileInfo(raw_array) { |
| 633 this.function_name = raw_array[0]; | 775 this.function_name = raw_array[0]; |
| 634 this.start_position = raw_array[1]; | 776 this.start_position = raw_array[1]; |
| 635 this.end_position = raw_array[2]; | 777 this.end_position = raw_array[2]; |
| 636 this.param_num = raw_array[3]; | 778 this.param_num = raw_array[3]; |
| 637 this.code = raw_array[4]; | 779 this.code = raw_array[4]; |
| 638 this.scope_info = raw_array[5]; | 780 this.scope_info = raw_array[5]; |
| 639 this.outer_index = raw_array[6]; | 781 this.outer_index = raw_array[6]; |
| 782 this.shared_function_info = raw_array[7]; |
| 640 this.next_sibling_index = null; | 783 this.next_sibling_index = null; |
| 641 this.raw_array = raw_array; | 784 this.raw_array = raw_array; |
| 642 } | 785 } |
| 643 | 786 |
| 644 function SharedInfoWrapper(raw_array) { | 787 function SharedInfoWrapper(raw_array) { |
| 645 this.function_name = raw_array[0]; | 788 this.function_name = raw_array[0]; |
| 646 this.start_position = raw_array[1]; | 789 this.start_position = raw_array[1]; |
| 647 this.end_position = raw_array[2]; | 790 this.end_position = raw_array[2]; |
| 648 this.info = raw_array[3]; | 791 this.info = raw_array[3]; |
| 649 this.raw_array = raw_array; | 792 this.raw_array = raw_array; |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 769 return; | 912 return; |
| 770 } | 913 } |
| 771 ApplyPatchMultiChunk(script, diff, new_source, change_log); | 914 ApplyPatchMultiChunk(script, diff, new_source, change_log); |
| 772 } | 915 } |
| 773 // Function is public. | 916 // Function is public. |
| 774 this.SetScriptSource = SetScriptSource; | 917 this.SetScriptSource = SetScriptSource; |
| 775 | 918 |
| 776 function CompareStringsLinewise(s1, s2) { | 919 function CompareStringsLinewise(s1, s2) { |
| 777 return %LiveEditCompareStringsLinewise(s1, s2); | 920 return %LiveEditCompareStringsLinewise(s1, s2); |
| 778 } | 921 } |
| 779 // Function is public (for tests). | |
| 780 this.CompareStringsLinewise = CompareStringsLinewise; | |
| 781 | |
| 782 | 922 |
| 783 // Finds a difference between 2 strings in form of a single chunk. | 923 // Functions are public for tests. |
| 784 // This is a temporary solution. We should calculate a read diff instead. | 924 this.TestApi = { |
| 785 function FindSimpleDiff(old_source, new_source) { | 925 PosTranslator: PosTranslator, |
| 786 var change_pos; | 926 CompareStringsLinewise: CompareStringsLinewise |
| 787 var old_len; | |
| 788 var new_len; | |
| 789 | |
| 790 // A find range block. Whenever control leaves it, it should set 3 local | |
| 791 // variables declared above. | |
| 792 find_range: | |
| 793 { | |
| 794 // First look from the beginning of strings. | |
| 795 var pos1; | |
| 796 { | |
| 797 var next_pos; | |
| 798 for (pos1 = 0; true; pos1 = next_pos) { | |
| 799 if (pos1 >= old_source.length) { | |
| 800 change_pos = pos1; | |
| 801 old_len = 0; | |
| 802 new_len = new_source.length - pos1; | |
| 803 break find_range; | |
| 804 } | |
| 805 if (pos1 >= new_source.length) { | |
| 806 change_pos = pos1; | |
| 807 old_len = old_source.length - pos1; | |
| 808 new_len = 0; | |
| 809 break find_range; | |
| 810 } | |
| 811 if (old_source[pos1] != new_source[pos1]) { | |
| 812 break; | |
| 813 } | |
| 814 next_pos = pos1 + 1; | |
| 815 } | |
| 816 } | |
| 817 // Now compare strings from the ends. | |
| 818 change_pos = pos1; | |
| 819 var pos_old; | |
| 820 var pos_new; | |
| 821 { | |
| 822 for (pos_old = old_source.length - 1, pos_new = new_source.length - 1; | |
| 823 true; | |
| 824 pos_old--, pos_new--) { | |
| 825 if (pos_old - change_pos + 1 < 0 || pos_new - change_pos + 1 < 0) { | |
| 826 old_len = pos_old - change_pos + 2; | |
| 827 new_len = pos_new - change_pos + 2; | |
| 828 break find_range; | |
| 829 } | |
| 830 if (old_source[pos_old] != new_source[pos_new]) { | |
| 831 old_len = pos_old - change_pos + 1; | |
| 832 new_len = pos_new - change_pos + 1; | |
| 833 break find_range; | |
| 834 } | |
| 835 } | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 if (old_len == 0 && new_len == 0) { | |
| 840 // no change | |
| 841 return; | |
| 842 } | |
| 843 | |
| 844 return { "change_pos": change_pos, "old_len": old_len, "new_len": new_len }; | |
| 845 } | 927 } |
| 846 } | 928 } |
| OLD | NEW |