blob: 382f209b623f37a9efc6bbc34ee00f36b47cb5dd [file] [log] [blame]
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001// Copyright 2012 the V8 project authors. All rights reserved.
ager@chromium.org5c838252010-02-19 08:53:10 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
27
28
29#include "v8.h"
30
31#include "liveedit.h"
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +000032
jkummerow@chromium.org212d9642012-05-11 15:02:09 +000033#include "code-stubs.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000034#include "compilation-cache.h"
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +000035#include "compiler.h"
ager@chromium.org5c838252010-02-19 08:53:10 +000036#include "debug.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000037#include "deoptimizer.h"
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +000038#include "global-handles.h"
mmassi@chromium.org49a44672012-12-04 13:52:03 +000039#include "messages.h"
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +000040#include "parser.h"
41#include "scopeinfo.h"
42#include "scopes.h"
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000043#include "v8memory.h"
ager@chromium.org5c838252010-02-19 08:53:10 +000044
45namespace v8 {
46namespace internal {
47
48
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000049#ifdef ENABLE_DEBUGGER_SUPPORT
50
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000051
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000052void SetElementNonStrict(Handle<JSObject> object,
53 uint32_t index,
54 Handle<Object> value) {
55 // Ignore return value from SetElement. It can only be a failure if there
56 // are element setters causing exceptions and the debugger context has none
57 // of these.
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +000058 Handle<Object> no_failure =
59 JSObject::SetElement(object, index, value, NONE, kNonStrictMode);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +000060 ASSERT(!no_failure.is_null());
61 USE(no_failure);
62}
63
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000064// A simple implementation of dynamic programming algorithm. It solves
65// the problem of finding the difference of 2 arrays. It uses a table of results
66// of subproblems. Each cell contains a number together with 2-bit flag
67// that helps building the chunk list.
68class Differencer {
69 public:
lrn@chromium.orgc34f5802010-04-28 12:53:43 +000070 explicit Differencer(Comparator::Input* input)
ricow@chromium.orgd2be9012011-06-01 06:00:58 +000071 : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000072 buffer_ = NewArray<int>(len1_ * len2_);
73 }
74 ~Differencer() {
75 DeleteArray(buffer_);
76 }
77
78 void Initialize() {
79 int array_size = len1_ * len2_;
80 for (int i = 0; i < array_size; i++) {
81 buffer_[i] = kEmptyCellValue;
82 }
83 }
84
85 // Makes sure that result for the full problem is calculated and stored
86 // in the table together with flags showing a path through subproblems.
87 void FillTable() {
88 CompareUpToTail(0, 0);
89 }
90
lrn@chromium.orgc34f5802010-04-28 12:53:43 +000091 void SaveResult(Comparator::Output* chunk_writer) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000092 ResultWriter writer(chunk_writer);
93
94 int pos1 = 0;
95 int pos2 = 0;
96 while (true) {
97 if (pos1 < len1_) {
98 if (pos2 < len2_) {
99 Direction dir = get_direction(pos1, pos2);
100 switch (dir) {
101 case EQ:
102 writer.eq();
103 pos1++;
104 pos2++;
105 break;
106 case SKIP1:
107 writer.skip1(1);
108 pos1++;
109 break;
110 case SKIP2:
111 case SKIP_ANY:
112 writer.skip2(1);
113 pos2++;
114 break;
115 default:
116 UNREACHABLE();
117 }
118 } else {
119 writer.skip1(len1_ - pos1);
120 break;
121 }
122 } else {
123 if (len2_ != pos2) {
124 writer.skip2(len2_ - pos2);
125 }
126 break;
127 }
128 }
129 writer.close();
130 }
131
132 private:
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000133 Comparator::Input* input_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000134 int* buffer_;
135 int len1_;
136 int len2_;
137
138 enum Direction {
139 EQ = 0,
140 SKIP1,
141 SKIP2,
142 SKIP_ANY,
143
144 MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
145 };
146
147 // Computes result for a subtask and optionally caches it in the buffer table.
148 // All results values are shifted to make space for flags in the lower bits.
149 int CompareUpToTail(int pos1, int pos2) {
150 if (pos1 < len1_) {
151 if (pos2 < len2_) {
152 int cached_res = get_value4(pos1, pos2);
153 if (cached_res == kEmptyCellValue) {
154 Direction dir;
155 int res;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000156 if (input_->Equals(pos1, pos2)) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000157 res = CompareUpToTail(pos1 + 1, pos2 + 1);
158 dir = EQ;
159 } else {
160 int res1 = CompareUpToTail(pos1 + 1, pos2) +
161 (1 << kDirectionSizeBits);
162 int res2 = CompareUpToTail(pos1, pos2 + 1) +
163 (1 << kDirectionSizeBits);
164 if (res1 == res2) {
165 res = res1;
166 dir = SKIP_ANY;
167 } else if (res1 < res2) {
168 res = res1;
169 dir = SKIP1;
170 } else {
171 res = res2;
172 dir = SKIP2;
173 }
174 }
175 set_value4_and_dir(pos1, pos2, res, dir);
176 cached_res = res;
177 }
178 return cached_res;
179 } else {
180 return (len1_ - pos1) << kDirectionSizeBits;
181 }
182 } else {
183 return (len2_ - pos2) << kDirectionSizeBits;
184 }
185 }
186
187 inline int& get_cell(int i1, int i2) {
188 return buffer_[i1 + i2 * len1_];
189 }
190
191 // Each cell keeps a value plus direction. Value is multiplied by 4.
192 void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
193 ASSERT((value4 & kDirectionMask) == 0);
194 get_cell(i1, i2) = value4 | dir;
195 }
196
197 int get_value4(int i1, int i2) {
198 return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
199 }
200 Direction get_direction(int i1, int i2) {
201 return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
202 }
203
204 static const int kDirectionSizeBits = 2;
205 static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
206 static const int kEmptyCellValue = -1 << kDirectionSizeBits;
207
208 // This method only holds static assert statement (unfortunately you cannot
209 // place one in class scope).
210 void StaticAssertHolder() {
211 STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
212 }
213
214 class ResultWriter {
215 public:
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000216 explicit ResultWriter(Comparator::Output* chunk_writer)
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000217 : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
218 pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
219 }
220 void eq() {
221 FlushChunk();
222 pos1_++;
223 pos2_++;
224 }
225 void skip1(int len1) {
226 StartChunk();
227 pos1_ += len1;
228 }
229 void skip2(int len2) {
230 StartChunk();
231 pos2_ += len2;
232 }
233 void close() {
234 FlushChunk();
235 }
236
237 private:
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000238 Comparator::Output* chunk_writer_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000239 int pos1_;
240 int pos2_;
241 int pos1_begin_;
242 int pos2_begin_;
243 bool has_open_chunk_;
244
245 void StartChunk() {
246 if (!has_open_chunk_) {
247 pos1_begin_ = pos1_;
248 pos2_begin_ = pos2_;
249 has_open_chunk_ = true;
250 }
251 }
252
253 void FlushChunk() {
254 if (has_open_chunk_) {
255 chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
256 pos1_ - pos1_begin_, pos2_ - pos2_begin_);
257 has_open_chunk_ = false;
258 }
259 }
260 };
261};
262
263
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000264void Comparator::CalculateDifference(Comparator::Input* input,
265 Comparator::Output* result_writer) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000266 Differencer differencer(input);
267 differencer.Initialize();
268 differencer.FillTable();
269 differencer.SaveResult(result_writer);
270}
271
272
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000273static bool CompareSubstrings(Handle<String> s1, int pos1,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000274 Handle<String> s2, int pos2, int len) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000275 for (int i = 0; i < len; i++) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000276 if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000277 return false;
278 }
279 }
280 return true;
281}
282
283
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000284// Additional to Input interface. Lets switch Input range to subrange.
285// More elegant way would be to wrap one Input as another Input object
286// and translate positions there, but that would cost us additional virtual
287// call per comparison.
288class SubrangableInput : public Comparator::Input {
289 public:
290 virtual void SetSubrange1(int offset, int len) = 0;
291 virtual void SetSubrange2(int offset, int len) = 0;
292};
293
294
295class SubrangableOutput : public Comparator::Output {
296 public:
297 virtual void SetSubrange1(int offset, int len) = 0;
298 virtual void SetSubrange2(int offset, int len) = 0;
299};
300
301
302static int min(int a, int b) {
303 return a < b ? a : b;
304}
305
306
307// Finds common prefix and suffix in input. This parts shouldn't take space in
308// linear programming table. Enable subranging in input and output.
309static void NarrowDownInput(SubrangableInput* input,
310 SubrangableOutput* output) {
311 const int len1 = input->GetLength1();
312 const int len2 = input->GetLength2();
313
314 int common_prefix_len;
315 int common_suffix_len;
316
317 {
318 common_prefix_len = 0;
319 int prefix_limit = min(len1, len2);
320 while (common_prefix_len < prefix_limit &&
321 input->Equals(common_prefix_len, common_prefix_len)) {
322 common_prefix_len++;
323 }
324
325 common_suffix_len = 0;
326 int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
327
328 while (common_suffix_len < suffix_limit &&
329 input->Equals(len1 - common_suffix_len - 1,
330 len2 - common_suffix_len - 1)) {
331 common_suffix_len++;
332 }
333 }
334
335 if (common_prefix_len > 0 || common_suffix_len > 0) {
336 int new_len1 = len1 - common_suffix_len - common_prefix_len;
337 int new_len2 = len2 - common_suffix_len - common_prefix_len;
338
339 input->SetSubrange1(common_prefix_len, new_len1);
340 input->SetSubrange2(common_prefix_len, new_len2);
341
342 output->SetSubrange1(common_prefix_len, new_len1);
343 output->SetSubrange2(common_prefix_len, new_len2);
344 }
345}
346
347
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000348// A helper class that writes chunk numbers into JSArray.
349// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
350class CompareOutputArrayWriter {
351 public:
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000352 explicit CompareOutputArrayWriter(Isolate* isolate)
353 : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000354
355 Handle<JSArray> GetResult() {
356 return array_;
357 }
358
359 void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000360 Isolate* isolate = array_->GetIsolate();
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000361 SetElementNonStrict(array_,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000362 current_size_,
363 Handle<Object>(Smi::FromInt(char_pos1), isolate));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000364 SetElementNonStrict(array_,
365 current_size_ + 1,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000366 Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
367 isolate));
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000368 SetElementNonStrict(array_,
369 current_size_ + 2,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000370 Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
371 isolate));
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000372 current_size_ += 3;
373 }
374
375 private:
376 Handle<JSArray> array_;
377 int current_size_;
378};
379
380
381// Represents 2 strings as 2 arrays of tokens.
382// TODO(LiveEdit): Currently it's actually an array of charactres.
383// Make array of tokens instead.
384class TokensCompareInput : public Comparator::Input {
385 public:
386 TokensCompareInput(Handle<String> s1, int offset1, int len1,
387 Handle<String> s2, int offset2, int len2)
388 : s1_(s1), offset1_(offset1), len1_(len1),
389 s2_(s2), offset2_(offset2), len2_(len2) {
390 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000391 virtual int GetLength1() {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000392 return len1_;
393 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000394 virtual int GetLength2() {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000395 return len2_;
396 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000397 bool Equals(int index1, int index2) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000398 return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
399 }
400
401 private:
402 Handle<String> s1_;
403 int offset1_;
404 int len1_;
405 Handle<String> s2_;
406 int offset2_;
407 int len2_;
408};
409
410
411// Stores compare result in JSArray. Converts substring positions
412// to absolute positions.
413class TokensCompareOutput : public Comparator::Output {
414 public:
415 TokensCompareOutput(CompareOutputArrayWriter* array_writer,
416 int offset1, int offset2)
417 : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
418 }
419
420 void AddChunk(int pos1, int pos2, int len1, int len2) {
421 array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
422 }
423
424 private:
425 CompareOutputArrayWriter* array_writer_;
426 int offset1_;
427 int offset2_;
428};
429
430
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000431// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
432// never has terminating new line character.
433class LineEndsWrapper {
434 public:
435 explicit LineEndsWrapper(Handle<String> string)
436 : ends_array_(CalculateLineEnds(string, false)),
437 string_len_(string->length()) {
438 }
439 int length() {
440 return ends_array_->length() + 1;
441 }
442 // Returns start for any line including start of the imaginary line after
443 // the last line.
444 int GetLineStart(int index) {
445 if (index == 0) {
446 return 0;
447 } else {
448 return GetLineEnd(index - 1);
449 }
450 }
451 int GetLineEnd(int index) {
452 if (index == ends_array_->length()) {
453 // End of the last line is always an end of the whole string.
454 // If the string ends with a new line character, the last line is an
455 // empty string after this character.
456 return string_len_;
457 } else {
458 return GetPosAfterNewLine(index);
459 }
460 }
461
462 private:
463 Handle<FixedArray> ends_array_;
464 int string_len_;
465
466 int GetPosAfterNewLine(int index) {
467 return Smi::cast(ends_array_->get(index))->value() + 1;
468 }
469};
470
471
472// Represents 2 strings as 2 arrays of lines.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000473class LineArrayCompareInput : public SubrangableInput {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000474 public:
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000475 LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000476 LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000477 : s1_(s1), s2_(s2), line_ends1_(line_ends1),
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000478 line_ends2_(line_ends2),
479 subrange_offset1_(0), subrange_offset2_(0),
480 subrange_len1_(line_ends1_.length()),
481 subrange_len2_(line_ends2_.length()) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000482 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000483 int GetLength1() {
484 return subrange_len1_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000485 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000486 int GetLength2() {
487 return subrange_len2_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000488 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000489 bool Equals(int index1, int index2) {
490 index1 += subrange_offset1_;
491 index2 += subrange_offset2_;
492
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000493 int line_start1 = line_ends1_.GetLineStart(index1);
494 int line_start2 = line_ends2_.GetLineStart(index2);
495 int line_end1 = line_ends1_.GetLineEnd(index1);
496 int line_end2 = line_ends2_.GetLineEnd(index2);
497 int len1 = line_end1 - line_start1;
498 int len2 = line_end2 - line_start2;
499 if (len1 != len2) {
500 return false;
501 }
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000502 return CompareSubstrings(s1_, line_start1, s2_, line_start2,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000503 len1);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000504 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000505 void SetSubrange1(int offset, int len) {
506 subrange_offset1_ = offset;
507 subrange_len1_ = len;
508 }
509 void SetSubrange2(int offset, int len) {
510 subrange_offset2_ = offset;
511 subrange_len2_ = len;
512 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000513
514 private:
515 Handle<String> s1_;
516 Handle<String> s2_;
517 LineEndsWrapper line_ends1_;
518 LineEndsWrapper line_ends2_;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000519 int subrange_offset1_;
520 int subrange_offset2_;
521 int subrange_len1_;
522 int subrange_len2_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000523};
524
525
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000526// Stores compare result in JSArray. For each chunk tries to conduct
527// a fine-grained nested diff token-wise.
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000528class TokenizingLineArrayCompareOutput : public SubrangableOutput {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000529 public:
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000530 TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
531 LineEndsWrapper line_ends2,
532 Handle<String> s1, Handle<String> s2)
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000533 : array_writer_(s1->GetIsolate()),
534 line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000535 subrange_offset1_(0), subrange_offset2_(0) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000536 }
537
538 void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000539 line_pos1 += subrange_offset1_;
540 line_pos2 += subrange_offset2_;
541
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000542 int char_pos1 = line_ends1_.GetLineStart(line_pos1);
543 int char_pos2 = line_ends2_.GetLineStart(line_pos2);
544 int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
545 int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
546
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000547 if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
548 // Chunk is small enough to conduct a nested token-level diff.
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000549 HandleScope subTaskScope(s1_->GetIsolate());
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000550
551 TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
552 s2_, char_pos2, char_len2);
553 TokensCompareOutput tokens_output(&array_writer_, char_pos1,
554 char_pos2);
555
556 Comparator::CalculateDifference(&tokens_input, &tokens_output);
557 } else {
558 array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
559 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000560 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000561 void SetSubrange1(int offset, int len) {
562 subrange_offset1_ = offset;
563 }
564 void SetSubrange2(int offset, int len) {
565 subrange_offset2_ = offset;
566 }
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000567
568 Handle<JSArray> GetResult() {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000569 return array_writer_.GetResult();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000570 }
571
572 private:
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000573 static const int CHUNK_LEN_LIMIT = 800;
574
575 CompareOutputArrayWriter array_writer_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000576 LineEndsWrapper line_ends1_;
577 LineEndsWrapper line_ends2_;
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000578 Handle<String> s1_;
579 Handle<String> s2_;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000580 int subrange_offset1_;
581 int subrange_offset2_;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000582};
583
584
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000585Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
586 Handle<String> s2) {
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000587 s1 = FlattenGetString(s1);
588 s2 = FlattenGetString(s2);
589
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000590 LineEndsWrapper line_ends1(s1);
591 LineEndsWrapper line_ends2(s2);
592
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000593 LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000594 TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000595
ricow@chromium.orgd2be9012011-06-01 06:00:58 +0000596 NarrowDownInput(&input, &output);
597
lrn@chromium.orgc34f5802010-04-28 12:53:43 +0000598 Comparator::CalculateDifference(&input, &output);
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000599
600 return output.GetResult();
601}
602
603
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000604static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000605 // TODO(635): support extensions.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000606 PostponeInterruptsScope postpone(isolate);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000607
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000608 // Build AST.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000609 CompilationInfoWithZone info(script);
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000610 info.MarkAsGlobal();
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000611 // Parse and don't allow skipping lazy functions.
612 if (ParserApi::Parse(&info, kNoParsingFlags)) {
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000613 // Compile the code.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000614 LiveEditFunctionTracker tracker(info.isolate(), info.function());
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000615 if (Compiler::MakeCodeForLiveEdit(&info)) {
616 ASSERT(!info.code().is_null());
617 tracker.RecordRootFunctionInfo(info.code());
618 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000619 info.isolate()->StackOverflow();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000620 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000621 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000622}
623
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000624
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000625// Unwraps JSValue object, returning its field "value"
626static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000627 return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000628}
629
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000630
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000631// Wraps any object into a OpaqueReference, that will hide the object
632// from JavaScript.
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000633static Handle<JSValue> WrapInJSValue(Handle<Object> object) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000634 Handle<JSFunction> constructor =
635 Isolate::Current()->opaque_reference_function();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000636 Handle<JSValue> result =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000637 Handle<JSValue>::cast(FACTORY->NewJSObject(constructor));
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000638 result->set_value(*object);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000639 return result;
640}
641
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000642
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000643static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
644 Handle<JSValue> jsValue) {
645 Object* shared = jsValue->value();
646 CHECK(shared->IsSharedFunctionInfo());
647 return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
648}
649
650
651static int GetArrayLength(Handle<JSArray> array) {
652 Object* length = array->length();
653 CHECK(length->IsSmi());
654 return Smi::cast(length)->value();
655}
656
657
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000658// Simple helper class that creates more or less typed structures over
659// JSArray object. This is an adhoc method of passing structures from C++
660// to JavaScript.
661template<typename S>
662class JSArrayBasedStruct {
663 public:
664 static S Create() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000665 Handle<JSArray> array = FACTORY->NewJSArray(S::kSize_);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000666 return S(array);
667 }
668 static S cast(Object* object) {
669 JSArray* array = JSArray::cast(object);
670 Handle<JSArray> array_handle(array);
671 return S(array_handle);
672 }
673 explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
674 }
675 Handle<JSArray> GetJSArray() {
676 return array_;
677 }
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000678 Isolate* isolate() const {
679 return array_->GetIsolate();
680 }
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000681
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000682 protected:
683 void SetField(int field_position, Handle<Object> value) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000684 SetElementNonStrict(array_, field_position, value);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000685 }
686 void SetSmiValueField(int field_position, int value) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000687 SetElementNonStrict(array_,
688 field_position,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000689 Handle<Smi>(Smi::FromInt(value), isolate()));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000690 }
691 Object* GetField(int field_position) {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000692 return array_->GetElementNoExceptionThrown(field_position);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000693 }
694 int GetSmiValueField(int field_position) {
695 Object* res = GetField(field_position);
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +0000696 CHECK(res->IsSmi());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000697 return Smi::cast(res)->value();
698 }
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000699
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000700 private:
701 Handle<JSArray> array_;
702};
703
704
705// Represents some function compilation details. This structure will be used
706// from JavaScript. It contains Code object, which is kept wrapped
707// into a BlindReference for sanitizing reasons.
708class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
709 public:
710 explicit FunctionInfoWrapper(Handle<JSArray> array)
711 : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
712 }
713 void SetInitialProperties(Handle<String> name, int start_position,
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000714 int end_position, int param_num,
715 int literal_count, int parent_index) {
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000716 HandleScope scope(isolate());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000717 this->SetField(kFunctionNameOffset_, name);
718 this->SetSmiValueField(kStartPositionOffset_, start_position);
719 this->SetSmiValueField(kEndPositionOffset_, end_position);
720 this->SetSmiValueField(kParamNumOffset_, param_num);
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000721 this->SetSmiValueField(kLiteralNumOffset_, literal_count);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000722 this->SetSmiValueField(kParentIndexOffset_, parent_index);
723 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000724 void SetFunctionCode(Handle<Code> function_code,
725 Handle<Object> code_scope_info) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000726 Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000727 this->SetField(kCodeOffset_, code_wrapper);
728
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000729 Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000730 this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000731 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000732 void SetOuterScopeInfo(Handle<Object> scope_info_array) {
733 this->SetField(kOuterScopeInfoOffset_, scope_info_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000734 }
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000735 void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000736 Handle<JSValue> info_holder = WrapInJSValue(info);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000737 this->SetField(kSharedFunctionInfoOffset_, info_holder);
738 }
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000739 int GetLiteralCount() {
740 return this->GetSmiValueField(kLiteralNumOffset_);
741 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000742 int GetParentIndex() {
743 return this->GetSmiValueField(kParentIndexOffset_);
744 }
745 Handle<Code> GetFunctionCode() {
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +0000746 Object* element = this->GetField(kCodeOffset_);
747 CHECK(element->IsJSValue());
748 Handle<JSValue> value_wrapper(JSValue::cast(element));
749 Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
750 CHECK(raw_result->IsCode());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000751 return Handle<Code>::cast(raw_result);
752 }
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000753 Handle<Object> GetCodeScopeInfo() {
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +0000754 Object* element = this->GetField(kCodeScopeInfoOffset_);
755 CHECK(element->IsJSValue());
756 return UnwrapJSValue(Handle<JSValue>(JSValue::cast(element)));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000757 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000758 int GetStartPosition() {
759 return this->GetSmiValueField(kStartPositionOffset_);
760 }
761 int GetEndPosition() {
762 return this->GetSmiValueField(kEndPositionOffset_);
763 }
764
765 private:
766 static const int kFunctionNameOffset_ = 0;
767 static const int kStartPositionOffset_ = 1;
768 static const int kEndPositionOffset_ = 2;
769 static const int kParamNumOffset_ = 3;
770 static const int kCodeOffset_ = 4;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000771 static const int kCodeScopeInfoOffset_ = 5;
772 static const int kOuterScopeInfoOffset_ = 6;
773 static const int kParentIndexOffset_ = 7;
774 static const int kSharedFunctionInfoOffset_ = 8;
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000775 static const int kLiteralNumOffset_ = 9;
776 static const int kSize_ = 10;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000777
778 friend class JSArrayBasedStruct<FunctionInfoWrapper>;
779};
780
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000781
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000782// Wraps SharedFunctionInfo along with some of its fields for passing it
783// back to JavaScript. SharedFunctionInfo object itself is additionally
784// wrapped into BlindReference for sanitizing reasons.
785class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
786 public:
ager@chromium.orgac091b72010-05-05 07:34:42 +0000787 static bool IsInstance(Handle<JSArray> array) {
788 return array->length() == Smi::FromInt(kSize_) &&
lrn@chromium.org303ada72010-10-27 09:33:13 +0000789 array->GetElementNoExceptionThrown(kSharedInfoOffset_)->IsJSValue();
ager@chromium.orgac091b72010-05-05 07:34:42 +0000790 }
791
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000792 explicit SharedInfoWrapper(Handle<JSArray> array)
793 : JSArrayBasedStruct<SharedInfoWrapper>(array) {
794 }
795
796 void SetProperties(Handle<String> name, int start_position, int end_position,
797 Handle<SharedFunctionInfo> info) {
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000798 HandleScope scope(isolate());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000799 this->SetField(kFunctionNameOffset_, name);
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000800 Handle<JSValue> info_holder = WrapInJSValue(info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000801 this->SetField(kSharedInfoOffset_, info_holder);
802 this->SetSmiValueField(kStartPositionOffset_, start_position);
803 this->SetSmiValueField(kEndPositionOffset_, end_position);
804 }
805 Handle<SharedFunctionInfo> GetInfo() {
806 Object* element = this->GetField(kSharedInfoOffset_);
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +0000807 CHECK(element->IsJSValue());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000808 Handle<JSValue> value_wrapper(JSValue::cast(element));
ulan@chromium.org56c14af2012-09-20 12:51:09 +0000809 return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000810 }
811
812 private:
813 static const int kFunctionNameOffset_ = 0;
814 static const int kStartPositionOffset_ = 1;
815 static const int kEndPositionOffset_ = 2;
816 static const int kSharedInfoOffset_ = 3;
817 static const int kSize_ = 4;
818
819 friend class JSArrayBasedStruct<SharedInfoWrapper>;
820};
821
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000822
ager@chromium.org5c838252010-02-19 08:53:10 +0000823class FunctionInfoListener {
824 public:
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000825 explicit FunctionInfoListener(Isolate* isolate) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000826 current_parent_index_ = -1;
827 len_ = 0;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000828 result_ = isolate->factory()->NewJSArray(10);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000829 }
830
ager@chromium.org5c838252010-02-19 08:53:10 +0000831 void FunctionStarted(FunctionLiteral* fun) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000832 HandleScope scope(isolate());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000833 FunctionInfoWrapper info = FunctionInfoWrapper::Create();
834 info.SetInitialProperties(fun->name(), fun->start_position(),
danno@chromium.orgc612e022011-11-10 11:38:15 +0000835 fun->end_position(), fun->parameter_count(),
yangguo@chromium.orgfb377212012-11-16 14:43:43 +0000836 fun->materialized_literal_count(),
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000837 current_parent_index_);
838 current_parent_index_ = len_;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000839 SetElementNonStrict(result_, len_, info.GetJSArray());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000840 len_++;
ager@chromium.org5c838252010-02-19 08:53:10 +0000841 }
842
843 void FunctionDone() {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000844 HandleScope scope(isolate());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000845 FunctionInfoWrapper info =
846 FunctionInfoWrapper::cast(
847 result_->GetElementNoExceptionThrown(current_parent_index_));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000848 current_parent_index_ = info.GetParentIndex();
ager@chromium.org5c838252010-02-19 08:53:10 +0000849 }
850
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000851 // Saves only function code, because for a script function we
852 // may never create a SharedFunctionInfo object.
853 void FunctionCode(Handle<Code> function_code) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000854 FunctionInfoWrapper info =
855 FunctionInfoWrapper::cast(
856 result_->GetElementNoExceptionThrown(current_parent_index_));
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000857 info.SetFunctionCode(function_code,
858 Handle<Object>(isolate()->heap()->null_value(),
859 isolate()));
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000860 }
861
862 // Saves full information about a function: its code, its scope info
863 // and a SharedFunctionInfo object.
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000864 void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope,
865 Zone* zone) {
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000866 if (!shared->IsSharedFunctionInfo()) {
867 return;
868 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000869 FunctionInfoWrapper info =
870 FunctionInfoWrapper::cast(
871 result_->GetElementNoExceptionThrown(current_parent_index_));
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000872 info.SetFunctionCode(Handle<Code>(shared->code()),
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000873 Handle<Object>(shared->scope_info(), isolate()));
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000874 info.SetSharedFunctionInfo(shared);
875
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000876 Handle<Object> scope_info_list(SerializeFunctionScope(scope, zone),
877 isolate());
sgjesse@chromium.org2ec107f2010-09-13 09:19:46 +0000878 info.SetOuterScopeInfo(scope_info_list);
879 }
880
881 Handle<JSArray> GetResult() { return result_; }
882
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000883 private:
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000884 Isolate* isolate() const { return result_->GetIsolate(); }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000885
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000886 Object* SerializeFunctionScope(Scope* scope, Zone* zone) {
887 HandleScope handle_scope(isolate());
888
889 Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000890 int scope_info_length = 0;
891
892 // Saves some description of scope. It stores name and indexes of
893 // variables in the whole scope chain. Null-named slots delimit
894 // scopes of this chain.
895 Scope* outer_scope = scope->outer_scope();
896 if (outer_scope == NULL) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000897 return isolate()->heap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000898 }
899 do {
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000900 ZoneList<Variable*> stack_list(outer_scope->StackLocalCount(), zone);
901 ZoneList<Variable*> context_list(outer_scope->ContextLocalCount(), zone);
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000902 outer_scope->CollectStackAndContextLocals(&stack_list, &context_list);
903 context_list.Sort(&Variable::CompareIndex);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000904
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000905 for (int i = 0; i < context_list.length(); i++) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000906 SetElementNonStrict(scope_info_list,
907 scope_info_length,
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000908 context_list[i]->name());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000909 scope_info_length++;
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000910 SetElementNonStrict(
911 scope_info_list,
912 scope_info_length,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000913 Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate()));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000914 scope_info_length++;
915 }
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +0000916 SetElementNonStrict(scope_info_list,
917 scope_info_length,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000918 Handle<Object>(isolate()->heap()->null_value(),
919 isolate()));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000920 scope_info_length++;
921
922 outer_scope = outer_scope->outer_scope();
923 } while (outer_scope != NULL);
924
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000925 return *scope_info_list;
ager@chromium.org5c838252010-02-19 08:53:10 +0000926 }
927
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000928 Handle<JSArray> result_;
929 int len_;
930 int current_parent_index_;
ager@chromium.org5c838252010-02-19 08:53:10 +0000931};
932
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000933
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000934JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
935 Handle<String> source) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000936 Isolate* isolate = Isolate::Current();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000937
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000938 FunctionInfoListener listener(isolate);
939 Handle<Object> original_source =
940 Handle<Object>(script->source(), isolate);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000941 script->set_source(*source);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000942 isolate->set_active_function_info_listener(&listener);
mmassi@chromium.org49a44672012-12-04 13:52:03 +0000943
944 {
945 // Creating verbose TryCatch from public API is currently the only way to
946 // force code save location. We do not use this the object directly.
947 v8::TryCatch try_catch;
948 try_catch.SetVerbose(true);
949
950 // A logical 'try' section.
951 CompileScriptForTracker(isolate, script);
952 }
953
954 // A logical 'catch' section.
955 Handle<JSObject> rethrow_exception;
956 if (isolate->has_pending_exception()) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000957 Handle<Object> exception(isolate->pending_exception()->ToObjectChecked(),
958 isolate);
mmassi@chromium.org49a44672012-12-04 13:52:03 +0000959 MessageLocation message_location = isolate->GetMessageLocation();
960
961 isolate->clear_pending_message();
962 isolate->clear_pending_exception();
963
964 // If possible, copy positions from message object to exception object.
965 if (exception->IsJSObject() && !message_location.script().is_null()) {
966 rethrow_exception = Handle<JSObject>::cast(exception);
967
968 Factory* factory = isolate->factory();
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000969 Handle<String> start_pos_key = factory->InternalizeOneByteString(
970 STATIC_ASCII_VECTOR("startPosition"));
971 Handle<String> end_pos_key = factory->InternalizeOneByteString(
972 STATIC_ASCII_VECTOR("endPosition"));
973 Handle<String> script_obj_key = factory->InternalizeOneByteString(
974 STATIC_ASCII_VECTOR("scriptObject"));
975 Handle<Smi> start_pos(
976 Smi::FromInt(message_location.start_pos()), isolate);
ulan@chromium.org09d7ab52013-02-25 15:50:35 +0000977 Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
mmassi@chromium.org49a44672012-12-04 13:52:03 +0000978 Handle<JSValue> script_obj = GetScriptWrapper(message_location.script());
979 JSReceiver::SetProperty(
980 rethrow_exception, start_pos_key, start_pos, NONE, kNonStrictMode);
981 JSReceiver::SetProperty(
982 rethrow_exception, end_pos_key, end_pos, NONE, kNonStrictMode);
983 JSReceiver::SetProperty(
984 rethrow_exception, script_obj_key, script_obj, NONE, kNonStrictMode);
985 }
986 }
987
988 // A logical 'finally' section.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000989 isolate->set_active_function_info_listener(NULL);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000990 script->set_source(*original_source);
991
mmassi@chromium.org49a44672012-12-04 13:52:03 +0000992 if (rethrow_exception.is_null()) {
993 return *(listener.GetResult());
994 } else {
995 isolate->Throw(*rethrow_exception);
996 return 0;
997 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000998}
999
1000
1001void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001002 HandleScope scope(array->GetIsolate());
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001003 int len = GetArrayLength(array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001004 for (int i = 0; i < len; i++) {
1005 Handle<SharedFunctionInfo> info(
lrn@chromium.org303ada72010-10-27 09:33:13 +00001006 SharedFunctionInfo::cast(array->GetElementNoExceptionThrown(i)));
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001007 SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
1008 Handle<String> name_handle(String::cast(info->name()));
1009 info_wrapper.SetProperties(name_handle, info->start_position(),
1010 info->end_position(), info);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001011 SetElementNonStrict(array, i, info_wrapper.GetJSArray());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001012 }
1013}
1014
1015
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001016// Visitor that finds all references to a particular code object,
1017// including "CODE_TARGET" references in other code objects and replaces
1018// them on the fly.
1019class ReplacingVisitor : public ObjectVisitor {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001020 public:
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001021 explicit ReplacingVisitor(Code* original, Code* substitution)
1022 : original_(original), substitution_(substitution) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001023 }
1024
1025 virtual void VisitPointers(Object** start, Object** end) {
1026 for (Object** p = start; p < end; p++) {
1027 if (*p == original_) {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001028 *p = substitution_;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001029 }
1030 }
1031 }
1032
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001033 virtual void VisitCodeEntry(Address entry) {
1034 if (Code::GetObjectFromEntryAddress(entry) == original_) {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001035 Address substitution_entry = substitution_->instruction_start();
1036 Memory::Address_at(entry) = substitution_entry;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001037 }
1038 }
1039
1040 virtual void VisitCodeTarget(RelocInfo* rinfo) {
fschneider@chromium.org013f3e12010-04-26 13:27:52 +00001041 if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
1042 Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001043 Address substitution_entry = substitution_->instruction_start();
1044 rinfo->set_target_address(substitution_entry);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001045 }
1046 }
1047
1048 virtual void VisitDebugTarget(RelocInfo* rinfo) {
1049 VisitCodeTarget(rinfo);
1050 }
1051
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001052 private:
1053 Code* original_;
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001054 Code* substitution_;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001055};
1056
whesse@chromium.orgcec079d2010-03-22 14:44:04 +00001057
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001058// Finds all references to original and replaces them with substitution.
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00001059static void ReplaceCodeObject(Handle<Code> original,
1060 Handle<Code> substitution) {
1061 // Perform a full GC in order to ensure that we are not in the middle of an
1062 // incremental marking phase when we are replacing the code object.
1063 // Since we are not in an incremental marking phase we can write pointers
1064 // to code objects (that are never in new space) without worrying about
1065 // write barriers.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001066 Heap* heap = original->GetHeap();
1067 heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00001068 "liveedit.cc ReplaceCodeObject");
1069
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001070 ASSERT(!heap->InNewSpace(*substitution));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001071
1072 AssertNoAllocation no_allocations_please;
1073
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00001074 ReplacingVisitor visitor(*original, *substitution);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001075
1076 // Iterate over all roots. Stack frames may have pointer into original code,
1077 // so temporary replace the pointers with offset numbers
1078 // in prologue/epilogue.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001079 heap->IterateRoots(&visitor, VISIT_ALL);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001080
1081 // Now iterate over all pointers of all objects, including code_target
1082 // implicit pointers.
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001083 HeapIterator iterator(heap);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001084 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1085 obj->Iterate(&visitor);
1086 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001087}
1088
1089
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001090// Patch function literals.
1091// Name 'literals' is a misnomer. Rather it's a cache for complex object
1092// boilerplates and for a native context. We must clean cached values.
1093// Additionally we may need to allocate a new array if number of literals
1094// changed.
1095class LiteralFixer {
1096 public:
1097 static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
1098 Handle<SharedFunctionInfo> shared_info,
1099 Isolate* isolate) {
1100 int new_literal_count = compile_info_wrapper->GetLiteralCount();
1101 if (new_literal_count > 0) {
1102 new_literal_count += JSFunction::kLiteralsPrefixSize;
1103 }
1104 int old_literal_count = shared_info->num_literals();
1105
1106 if (old_literal_count == new_literal_count) {
1107 // If literal count didn't change, simply go over all functions
1108 // and clear literal arrays.
1109 ClearValuesVisitor visitor;
1110 IterateJSFunctions(*shared_info, &visitor);
1111 } else {
1112 // When literal count changes, we have to create new array instances.
1113 // Since we cannot create instances when iterating heap, we should first
1114 // collect all functions and fix their literal arrays.
1115 Handle<FixedArray> function_instances =
1116 CollectJSFunctions(shared_info, isolate);
1117 for (int i = 0; i < function_instances->length(); i++) {
1118 Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
1119 Handle<FixedArray> old_literals(fun->literals());
1120 Handle<FixedArray> new_literals =
1121 isolate->factory()->NewFixedArray(new_literal_count);
1122 if (new_literal_count > 0) {
1123 Handle<Context> native_context;
1124 if (old_literals->length() >
1125 JSFunction::kLiteralNativeContextIndex) {
1126 native_context = Handle<Context>(
1127 JSFunction::NativeContextFromLiterals(fun->literals()));
1128 } else {
1129 native_context = Handle<Context>(fun->context()->native_context());
1130 }
1131 new_literals->set(JSFunction::kLiteralNativeContextIndex,
1132 *native_context);
1133 }
1134 fun->set_literals(*new_literals);
1135 }
1136
1137 shared_info->set_num_literals(new_literal_count);
1138 }
1139 }
1140
1141 private:
1142 // Iterates all function instances in the HEAP that refers to the
1143 // provided shared_info.
1144 template<typename Visitor>
1145 static void IterateJSFunctions(SharedFunctionInfo* shared_info,
1146 Visitor* visitor) {
1147 AssertNoAllocation no_allocations_please;
1148
hpayer@chromium.org7c3372b2013-02-13 17:26:04 +00001149 HeapIterator iterator(shared_info->GetHeap());
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001150 for (HeapObject* obj = iterator.next(); obj != NULL;
1151 obj = iterator.next()) {
1152 if (obj->IsJSFunction()) {
1153 JSFunction* function = JSFunction::cast(obj);
1154 if (function->shared() == shared_info) {
1155 visitor->visit(function);
1156 }
1157 }
1158 }
1159 }
1160
1161 // Finds all instances of JSFunction that refers to the provided shared_info
1162 // and returns array with them.
1163 static Handle<FixedArray> CollectJSFunctions(
1164 Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
1165 CountVisitor count_visitor;
1166 count_visitor.count = 0;
1167 IterateJSFunctions(*shared_info, &count_visitor);
1168 int size = count_visitor.count;
1169
1170 Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
1171 if (size > 0) {
1172 CollectVisitor collect_visitor(result);
1173 IterateJSFunctions(*shared_info, &collect_visitor);
1174 }
1175 return result;
1176 }
1177
1178 class ClearValuesVisitor {
1179 public:
1180 void visit(JSFunction* fun) {
1181 FixedArray* literals = fun->literals();
1182 int len = literals->length();
1183 for (int j = JSFunction::kLiteralsPrefixSize; j < len; j++) {
1184 literals->set_undefined(j);
1185 }
1186 }
1187 };
1188
1189 class CountVisitor {
1190 public:
1191 void visit(JSFunction* fun) {
1192 count++;
1193 }
1194 int count;
1195 };
1196
1197 class CollectVisitor {
1198 public:
1199 explicit CollectVisitor(Handle<FixedArray> output)
1200 : m_output(output), m_pos(0) {}
1201
1202 void visit(JSFunction* fun) {
1203 m_output->set(m_pos, fun);
1204 m_pos++;
1205 }
1206 private:
1207 Handle<FixedArray> m_output;
1208 int m_pos;
1209 };
1210};
1211
1212
ager@chromium.org357bf652010-04-12 11:30:10 +00001213// Check whether the code is natural function code (not a lazy-compile stub
1214// code).
1215static bool IsJSFunctionCode(Code* code) {
1216 return code->kind() == Code::FUNCTION;
1217}
1218
1219
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001220// Returns true if an instance of candidate were inlined into function's code.
1221static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
1222 AssertNoAllocation no_gc;
1223
1224 if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
1225
1226 DeoptimizationInputData* data =
1227 DeoptimizationInputData::cast(function->code()->deoptimization_data());
1228
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001229 if (data == HEAP->empty_fixed_array()) return false;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001230
1231 FixedArray* literals = data->LiteralArray();
1232
1233 int inlined_count = data->InlinedFunctionCount()->value();
1234 for (int i = 0; i < inlined_count; ++i) {
1235 JSFunction* inlined = JSFunction::cast(literals->get(i));
1236 if (inlined->shared() == candidate) return true;
1237 }
1238
1239 return false;
1240}
1241
1242
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001243class DependentFunctionFilter : public OptimizedFunctionFilter {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001244 public:
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001245 explicit DependentFunctionFilter(
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001246 SharedFunctionInfo* function_info)
1247 : function_info_(function_info) {}
1248
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001249 virtual bool TakeFunction(JSFunction* function) {
1250 return (function->shared() == function_info_ ||
1251 IsInlined(function, function_info_));
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001252 }
1253
1254 private:
1255 SharedFunctionInfo* function_info_;
1256};
1257
1258
1259static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
1260 AssertNoAllocation no_allocation;
1261
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +00001262 DependentFunctionFilter filter(function_info);
1263 Deoptimizer::DeoptimizeAllFunctionsWith(&filter);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001264}
1265
1266
lrn@chromium.org303ada72010-10-27 09:33:13 +00001267MaybeObject* LiveEdit::ReplaceFunctionCode(
1268 Handle<JSArray> new_compile_info_array,
1269 Handle<JSArray> shared_info_array) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001270 Isolate* isolate = Isolate::Current();
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001271 HandleScope scope(isolate);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001272
ager@chromium.orgac091b72010-05-05 07:34:42 +00001273 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001274 return isolate->ThrowIllegalOperation();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001275 }
1276
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001277 FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
1278 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1279
1280 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1281
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001282 isolate->heap()->EnsureHeapIsIterable();
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001283
ager@chromium.org357bf652010-04-12 11:30:10 +00001284 if (IsJSFunctionCode(shared_info->code())) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001285 Handle<Code> code = compile_info_wrapper.GetFunctionCode();
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00001286 ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
1287 Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001288 if (code_scope_info->IsFixedArray()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00001289 shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001290 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001291 }
1292
1293 if (shared_info->debug_info()->IsDebugInfo()) {
1294 Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
1295 Handle<Code> new_original_code =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001296 FACTORY->CopyCode(compile_info_wrapper.GetFunctionCode());
ager@chromium.org357bf652010-04-12 11:30:10 +00001297 debug_info->set_original_code(*new_original_code);
1298 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001299
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001300 int start_position = compile_info_wrapper.GetStartPosition();
1301 int end_position = compile_info_wrapper.GetEndPosition();
1302 shared_info->set_start_position(start_position);
1303 shared_info->set_end_position(end_position);
ager@chromium.org357bf652010-04-12 11:30:10 +00001304
yangguo@chromium.orgfb377212012-11-16 14:43:43 +00001305 LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
1306
ager@chromium.org357bf652010-04-12 11:30:10 +00001307 shared_info->set_construct_stub(
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001308 isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric));
ager@chromium.orgac091b72010-05-05 07:34:42 +00001309
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001310 DeoptimizeDependentFunctions(*shared_info);
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001311 isolate->compilation_cache()->Remove(shared_info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001312
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001313 return isolate->heap()->undefined_value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001314}
1315
1316
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001317MaybeObject* LiveEdit::FunctionSourceUpdated(
1318 Handle<JSArray> shared_info_array) {
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001319 Isolate* isolate = shared_info_array->GetIsolate();
1320 HandleScope scope(isolate);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001321
1322 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001323 return isolate->ThrowIllegalOperation();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001324 }
1325
1326 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1327 Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
1328
1329 DeoptimizeDependentFunctions(*shared_info);
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001330 isolate->compilation_cache()->Remove(shared_info);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001331
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001332 return isolate->heap()->undefined_value();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001333}
1334
1335
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001336void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
1337 Handle<Object> script_handle) {
1338 Handle<SharedFunctionInfo> shared_info =
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001339 UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
mstarzinger@chromium.orgde886792012-09-11 13:22:37 +00001340 CHECK(script_handle->IsScript() || script_handle->IsUndefined());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001341 shared_info->set_script(*script_handle);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001342
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001343 Isolate::Current()->compilation_cache()->Remove(shared_info);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001344}
1345
1346
1347// For a script text change (defined as position_change_array), translates
1348// position in unchanged text to position in changed text.
1349// Text change is a set of non-overlapping regions in text, that have changed
1350// their contents and length. It is specified as array of groups of 3 numbers:
1351// (change_begin, change_end, change_end_new_position).
1352// Each group describes a change in text; groups are sorted by change_begin.
1353// Only position in text beyond any changes may be successfully translated.
1354// If a positions is inside some region that changed, result is currently
1355// undefined.
1356static int TranslatePosition(int original_position,
1357 Handle<JSArray> position_change_array) {
1358 int position_diff = 0;
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001359 int array_len = GetArrayLength(position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001360 // TODO(635): binary search may be used here
1361 for (int i = 0; i < array_len; i += 3) {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001362 Object* element = position_change_array->GetElementNoExceptionThrown(i);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001363 CHECK(element->IsSmi());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001364 int chunk_start = Smi::cast(element)->value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001365 if (original_position < chunk_start) {
1366 break;
1367 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00001368 element = position_change_array->GetElementNoExceptionThrown(i + 1);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001369 CHECK(element->IsSmi());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001370 int chunk_end = Smi::cast(element)->value();
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001371 // Position mustn't be inside a chunk.
1372 ASSERT(original_position >= chunk_end);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001373 element = position_change_array->GetElementNoExceptionThrown(i + 2);
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001374 CHECK(element->IsSmi());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001375 int chunk_changed_end = Smi::cast(element)->value();
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001376 position_diff = chunk_changed_end - chunk_end;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001377 }
1378
1379 return original_position + position_diff;
1380}
1381
1382
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001383// Auto-growing buffer for writing relocation info code section. This buffer
1384// is a simplified version of buffer from Assembler. Unlike Assembler, this
1385// class is platform-independent and it works without dealing with instructions.
1386// As specified by RelocInfo format, the buffer is filled in reversed order:
1387// from upper to lower addresses.
1388// It uses NewArray/DeleteArray for memory management.
1389class RelocInfoBuffer {
1390 public:
1391 RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
1392 buffer_size_ = buffer_initial_capicity + kBufferGap;
1393 buffer_ = NewArray<byte>(buffer_size_);
1394
1395 reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
1396 }
1397 ~RelocInfoBuffer() {
1398 DeleteArray(buffer_);
1399 }
1400
1401 // As specified by RelocInfo format, the buffer is filled in reversed order:
1402 // from upper to lower addresses.
1403 void Write(const RelocInfo* rinfo) {
1404 if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
1405 Grow();
1406 }
1407 reloc_info_writer_.Write(rinfo);
1408 }
1409
1410 Vector<byte> GetResult() {
1411 // Return the bytes from pos up to end of buffer.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001412 int result_size =
1413 static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
1414 return Vector<byte>(reloc_info_writer_.pos(), result_size);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001415 }
1416
1417 private:
1418 void Grow() {
1419 // Compute new buffer size.
1420 int new_buffer_size;
1421 if (buffer_size_ < 2 * KB) {
1422 new_buffer_size = 4 * KB;
1423 } else {
1424 new_buffer_size = 2 * buffer_size_;
1425 }
1426 // Some internal data structures overflow for very large buffers,
1427 // they must ensure that kMaximalBufferSize is not too large.
1428 if (new_buffer_size > kMaximalBufferSize) {
1429 V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
1430 }
1431
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001432 // Set up new buffer.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001433 byte* new_buffer = NewArray<byte>(new_buffer_size);
1434
1435 // Copy the data.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00001436 int curently_used_size =
1437 static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001438 memmove(new_buffer + new_buffer_size - curently_used_size,
1439 reloc_info_writer_.pos(), curently_used_size);
1440
1441 reloc_info_writer_.Reposition(
1442 new_buffer + new_buffer_size - curently_used_size,
1443 reloc_info_writer_.last_pc());
1444
1445 DeleteArray(buffer_);
1446 buffer_ = new_buffer;
1447 buffer_size_ = new_buffer_size;
1448 }
1449
1450 RelocInfoWriter reloc_info_writer_;
1451 byte* buffer_;
1452 int buffer_size_;
1453
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001454 static const int kBufferGap = RelocInfoWriter::kMaxSize;
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001455 static const int kMaximalBufferSize = 512*MB;
1456};
1457
1458// Patch positions in code (changes relocation info section) and possibly
1459// returns new instance of code.
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001460static Handle<Code> PatchPositionsInCode(
1461 Handle<Code> code,
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001462 Handle<JSArray> position_change_array) {
1463
1464 RelocInfoBuffer buffer_writer(code->relocation_size(),
1465 code->instruction_start());
1466
1467 {
1468 AssertNoAllocation no_allocations_please;
1469 for (RelocIterator it(*code); !it.done(); it.next()) {
1470 RelocInfo* rinfo = it.rinfo();
1471 if (RelocInfo::IsPosition(rinfo->rmode())) {
1472 int position = static_cast<int>(rinfo->data());
1473 int new_position = TranslatePosition(position,
1474 position_change_array);
1475 if (position != new_position) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001476 RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001477 buffer_writer.Write(&info_copy);
1478 continue;
1479 }
1480 }
mvstanton@chromium.orge4ac3ef2012-11-12 14:53:34 +00001481 if (RelocInfo::IsRealRelocMode(rinfo->rmode())) {
1482 buffer_writer.Write(it.rinfo());
1483 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001484 }
1485 }
1486
1487 Vector<byte> buffer = buffer_writer.GetResult();
1488
1489 if (buffer.length() == code->relocation_size()) {
1490 // Simply patch relocation area of code.
1491 memcpy(code->relocation_start(), buffer.start(), buffer.length());
1492 return code;
1493 } else {
1494 // Relocation info section now has different size. We cannot simply
1495 // rewrite it inside code object. Instead we have to create a new
1496 // code object.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001497 Handle<Code> result(FACTORY->CopyCode(code, buffer));
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001498 return result;
1499 }
1500}
1501
1502
lrn@chromium.org303ada72010-10-27 09:33:13 +00001503MaybeObject* LiveEdit::PatchFunctionPositions(
ager@chromium.org357bf652010-04-12 11:30:10 +00001504 Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) {
ager@chromium.orgac091b72010-05-05 07:34:42 +00001505 if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001506 return Isolate::Current()->ThrowIllegalOperation();
ager@chromium.orgac091b72010-05-05 07:34:42 +00001507 }
1508
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001509 SharedInfoWrapper shared_info_wrapper(shared_info_array);
1510 Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
1511
ager@chromium.org357bf652010-04-12 11:30:10 +00001512 int old_function_start = info->start_position();
1513 int new_function_start = TranslatePosition(old_function_start,
1514 position_change_array);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001515 int new_function_end = TranslatePosition(info->end_position(),
1516 position_change_array);
1517 int new_function_token_pos =
1518 TranslatePosition(info->function_token_position(), position_change_array);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001519
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001520 info->set_start_position(new_function_start);
1521 info->set_end_position(new_function_end);
1522 info->set_function_token_position(new_function_token_pos);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001523
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001524 HEAP->EnsureHeapIsIterable();
1525
ager@chromium.org357bf652010-04-12 11:30:10 +00001526 if (IsJSFunctionCode(info->code())) {
1527 // Patch relocation info section of the code.
1528 Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
1529 position_change_array);
1530 if (*patched_code != info->code()) {
1531 // Replace all references to the code across the heap. In particular,
1532 // some stubs may refer to this code and this code may be being executed
1533 // on stack (it is safe to substitute the code object on stack, because
1534 // we only change the structure of rinfo and leave instructions
1535 // untouched).
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00001536 ReplaceCodeObject(Handle<Code>(info->code()), patched_code);
ager@chromium.org357bf652010-04-12 11:30:10 +00001537 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001538 }
ager@chromium.orgac091b72010-05-05 07:34:42 +00001539
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001540 return HEAP->undefined_value();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001541}
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001542
ager@chromium.org357bf652010-04-12 11:30:10 +00001543
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001544static Handle<Script> CreateScriptCopy(Handle<Script> original) {
1545 Handle<String> original_source(String::cast(original->source()));
ager@chromium.org357bf652010-04-12 11:30:10 +00001546
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001547 Handle<Script> copy = FACTORY->NewScript(original_source);
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001548
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001549 copy->set_name(original->name());
1550 copy->set_line_offset(original->line_offset());
1551 copy->set_column_offset(original->column_offset());
1552 copy->set_data(original->data());
1553 copy->set_type(original->type());
1554 copy->set_context_data(original->context_data());
1555 copy->set_compilation_type(original->compilation_type());
1556 copy->set_eval_from_shared(original->eval_from_shared());
1557 copy->set_eval_from_instructions_offset(
1558 original->eval_from_instructions_offset());
1559
1560 return copy;
1561}
1562
1563
1564Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script,
1565 Handle<String> new_source,
1566 Handle<Object> old_script_name) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001567 Isolate* isolate = original_script->GetIsolate();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001568 Handle<Object> old_script_object;
1569 if (old_script_name->IsString()) {
1570 Handle<Script> old_script = CreateScriptCopy(original_script);
1571 old_script->set_name(String::cast(*old_script_name));
1572 old_script_object = old_script;
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001573 isolate->debugger()->OnAfterCompile(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001574 old_script, Debugger::SEND_WHEN_DEBUGGING);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001575 } else {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001576 old_script_object = isolate->factory()->null_value();
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001577 }
1578
1579 original_script->set_source(*new_source);
1580
1581 // Drop line ends so that they will be recalculated.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001582 original_script->set_line_ends(HEAP->undefined_value());
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001583
1584 return *old_script_object;
1585}
1586
1587
1588
1589void LiveEdit::ReplaceRefToNestedFunction(
1590 Handle<JSValue> parent_function_wrapper,
1591 Handle<JSValue> orig_function_wrapper,
1592 Handle<JSValue> subst_function_wrapper) {
1593
1594 Handle<SharedFunctionInfo> parent_shared =
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001595 UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001596 Handle<SharedFunctionInfo> orig_shared =
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001597 UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001598 Handle<SharedFunctionInfo> subst_shared =
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001599 UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001600
1601 for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
1602 if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
1603 if (it.rinfo()->target_object() == *orig_shared) {
1604 it.rinfo()->set_target_object(*subst_shared);
ager@chromium.org357bf652010-04-12 11:30:10 +00001605 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001606 }
1607 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001608}
1609
1610
1611// Check an activation against list of functions. If there is a function
1612// that matches, its status in result array is changed to status argument value.
1613static bool CheckActivation(Handle<JSArray> shared_info_array,
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001614 Handle<JSArray> result,
1615 StackFrame* frame,
ager@chromium.org357bf652010-04-12 11:30:10 +00001616 LiveEdit::FunctionPatchabilityStatus status) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001617 if (!frame->is_java_script()) return false;
1618
1619 Handle<JSFunction> function(
1620 JSFunction::cast(JavaScriptFrame::cast(frame)->function()));
1621
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001622 Isolate* isolate = shared_info_array->GetIsolate();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001623 int len = GetArrayLength(shared_info_array);
ager@chromium.org357bf652010-04-12 11:30:10 +00001624 for (int i = 0; i < len; i++) {
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001625 Object* element = shared_info_array->GetElementNoExceptionThrown(i);
1626 CHECK(element->IsJSValue());
1627 Handle<JSValue> jsvalue(JSValue::cast(element));
1628 Handle<SharedFunctionInfo> shared =
1629 UnwrapSharedFunctionInfoFromJSValue(jsvalue);
ager@chromium.org357bf652010-04-12 11:30:10 +00001630
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001631 if (function->shared() == *shared || IsInlined(*function, *shared)) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001632 SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status),
1633 isolate));
ager@chromium.org357bf652010-04-12 11:30:10 +00001634 return true;
1635 }
1636 }
1637 return false;
1638}
1639
1640
1641// Iterates over handler chain and removes all elements that are inside
1642// frames being dropped.
1643static bool FixTryCatchHandler(StackFrame* top_frame,
1644 StackFrame* bottom_frame) {
1645 Address* pointer_address =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001646 &Memory::Address_at(Isolate::Current()->get_address_from_id(
kmillikin@chromium.org83e16822011-09-13 08:21:47 +00001647 Isolate::kHandlerAddress));
ager@chromium.org357bf652010-04-12 11:30:10 +00001648
1649 while (*pointer_address < top_frame->sp()) {
1650 pointer_address = &Memory::Address_at(*pointer_address);
1651 }
1652 Address* above_frame_address = pointer_address;
1653 while (*pointer_address < bottom_frame->fp()) {
1654 pointer_address = &Memory::Address_at(*pointer_address);
1655 }
1656 bool change = *above_frame_address != *pointer_address;
1657 *above_frame_address = *pointer_address;
1658 return change;
1659}
1660
1661
1662// Removes specified range of frames from stack. There may be 1 or more
1663// frames in range. Anyway the bottom frame is restarted rather than dropped,
1664// and therefore has to be a JavaScript frame.
1665// Returns error message or NULL.
1666static const char* DropFrames(Vector<StackFrame*> frames,
1667 int top_frame_index,
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001668 int bottom_js_frame_index,
whesse@chromium.orge90029b2010-08-02 11:52:17 +00001669 Debug::FrameDropMode* mode,
1670 Object*** restarter_frame_function_pointer) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +00001671 if (!Debug::kFrameDropperSupported) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001672 return "Stack manipulations are not supported in this architecture.";
1673 }
1674
ager@chromium.org357bf652010-04-12 11:30:10 +00001675 StackFrame* pre_top_frame = frames[top_frame_index - 1];
1676 StackFrame* top_frame = frames[top_frame_index];
1677 StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
1678
1679 ASSERT(bottom_js_frame->is_java_script());
1680
1681 // Check the nature of the top frame.
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001682 Isolate* isolate = Isolate::Current();
1683 Code* pre_top_frame_code = pre_top_frame->LookupCode();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001684 bool frame_has_padding;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001685 if (pre_top_frame_code->is_inline_cache_stub() &&
yangguo@chromium.org9768bf12013-01-11 14:51:07 +00001686 pre_top_frame_code->is_debug_break()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001687 // OK, we can drop inline cache calls.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001688 *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001689 frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001690 } else if (pre_top_frame_code ==
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001691 isolate->debug()->debug_break_slot()) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001692 // OK, we can drop debug break slot.
1693 *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001694 frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001695 } else if (pre_top_frame_code ==
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001696 isolate->builtins()->builtin(
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001697 Builtins::kFrameDropper_LiveEdit)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001698 // OK, we can drop our own code.
ulan@chromium.orgd9e468a2012-06-25 09:47:40 +00001699 pre_top_frame = frames[top_frame_index - 2];
1700 top_frame = frames[top_frame_index - 1];
1701 *mode = Debug::CURRENTLY_SET_MODE;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001702 frame_has_padding = false;
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001703 } else if (pre_top_frame_code ==
1704 isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
1705 *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001706 frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001707 } else if (pre_top_frame_code->kind() == Code::STUB &&
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001708 pre_top_frame_code->major_key() == CodeStub::CEntry) {
1709 // Entry from our unit tests on 'debugger' statement.
1710 // It's fine, we support this case.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001711 *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001712 // We don't have a padding from 'debugger' statement call.
1713 // Here the stub is CEntry, it's not debug-only and can't be padded.
1714 // If anyone would complain, a proxy padded stub could be added.
1715 frame_has_padding = false;
ulan@chromium.orgd9e468a2012-06-25 09:47:40 +00001716 } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
1717 // This must be adaptor that remain from the frame dropping that
1718 // is still on stack. A frame dropper frame must be above it.
1719 ASSERT(frames[top_frame_index - 2]->LookupCode() ==
1720 isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
1721 pre_top_frame = frames[top_frame_index - 3];
1722 top_frame = frames[top_frame_index - 2];
1723 *mode = Debug::CURRENTLY_SET_MODE;
1724 frame_has_padding = false;
ager@chromium.org357bf652010-04-12 11:30:10 +00001725 } else {
1726 return "Unknown structure of stack above changing function";
1727 }
1728
1729 Address unused_stack_top = top_frame->sp();
1730 Address unused_stack_bottom = bottom_js_frame->fp()
1731 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
1732 + kPointerSize; // Bigger address end is exclusive.
1733
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001734 Address* top_frame_pc_address = top_frame->pc_address();
1735
1736 // top_frame may be damaged below this point. Do not used it.
1737 ASSERT(!(top_frame = NULL));
1738
ager@chromium.org357bf652010-04-12 11:30:10 +00001739 if (unused_stack_top > unused_stack_bottom) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001740 if (frame_has_padding) {
1741 int shortage_bytes =
1742 static_cast<int>(unused_stack_top - unused_stack_bottom);
1743
1744 Address padding_start = pre_top_frame->fp() -
1745 Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize;
1746
1747 Address padding_pointer = padding_start;
1748 Smi* padding_object =
1749 Smi::FromInt(Debug::FramePaddingLayout::kPaddingValue);
1750 while (Memory::Object_at(padding_pointer) == padding_object) {
1751 padding_pointer -= kPointerSize;
1752 }
1753 int padding_counter =
1754 Smi::cast(Memory::Object_at(padding_pointer))->value();
1755 if (padding_counter * kPointerSize < shortage_bytes) {
1756 return "Not enough space for frame dropper frame "
1757 "(even with padding frame)";
1758 }
1759 Memory::Object_at(padding_pointer) =
1760 Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
1761
1762 StackFrame* pre_pre_frame = frames[top_frame_index - 2];
1763
1764 memmove(padding_start + kPointerSize - shortage_bytes,
1765 padding_start + kPointerSize,
1766 Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize);
1767
1768 pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
1769 pre_pre_frame->SetCallerFp(pre_top_frame->fp());
1770 unused_stack_top -= shortage_bytes;
1771
1772 STATIC_ASSERT(sizeof(Address) == kPointerSize);
1773 top_frame_pc_address -= shortage_bytes / kPointerSize;
1774 } else {
1775 return "Not enough space for frame dropper frame";
1776 }
ager@chromium.org357bf652010-04-12 11:30:10 +00001777 }
1778
1779 // Committing now. After this point we should return only NULL value.
1780
1781 FixTryCatchHandler(pre_top_frame, bottom_js_frame);
1782 // Make sure FixTryCatchHandler is idempotent.
1783 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
1784
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +00001785 Handle<Code> code = Isolate::Current()->builtins()->FrameDropper_LiveEdit();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001786 *top_frame_pc_address = code->entry();
ager@chromium.org357bf652010-04-12 11:30:10 +00001787 pre_top_frame->SetCallerFp(bottom_js_frame->fp());
1788
whesse@chromium.orge90029b2010-08-02 11:52:17 +00001789 *restarter_frame_function_pointer =
1790 Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
1791
1792 ASSERT((**restarter_frame_function_pointer)->IsJSFunction());
ager@chromium.org357bf652010-04-12 11:30:10 +00001793
1794 for (Address a = unused_stack_top;
1795 a < unused_stack_bottom;
1796 a += kPointerSize) {
1797 Memory::Object_at(a) = Smi::FromInt(0);
1798 }
1799
1800 return NULL;
1801}
1802
1803
1804static bool IsDropableFrame(StackFrame* frame) {
1805 return !frame->is_exit();
1806}
1807
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001808
1809// Describes a set of call frames that execute any of listed functions.
1810// Finding no such frames does not mean error.
1811class MultipleFunctionTarget {
1812 public:
1813 MultipleFunctionTarget(Handle<JSArray> shared_info_array,
1814 Handle<JSArray> result)
1815 : m_shared_info_array(shared_info_array),
1816 m_result(result) {}
1817 bool MatchActivation(StackFrame* frame,
1818 LiveEdit::FunctionPatchabilityStatus status) {
1819 return CheckActivation(m_shared_info_array, m_result, frame, status);
1820 }
1821 const char* GetNotFoundMessage() {
1822 return NULL;
1823 }
1824 private:
1825 Handle<JSArray> m_shared_info_array;
1826 Handle<JSArray> m_result;
1827};
1828
1829// Drops all call frame matched by target and all frames above them.
1830template<typename TARGET>
1831static const char* DropActivationsInActiveThreadImpl(
1832 TARGET& target, bool do_drop, Zone* zone) {
danno@chromium.org40cb8782011-05-25 07:58:50 +00001833 Isolate* isolate = Isolate::Current();
1834 Debug* debug = isolate->debug();
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001835 ZoneScope scope(zone, DELETE_ON_EXIT);
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +00001836 Vector<StackFrame*> frames = CreateStackMap(isolate, zone);
ager@chromium.org357bf652010-04-12 11:30:10 +00001837
ager@chromium.org357bf652010-04-12 11:30:10 +00001838
1839 int top_frame_index = -1;
1840 int frame_index = 0;
1841 for (; frame_index < frames.length(); frame_index++) {
1842 StackFrame* frame = frames[frame_index];
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001843 if (frame->id() == debug->break_frame_id()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001844 top_frame_index = frame_index;
1845 break;
1846 }
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001847 if (target.MatchActivation(
1848 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001849 // We are still above break_frame. It is not a target frame,
1850 // it is a problem.
1851 return "Debugger mark-up on stack is not found";
1852 }
1853 }
1854
1855 if (top_frame_index == -1) {
1856 // We haven't found break frame, but no function is blocking us anyway.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001857 return target.GetNotFoundMessage();
ager@chromium.org357bf652010-04-12 11:30:10 +00001858 }
1859
1860 bool target_frame_found = false;
1861 int bottom_js_frame_index = top_frame_index;
1862 bool c_code_found = false;
1863
1864 for (; frame_index < frames.length(); frame_index++) {
1865 StackFrame* frame = frames[frame_index];
1866 if (!IsDropableFrame(frame)) {
1867 c_code_found = true;
1868 break;
1869 }
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001870 if (target.MatchActivation(
1871 frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001872 target_frame_found = true;
1873 bottom_js_frame_index = frame_index;
1874 }
1875 }
1876
1877 if (c_code_found) {
1878 // There is a C frames on stack. Check that there are no target frames
1879 // below them.
1880 for (; frame_index < frames.length(); frame_index++) {
1881 StackFrame* frame = frames[frame_index];
1882 if (frame->is_java_script()) {
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001883 if (target.MatchActivation(
1884 frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001885 // Cannot drop frame under C frames.
1886 return NULL;
1887 }
1888 }
1889 }
1890 }
1891
1892 if (!do_drop) {
1893 // We are in check-only mode.
1894 return NULL;
1895 }
1896
1897 if (!target_frame_found) {
1898 // Nothing to drop.
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001899 return target.GetNotFoundMessage();
ager@chromium.org357bf652010-04-12 11:30:10 +00001900 }
1901
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001902 Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
whesse@chromium.orge90029b2010-08-02 11:52:17 +00001903 Object** restarter_frame_function_pointer = NULL;
ager@chromium.org357bf652010-04-12 11:30:10 +00001904 const char* error_message = DropFrames(frames, top_frame_index,
whesse@chromium.orge90029b2010-08-02 11:52:17 +00001905 bottom_js_frame_index, &drop_mode,
1906 &restarter_frame_function_pointer);
ager@chromium.org357bf652010-04-12 11:30:10 +00001907
1908 if (error_message != NULL) {
1909 return error_message;
1910 }
1911
1912 // Adjust break_frame after some frames has been dropped.
1913 StackFrame::Id new_id = StackFrame::NO_ID;
1914 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
1915 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
1916 new_id = frames[i]->id();
1917 break;
1918 }
1919 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001920 debug->FramesHaveBeenDropped(new_id, drop_mode,
whesse@chromium.orge90029b2010-08-02 11:52:17 +00001921 restarter_frame_function_pointer);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00001922 return NULL;
1923}
1924
1925// Fills result array with statuses of functions. Modifies the stack
1926// removing all listed function if possible and if do_drop is true.
1927static const char* DropActivationsInActiveThread(
1928 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop,
1929 Zone* zone) {
1930 MultipleFunctionTarget target(shared_info_array, result);
1931
1932 const char* message =
1933 DropActivationsInActiveThreadImpl(target, do_drop, zone);
1934 if (message) {
1935 return message;
1936 }
1937
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001938 Isolate* isolate = shared_info_array->GetIsolate();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001939 int array_len = GetArrayLength(shared_info_array);
ager@chromium.org357bf652010-04-12 11:30:10 +00001940
1941 // Replace "blocked on active" with "replaced on active" status.
1942 for (int i = 0; i < array_len; i++) {
1943 if (result->GetElement(i) ==
1944 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +00001945 Handle<Object> replaced(
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001946 Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001947 SetElementNonStrict(result, i, replaced);
ager@chromium.org357bf652010-04-12 11:30:10 +00001948 }
1949 }
1950 return NULL;
1951}
1952
1953
1954class InactiveThreadActivationsChecker : public ThreadVisitor {
1955 public:
1956 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
1957 Handle<JSArray> result)
1958 : shared_info_array_(shared_info_array), result_(result),
1959 has_blocked_functions_(false) {
1960 }
vegorov@chromium.org74f333b2011-04-06 11:17:46 +00001961 void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
1962 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001963 has_blocked_functions_ |= CheckActivation(
1964 shared_info_array_, result_, it.frame(),
1965 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
1966 }
1967 }
1968 bool HasBlockedFunctions() {
1969 return has_blocked_functions_;
1970 }
1971
1972 private:
1973 Handle<JSArray> shared_info_array_;
1974 Handle<JSArray> result_;
1975 bool has_blocked_functions_;
1976};
1977
1978
1979Handle<JSArray> LiveEdit::CheckAndDropActivations(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001980 Handle<JSArray> shared_info_array, bool do_drop, Zone* zone) {
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001981 Isolate* isolate = shared_info_array->GetIsolate();
ulan@chromium.org56c14af2012-09-20 12:51:09 +00001982 int len = GetArrayLength(shared_info_array);
ager@chromium.org357bf652010-04-12 11:30:10 +00001983
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001984 Handle<JSArray> result = isolate->factory()->NewJSArray(len);
ager@chromium.org357bf652010-04-12 11:30:10 +00001985
1986 // Fill the default values.
1987 for (int i = 0; i < len; i++) {
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00001988 SetElementNonStrict(
1989 result,
1990 i,
ulan@chromium.org09d7ab52013-02-25 15:50:35 +00001991 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH), isolate));
ager@chromium.org357bf652010-04-12 11:30:10 +00001992 }
1993
1994
1995 // First check inactive threads. Fail if some functions are blocked there.
1996 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
1997 result);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001998 Isolate::Current()->thread_manager()->IterateArchivedThreads(
1999 &inactive_threads_checker);
ager@chromium.org357bf652010-04-12 11:30:10 +00002000 if (inactive_threads_checker.HasBlockedFunctions()) {
2001 return result;
2002 }
2003
2004 // Try to drop activations from the current stack.
2005 const char* error_message =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002006 DropActivationsInActiveThread(shared_info_array, result, do_drop, zone);
ager@chromium.org357bf652010-04-12 11:30:10 +00002007 if (error_message != NULL) {
2008 // Add error message as an array extra element.
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +00002009 Vector<const char> vector_message(error_message, StrLength(error_message));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002010 Handle<String> str = FACTORY->NewStringFromAscii(vector_message);
danno@chromium.org4d3fe4e2011-03-10 10:14:28 +00002011 SetElementNonStrict(result, len, str);
ager@chromium.org357bf652010-04-12 11:30:10 +00002012 }
2013 return result;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002014}
2015
2016
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002017// Describes a single callframe a target. Not finding this frame
2018// means an error.
2019class SingleFrameTarget {
2020 public:
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00002021 explicit SingleFrameTarget(JavaScriptFrame* frame)
2022 : m_frame(frame),
2023 m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002024
2025 bool MatchActivation(StackFrame* frame,
2026 LiveEdit::FunctionPatchabilityStatus status) {
2027 if (frame->fp() == m_frame->fp()) {
2028 m_saved_status = status;
2029 return true;
2030 }
2031 return false;
2032 }
2033 const char* GetNotFoundMessage() {
2034 return "Failed to found requested frame";
2035 }
2036 LiveEdit::FunctionPatchabilityStatus saved_status() {
2037 return m_saved_status;
2038 }
2039 private:
2040 JavaScriptFrame* m_frame;
2041 LiveEdit::FunctionPatchabilityStatus m_saved_status;
2042};
2043
2044
2045// Finds a drops required frame and all frames above.
2046// Returns error message or NULL.
2047const char* LiveEdit::RestartFrame(JavaScriptFrame* frame, Zone* zone) {
2048 SingleFrameTarget target(frame);
2049
jkummerow@chromium.org28583c92012-07-16 11:31:55 +00002050 const char* result = DropActivationsInActiveThreadImpl(target, true, zone);
2051 if (result != NULL) {
2052 return result;
2053 }
2054 if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
2055 return "Function is blocked under native code";
2056 }
2057 return NULL;
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +00002058}
2059
2060
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002061LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
2062 FunctionLiteral* fun)
2063 : isolate_(isolate) {
2064 if (isolate_->active_function_info_listener() != NULL) {
2065 isolate_->active_function_info_listener()->FunctionStarted(fun);
ager@chromium.org5c838252010-02-19 08:53:10 +00002066 }
2067}
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002068
2069
ager@chromium.org5c838252010-02-19 08:53:10 +00002070LiveEditFunctionTracker::~LiveEditFunctionTracker() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002071 if (isolate_->active_function_info_listener() != NULL) {
2072 isolate_->active_function_info_listener()->FunctionDone();
ager@chromium.org5c838252010-02-19 08:53:10 +00002073 }
2074}
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002075
2076
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00002077void LiveEditFunctionTracker::RecordFunctionInfo(
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002078 Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
2079 Zone* zone) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002080 if (isolate_->active_function_info_listener() != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00002081 isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(),
2082 zone);
ager@chromium.org5c838252010-02-19 08:53:10 +00002083 }
2084}
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002085
2086
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00002087void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002088 isolate_->active_function_info_listener()->FunctionCode(code);
ager@chromium.org5c838252010-02-19 08:53:10 +00002089}
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002090
2091
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002092bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
2093 return isolate->active_function_info_listener() != NULL;
ager@chromium.org5c838252010-02-19 08:53:10 +00002094}
2095
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002096
2097#else // ENABLE_DEBUGGER_SUPPORT
2098
2099// This ifdef-else-endif section provides working or stub implementation of
2100// LiveEditFunctionTracker.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002101LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
2102 FunctionLiteral* fun) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002103}
2104
2105
2106LiveEditFunctionTracker::~LiveEditFunctionTracker() {
2107}
2108
2109
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00002110void LiveEditFunctionTracker::RecordFunctionInfo(
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00002111 Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
2112 Zone* zone) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002113}
2114
2115
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00002116void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002117}
2118
2119
erik.corry@gmail.com3847bd52011-04-27 10:38:56 +00002120bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002121 return false;
2122}
2123
2124#endif // ENABLE_DEBUGGER_SUPPORT
2125
2126
2127
ager@chromium.org5c838252010-02-19 08:53:10 +00002128} } // namespace v8::internal