Line data Source code
1 : // Copyright 2007-2008 the V8 project authors. All rights reserved.
2 : // 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 : #include <stdlib.h>
29 :
30 : #include "src/v8.h"
31 :
32 : #include "src/api-inl.h"
33 : #include "src/debug/liveedit.h"
34 : #include "src/objects-inl.h"
35 : #include "test/cctest/cctest.h"
36 :
37 : namespace v8 {
38 : namespace internal {
39 : namespace {
40 450 : void CompareStringsOneWay(const char* s1, const char* s2,
41 : int expected_diff_parameter,
42 : std::vector<SourceChangeRange>* changes) {
43 : i::Isolate* isolate = CcTest::i_isolate();
44 450 : i::Handle<i::String> i_s1 = isolate->factory()->NewStringFromAsciiChecked(s1);
45 450 : i::Handle<i::String> i_s2 = isolate->factory()->NewStringFromAsciiChecked(s2);
46 : changes->clear();
47 450 : LiveEdit::CompareStrings(isolate, i_s1, i_s2, changes);
48 :
49 : int len1 = StrLength(s1);
50 : int len2 = StrLength(s2);
51 :
52 : int pos1 = 0;
53 : int pos2 = 0;
54 :
55 : int diff_parameter = 0;
56 1610 : for (const auto& diff : *changes) {
57 1160 : int diff_pos1 = diff.start_position;
58 1160 : int similar_part_length = diff_pos1 - pos1;
59 1160 : int diff_pos2 = pos2 + similar_part_length;
60 :
61 1160 : CHECK_EQ(diff_pos2, diff.new_start_position);
62 :
63 12600 : for (int j = 0; j < similar_part_length; j++) {
64 5720 : CHECK(pos1 + j < len1);
65 5720 : CHECK(pos2 + j < len2);
66 5720 : CHECK_EQ(s1[pos1 + j], s2[pos2 + j]);
67 : }
68 1160 : int diff_len1 = diff.end_position - diff.start_position;
69 1160 : int diff_len2 = diff.new_end_position - diff.new_start_position;
70 1160 : diff_parameter += diff_len1 + diff_len2;
71 : pos1 = diff_pos1 + diff_len1;
72 1160 : pos2 = diff_pos2 + diff_len2;
73 : }
74 : {
75 : // After last chunk.
76 450 : int similar_part_length = len1 - pos1;
77 450 : CHECK_EQ(similar_part_length, len2 - pos2);
78 : USE(len2);
79 4610 : for (int j = 0; j < similar_part_length; j++) {
80 2080 : CHECK(pos1 + j < len1);
81 2080 : CHECK(pos2 + j < len2);
82 2080 : CHECK_EQ(s1[pos1 + j], s2[pos2 + j]);
83 : }
84 : }
85 :
86 450 : if (expected_diff_parameter != -1) {
87 130 : CHECK_EQ(expected_diff_parameter, diff_parameter);
88 : }
89 450 : }
90 :
91 420 : void CompareStringsOneWay(const char* s1, const char* s2,
92 : int expected_diff_parameter = -1) {
93 : std::vector<SourceChangeRange> changes;
94 420 : CompareStringsOneWay(s1, s2, expected_diff_parameter, &changes);
95 420 : }
96 :
97 : void CompareStringsOneWay(const char* s1, const char* s2,
98 : std::vector<SourceChangeRange>* changes) {
99 30 : CompareStringsOneWay(s1, s2, -1, changes);
100 : }
101 :
102 : void CompareStrings(const char* s1, const char* s2,
103 : int expected_diff_parameter = -1) {
104 70 : CompareStringsOneWay(s1, s2, expected_diff_parameter);
105 70 : CompareStringsOneWay(s2, s1, expected_diff_parameter);
106 : }
107 :
108 70 : void CompareOneWayPlayWithLF(const char* s1, const char* s2) {
109 70 : std::string s1_one_line(s1);
110 : std::replace(s1_one_line.begin(), s1_one_line.end(), '\n', ' ');
111 70 : std::string s2_one_line(s2);
112 : std::replace(s2_one_line.begin(), s2_one_line.end(), '\n', ' ');
113 70 : CompareStringsOneWay(s1, s2, -1);
114 70 : CompareStringsOneWay(s1_one_line.c_str(), s2, -1);
115 70 : CompareStringsOneWay(s1, s2_one_line.c_str(), -1);
116 70 : CompareStringsOneWay(s1_one_line.c_str(), s2_one_line.c_str(), -1);
117 70 : }
118 :
119 : void CompareStringsPlayWithLF(const char* s1, const char* s2) {
120 35 : CompareOneWayPlayWithLF(s1, s2);
121 35 : CompareOneWayPlayWithLF(s2, s1);
122 : }
123 : } // anonymous namespace
124 :
125 26644 : TEST(LiveEditDiffer) {
126 10 : v8::HandleScope handle_scope(CcTest::isolate());
127 : CompareStrings("zz1zzz12zz123zzz", "zzzzzzzzzz", 6);
128 : CompareStrings("zz1zzz12zz123zzz", "zz0zzz0zz0zzz", 9);
129 : CompareStrings("123456789", "987654321", 16);
130 : CompareStrings("zzz", "yyy", 6);
131 : CompareStrings("zzz", "zzz12", 2);
132 : CompareStrings("zzz", "21zzz", 2);
133 : CompareStrings("cat", "cut", 2);
134 : CompareStrings("ct", "cut", 1);
135 : CompareStrings("cat", "ct", 1);
136 : CompareStrings("cat", "cat", 0);
137 : CompareStrings("", "", 0);
138 : CompareStrings("cat", "", 3);
139 : CompareStrings("a cat", "a capybara", 7);
140 : CompareStrings("abbabababababaaabbabababababbabbbbbbbababa",
141 : "bbbbabababbbabababbbabababababbabbababa");
142 : CompareStringsPlayWithLF("", "");
143 : CompareStringsPlayWithLF("a", "b");
144 : CompareStringsPlayWithLF(
145 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway",
146 : "yesterday\nall\nmy\ntroubles\nseem\nso\nfar\naway");
147 : CompareStringsPlayWithLF(
148 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway",
149 : "\nall\nmy\ntroubles\nseemed\nso\nfar\naway");
150 : CompareStringsPlayWithLF(
151 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway",
152 : "all\nmy\ntroubles\nseemed\nso\nfar\naway");
153 : CompareStringsPlayWithLF(
154 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway",
155 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway\n");
156 : CompareStringsPlayWithLF(
157 : "yesterday\nall\nmy\ntroubles\nseemed\nso\nfar\naway",
158 : "yesterday\nall\nmy\ntroubles\nseemed\nso\n");
159 5 : }
160 :
161 26644 : TEST(LiveEditTranslatePosition) {
162 10 : v8::HandleScope handle_scope(CcTest::isolate());
163 : std::vector<SourceChangeRange> changes;
164 : CompareStringsOneWay("a", "a", &changes);
165 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
166 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 1);
167 : CompareStringsOneWay("a", "b", &changes);
168 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
169 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 1);
170 : CompareStringsOneWay("ababa", "aaa", &changes);
171 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
172 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 1);
173 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 2), 1);
174 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 3), 2);
175 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 4), 2);
176 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 5), 3);
177 : CompareStringsOneWay("ababa", "acaca", &changes);
178 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
179 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 1);
180 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 2), 2);
181 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 3), 3);
182 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 4), 4);
183 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 5), 5);
184 : CompareStringsOneWay("aaa", "ababa", &changes);
185 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
186 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 2);
187 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 2), 4);
188 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 3), 5);
189 : CompareStringsOneWay("aabbaaaa", "aaaabbaa", &changes);
190 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 0), 0);
191 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 1), 1);
192 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 2), 4);
193 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 3), 5);
194 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 4), 6);
195 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 5), 7);
196 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 6), 8);
197 5 : CHECK_EQ(LiveEdit::TranslatePosition(changes, 8), 8);
198 5 : }
199 :
200 : namespace {
201 150 : void PatchFunctions(v8::Local<v8::Context> context, const char* source_a,
202 : const char* source_b,
203 : v8::debug::LiveEditResult* result = nullptr) {
204 150 : v8::Isolate* isolate = context->GetIsolate();
205 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
206 150 : v8::EscapableHandleScope scope(isolate);
207 : v8::Local<v8::Script> script_a =
208 150 : v8::Script::Compile(context, v8_str(isolate, source_a)).ToLocalChecked();
209 150 : script_a->Run(context).ToLocalChecked();
210 : i::Handle<i::Script> i_script_a(
211 300 : i::Script::cast(v8::Utils::OpenHandle(*script_a)->shared()->script()),
212 150 : i_isolate);
213 :
214 150 : if (result) {
215 10 : LiveEdit::PatchScript(
216 : i_isolate, i_script_a,
217 : i_isolate->factory()->NewStringFromAsciiChecked(source_b), false,
218 10 : result);
219 10 : if (result->status == v8::debug::LiveEditResult::COMPILE_ERROR) {
220 10 : result->message = scope.Escape(result->message);
221 : }
222 : } else {
223 : v8::debug::LiveEditResult result;
224 140 : LiveEdit::PatchScript(
225 : i_isolate, i_script_a,
226 : i_isolate->factory()->NewStringFromAsciiChecked(source_b), false,
227 140 : &result);
228 140 : CHECK_EQ(result.status, v8::debug::LiveEditResult::OK);
229 : }
230 150 : }
231 : } // anonymous namespace
232 :
233 26644 : TEST(LiveEditPatchFunctions) {
234 5 : LocalContext env;
235 10 : v8::HandleScope scope(env->GetIsolate());
236 5 : v8::Local<v8::Context> context = env.local();
237 : // Check that function is removed from compilation cache.
238 5 : i::FLAG_allow_natives_syntax = true;
239 5 : PatchFunctions(context, "42;", "%AbortJS('')");
240 5 : PatchFunctions(context, "42;", "239;");
241 5 : i::FLAG_allow_natives_syntax = false;
242 :
243 : // Basic test cases.
244 5 : PatchFunctions(context, "42;", "2;");
245 5 : PatchFunctions(context, "42;", " 42;");
246 5 : PatchFunctions(context, "42;", "42;");
247 : // Trivial return value change.
248 : PatchFunctions(context, "function foo() { return 1; }",
249 5 : "function foo() { return 42; }");
250 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
251 : ->ToInt32(context)
252 : .ToLocalChecked()
253 : ->Value(),
254 : 42);
255 : // It is expected, we do not reevaluate top level function.
256 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
257 5 : "var a = 3; function foo() { return a; }");
258 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
259 : ->ToInt32(context)
260 : .ToLocalChecked()
261 : ->Value(),
262 : 1);
263 : // Throw exception since var b is not defined in original source.
264 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
265 5 : "var b = 4; function foo() { return b; }");
266 : {
267 10 : v8::TryCatch try_catch(env->GetIsolate());
268 : CompileRun("foo()");
269 5 : CHECK(try_catch.HasCaught());
270 : }
271 : // But user always can add new variable to function and use it.
272 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
273 5 : "var b = 4; function foo() { var b = 5; return b; }");
274 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
275 : ->ToInt32(context)
276 : .ToLocalChecked()
277 : ->Value(),
278 : 5);
279 :
280 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
281 5 : "var b = 4; function foo() { var a = 6; return a; }");
282 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
283 : ->ToInt32(context)
284 : .ToLocalChecked()
285 : ->Value(),
286 : 6);
287 :
288 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
289 5 : "var d = (() => ({a:2}))(); function foo() { return d; }");
290 : {
291 10 : v8::TryCatch try_catch(env->GetIsolate());
292 : CompileRun("foo()");
293 5 : CHECK(try_catch.HasCaught());
294 : }
295 :
296 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
297 5 : "var b = 1; var a = 2; function foo() { return a; }");
298 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
299 : ->ToInt32(context)
300 : .ToLocalChecked()
301 : ->Value(),
302 : 1);
303 :
304 : PatchFunctions(context, "var a = 1; function foo() { return a; }",
305 5 : "var b = 1; var a = 2; function foo() { return b; }");
306 : {
307 10 : v8::TryCatch try_catch(env->GetIsolate());
308 : CompileRun("foo()");
309 5 : CHECK(try_catch.HasCaught());
310 : }
311 :
312 : PatchFunctions(context, "function foo() { var a = 1; return a; }",
313 5 : "function foo() { var b = 1; return b; }");
314 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
315 : ->ToInt32(context)
316 : .ToLocalChecked()
317 : ->Value(),
318 : 1);
319 :
320 : PatchFunctions(context, "var a = 3; function foo() { var a = 1; return a; }",
321 5 : "function foo() { var b = 1; return a; }");
322 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
323 : ->ToInt32(context)
324 : .ToLocalChecked()
325 : ->Value(),
326 : 3);
327 :
328 : PatchFunctions(context, "var a = 3; var c = 7; function foo() { return a; }",
329 5 : "var b = 5; var a = 3; function foo() { return b; }");
330 : {
331 10 : v8::TryCatch try_catch(env->GetIsolate());
332 : CompileRun("foo()");
333 5 : CHECK(try_catch.HasCaught());
334 : }
335 :
336 : // Add argument.
337 : PatchFunctions(context, "function fooArgs(a1, b1) { return a1 + b1; }",
338 5 : "function fooArgs(a2, b2, c2) { return a2 + b2 + c2; }");
339 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "fooArgs(1,2,3)")
340 : ->ToInt32(context)
341 : .ToLocalChecked()
342 : ->Value(),
343 : 6);
344 :
345 : PatchFunctions(context, "function fooArgs(a1, b1) { return a1 + b1; }",
346 5 : "function fooArgs(a1, b1, c1) { return a1 + b1 + c1; }");
347 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "fooArgs(1,2,3)")
348 : ->ToInt32(context)
349 : .ToLocalChecked()
350 : ->Value(),
351 : 6);
352 :
353 5 : i::FLAG_allow_natives_syntax = true;
354 : PatchFunctions(context,
355 : "function foo(a, b) { return a + b; }; "
356 : "%OptimizeFunctionOnNextCall(foo); foo(1,2);",
357 5 : "function foo(a, b) { return a * b; };");
358 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo(5,7)")
359 : ->ToInt32(context)
360 : .ToLocalChecked()
361 : ->Value(),
362 : 35);
363 5 : i::FLAG_allow_natives_syntax = false;
364 :
365 : // Check inner function.
366 : PatchFunctions(
367 : context,
368 : "function foo(a,b) { function op(a,b) { return a + b } return op(a,b); }",
369 : "function foo(a,b) { function op(a,b) { return a * b } return op(a,b); "
370 5 : "}");
371 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo(8,9)")
372 : ->ToInt32(context)
373 : .ToLocalChecked()
374 : ->Value(),
375 : 72);
376 :
377 : // Update constructor.
378 : PatchFunctions(context,
379 : "class Foo { constructor(a,b) { this.data = a + b; } };",
380 5 : "class Foo { constructor(a,b) { this.data = a * b; } };");
381 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "new Foo(4,5).data")
382 : ->ToInt32(context)
383 : .ToLocalChecked()
384 : ->Value(),
385 : 20);
386 : // Change inner functions.
387 : PatchFunctions(
388 : context,
389 : "function f(evt) { function f2() {} f2(),f3(); function f3() {} } "
390 : "function f4() {}",
391 : "function f(evt) { function f2() { return 1; } return f2() + f3(); "
392 5 : "function f3() { return 2; } } function f4() {}");
393 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
394 : ->ToInt32(context)
395 : .ToLocalChecked()
396 : ->Value(),
397 : 3);
398 : // Change usage of outer scope.
399 : PatchFunctions(context,
400 : "function ChooseAnimal(a, b) {\n "
401 : " if (a == 7 && b == 7) {\n"
402 : " return;\n"
403 : " }\n"
404 : " return function Chooser() {\n"
405 : " return 'Cat' + a;\n"
406 : " };\n"
407 : "}\n"
408 : "var old_closure = ChooseAnimal(2, 3);",
409 : "function ChooseAnimal(a, b) {\n "
410 : " if (a == 7 && b == 7) {\n"
411 : " return;\n"
412 : " }\n"
413 : " return function Chooser() {\n"
414 : " return 'Capybara' + b;\n"
415 : " };\n"
416 5 : "}\n");
417 5 : CompileRunChecked(env->GetIsolate(), "var new_closure = ChooseAnimal(3, 4);");
418 : v8::Local<v8::String> call_result =
419 5 : CompileRunChecked(env->GetIsolate(), "new_closure()").As<v8::String>();
420 10 : v8::String::Utf8Value new_result_utf8(env->GetIsolate(), call_result);
421 5 : CHECK_NOT_NULL(strstr(*new_result_utf8, "Capybara4"));
422 : call_result =
423 5 : CompileRunChecked(env->GetIsolate(), "old_closure()").As<v8::String>();
424 10 : v8::String::Utf8Value old_result_utf8(env->GetIsolate(), call_result);
425 5 : CHECK_NOT_NULL(strstr(*old_result_utf8, "Cat2"));
426 :
427 : // Update const literals.
428 : PatchFunctions(context, "function foo() { return 'a' + 'b'; }",
429 5 : "function foo() { return 'c' + 'b'; }");
430 : {
431 : v8::Local<v8::String> result =
432 5 : CompileRunChecked(env->GetIsolate(), "foo()").As<v8::String>();
433 10 : v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
434 5 : CHECK_NOT_NULL(strstr(*new_result_utf8, "cb"));
435 : }
436 :
437 : // TODO(kozyatinskiy): should work when we remove (.
438 5 : PatchFunctions(context, "f = () => 2", "f = a => a");
439 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f(3)")
440 : ->ToInt32(context)
441 : .ToLocalChecked()
442 : ->Value(),
443 : 2);
444 :
445 : // Replace function with not a function.
446 5 : PatchFunctions(context, "f = () => 2", "f = a == 2");
447 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f(3)")
448 : ->ToInt32(context)
449 : .ToLocalChecked()
450 : ->Value(),
451 : 2);
452 :
453 : // TODO(kozyatinskiy): should work when we put function into (...).
454 5 : PatchFunctions(context, "f = a => 2", "f = (a => 5)()");
455 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
456 : ->ToInt32(context)
457 : .ToLocalChecked()
458 : ->Value(),
459 : 2);
460 :
461 : PatchFunctions(context,
462 : "f2 = null;\n"
463 : "f = () => {\n"
464 : " f2 = () => 5;\n"
465 : " return f2();\n"
466 : "}\n"
467 : "f()\n",
468 : "f2 = null;\n"
469 : "f = () => {\n"
470 : " for (var a = (() => 7)(), b = 0; a < 10; ++a,++b);\n"
471 : " return b;\n"
472 : "}\n"
473 5 : "f()\n");
474 : // TODO(kozyatinskiy): ditto.
475 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f2()")
476 : ->ToInt32(context)
477 : .ToLocalChecked()
478 : ->Value(),
479 : 5);
480 15 : CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
481 : ->ToInt32(context)
482 : .ToLocalChecked()
483 : ->Value(),
484 : 3);
485 5 : }
486 :
487 26644 : TEST(LiveEditCompileError) {
488 5 : LocalContext env;
489 10 : v8::HandleScope scope(env->GetIsolate());
490 5 : v8::Local<v8::Context> context = env.local();
491 : debug::LiveEditResult result;
492 : PatchFunctions(
493 : context,
494 : "var something1 = 25; \n"
495 : " function ChooseAnimal() { return 'Cat'; } \n"
496 : " ChooseAnimal.Helper = function() { return 'Help!'; }\n",
497 : "var something1 = 25; \n"
498 : " function ChooseAnimal() { return 'Cap' + ) + 'bara'; "
499 : "} \n"
500 : " ChooseAnimal.Helper = function() { return 'Help!'; }\n",
501 5 : &result);
502 5 : CHECK_EQ(result.status, debug::LiveEditResult::COMPILE_ERROR);
503 5 : CHECK_EQ(result.line_number, 2);
504 5 : CHECK_EQ(result.column_number, 51);
505 10 : v8::String::Utf8Value result_message(env->GetIsolate(), result.message);
506 5 : CHECK_NOT_NULL(
507 : strstr(*result_message, "Uncaught SyntaxError: Unexpected token )"));
508 :
509 : {
510 : v8::Local<v8::String> result =
511 5 : CompileRunChecked(env->GetIsolate(), "ChooseAnimal()").As<v8::String>();
512 10 : v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
513 5 : CHECK_NOT_NULL(strstr(*new_result_utf8, "Cat"));
514 : }
515 :
516 : PatchFunctions(context, "function foo() {}",
517 5 : "function foo() { return a # b; }", &result);
518 5 : CHECK_EQ(result.status, debug::LiveEditResult::COMPILE_ERROR);
519 5 : CHECK_EQ(result.line_number, 1);
520 5 : CHECK_EQ(result.column_number, 26);
521 5 : }
522 :
523 26644 : TEST(LiveEditFunctionExpression) {
524 : const char* original_source =
525 : "(function() {\n "
526 : " return 'Cat';\n"
527 : "})\n";
528 : const char* updated_source =
529 : "(function() {\n "
530 : " return 'Capy' + 'bara';\n"
531 : "})\n";
532 5 : LocalContext env;
533 10 : v8::HandleScope scope(env->GetIsolate());
534 : v8::Local<v8::Context> context = env.local();
535 5 : v8::Isolate* isolate = context->GetIsolate();
536 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
537 : v8::Local<v8::Script> script =
538 5 : v8::Script::Compile(context, v8_str(isolate, original_source))
539 : .ToLocalChecked();
540 : v8::Local<v8::Function> f =
541 5 : script->Run(context).ToLocalChecked().As<v8::Function>();
542 : i::Handle<i::Script> i_script(
543 10 : i::Script::cast(v8::Utils::OpenHandle(*script)->shared()->script()),
544 5 : i_isolate);
545 : debug::LiveEditResult result;
546 5 : LiveEdit::PatchScript(
547 : i_isolate, i_script,
548 : i_isolate->factory()->NewStringFromAsciiChecked(updated_source), false,
549 5 : &result);
550 5 : CHECK_EQ(result.status, debug::LiveEditResult::OK);
551 : {
552 : v8::Local<v8::String> result =
553 15 : f->Call(context, context->Global(), 0, nullptr)
554 : .ToLocalChecked()
555 : .As<v8::String>();
556 10 : v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
557 5 : CHECK_NOT_NULL(strstr(*new_result_utf8, "Capybara"));
558 : }
559 5 : }
560 : } // namespace internal
561 79917 : } // namespace v8
|