/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/test/mjsunit/wasm/stack-switching.js
404 строки
14 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// Copyright 2025 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: --experimental-wasm-wasmfx --expose-gc d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); let builder = new WasmModuleBuilder(); let sig_v_v = builder.addType(kSig_v_v); let cont_index = builder.addCont(sig_v_v); let gc_index = builder.addImport("m", "gc", kSig_v_v); builder.addExport("gc", gc_index); let jspi_suspending_index = builder.addImport("m", "jspi_suspending", sig_v_v); builder.addExport("jspi_suspending", jspi_suspending_index); let call_next_from_js_index = builder.addImport("m", "call_next_from_js", kSig_v_v); builder.addExport("call_next_from_js", call_next_from_js_index); let get_next_sig = builder.addType(makeSig([], [wasmRefNullType(sig_v_v)])); let get_next_index = builder.addImport("m", "get_next", get_next_sig); let tag0_index = builder.addTag(kSig_v_v); let tag1_index = builder.addTag(kSig_v_v); // A list of wasm functions where each function is expected to pop and call // the next one in the list, to easily create specific call stacks. let call_stack = builder.addGlobal(kWasmAnyRef, true).exportAs("call_stack"); let rejecting_promise_index = builder.addImport( "m", "rejecting_promise", sig_v_v); builder.addExport("rejecting_promise", rejecting_promise_index); let g_cont = builder.addGlobal(wasmRefNullType(cont_index), true, false); builder.addFunction("cont_new_null", kSig_v_v) .addBody([ kExprRefNull, kNullFuncRefCode, kExprContNew, cont_index, kExprUnreachable, ]).exportFunc(); builder.addFunction("resume_null", kSig_v_v) .addBody([ kExprRefNull, kNullContRefCode, kExprResume, cont_index, kExprUnreachable, ]).exportFunc(); builder.addFunction("throw_error", kSig_v_v) .addBody([kExprThrow, tag0_index]).exportFunc(); builder.addFunction("call_next_as_cont", kSig_v_v) .addBody([ kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 0, ]).exportFunc(); builder.addFunction("call_next", kSig_v_v) .addBody([ kExprCallFunction, get_next_index, kExprCallRef, sig_v_v, ]).exportFunc(); builder.addFunction("call_next_in_catch_all", kSig_v_v) .addBody([ kExprTryTable, kWasmVoid, 1, kCatchAllNoRef, 0, kExprCallFunction, get_next_index, kExprCallRef, sig_v_v, kExprEnd, ]).exportFunc(); let nop_index = builder.addFunction("nop", kSig_v_v).addBody([]).exportFunc().index; builder.addFunction("resume_next_handle_tag0", kSig_v_v) .addBody([ kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 1, kOnSuspend, tag0_index, 0, kExprUnreachable, kExprEnd, kExprDrop, ]).exportFunc(); builder.addFunction("resume_next_handle_tag1", kSig_v_v) .addBody([ kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 1, kOnSuspend, tag1_index, 0, kExprUnreachable, kExprEnd, kExprDrop, kExprUnreachable, ]).exportFunc(); builder.addFunction("resume_next_twice", kSig_v_v) .addBody([ kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 1, kOnSuspend, tag0_index, 0, kExprUnreachable, kExprEnd, kExprResume, cont_index, 0, ]).exportFunc(); builder.addFunction("resume_next_with_handler_and_catch_all", kSig_i_v) .addBody([ kExprBlock, kWasmVoid, kExprTryTable, kWasmVoid, 1, kCatchAllNoRef, 0, kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 1, kOnSuspend, tag0_index, 0, kExprI32Const, 10, kExprReturn, kExprEnd, kExprDrop, kExprI32Const, 11, kExprReturn, kExprEnd, kExprUnreachable, kExprEnd, kExprI32Const, 12, kExprReturn, ]).exportFunc(); builder.addFunction("throw_exn", kSig_v_v) .addBody([ kExprThrow, tag0_index ]).exportFunc(); let suspend_tag0 = builder.addFunction("suspend_tag0", kSig_v_v) .addBody([ kExprSuspend, tag0_index ]).exportFunc(); builder.addFunction("suspend_tag1", kSig_v_v) .addBody([ kExprSuspend, tag1_index ]).exportFunc(); builder.addFunction("resume_bad_cont_1", kSig_v_v) .addBody([ kExprRefFunc, nop_index, kExprContNew, cont_index, kExprGlobalSet, g_cont.index, kExprGlobalGet, g_cont.index, kExprResume, cont_index, 0, // Reusing cont after return. kExprGlobalGet, g_cont.index, kExprResume, cont_index, 0 ]).exportFunc(); builder.addFunction("resume_bad_cont_2", kSig_v_v) .addBody([ kExprBlock, kWasmRef, cont_index, kExprRefFunc, suspend_tag0.index, kExprContNew, cont_index, kExprGlobalSet, g_cont.index, kExprGlobalGet, g_cont.index, kExprResume, cont_index, 1, kOnSuspend, tag0_index, 0, kExprReturn, kExprEnd, kExprDrop, // Reusing cont after suspend. kExprGlobalGet, g_cont.index, kExprResume, cont_index, 0 ]).exportFunc(); let helper = builder.addFunction("resume_bad_cont_3_helper", kSig_v_v) .addBody([ kExprGlobalGet, g_cont.index, // Reusing cont before return/suspend. kExprResume, cont_index, 0, ]).exportFunc(); builder.addFunction("resume_bad_cont_3", kSig_v_v) .addLocals(wasmRefNullType(cont_index), 1) .addBody([ kExprRefFunc, helper.index, kExprContNew, cont_index, kExprGlobalSet, g_cont.index, kExprGlobalGet, g_cont.index, kExprResume, cont_index, 0 ]).exportFunc(); builder.addFunction("resume_next_with_two_handlers", kSig_i_v) .addBody([ kExprBlock, kWasmVoid, kExprBlock, kWasmRef, cont_index, kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 2, kOnSuspend, tag0_index, 0, kOnSuspend, tag1_index, 1, kExprUnreachable, kExprEnd, kExprDrop, kExprI32Const, 0, kExprReturn, kExprEnd, kExprDrop, kExprI32Const, 1, kExprReturn, kExprEnd, kExprUnreachable, ]).exportFunc(); builder.addFunction("resume_next_with_two_handlers_same_tag", kSig_i_v) .addBody([ kExprBlock, kWasmVoid, kExprBlock, kWasmRef, cont_index, kExprBlock, kWasmRef, cont_index, kExprCallFunction, get_next_index, kExprContNew, cont_index, kExprResume, cont_index, 2, kOnSuspend, tag0_index, 0, kOnSuspend, tag0_index, 1, kExprUnreachable, kExprEnd, kExprDrop, kExprI32Const, 0, kExprReturn, kExprEnd, kExprDrop, kExprI32Const, 1, kExprReturn, kExprEnd, kExprUnreachable, ]).exportFunc(); let loop_sig = builder.addType(makeSig([wasmRefNullType(cont_index)], [wasmRefNullType(cont_index)])); builder.addFunction("handler_is_loop", kSig_i_v) .addLocals(kWasmI32, 1) .addBody([ kExprRefFunc, suspend_tag0.index, kExprContNew, cont_index, kExprLoop, loop_sig, // The suspension is handled by the loop, so this should be // executed twice: kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 0, kExprResume, cont_index, 1, kOnSuspend, tag0_index, 0, // The second time, the continuation returns normally. kExprLocalGet, 0, kExprReturn, kExprEnd, kExprUnreachable, ]).exportFunc(); let instance; instance = builder.instantiate( {m: { gc, jspi_suspending: new WebAssembly.Suspending(() => {}), call_next_from_js: () => instance.exports.call_stack.value.shift()(), get_next: () => instance.exports.call_stack.value.shift(), rejecting_promise: new WebAssembly.Suspending(() => Promise.reject()) }}); (function TestNullRefs() { print(arguments.callee.name); assertThrows(instance.exports.cont_new_null, WebAssembly.RuntimeError); assertThrows(instance.exports.resume_null, WebAssembly.RuntimeError); })(); (function TestInitialResumeAndReturn() { print(arguments.callee.name); instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.gc ]; instance.exports.call_next(); instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_as_cont, instance.exports.call_next_as_cont, instance.exports.gc, ]; instance.exports.call_next(); instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_as_cont, instance.exports.call_next_as_cont, instance.exports.throw_error, ]; assertThrows(instance.exports.call_next, WebAssembly.Exception); })(); (function TestWasmFXResumeAndJSPISuspend() { print(arguments.callee.name); // Suspend multiple WasmFX stacks with JSPI. instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.jspi_suspending ]; WebAssembly.promising(instance.exports.call_next)(); // Throw if the top WasmFX stack contains JS frames: instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_from_js, instance.exports.jspi_suspending ]; assertThrowsAsync( WebAssembly.promising(instance.exports.call_next_as_cont)(), WebAssembly.SuspendError); // Throw if an intermediate stack contains JS frames. instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_from_js, instance.exports.call_next_as_cont, instance.exports.jspi_suspending ]; assertThrowsAsync( WebAssembly.promising(instance.exports.call_next_as_cont)(), WebAssembly.SuspendError); // Catch error in an intermediate stack. instance.exports.call_stack.value = [ instance.exports.call_next_in_catch_all, instance.exports.call_next_as_cont, instance.exports.rejecting_promise, ]; WebAssembly.promising(instance.exports.call_next_as_cont)() })(); (function TestSuspend() { print(arguments.callee.name); instance.exports.call_stack.value = [ instance.exports.suspend_tag0 ]; instance.exports.resume_next_twice(); // A resume instruction within a try scope triggers interesting code paths: // the resume builtin call must be able to handle either exceptions or // effects. // Test the three possible successors of the resume instruction: normal // return, exception handler and effect handler. instance.exports.call_stack.value = [ instance.exports.nop ]; assertEquals(10, instance.exports.resume_next_with_handler_and_catch_all()); instance.exports.call_stack.value = [ instance.exports.suspend_tag0 ]; assertEquals(11, instance.exports.resume_next_with_handler_and_catch_all()); instance.exports.call_stack.value = [ instance.exports.throw_exn ]; assertEquals(12, instance.exports.resume_next_with_handler_and_catch_all()); instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_as_cont, instance.exports.suspend_tag0, ]; instance.exports.resume_next_twice(); instance.exports.call_stack.value = [ instance.exports.resume_next_handle_tag1, instance.exports.suspend_tag0 ]; instance.exports.resume_next_handle_tag0(); })(); (function TestSuspendError() { print(arguments.callee.name); // Throw if the top WasmFX stack contains JS frames: instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_from_js, instance.exports.suspend_tag0, ]; assertThrows(instance.exports.resume_next_handle_tag0, WebAssembly.SuspendError, /WasmFX: unhandled suspend/); // Throw if an intermediate stack contains JS frames. instance.exports.call_stack.value = [ instance.exports.call_next_as_cont, instance.exports.call_next_from_js, instance.exports.call_next_as_cont, instance.exports.suspend_tag0, ]; assertThrows(instance.exports.resume_next_handle_tag0, WebAssembly.SuspendError, /WasmFX: unhandled suspend/); instance.exports.call_stack.value = [ instance.exports.suspend_tag1]; assertThrows(instance.exports.resume_next_handle_tag0, WebAssembly.SuspendError, /WasmFX: unhandled suspend/); })(); (function TestInvalidContinuation() { print(arguments.callee.name); assertThrows(instance.exports.resume_bad_cont_1, WebAssembly.RuntimeError); assertThrows(instance.exports.resume_bad_cont_2, WebAssembly.RuntimeError); assertThrows(instance.exports.resume_bad_cont_3, WebAssembly.RuntimeError); })(); (function TestMultipleHandlers() { print(arguments.callee.name); instance.exports.call_stack.value = [instance.exports.suspend_tag0]; assertEquals(0, instance.exports.resume_next_with_two_handlers()); instance.exports.call_stack.value = [instance.exports.suspend_tag1]; assertEquals(1, instance.exports.resume_next_with_two_handlers()); // If the same tag is used multiple times, use the first occurrence. instance.exports.call_stack.value = [instance.exports.suspend_tag0]; assertEquals(0, instance.exports.resume_next_with_two_handlers_same_tag()); })(); (function TestLoopHandler() { print(arguments.callee.name); assertEquals(2, instance.exports.handler_is_loop()); })();