Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: src/liveedit-debugger.js

Issue 1800007: LiveEdit: breakpoints updates and fixes for related problems (Closed)
Patch Set: follow codereview Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/liveedit.cc ('k') | src/runtime.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « src/liveedit.cc ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698