debug-async-liveedit.js 4.15 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// Flags: --allow-natives-syntax

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
var Debug = debug.Debug;

unique_id = 0;

var AsyncFunction = (async function(){}).constructor;

function assertPromiseValue(value, promise) {
  promise.then(resolve => {
    went = true;
    if (resolve !== value) {
      print(`expected ${value} found ${resolve}`);
      quit(1);
    }
  }, reject => {
    print(`rejected ${reject}`);
    quit(1);
  });
}

function MakeAsyncFunction() {
  // Prevents eval script caching.
  unique_id++;
  return AsyncFunction('callback',
      "/* " + unique_id + "*/\n" +
      "await callback();\n" +
      "return 'Cat';\n");
}

function MakeFunction() {
  // Prevents eval script caching.
  unique_id++;
  return Function('callback',
      "/* " + unique_id + "*/\n" +
      "callback();\n" +
      "return 'Cat';\n");
}

// First, try MakeGenerator with no perturbations.
(function(){
  var asyncfn = MakeAsyncFunction();
  function callback() {};
  var promise = asyncfn(callback);
  assertPromiseValue('Cat', promise);
})();

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
function ExecuteInDebugContext(f) {
  var result;
  var exception = null;
  Debug.setListener(function(event) {
    if (event == Debug.DebugEvent.Break) {
      try {
        result = f();
      } catch (e) {
        // Rethrow this exception later.
        exception = e;
      }
    }
  });
  debugger;
  Debug.setListener(null);
  if (exception !== null) throw exception;
  return result;
}

71 72
function patch(fun, from, to) {
  function debug() {
73
    %LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(from, to));
74
  }
75
  ExecuteInDebugContext(debug);
76 77 78 79 80 81 82 83 84 85 86
}

// Try to edit a MakeAsyncFunction while it's running, then again while it's
// stopped.
(function(){
  var asyncfn = MakeAsyncFunction();

  var patch_attempted = false;
  function attempt_patch() {
    assertFalse(patch_attempted);
    patch_attempted = true;
87 88 89
    assertThrowsEquals(function() {
      patch(asyncfn, '\'Cat\'', '\'Capybara\'')
    }, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
90 91 92 93 94 95 96
  };
  var promise = asyncfn(attempt_patch);
  // Patch should not succeed because there is a live async function activation
  // on the stack.
  assertPromiseValue("Cat", promise);
  assertTrue(patch_attempted);

97
  %PerformMicrotaskCheckpoint();
98 99 100 101 102 103 104 105 106 107

  // At this point one iterator is live, but closed, so the patch will succeed.
  patch(asyncfn, "'Cat'", "'Capybara'");
  promise = asyncfn(function(){});
  // Patch successful.
  assertPromiseValue("Capybara", promise);

  // Patching will fail however when an async function is suspended.
  var resolve;
  promise = asyncfn(function(){return new Promise(function(r){resolve = r})});
108 109
  assertThrowsEquals(function() {
    patch(asyncfn, '\'Capybara\'', '\'Tapir\'')
110
  }, 'LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR');
111 112 113 114 115 116
  resolve();
  assertPromiseValue("Capybara", promise);

  // Try to patch functions with activations inside and outside async
  // function activations.  We should succeed in the former case, but not in the
  // latter.
117
  var fun_outside = eval('((callback) => { callback(); return \'Cat\';})');
118 119 120 121 122 123 124 125 126 127 128
  var fun_inside = MakeFunction();
  var fun_patch_attempted = false;
  var fun_patch_restarted = false;
  function attempt_fun_patches() {
    if (fun_patch_attempted) {
      assertFalse(fun_patch_restarted);
      fun_patch_restarted = true;
      return;
    }
    fun_patch_attempted = true;
    // Patching outside an async function activation must fail.
129 130 131
    assertThrowsEquals(function() {
      patch(fun_outside, '\'Cat\'', '\'Cobra\'')
    }, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
132 133 134
    // Patching inside an async function activation may succeed.
    patch(fun_inside, "'Cat'", "'Koala'");
  }
135 136 137
  result = fun_outside(() => asyncfn(function() {
    return fun_inside(attempt_fun_patches);
  }));
138 139
  assertEquals('Cat',
               fun_outside(function () {
140
                 assertEquals(result, 'Cat');
141 142 143 144 145
                 assertTrue(fun_patch_restarted);
                 assertTrue(fun_inside.toString().includes("'Koala'"));
               }));
})();

146
%PerformMicrotaskCheckpoint();