LCOV - code coverage report
Current view: top level - test/cctest - test-liveedit.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 199 199 100.0 %
Date: 2019-04-17 Functions: 11 11 100.0 %

          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

Generated by: LCOV version 1.10