blob: 761a24fdfa75526f191ff9efdb5a7b0c07399160 [file] [log] [blame]
// 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.
var Debug = debug.Debug;
var LiveEdit = Debug.LiveEdit;
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);
})();
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;
}
function patch(fun, from, to) {
function debug() {
var log = new Array();
var script = Debug.findScript(fun);
var pos = script.source.indexOf(from);
print(`pos ${pos}`);
try {
LiveEdit.TestApi.ApplySingleChunkPatch(script, pos, from.length, to,
log);
} finally {
print("Change log: " + JSON.stringify(log) + "\n");
}
}
ExecuteInDebugContext(debug);
}
// 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;
assertThrows(function() { patch(asyncfn, "'Cat'", "'Capybara'") },
LiveEdit.Failure);
};
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);
%RunMicrotasks();
// 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})});
assertThrows(function() { patch(asyncfn, "'Capybara'", "'Tapir'") },
LiveEdit.Failure);
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.
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 an async function activation must fail.
assertThrows(function() { patch(fun_outside, "'Cat'", "'Cobra'") },
LiveEdit.Failure);
// Patching inside an async function activation may succeed.
patch(fun_inside, "'Cat'", "'Koala'");
}
promise = asyncfn(function() { return fun_inside(attempt_fun_patches) });
assertEquals('Cat',
fun_outside(function () {
assertPromiseValue('Capybara', promise);
assertTrue(fun_patch_restarted);
assertTrue(fun_inside.toString().includes("'Koala'"));
}));
})();
%RunMicrotasks();