blob: e85cbe38967439bd1a960e6f4de1ba5e003a82e6 [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.
// Flags: --allow-natives-syntax
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);
})();
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() {
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(from, to));
}
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;
assertThrowsEquals(function() {
patch(asyncfn, '\'Cat\'', '\'Capybara\'')
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
};
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);
%PerformMicrotaskCheckpoint();
// 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})});
assertThrowsEquals(function() {
patch(asyncfn, '\'Capybara\'', '\'Tapir\'')
}, 'LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR');
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 = eval('((callback) => { callback(); return \'Cat\';})');
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.
assertThrowsEquals(function() {
patch(fun_outside, '\'Cat\'', '\'Cobra\'')
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
// Patching inside an async function activation may succeed.
patch(fun_inside, "'Cat'", "'Koala'");
}
result = fun_outside(() => asyncfn(function() {
return fun_inside(attempt_fun_patches);
}));
assertEquals('Cat',
fun_outside(function () {
assertEquals(result, 'Cat');
assertTrue(fun_patch_restarted);
assertTrue(fun_inside.toString().includes("'Koala'"));
}));
})();
%PerformMicrotaskCheckpoint();