generators-debug-liveedit.js 3.99 KB
Newer Older
1 2 3 4
// Copyright 2014 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
// Flags: --allow-natives-syntax
6 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
var Debug = debug.Debug;

unique_id = 0;

var Generator = (function*(){}).constructor;

function assertIteratorResult(value, done, result) {
  assertEquals({value: value, done: done}, result);
}

function MakeGenerator() {
  // Prevents eval script caching.
  unique_id++;
  return Generator('callback',
      "/* " + unique_id + "*/\n" +
      "yield 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 generator = MakeGenerator();
  function callback() {};
  var iter = generator(callback);
  assertIteratorResult(undefined, false, iter.next());
  assertIteratorResult("Cat", true, iter.next());
})();

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
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;
}

62 63
function patch(fun, from, to) {
  function debug() {
64
    %LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(from, to));
65
  }
66
  ExecuteInDebugContext(debug);
67 68 69 70 71 72 73 74 75 76 77
}

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

  var gen_patch_attempted = false;
  function attempt_gen_patch() {
    assertFalse(gen_patch_attempted);
    gen_patch_attempted = true;
78 79 80
    assertThrowsEquals(function() {
      patch(generator, '\'Cat\'', '\'Capybara\'')
    }, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
  };
  var iter = generator(attempt_gen_patch);
  assertIteratorResult(undefined, false, iter.next());
  // Patch should not succeed because there is a live generator activation on
  // the stack.
  assertIteratorResult("Cat", true, iter.next());
  assertTrue(gen_patch_attempted);

  // At this point one iterator is live, but closed, so the patch will succeed.
  patch(generator, "'Cat'", "'Capybara'");
  iter = generator(function(){});
  assertIteratorResult(undefined, false, iter.next());
  // Patch successful.
  assertIteratorResult("Capybara", true, iter.next());

  // Patching will fail however when a live iterator is suspended.
  iter = generator(function(){});
  assertIteratorResult(undefined, false, iter.next());
99 100
  assertThrowsEquals(function() {
    patch(generator, '\'Capybara\'', '\'Tapir\'')
101
  }, 'LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR');
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
  assertIteratorResult("Capybara", true, iter.next());

  // Try to patch functions with activations inside and outside generator
  // function activations.  We should succeed in the former case, but not in the
  // latter.
  var fun_outside = MakeFunction();
  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 a generator activation must fail.
119 120 121
    assertThrowsEquals(function() {
      patch(fun_outside, '\'Cat\'', '\'Cobra\'')
    }, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
122 123 124 125 126 127 128 129 130 131
    // Patching inside a generator activation may succeed.
    patch(fun_inside, "'Cat'", "'Koala'");
  }
  iter = generator(function() { return fun_inside(attempt_fun_patches) });
  assertEquals('Cat',
               fun_outside(function () {
                 assertIteratorResult('Koala', false, iter.next());
                 assertTrue(fun_patch_restarted);
               }));
})();