| // 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(); |