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 |