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

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

Issue 1589036: Format in liveedit-debugger.js (Closed)
Patch Set: 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 | « no previous file | no next file » | 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 13 matching lines...) Expand all
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 // LiveEdit feature implementation. The script should be executed after 28 // LiveEdit feature implementation. The script should be executed after
29 // debug-debugger.js. 29 // debug-debugger.js.
30 30
31 // A LiveEdit namespace is declared inside a single function constructor. 31 // A LiveEdit namespace is declared inside a single function constructor.
32 Debug.LiveEdit = new function() { 32 Debug.LiveEdit = new function() {
33 33
34 // TODO(LiveEdit): restore indentation below 34 // Changes script text and recompiles all relevant functions if possible.
35 // The change is always a substring (change_pos, change_pos + change_len)
36 // being replaced with a completely different string new_str.
37 //
38 // Only one function will have its Code changed in result of this function.
39 // All nested functions (should they have any instances at the moment) are lef t
40 // unchanged and re-linked to a newly created script instance representing old
41 // version of the source. (Generally speaking,
42 // during the change all nested functions are erased and completely different
43 // set of nested functions are introduced.) All other functions just have
44 // their positions updated.
45 //
46 // @param {Script} script that is being changed
47 // @param {Array} change_log a list that collects engineer-readable descriptio n
48 // of what happened.
49 function ApplyPatch(script, change_pos, change_len, new_str,
50 change_log) {
51
52 // Fully compiles source string as a script. Returns Array of
53 // FunctionCompileInfo -- a descriptions of all functions of the script.
54 // Elements of array are ordered by start positions of functions (from top
55 // to bottom) in the source. Fields outer_index and next_sibling_index help
56 // to navigate the nesting structure of functions.
57 //
58 // The script is used for compilation, because it produces code that
59 // needs to be linked with some particular script (for nested functions).
60 function DebugGatherCompileInfo(source) {
61 // Get function info, elements are partially sorted (it is a tree
62 // of nested functions serialized as parent followed by serialized childre n.
63 var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
64
65 // Sort function infos by start position field.
66 var compile_info = new Array();
67 var old_index_map = new Array();
68 for (var i = 0; i < raw_compile_info.length; i++) {
69 compile_info.push(new FunctionCompileInfo(raw_compile_info[i]));
70 old_index_map.push(i);
71 }
72
73 for (var i = 0; i < compile_info.length; i++) {
74 var k = i;
75 for (var j = i + 1; j < compile_info.length; j++) {
76 if (compile_info[k].start_position > compile_info[j].start_position) {
77 k = j;
78 }
79 }
80 if (k != i) {
81 var temp_info = compile_info[k];
82 var temp_index = old_index_map[k];
83 compile_info[k] = compile_info[i];
84 old_index_map[k] = old_index_map[i];
85 compile_info[i] = temp_info;
86 old_index_map[i] = temp_index;
87 }
88 }
89
90 // After sorting update outer_inder field using old_index_map. Also
91 // set next_sibling_index field.
92 var current_index = 0;
93
94 // The recursive function, that goes over all children of a particular
95 // node (i.e. function info).
96 function ResetIndexes(new_parent_index, old_parent_index) {
97 var previous_sibling = -1;
98 while (current_index < compile_info.length &&
99 compile_info[current_index].outer_index == old_parent_index) {
100 var saved_index = current_index;
101 compile_info[saved_index].outer_index = new_parent_index;
102 if (previous_sibling != -1) {
103 compile_info[previous_sibling].next_sibling_index = saved_index;
104 }
105 previous_sibling = saved_index;
106 current_index++;
107 ResetIndexes(saved_index, old_index_map[saved_index]);
108 }
109 if (previous_sibling != -1) {
110 compile_info[previous_sibling].next_sibling_index = -1;
111 }
112 }
113
114 ResetIndexes(-1, -1);
115 Assert(current_index == compile_info.length);
116
117 return compile_info;
118 }
119
120 // Given a positions, finds a function that fully includes the entire change .
121 function FindChangedFunction(compile_info, offset, len) {
122 // First condition: function should start before the change region.
123 // Function #0 (whole-script function) always does, but we want
124 // one, that is later in this list.
125 var index = 0;
126 while (index + 1 < compile_info.length &&
127 compile_info[index + 1].start_position <= offset) {
128 index++;
129 }
130 // Now we are at the last function that begins before the change
131 // region. The function that covers entire change region is either
132 // this function or the enclosing one.
133 for (; compile_info[index].end_position < offset + len;
134 index = compile_info[index].outer_index) {
135 Assert(index != -1);
136 }
137 return index;
138 }
139
140 // Variable forward declarations. Preprocessor "Minifier" needs them.
141 var old_compile_info;
142 var shared_infos;
143 // Finds SharedFunctionInfo that corresponds compile info with index
144 // in old version of the script.
145 function FindFunctionInfo(index) {
146 var old_info = old_compile_info[index];
147 for (var i = 0; i < shared_infos.length; i++) {
148 var info = shared_infos[i];
149 if (info.start_position == old_info.start_position &&
150 info.end_position == old_info.end_position) {
151 return info;
152 }
153 }
154 }
155
156 // Replaces function's Code.
157 function PatchCode(new_info, shared_info) {
158 %LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array);
159
160 change_log.push( {function_patched: new_info.function_name} );
161 }
162
163 var change_len_old;
164 var change_len_new;
165 // Translate position in old version of script into position in new
166 // version of script.
167 function PosTranslator(old_pos) {
168 if (old_pos <= change_pos) {
169 return old_pos;
170 }
171 if (old_pos >= change_pos + change_len_old) {
172 return old_pos + change_len_new - change_len_old;
173 }
174 return -1;
175 }
176
177 var position_change_array;
178 var position_patch_report;
179 function PatchPositions(new_info, shared_info) {
180 if (!shared_info) {
181 // TODO(LiveEdit): explain what is happening.
182 return;
183 }
184 var breakpoint_position_update = %LiveEditPatchFunctionPositions(
185 shared_info.raw_array, position_change_array);
186 for (var i = 0; i < breakpoint_position_update.length; i += 2) {
187 var new_pos = breakpoint_position_update[i];
188 var break_point_object = breakpoint_position_update[i + 1];
189 change_log.push( { breakpoint_position_update:
190 { from: break_point_object.source_position(), to: new_pos } } );
191 break_point_object.updateSourcePosition(new_pos, script);
192 }
193 position_patch_report.push( { name: new_info.function_name } );
194 }
195
196 var link_to_old_script_report;
197 var old_script;
198 // Makes a function associated with another instance of a script (the
199 // one representing its old version). This way the function still
200 // may access its own text.
201 function LinkToOldScript(shared_info) {
202 %LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script);
203
204 link_to_old_script_report.push( { name: shared_info.function_name } );
205 }
206
207
208
209 var old_source = script.source;
210 var change_len_old = change_len;
211 var change_len_new = new_str.length;
212
213 // Prepare new source string.
214 var new_source = old_source.substring(0, change_pos) +
215 new_str + old_source.substring(change_pos + change_len);
216
217 // Find all SharedFunctionInfo's that are compiled from this script.
218 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
219
220 var shared_infos = new Array();
221
222 for (var i = 0; i < shared_raw_list.length; i++) {
223 shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
224 }
225
226 // Gather compile information about old version of script.
227 var old_compile_info = DebugGatherCompileInfo(old_source);
228
229 // Gather compile information about new version of script.
230 var new_compile_info;
231 try {
232 new_compile_info = DebugGatherCompileInfo(new_source);
233 } catch (e) {
234 throw new Failure("Failed to compile new version of script: " + e);
235 }
236
237 // An index of a single function, that is going to have its code replaced.
238 var function_being_patched =
239 FindChangedFunction(old_compile_info, change_pos, change_len_old);
240
241 // In old and new script versions function with a change should have the
242 // same indexes.
243 var function_being_patched2 =
244 FindChangedFunction(new_compile_info, change_pos, change_len_new);
245 Assert(function_being_patched == function_being_patched2,
246 "inconsistent old/new compile info");
247
248 // Check that function being patched has the same expectations in a new
249 // version. Otherwise we cannot safely patch its behavior and should
250 // choose the outer function instead.
251 while (!CompareFunctionExpectations(
252 old_compile_info[function_being_patched],
253 new_compile_info[function_being_patched])) {
254
255 Assert(old_compile_info[function_being_patched].outer_index ==
256 new_compile_info[function_being_patched].outer_index);
257 function_being_patched =
258 old_compile_info[function_being_patched].outer_index;
259 Assert(function_being_patched != -1);
260 }
261
262 // Check that function being patched is not currently on stack.
263 CheckStackActivations(
264 [ FindFunctionInfo(function_being_patched) ], change_log );
265
266
267 // Committing all changes.
268 var old_script_name = CreateNameForOldScript(script);
269
270 // Update the script text and create a new script representing an old
271 // version of the script.
272 var old_script = %LiveEditReplaceScript(script, new_source, old_script_name) ;
273
274 PatchCode(new_compile_info[function_being_patched],
275 FindFunctionInfo(function_being_patched));
276
277 var position_patch_report = new Array();
278 change_log.push( {position_patched: position_patch_report} );
279
280 var position_change_array = [ change_pos,
281 change_pos + change_len_old,
282 change_pos + change_len_new ];
283
284 // Update positions of all outer functions (i.e. all functions, that
285 // are partially below the function being patched).
286 for (var i = new_compile_info[function_being_patched].outer_index;
287 i != -1;
288 i = new_compile_info[i].outer_index) {
289 PatchPositions(new_compile_info[i], FindFunctionInfo(i));
290 }
291
292 // Update positions of all functions that are fully below the function
293 // being patched.
294 var old_next_sibling =
295 old_compile_info[function_being_patched].next_sibling_index;
296 var new_next_sibling =
297 new_compile_info[function_being_patched].next_sibling_index;
298
299 // We simply go over the tail of both old and new lists. Their tails should
300 // have an identical structure.
301 if (old_next_sibling == -1) {
302 Assert(new_next_sibling == -1);
303 } else {
304 Assert(old_compile_info.length - old_next_sibling ==
305 new_compile_info.length - new_next_sibling);
306
307 for (var i = old_next_sibling, j = new_next_sibling;
308 i < old_compile_info.length; i++, j++) {
309 PatchPositions(new_compile_info[j], FindFunctionInfo(i));
310 }
311 }
312
313 var link_to_old_script_report = new Array();
314 change_log.push( { linked_to_old_script: link_to_old_script_report } );
315
316 // We need to link to old script all former nested functions.
317 for (var i = function_being_patched + 1; i < old_next_sibling; i++) {
318 LinkToOldScript(FindFunctionInfo(i), old_script);
319 }
320 }
321 // Function is public.
322 this.ApplyPatch = ApplyPatch;
323
324 function Assert(condition, message) {
325 if (!condition) {
326 if (message) {
327 throw "Assert " + message;
328 } else {
329 throw "Assert";
330 }
331 }
332 }
333
334 // An object describing function compilation details. Its index fields
335 // apply to indexes inside array that stores these objects.
336 function FunctionCompileInfo(raw_array) {
337 this.function_name = raw_array[0];
338 this.start_position = raw_array[1];
339 this.end_position = raw_array[2];
340 this.param_num = raw_array[3];
341 this.code = raw_array[4];
342 this.scope_info = raw_array[5];
343 this.outer_index = raw_array[6];
344 this.next_sibling_index = null;
345 this.raw_array = raw_array;
346 }
347
348 function SharedInfoWrapper(raw_array) {
349 this.function_name = raw_array[0];
350 this.start_position = raw_array[1];
351 this.end_position = raw_array[2];
352 this.info = raw_array[3];
353 this.raw_array = raw_array;
354 }
355
356 // Adds a suffix to script name to mark that it is old version.
357 function CreateNameForOldScript(script) {
358 // TODO(635): try better than this; support several changes.
359 return script.name + " (old)";
360 }
361
362 // Compares a function interface old and new version, whether it
363 // changed or not.
364 function CompareFunctionExpectations(function_info1, function_info2) {
365 // Check that function has the same number of parameters (there may exist
366 // an adapter, that won't survive function parameter number change).
367 if (function_info1.param_num != function_info2.param_num) {
368 return false;
369 }
370 var scope_info1 = function_info1.scope_info;
371 var scope_info2 = function_info2.scope_info;
372
373 if (!scope_info1) {
374 return !scope_info2;
375 }
376
377 if (scope_info1.length != scope_info2.length) {
378 return false;
379 }
380
381 // Check that outer scope structure is not changed. Otherwise the function
382 // will not properly work with existing scopes.
383 return scope_info1.toString() == scope_info2.toString();
384 }
385
386 // Minifier forward declaration.
387 var FunctionPatchabilityStatus;
388
389 // For array of wrapped shared function infos checks that none of them
390 // have activations on stack (of any thread). Throws a Failure exception
391 // if this proves to be false.
392 function CheckStackActivations(shared_wrapper_list, change_log) {
393 var shared_list = new Array();
394 for (var i = 0; i < shared_wrapper_list.length; i++) {
395 shared_list[i] = shared_wrapper_list[i].info;
396 }
397 var result = %LiveEditCheckAndDropActivations(shared_list, true);
398 if (result[shared_list.length]) {
399 // Extra array element may contain error message.
400 throw new Failure(result[shared_list.length]);
401 }
402
403 var problems = new Array();
404 var dropped = new Array();
405 for (var i = 0; i < shared_list.length; i++) {
406 var shared = shared_wrapper_list[i];
407 if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
408 dropped.push({ name: shared.function_name } );
409 } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
410 var description = {
411 name: shared.function_name,
412 start_pos: shared.start_position,
413 end_pos: shared.end_position,
414 replace_problem:
415 FunctionPatchabilityStatus.SymbolName(result[i])
416 };
417 problems.push(description);
418 }
419 }
420 if (dropped.length > 0) {
421 change_log.push({ dropped_from_stack: dropped });
422 }
423 if (problems.length > 0) {
424 change_log.push( { functions_on_stack: problems } );
425 throw new Failure("Blocked by functions on stack");
426 }
427 }
428
429 // A copy of the FunctionPatchabilityStatus enum from liveedit.h
430 var FunctionPatchabilityStatus = {
431 AVAILABLE_FOR_PATCH: 1,
432 BLOCKED_ON_ACTIVE_STACK: 2,
433 BLOCKED_ON_OTHER_STACK: 3,
434 BLOCKED_UNDER_NATIVE_CODE: 4,
435 REPLACED_ON_ACTIVE_STACK: 5
436 }
437
438 FunctionPatchabilityStatus.SymbolName = function(code) {
439 var enum = FunctionPatchabilityStatus;
440 for (name in enum) {
441 if (enum[name] == code) {
442 return name;
443 }
444 }
445 }
446
447
448 // A logical failure in liveedit process. This means that change_log
449 // is valid and consistent description of what happened.
450 function Failure(message) {
451 this.message = message;
452 }
453 // Function (constructor) is public.
454 this.Failure = Failure;
455
456 Failure.prototype.toString = function() {
457 return "LiveEdit Failure: " + this.message;
458 }
459
460 // A testing entry.
461 function GetPcFromSourcePos(func, source_pos) {
462 return %GetFunctionCodePositionFromSource(func, source_pos);
463 }
464 // Function is public.
465 this.GetPcFromSourcePos = GetPcFromSourcePos;
35 466
36 // Changes script text and recompiles all relevant functions if possible.
37 // The change is always a substring (change_pos, change_pos + change_len)
38 // being replaced with a completely different string new_str.
39 //
40 // Only one function will have its Code changed in result of this function.
41 // All nested functions (should they have any instances at the moment) are left
42 // unchanged and re-linked to a newly created script instance representing old
43 // version of the source. (Generally speaking,
44 // during the change all nested functions are erased and completely different
45 // set of nested functions are introduced.) All other functions just have
46 // their positions updated.
47 //
48 // @param {Script} script that is being changed
49 // @param {Array} change_log a list that collects engineer-readable description
50 // of what happened.
51 function ApplyPatch(script, change_pos, change_len, new_str,
52 change_log) {
53
54 // Fully compiles source string as a script. Returns Array of
55 // FunctionCompileInfo -- a descriptions of all functions of the script.
56 // Elements of array are ordered by start positions of functions (from top
57 // to bottom) in the source. Fields outer_index and next_sibling_index help
58 // to navigate the nesting structure of functions.
59 //
60 // The script is used for compilation, because it produces code that
61 // needs to be linked with some particular script (for nested functions).
62 function DebugGatherCompileInfo(source) {
63 // Get function info, elements are partially sorted (it is a tree
64 // of nested functions serialized as parent followed by serialized children.
65 var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
66
67 // Sort function infos by start position field.
68 var compile_info = new Array();
69 var old_index_map = new Array();
70 for (var i = 0; i < raw_compile_info.length; i++) {
71 compile_info.push(new FunctionCompileInfo(raw_compile_info[i]));
72 old_index_map.push(i);
73 }
74
75 for (var i = 0; i < compile_info.length; i++) {
76 var k = i;
77 for (var j = i + 1; j < compile_info.length; j++) {
78 if (compile_info[k].start_position > compile_info[j].start_position) {
79 k = j;
80 }
81 }
82 if (k != i) {
83 var temp_info = compile_info[k];
84 var temp_index = old_index_map[k];
85 compile_info[k] = compile_info[i];
86 old_index_map[k] = old_index_map[i];
87 compile_info[i] = temp_info;
88 old_index_map[i] = temp_index;
89 }
90 }
91
92 // After sorting update outer_inder field using old_index_map. Also
93 // set next_sibling_index field.
94 var current_index = 0;
95
96 // The recursive function, that goes over all children of a particular
97 // node (i.e. function info).
98 function ResetIndexes(new_parent_index, old_parent_index) {
99 var previous_sibling = -1;
100 while (current_index < compile_info.length &&
101 compile_info[current_index].outer_index == old_parent_index) {
102 var saved_index = current_index;
103 compile_info[saved_index].outer_index = new_parent_index;
104 if (previous_sibling != -1) {
105 compile_info[previous_sibling].next_sibling_index = saved_index;
106 }
107 previous_sibling = saved_index;
108 current_index++;
109 ResetIndexes(saved_index, old_index_map[saved_index]);
110 }
111 if (previous_sibling != -1) {
112 compile_info[previous_sibling].next_sibling_index = -1;
113 }
114 }
115
116 ResetIndexes(-1, -1);
117 Assert(current_index == compile_info.length);
118
119 return compile_info;
120 }
121
122 // Given a positions, finds a function that fully includes the entire change.
123 function FindChangedFunction(compile_info, offset, len) {
124 // First condition: function should start before the change region.
125 // Function #0 (whole-script function) always does, but we want
126 // one, that is later in this list.
127 var index = 0;
128 while (index + 1 < compile_info.length &&
129 compile_info[index + 1].start_position <= offset) {
130 index++;
131 }
132 // Now we are at the last function that begins before the change
133 // region. The function that covers entire change region is either
134 // this function or the enclosing one.
135 for (; compile_info[index].end_position < offset + len;
136 index = compile_info[index].outer_index) {
137 Assert(index != -1);
138 }
139 return index;
140 }
141
142 // Variable forward declarations. Preprocessor "Minifier" needs them.
143 var old_compile_info;
144 var shared_infos;
145 // Finds SharedFunctionInfo that corresponds compile info with index
146 // in old version of the script.
147 function FindFunctionInfo(index) {
148 var old_info = old_compile_info[index];
149 for (var i = 0; i < shared_infos.length; i++) {
150 var info = shared_infos[i];
151 if (info.start_position == old_info.start_position &&
152 info.end_position == old_info.end_position) {
153 return info;
154 }
155 }
156 }
157
158 // Replaces function's Code.
159 function PatchCode(new_info, shared_info) {
160 %LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array);
161
162 change_log.push( {function_patched: new_info.function_name} );
163 }
164
165 var change_len_old;
166 var change_len_new;
167 // Translate position in old version of script into position in new
168 // version of script.
169 function PosTranslator(old_pos) {
170 if (old_pos <= change_pos) {
171 return old_pos;
172 }
173 if (old_pos >= change_pos + change_len_old) {
174 return old_pos + change_len_new - change_len_old;
175 }
176 return -1;
177 }
178
179 var position_change_array;
180 var position_patch_report;
181 function PatchPositions(new_info, shared_info) {
182 if (!shared_info) {
183 // TODO(LiveEdit): explain what is happening.
184 return;
185 }
186 var breakpoint_position_update = %LiveEditPatchFunctionPositions(
187 shared_info.raw_array, position_change_array);
188 for (var i = 0; i < breakpoint_position_update.length; i += 2) {
189 var new_pos = breakpoint_position_update[i];
190 var break_point_object = breakpoint_position_update[i + 1];
191 change_log.push( { breakpoint_position_update:
192 { from: break_point_object.source_position(), to: new_pos } } );
193 break_point_object.updateSourcePosition(new_pos, script);
194 }
195 position_patch_report.push( { name: new_info.function_name } );
196 }
197
198 var link_to_old_script_report;
199 var old_script;
200 // Makes a function associated with another instance of a script (the
201 // one representing its old version). This way the function still
202 // may access its own text.
203 function LinkToOldScript(shared_info) {
204 %LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script);
205
206 link_to_old_script_report.push( { name: shared_info.function_name } );
207 }
208
209
210
211 var old_source = script.source;
212 var change_len_old = change_len;
213 var change_len_new = new_str.length;
214
215 // Prepare new source string.
216 var new_source = old_source.substring(0, change_pos) +
217 new_str + old_source.substring(change_pos + change_len);
218
219 // Find all SharedFunctionInfo's that are compiled from this script.
220 var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
221
222 var shared_infos = new Array();
223
224 for (var i = 0; i < shared_raw_list.length; i++) {
225 shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
226 }
227
228 // Gather compile information about old version of script.
229 var old_compile_info = DebugGatherCompileInfo(old_source);
230
231 // Gather compile information about new version of script.
232 var new_compile_info;
233 try {
234 new_compile_info = DebugGatherCompileInfo(new_source);
235 } catch (e) {
236 throw new Failure("Failed to compile new version of script: " + e);
237 }
238
239 // An index of a single function, that is going to have its code replaced.
240 var function_being_patched =
241 FindChangedFunction(old_compile_info, change_pos, change_len_old);
242
243 // In old and new script versions function with a change should have the
244 // same indexes.
245 var function_being_patched2 =
246 FindChangedFunction(new_compile_info, change_pos, change_len_new);
247 Assert(function_being_patched == function_being_patched2,
248 "inconsistent old/new compile info");
249
250 // Check that function being patched has the same expectations in a new
251 // version. Otherwise we cannot safely patch its behavior and should
252 // choose the outer function instead.
253 while (!CompareFunctionExpectations(
254 old_compile_info[function_being_patched],
255 new_compile_info[function_being_patched])) {
256
257 Assert(old_compile_info[function_being_patched].outer_index ==
258 new_compile_info[function_being_patched].outer_index);
259 function_being_patched =
260 old_compile_info[function_being_patched].outer_index;
261 Assert(function_being_patched != -1);
262 }
263
264 // Check that function being patched is not currently on stack.
265 CheckStackActivations(
266 [ FindFunctionInfo(function_being_patched) ], change_log );
267
268
269 // Committing all changes.
270 var old_script_name = CreateNameForOldScript(script);
271
272 // Update the script text and create a new script representing an old
273 // version of the script.
274 var old_script = %LiveEditReplaceScript(script, new_source, old_script_name);
275
276 PatchCode(new_compile_info[function_being_patched],
277 FindFunctionInfo(function_being_patched));
278
279 var position_patch_report = new Array();
280 change_log.push( {position_patched: position_patch_report} );
281
282 var position_change_array = [ change_pos,
283 change_pos + change_len_old,
284 change_pos + change_len_new ];
285
286 // Update positions of all outer functions (i.e. all functions, that
287 // are partially below the function being patched).
288 for (var i = new_compile_info[function_being_patched].outer_index;
289 i != -1;
290 i = new_compile_info[i].outer_index) {
291 PatchPositions(new_compile_info[i], FindFunctionInfo(i));
292 }
293
294 // Update positions of all functions that are fully below the function
295 // being patched.
296 var old_next_sibling =
297 old_compile_info[function_being_patched].next_sibling_index;
298 var new_next_sibling =
299 new_compile_info[function_being_patched].next_sibling_index;
300
301 // We simply go over the tail of both old and new lists. Their tails should
302 // have an identical structure.
303 if (old_next_sibling == -1) {
304 Assert(new_next_sibling == -1);
305 } else {
306 Assert(old_compile_info.length - old_next_sibling ==
307 new_compile_info.length - new_next_sibling);
308
309 for (var i = old_next_sibling, j = new_next_sibling;
310 i < old_compile_info.length; i++, j++) {
311 PatchPositions(new_compile_info[j], FindFunctionInfo(i));
312 }
313 }
314
315 var link_to_old_script_report = new Array();
316 change_log.push( { linked_to_old_script: link_to_old_script_report } );
317
318 // We need to link to old script all former nested functions.
319 for (var i = function_being_patched + 1; i < old_next_sibling; i++) {
320 LinkToOldScript(FindFunctionInfo(i), old_script);
321 }
322 }
323 // Function is public.
324 this.ApplyPatch = ApplyPatch;
325
326 function Assert(condition, message) {
327 if (!condition) {
328 if (message) {
329 throw "Assert " + message;
330 } else {
331 throw "Assert";
332 }
333 }
334 }
335
336 // An object describing function compilation details. Its index fields
337 // apply to indexes inside array that stores these objects.
338 function FunctionCompileInfo(raw_array) {
339 this.function_name = raw_array[0];
340 this.start_position = raw_array[1];
341 this.end_position = raw_array[2];
342 this.param_num = raw_array[3];
343 this.code = raw_array[4];
344 this.scope_info = raw_array[5];
345 this.outer_index = raw_array[6];
346 this.next_sibling_index = null;
347 this.raw_array = raw_array;
348 }
349
350 function SharedInfoWrapper(raw_array) {
351 this.function_name = raw_array[0];
352 this.start_position = raw_array[1];
353 this.end_position = raw_array[2];
354 this.info = raw_array[3];
355 this.raw_array = raw_array;
356 }
357
358 // Adds a suffix to script name to mark that it is old version.
359 function CreateNameForOldScript(script) {
360 // TODO(635): try better than this; support several changes.
361 return script.name + " (old)";
362 }
363
364 // Compares a function interface old and new version, whether it
365 // changed or not.
366 function CompareFunctionExpectations(function_info1, function_info2) {
367 // Check that function has the same number of parameters (there may exist
368 // an adapter, that won't survive function parameter number change).
369 if (function_info1.param_num != function_info2.param_num) {
370 return false;
371 }
372 var scope_info1 = function_info1.scope_info;
373 var scope_info2 = function_info2.scope_info;
374
375 if (!scope_info1) {
376 return !scope_info2;
377 }
378
379 if (scope_info1.length != scope_info2.length) {
380 return false;
381 }
382
383 // Check that outer scope structure is not changed. Otherwise the function
384 // will not properly work with existing scopes.
385 return scope_info1.toString() == scope_info2.toString();
386 }
387
388 // Minifier forward declaration.
389 var FunctionPatchabilityStatus;
390
391 // For array of wrapped shared function infos checks that none of them
392 // have activations on stack (of any thread). Throws a Failure exception
393 // if this proves to be false.
394 function CheckStackActivations(shared_wrapper_list, change_log) {
395 var shared_list = new Array();
396 for (var i = 0; i < shared_wrapper_list.length; i++) {
397 shared_list[i] = shared_wrapper_list[i].info;
398 }
399 var result = %LiveEditCheckAndDropActivations(shared_list, true);
400 if (result[shared_list.length]) {
401 // Extra array element may contain error message.
402 throw new Failure(result[shared_list.length]);
403 }
404
405 var problems = new Array();
406 var dropped = new Array();
407 for (var i = 0; i < shared_list.length; i++) {
408 var shared = shared_wrapper_list[i];
409 if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
410 dropped.push({ name: shared.function_name } );
411 } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
412 var description = {
413 name: shared.function_name,
414 start_pos: shared.start_position,
415 end_pos: shared.end_position,
416 replace_problem:
417 FunctionPatchabilityStatus.SymbolName(result[i])
418 };
419 problems.push(description);
420 }
421 }
422 if (dropped.length > 0) {
423 change_log.push({ dropped_from_stack: dropped });
424 }
425 if (problems.length > 0) {
426 change_log.push( { functions_on_stack: problems } );
427 throw new Failure("Blocked by functions on stack");
428 }
429 }
430
431 // A copy of the FunctionPatchabilityStatus enum from liveedit.h
432 var FunctionPatchabilityStatus = {
433 AVAILABLE_FOR_PATCH: 1,
434 BLOCKED_ON_ACTIVE_STACK: 2,
435 BLOCKED_ON_OTHER_STACK: 3,
436 BLOCKED_UNDER_NATIVE_CODE: 4,
437 REPLACED_ON_ACTIVE_STACK: 5
438 }
439
440 FunctionPatchabilityStatus.SymbolName = function(code) {
441 var enum = FunctionPatchabilityStatus;
442 for (name in enum) {
443 if (enum[name] == code) {
444 return name;
445 }
446 }
447 }
448
449
450 // A logical failure in liveedit process. This means that change_log
451 // is valid and consistent description of what happened.
452 function Failure(message) {
453 this.message = message;
454 }
455 // Function (constructor) is public.
456 this.Failure = Failure;
457
458 Failure.prototype.toString = function() {
459 return "LiveEdit Failure: " + this.message;
460 }
461
462 // A testing entry.
463 function GetPcFromSourcePos(func, source_pos) {
464 return %GetFunctionCodePositionFromSource(func, source_pos);
465 }
466 // Function is public.
467 this.GetPcFromSourcePos = GetPcFromSourcePos;
468
469 // TODO(LiveEdit): restore indentation above
470
471 // LiveEdit main entry point: changes a script text to a new string. 467 // LiveEdit main entry point: changes a script text to a new string.
472 function SetScriptSource(script, new_source, change_log) { 468 function SetScriptSource(script, new_source, change_log) {
473 var old_source = script.source; 469 var old_source = script.source;
474 var diff = FindSimpleDiff(old_source, new_source); 470 var diff = FindSimpleDiff(old_source, new_source);
475 if (!diff) { 471 if (!diff) {
476 return; 472 return;
477 } 473 }
478 ApplyPatch(script, diff.change_pos, diff.old_len, 474 ApplyPatch(script, diff.change_pos, diff.old_len,
479 new_source.substring(diff.change_pos, diff.change_pos + diff.new_len), 475 new_source.substring(diff.change_pos, diff.change_pos + diff.new_len),
480 change_log); 476 change_log);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
540 } 536 }
541 537
542 if (old_len == 0 && new_len == 0) { 538 if (old_len == 0 && new_len == 0) {
543 // no change 539 // no change
544 return; 540 return;
545 } 541 }
546 542
547 return { "change_pos": change_pos, "old_len": old_len, "new_len": new_len }; 543 return { "change_pos": change_pos, "old_len": old_len, "new_len": new_len };
548 } 544 }
549 } 545 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698