@amxts/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/README.ru.md +162 -0
- package/amxmodx/base/include/amxconst.inc +594 -0
- package/amxmodx/base/include/amxmisc.inc +902 -0
- package/amxmodx/base/include/amxmodx.inc +3447 -0
- package/amxmodx/base/include/amxmodx_version.inc +20 -0
- package/amxmodx/base/include/cellarray.inc +525 -0
- package/amxmodx/base/include/cellstack.inc +166 -0
- package/amxmodx/base/include/celltrie.inc +412 -0
- package/amxmodx/base/include/core.inc +202 -0
- package/amxmodx/base/include/csstats.inc +298 -0
- package/amxmodx/base/include/csstats_const.inc +29 -0
- package/amxmodx/base/include/cstrike.inc +1287 -0
- package/amxmodx/base/include/cstrike_const.inc +521 -0
- package/amxmodx/base/include/csx.inc +259 -0
- package/amxmodx/base/include/cvars.inc +547 -0
- package/amxmodx/base/include/datapack.inc +161 -0
- package/amxmodx/base/include/dbi.inc +154 -0
- package/amxmodx/base/include/dodconst.inc +152 -0
- package/amxmodx/base/include/dodfun.inc +152 -0
- package/amxmodx/base/include/dodstats.inc +69 -0
- package/amxmodx/base/include/dodx.inc +161 -0
- package/amxmodx/base/include/engine.inc +1343 -0
- package/amxmodx/base/include/engine_const.inc +310 -0
- package/amxmodx/base/include/engine_stocks.inc +248 -0
- package/amxmodx/base/include/esf.inc +70 -0
- package/amxmodx/base/include/esf_const.inc +74 -0
- package/amxmodx/base/include/fakemeta.inc +1127 -0
- package/amxmodx/base/include/fakemeta_const.inc +793 -0
- package/amxmodx/base/include/fakemeta_stocks.inc +395 -0
- package/amxmodx/base/include/fakemeta_util.inc +882 -0
- package/amxmodx/base/include/file.inc +666 -0
- package/amxmodx/base/include/float.inc +422 -0
- package/amxmodx/base/include/fun.inc +333 -0
- package/amxmodx/base/include/gameconfig.inc +93 -0
- package/amxmodx/base/include/geoip.inc +264 -0
- package/amxmodx/base/include/ham_const.inc +4352 -0
- package/amxmodx/base/include/hamsandwich.inc +439 -0
- package/amxmodx/base/include/hlsdk_const.inc +808 -0
- package/amxmodx/base/include/json.inc +777 -0
- package/amxmodx/base/include/lang.inc +119 -0
- package/amxmodx/base/include/message_const.inc +1130 -0
- package/amxmodx/base/include/message_stocks.inc +161 -0
- package/amxmodx/base/include/messages.inc +604 -0
- package/amxmodx/base/include/newmenus.inc +387 -0
- package/amxmodx/base/include/ns.inc +754 -0
- package/amxmodx/base/include/ns_const.inc +196 -0
- package/amxmodx/base/include/nvault.inc +154 -0
- package/amxmodx/base/include/regex.inc +348 -0
- package/amxmodx/base/include/sockets.inc +183 -0
- package/amxmodx/base/include/sorting.inc +107 -0
- package/amxmodx/base/include/sqlx.inc +582 -0
- package/amxmodx/base/include/string.inc +786 -0
- package/amxmodx/base/include/string_const.inc +156 -0
- package/amxmodx/base/include/string_stocks.inc +325 -0
- package/amxmodx/base/include/textparse_ini.inc +214 -0
- package/amxmodx/base/include/textparse_smc.inc +261 -0
- package/amxmodx/base/include/tfcconst.inc +84 -0
- package/amxmodx/base/include/tfcstats.inc +62 -0
- package/amxmodx/base/include/tfcx.inc +148 -0
- package/amxmodx/base/include/time.inc +118 -0
- package/amxmodx/base/include/tsconst.inc +154 -0
- package/amxmodx/base/include/tsfun.inc +198 -0
- package/amxmodx/base/include/tsstats.inc +65 -0
- package/amxmodx/base/include/tsx.inc +88 -0
- package/amxmodx/base/include/vault.inc +60 -0
- package/amxmodx/base/include/vector.inc +131 -0
- package/amxmodx/base/include/xs.inc +1570 -0
- package/as/amxts.d.ts +153 -0
- package/as/constants.ts +4730 -0
- package/as/effects.ts +1449 -0
- package/as/entities.ts +5482 -0
- package/as/events.ts +3054 -0
- package/as/facade.ts +4639 -0
- package/as/flags.ts +299 -0
- package/as/fs.ts +287 -0
- package/as/hooks.ts +18536 -0
- package/as/kit.ts +25 -0
- package/as/lib/check.ts +86 -0
- package/as/modules/http.ts +184 -0
- package/as/natives.ts +11845 -0
- package/as/os.ts +30 -0
- package/as/promise.ts +1392 -0
- package/as/promise.types.d.ts +82 -0
- package/as/remote.ts +288 -0
- package/as/tsconfig.json +22 -0
- package/as/vector.ts +65 -0
- package/bin/amxts.mjs +10 -0
- package/includes/menu_core.inc +355 -0
- package/includes/order.txt +54 -0
- package/includes/sources.json +27 -0
- package/includes/universal_config.inc +355 -0
- package/lint/oxlint-plugin.mjs +77 -0
- package/node_modules/assemblyscript/LICENSE +201 -0
- package/node_modules/assemblyscript/NOTICE +93 -0
- package/node_modules/assemblyscript/package.json +63 -0
- package/node_modules/assemblyscript/std/README.md +6 -0
- package/node_modules/assemblyscript/std/assembly/array.ts +526 -0
- package/node_modules/assemblyscript/std/assembly/arraybuffer.ts +77 -0
- package/node_modules/assemblyscript/std/assembly/atomics.ts +127 -0
- package/node_modules/assemblyscript/std/assembly/bindings/asyncify.ts +16 -0
- package/node_modules/assemblyscript/std/assembly/bindings/dom.ts +291 -0
- package/node_modules/assemblyscript/std/assembly/bindings/node.ts +6 -0
- package/node_modules/assemblyscript/std/assembly/builtins.ts +2631 -0
- package/node_modules/assemblyscript/std/assembly/compat.ts +2 -0
- package/node_modules/assemblyscript/std/assembly/console.ts +42 -0
- package/node_modules/assemblyscript/std/assembly/crypto.ts +9 -0
- package/node_modules/assemblyscript/std/assembly/dataview.ts +181 -0
- package/node_modules/assemblyscript/std/assembly/date.ts +373 -0
- package/node_modules/assemblyscript/std/assembly/diagnostics.ts +11 -0
- package/node_modules/assemblyscript/std/assembly/error.ts +44 -0
- package/node_modules/assemblyscript/std/assembly/function.ts +38 -0
- package/node_modules/assemblyscript/std/assembly/index.d.ts +2822 -0
- package/node_modules/assemblyscript/std/assembly/iterator.ts +35 -0
- package/node_modules/assemblyscript/std/assembly/map.ts +260 -0
- package/node_modules/assemblyscript/std/assembly/math.ts +3289 -0
- package/node_modules/assemblyscript/std/assembly/memory.ts +123 -0
- package/node_modules/assemblyscript/std/assembly/number.ts +388 -0
- package/node_modules/assemblyscript/std/assembly/object.ts +36 -0
- package/node_modules/assemblyscript/std/assembly/performance.ts +9 -0
- package/node_modules/assemblyscript/std/assembly/polyfills.ts +27 -0
- package/node_modules/assemblyscript/std/assembly/process.ts +50 -0
- package/node_modules/assemblyscript/std/assembly/reference.ts +48 -0
- package/node_modules/assemblyscript/std/assembly/regexp.ts +12 -0
- package/node_modules/assemblyscript/std/assembly/rt/README.md +83 -0
- package/node_modules/assemblyscript/std/assembly/rt/common.ts +81 -0
- package/node_modules/assemblyscript/std/assembly/rt/index-incremental.ts +2 -0
- package/node_modules/assemblyscript/std/assembly/rt/index-minimal.ts +2 -0
- package/node_modules/assemblyscript/std/assembly/rt/index-stub.ts +1 -0
- package/node_modules/assemblyscript/std/assembly/rt/index.d.ts +37 -0
- package/node_modules/assemblyscript/std/assembly/rt/itcms.ts +419 -0
- package/node_modules/assemblyscript/std/assembly/rt/rtrace.ts +15 -0
- package/node_modules/assemblyscript/std/assembly/rt/stub.ts +133 -0
- package/node_modules/assemblyscript/std/assembly/rt/tcms.ts +254 -0
- package/node_modules/assemblyscript/std/assembly/rt/tlsf.ts +592 -0
- package/node_modules/assemblyscript/std/assembly/rt.ts +87 -0
- package/node_modules/assemblyscript/std/assembly/set.ts +220 -0
- package/node_modules/assemblyscript/std/assembly/shared/feature.ts +65 -0
- package/node_modules/assemblyscript/std/assembly/shared/runtime.ts +11 -0
- package/node_modules/assemblyscript/std/assembly/shared/target.ts +11 -0
- package/node_modules/assemblyscript/std/assembly/shared/tsconfig.json +11 -0
- package/node_modules/assemblyscript/std/assembly/shared/typeinfo.ts +72 -0
- package/node_modules/assemblyscript/std/assembly/staticarray.ts +401 -0
- package/node_modules/assemblyscript/std/assembly/string.ts +847 -0
- package/node_modules/assemblyscript/std/assembly/symbol.ts +114 -0
- package/node_modules/assemblyscript/std/assembly/table.ts +16 -0
- package/node_modules/assemblyscript/std/assembly/tsconfig.json +6 -0
- package/node_modules/assemblyscript/std/assembly/typedarray.ts +1945 -0
- package/node_modules/assemblyscript/std/assembly/uri.ts +17 -0
- package/node_modules/assemblyscript/std/assembly/util/bytes.ts +107 -0
- package/node_modules/assemblyscript/std/assembly/util/casemap.ts +497 -0
- package/node_modules/assemblyscript/std/assembly/util/error.ts +54 -0
- package/node_modules/assemblyscript/std/assembly/util/hash.ts +117 -0
- package/node_modules/assemblyscript/std/assembly/util/math.ts +1922 -0
- package/node_modules/assemblyscript/std/assembly/util/memory.ts +290 -0
- package/node_modules/assemblyscript/std/assembly/util/number.ts +873 -0
- package/node_modules/assemblyscript/std/assembly/util/sort.ts +313 -0
- package/node_modules/assemblyscript/std/assembly/util/string.ts +1202 -0
- package/node_modules/assemblyscript/std/assembly/util/uri.ts +275 -0
- package/node_modules/assemblyscript/std/assembly/vector.ts +4 -0
- package/node_modules/assemblyscript/std/assembly.json +16 -0
- package/node_modules/assemblyscript/std/portable/index.d.ts +462 -0
- package/node_modules/assemblyscript/std/portable/index.js +416 -0
- package/node_modules/assemblyscript/std/portable.json +11 -0
- package/node_modules/assemblyscript/std/types/assembly/index.d.ts +1 -0
- package/node_modules/assemblyscript/std/types/assembly/package.json +3 -0
- package/node_modules/assemblyscript/std/types/portable/index.d.ts +1 -0
- package/node_modules/assemblyscript/std/types/portable/package.json +3 -0
- package/node_modules/assemblyscript/tsconfig-base.json +13 -0
- package/package.json +112 -0
- package/runtime/deps/assemblyscript/LICENSE +201 -0
- package/runtime/deps/assemblyscript/NOTICE +93 -0
- package/runtime/deps/assemblyscript/bin/asc.js +35 -0
- package/runtime/deps/assemblyscript/dist/asc.js +25035 -0
- package/runtime/deps/assemblyscript/dist/assemblyscript.js +337 -0
- package/runtime/deps/assemblyscript/node_modules/binaryen/LICENSE +201 -0
- package/runtime/deps/assemblyscript/node_modules/binaryen/index.js +28905 -0
- package/runtime/deps/assemblyscript/node_modules/binaryen/package.json +50 -0
- package/runtime/deps/assemblyscript/node_modules/long/LICENSE +202 -0
- package/runtime/deps/assemblyscript/node_modules/long/index.js +1581 -0
- package/runtime/deps/assemblyscript/node_modules/long/package.json +58 -0
- package/runtime/deps/assemblyscript/package.json +114 -0
- package/runtime/host/amxts.inc +85 -0
- package/runtime/licenses/AMXX-LICENSE.txt +34 -0
- package/runtime/licenses/AssemblyScript-LICENSE +201 -0
- package/runtime/licenses/AssemblyScript-NOTICE +93 -0
- package/runtime/licenses/Binaryen-LICENSE +201 -0
- package/runtime/licenses/Bun-LICENSE.md +84 -0
- package/runtime/licenses/GPL-3.0.txt +674 -0
- package/runtime/licenses/LLVM-LICENSE.TXT +279 -0
- package/runtime/licenses/README.md +25 -0
- package/runtime/licenses/WAMR-LICENSE +219 -0
- package/runtime/natives.txt +1095 -0
- package/runtime/patches/README.md +59 -0
- package/runtime/patches/assemblyscript-0.28.20-amxts.patch +14174 -0
- package/runtime/patches/wamr-2.4.5-amxts.patch +364 -0
- package/scripts/apply-docs.ts +508 -0
- package/scripts/asc.ts +27 -0
- package/scripts/auto-imports.ts +258 -0
- package/scripts/build-wasm.ts +595 -0
- package/scripts/check.ts +93 -0
- package/scripts/compile-cache.ts +179 -0
- package/scripts/compile.ts +401 -0
- package/scripts/docker-server.ts +73 -0
- package/scripts/docs/as/amxts.ts +210 -0
- package/scripts/docs/as/effects.ts +1684 -0
- package/scripts/docs/as/facade.ts +3302 -0
- package/scripts/docs/as/fs.ts +173 -0
- package/scripts/docs/as/lib/check.ts +68 -0
- package/scripts/docs/as/modules/http.ts +66 -0
- package/scripts/docs/as/os.ts +16 -0
- package/scripts/docs/as/promise.ts +132 -0
- package/scripts/docs/as/promise.types.ts +63 -0
- package/scripts/docs/as/vector.ts +48 -0
- package/scripts/docs/entities.ts +2338 -0
- package/scripts/docs/events.ts +245 -0
- package/scripts/docs/game.ts +497 -0
- package/scripts/docs/messages.ts +148 -0
- package/scripts/file-contents.ts +74 -0
- package/scripts/includes.ts +134 -0
- package/scripts/player-fields.ts +866 -0
- package/scripts/plugin-cache.ts +92 -0
- package/scripts/plugin-natives.ts +1157 -0
- package/scripts/prebuilt.ts +268 -0
- package/scripts/prepare.ts +134 -0
- package/scripts/project.ts +973 -0
- package/scripts/run.ts +28 -0
- package/scripts/shared-modules.ts +906 -0
- package/scripts/system.ts +33 -0
- package/scripts/tracked-fs.ts +115 -0
- package/scripts/typed-configs.ts +788 -0
- package/scripts/ui.ts +77 -0
- package/src/cli-api.mjs +175 -0
- package/src/parser/include-parser.ts +506 -0
- package/src/system.mjs +155 -0
- package/src/testing/compile-cache.ts +31 -0
- package/src/testing/compile.ts +222 -0
- package/src/testing/coroutines.ts +191 -0
- package/src/testing/index.ts +39 -0
- package/src/testing/kits.ts +55 -0
- package/src/testing/memory.ts +125 -0
- package/src/testing/natives.ts +925 -0
- package/src/testing/server.ts +1874 -0
- package/src/testing/setup.ts +63 -0
- package/src/testing/tables.ts +139 -0
- package/src/types.ts +51 -0
package/as/promise.ts
ADDED
|
@@ -0,0 +1,1392 @@
|
|
|
1
|
+
// Promise, async/await and AbortSignal: the hood under them.
|
|
2
|
+
//
|
|
3
|
+
// The compiler reads the classes through `@global`, so `Promise`,
|
|
4
|
+
// `AbortController`, `AbortSignal` and `Event` are the globals they are in
|
|
5
|
+
// JavaScript; the facade imports the file for it, and nothing else needs to.
|
|
6
|
+
// The editor reads them through as/promise.types.d.ts, which makes what this
|
|
7
|
+
// file exports global - an `async function` is typed with this very Promise.
|
|
8
|
+
//
|
|
9
|
+
// How an async function runs (the compiler half is Compiler.compileAsyncPrologue
|
|
10
|
+
// in runtime/deps/assemblyscript/src/compiler.ts, the host half is the
|
|
11
|
+
// coroutine code in runtime/src/module.cpp):
|
|
12
|
+
//
|
|
13
|
+
// - A call to it makes its Promise and asks the host (co_spawn) to call it
|
|
14
|
+
// again through the table: that second call is the body, a coroutine of its
|
|
15
|
+
// own. The body runs at once, up to its first `await`.
|
|
16
|
+
// - `await p` (__await) parks the coroutine: the host unwinds it with
|
|
17
|
+
// Asyncify back to co_spawn, keeps its shadow stack here (__co_park), and
|
|
18
|
+
// the caller goes on with a pending Promise - a call without `await` does
|
|
19
|
+
// not wait, as in JavaScript.
|
|
20
|
+
// - When p settles, its reactions become jobs (microtasks). The host runs
|
|
21
|
+
// them (__co_next) once the plugin has nothing of its own on the stack, and
|
|
22
|
+
// a coroutine waiting on p is rewound from there.
|
|
23
|
+
//
|
|
24
|
+
// A rejection - or a `throw` in an async function, which the compiler makes
|
|
25
|
+
// one (__co_throw) - travels to whoever awaits it. Inside a `try` the await
|
|
26
|
+
// gives the reason to its `catch` (the compiler's exceptions, see
|
|
27
|
+
// std/assembly/error.ts); outside every one the awaiting coroutine gives up
|
|
28
|
+
// with the same reason. It ends in a `catch`, `.catch` or as one "Unhandled
|
|
29
|
+
// promise rejection" line. A coroutine counts its own `try` blocks
|
|
30
|
+
// (__tryDepth): the count is kept across an await and put back around a
|
|
31
|
+
// spawn and a resume.
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------- host
|
|
34
|
+
|
|
35
|
+
// @ts-ignore: decorator
|
|
36
|
+
@external("env", "co_entered") declare function __co_host_entered(): i32;
|
|
37
|
+
// @ts-ignore: decorator
|
|
38
|
+
@external("env", "co_spawn") declare function __co_host_spawn(fn: i32, args: usize, cells: i32, id: i32, buffer: usize): i32;
|
|
39
|
+
// @ts-ignore: decorator
|
|
40
|
+
@external("env", "co_suspend") declare function __co_host_suspend(drop: i32): void;
|
|
41
|
+
// @ts-ignore: decorator
|
|
42
|
+
@external("env", "co_wake") declare function __co_host_wake(): void;
|
|
43
|
+
// @ts-ignore: decorator
|
|
44
|
+
@external("env", "co_id") declare function __co_host_id(): i32;
|
|
45
|
+
// @ts-ignore: decorator
|
|
46
|
+
@external("env", "task") declare function __co_host_task(secondsBits: i32, fn: i32, id: i32, repeat: i32): i32;
|
|
47
|
+
// @ts-ignore: decorator
|
|
48
|
+
@external("env", "stop_task") declare function __co_host_stop_task(id: i32): i32;
|
|
49
|
+
// @ts-ignore: decorator
|
|
50
|
+
@external("env", "on") declare function __co_host_on(event: string, fn: i32, shape: i32): void;
|
|
51
|
+
|
|
52
|
+
// What co_spawn and a resume end with. Must match CO_* in runtime/src/module.cpp.
|
|
53
|
+
const __CO_FINISHED: i32 = 0;
|
|
54
|
+
const __CO_PARKED: i32 = 1;
|
|
55
|
+
const __CO_TRAPPED: i32 = 2;
|
|
56
|
+
const __CO_DROPPED: i32 = 3;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Bytes Asyncify may use to keep one parked coroutine: the locals of the
|
|
60
|
+
* async function and of __await. A deep async function needs a few hundred;
|
|
61
|
+
* running out is reported by the host rather than trapping silently.
|
|
62
|
+
*/
|
|
63
|
+
const __CO_BUFFER: i32 = 4096;
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------- jobs
|
|
66
|
+
|
|
67
|
+
/** A microtask: a reaction to a settled promise, or a coroutine to resume. */
|
|
68
|
+
class __Job {
|
|
69
|
+
/** The coroutine to resume, when this is one; the host resumes it. */
|
|
70
|
+
coroutine: i32 = 0;
|
|
71
|
+
/** Which wait of that coroutine this resumes - a stale one is skipped. */
|
|
72
|
+
wait: i32 = 0;
|
|
73
|
+
|
|
74
|
+
run(): void {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// @ts-ignore: decorator
|
|
78
|
+
@lazy const __jobs: (__Job | null)[] = [];
|
|
79
|
+
// @ts-ignore: decorator
|
|
80
|
+
@lazy let __jobHead: i32 = 0;
|
|
81
|
+
// @ts-ignore: decorator
|
|
82
|
+
@lazy let __woken = false;
|
|
83
|
+
// @ts-ignore: decorator
|
|
84
|
+
@lazy const __unhandled: PromiseBase[] = [];
|
|
85
|
+
|
|
86
|
+
function __co_queue(job: __Job): void {
|
|
87
|
+
__jobs.push(job);
|
|
88
|
+
__co_wake_up();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Tells the host there is work for when the plugin's stack is empty. */
|
|
92
|
+
function __co_wake_up(): void {
|
|
93
|
+
if (__woken) return;
|
|
94
|
+
__woken = true;
|
|
95
|
+
__co_host_wake();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------- promises
|
|
99
|
+
|
|
100
|
+
const __PENDING: i32 = 0;
|
|
101
|
+
const __FULFILLED: i32 = 1;
|
|
102
|
+
const __REJECTED: i32 = 2;
|
|
103
|
+
|
|
104
|
+
// Its state, and its value as raw storage - a reference, or the bits of a
|
|
105
|
+
// number - so the host and the compiler can settle one without knowing T.
|
|
106
|
+
/** The base of every Promise, whatever its value's type; a plugin uses Promise<T>. */
|
|
107
|
+
// @ts-ignore: decorator
|
|
108
|
+
@global export class PromiseBase {
|
|
109
|
+
/** @hidden */ __state: i32 = 0;
|
|
110
|
+
/** @hidden settled with a value, not with nothing - an async listener's answer. */
|
|
111
|
+
__hasValue: bool = false;
|
|
112
|
+
/** @hidden */ __ref: Object | null = null;
|
|
113
|
+
/** @hidden */ __bits: u64 = 0;
|
|
114
|
+
/** @hidden */ __reason: Error | null = null;
|
|
115
|
+
/** @hidden something reacts to a rejection: then, catch, await. */
|
|
116
|
+
__handled: bool = false;
|
|
117
|
+
/** @hidden */ __reactions: __Job[] | null = null;
|
|
118
|
+
|
|
119
|
+
/** @hidden Settles with a reference - like the promise it is given, as in JavaScript, when it is one. */
|
|
120
|
+
__resolveRef(ref: Object | null): void {
|
|
121
|
+
if (ref instanceof PromiseBase) {
|
|
122
|
+
(ref as PromiseBase).__react(new __AdoptJob(ref as PromiseBase, this));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.__fulfillRaw(ref, 0, true);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** @hidden */
|
|
129
|
+
__fulfillRaw(ref: Object | null, bits: u64, hasValue: bool): void {
|
|
130
|
+
if (this.__state != __PENDING) return;
|
|
131
|
+
this.__state = __FULFILLED;
|
|
132
|
+
this.__ref = ref;
|
|
133
|
+
this.__bits = bits;
|
|
134
|
+
this.__hasValue = hasValue;
|
|
135
|
+
this.__flush();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @hidden settled with nothing: `return;`, resolve(). */
|
|
139
|
+
__fulfillVoid(): void {
|
|
140
|
+
this.__fulfillRaw(null, 0, false);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @hidden */
|
|
144
|
+
__reject(reason: Error): void {
|
|
145
|
+
if (this.__state != __PENDING) return;
|
|
146
|
+
this.__state = __REJECTED;
|
|
147
|
+
this.__reason = reason;
|
|
148
|
+
// Nobody to hear it yet: reported when the jobs run out, unless a
|
|
149
|
+
// handler is attached before then.
|
|
150
|
+
if (!this.__handled) {
|
|
151
|
+
__unhandled.push(this);
|
|
152
|
+
__co_wake_up();
|
|
153
|
+
}
|
|
154
|
+
this.__flush();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @hidden the same state as `other`, which has settled. */
|
|
158
|
+
__settleLike(other: PromiseBase): void {
|
|
159
|
+
if (other.__state == __REJECTED) {
|
|
160
|
+
this.__reject(other.__reason as Error);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// A race of an integer and a number is a number: the integer's bits,
|
|
165
|
+
// read as one.
|
|
166
|
+
const bits = this.__isFloat() && !other.__isFloat() ? reinterpret<u64>(<f64><i64>other.__bits) : other.__bits;
|
|
167
|
+
this.__fulfillRaw(other.__ref, bits, other.__hasValue);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @hidden whether its value is stored as a float's bits. */
|
|
171
|
+
__isFloat(): bool {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** @hidden runs `job` once this settles - now, as a microtask, if it has. */
|
|
176
|
+
__react(job: __Job): void {
|
|
177
|
+
this.__handled = true;
|
|
178
|
+
if (this.__state != __PENDING) {
|
|
179
|
+
__co_queue(job);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
let reactions = this.__reactions;
|
|
183
|
+
if (!reactions) this.__reactions = reactions = [];
|
|
184
|
+
reactions.push(job);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private __flush(): void {
|
|
188
|
+
const reactions = this.__reactions;
|
|
189
|
+
if (!reactions) return;
|
|
190
|
+
this.__reactions = null;
|
|
191
|
+
for (let i = 0; i < reactions.length; i++) __co_queue(reactions[i]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Only the editor reads it (TypeScript asks for the global); the compiler
|
|
196
|
+
// awaits a Promise and nothing else.
|
|
197
|
+
/** Anything with a `then`: the type `await` takes in the editor. In a plugin, `await` a Promise. */
|
|
198
|
+
export interface PromiseLike<T> {
|
|
199
|
+
/** Calls `onFulfilled` with the value once there is one. */
|
|
200
|
+
then(onFulfilled: (value: T) => void): void;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** A value that is not there yet: the result of a request, a timer, an async function. */
|
|
204
|
+
// @ts-ignore: decorator
|
|
205
|
+
@global export class Promise<T> extends PromiseBase {
|
|
206
|
+
/**
|
|
207
|
+
* Wraps a callback API: `executor` runs at once and is handed the two
|
|
208
|
+
* functions that settle this promise, which the callbacks it sets up may
|
|
209
|
+
* call later.
|
|
210
|
+
*
|
|
211
|
+
* ```ts
|
|
212
|
+
* const later = new Promise<string>((resolve) => {
|
|
213
|
+
* setTimeout(() => resolve("done"), 1000);
|
|
214
|
+
* });
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
constructor(executor: (resolve: (value?: T) => void, reject: (reason: Error) => void) => void) {
|
|
218
|
+
super();
|
|
219
|
+
executor(__co_resolver<T>(this), __co_rejecter(this));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Calls `onFulfilled` with the value once there is one; the promise it
|
|
224
|
+
* returns settles with what that returns. A rejection passes through to
|
|
225
|
+
* it untouched, or to `onRejected` when there is one.
|
|
226
|
+
*/
|
|
227
|
+
then<U = void>(onFulfilled: (value: T) => U, onRejected: ((reason: Error) => U) | null = null): Promise<U> {
|
|
228
|
+
const next = __co_promise<U>();
|
|
229
|
+
this.__react(new __ThenJob<T, U>(this, next, onFulfilled, onRejected));
|
|
230
|
+
return next;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Calls `onRejected` with the reason if this is rejected. What it returns
|
|
235
|
+
* is the value instead: `readFile(path).catch(() => "")` - or nothing, when
|
|
236
|
+
* it only logs.
|
|
237
|
+
*/
|
|
238
|
+
catch<R = void>(onRejected: (reason: Error) => R): Promise<T> {
|
|
239
|
+
const next = __co_promise<T>();
|
|
240
|
+
this.__react(new __CatchJob<T, R>(this, next, onRejected));
|
|
241
|
+
return next;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Calls `onFinally` once this settles, either way, and passes the outcome on. */
|
|
245
|
+
finally(onFinally: () => void): Promise<T> {
|
|
246
|
+
const next = __co_promise<T>();
|
|
247
|
+
this.__react(new __FinallyJob<T>(this, next, onFinally));
|
|
248
|
+
return next;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** A promise already fulfilled with `value`. */
|
|
252
|
+
static resolve<T>(value: T): Promise<T> {
|
|
253
|
+
const promise = __co_promise<T>();
|
|
254
|
+
promise.__resolveWith(value);
|
|
255
|
+
return promise;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** A promise already rejected with `reason`. */
|
|
259
|
+
static reject<T = void>(reason: Error): Promise<T> {
|
|
260
|
+
const promise = __co_promise<T>();
|
|
261
|
+
promise.__reject(reason);
|
|
262
|
+
return promise;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** @hidden */
|
|
266
|
+
__isFloat(): bool {
|
|
267
|
+
return isFloat<T>();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** @hidden the value, once fulfilled. */
|
|
271
|
+
__value(): T {
|
|
272
|
+
if (isReference<T>()) return changetype<T>(this.__ref);
|
|
273
|
+
else if (isFloat<T>()) return <T>reinterpret<f64>(this.__bits);
|
|
274
|
+
else return <T><i64>this.__bits;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** @hidden */
|
|
278
|
+
__resolveWith(value: T): void {
|
|
279
|
+
if (isReference<T>()) {
|
|
280
|
+
this.__resolveRef(changetype<Object | null>(changetype<usize>(value)));
|
|
281
|
+
} else if (isFloat<T>()) {
|
|
282
|
+
this.__fulfillRaw(null, reinterpret<u64>(<f64>value), true);
|
|
283
|
+
} else {
|
|
284
|
+
this.__fulfillRaw(null, <u64><i64>value, true);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** @hidden a pending Promise<T>, made without an executor. */
|
|
290
|
+
// @ts-ignore: decorator
|
|
291
|
+
@global export function __co_promise<T>(): Promise<T> {
|
|
292
|
+
// @ts-ignore: the runtime's allocator, which the editor's typings leave out
|
|
293
|
+
return changetype<Promise<T>>(__new(offsetof<Promise<T>>(), idof<Promise<T>>()));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** @hidden fulfils `promise` from outside: what fetch and sleep settle with. */
|
|
297
|
+
// @ts-ignore: decorator
|
|
298
|
+
@global export function __co_resolve<T>(promise: Promise<T>, value: T): void {
|
|
299
|
+
promise.__resolveWith(value);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** @hidden */
|
|
303
|
+
// @ts-ignore: decorator
|
|
304
|
+
@global export function __co_reject(promise: PromiseBase, reason: Error): void {
|
|
305
|
+
promise.__reject(reason);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
class __ThenJob<T, U> extends __Job {
|
|
309
|
+
constructor(
|
|
310
|
+
public source: Promise<T>,
|
|
311
|
+
public target: Promise<U>,
|
|
312
|
+
public onFulfilled: (value: T) => U,
|
|
313
|
+
public onRejected: ((reason: Error) => U) | null
|
|
314
|
+
) {
|
|
315
|
+
super();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
run(): void {
|
|
319
|
+
const source = this.source;
|
|
320
|
+
const target = this.target;
|
|
321
|
+
if (source.__state == __FULFILLED) {
|
|
322
|
+
const fn = this.onFulfilled;
|
|
323
|
+
if (isVoid<U>()) {
|
|
324
|
+
fn(source.__value());
|
|
325
|
+
target.__fulfillVoid();
|
|
326
|
+
} else {
|
|
327
|
+
target.__resolveWith(fn(source.__value()));
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const reason = source.__reason as Error;
|
|
332
|
+
const fn = this.onRejected;
|
|
333
|
+
if (!fn) {
|
|
334
|
+
target.__reject(reason);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (isVoid<U>()) {
|
|
338
|
+
fn(reason);
|
|
339
|
+
target.__fulfillVoid();
|
|
340
|
+
} else {
|
|
341
|
+
target.__resolveWith(fn(reason));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
class __CatchJob<T, R> extends __Job {
|
|
347
|
+
constructor(public source: Promise<T>, public target: Promise<T>, public onRejected: (reason: Error) => R) {
|
|
348
|
+
super();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
run(): void {
|
|
352
|
+
const source = this.source;
|
|
353
|
+
if (source.__state == __FULFILLED) {
|
|
354
|
+
this.target.__settleLike(source);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const fn = this.onRejected;
|
|
358
|
+
if (isVoid<R>()) {
|
|
359
|
+
fn(source.__reason as Error);
|
|
360
|
+
this.target.__fulfillVoid();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// `.catch((error) => fallback)`: the handler's answer is the value, as
|
|
364
|
+
// in JavaScript. It used to be dropped, and an awaited
|
|
365
|
+
// `readFile(path).catch(() => "")` came back as an empty pointer.
|
|
366
|
+
this.target.__resolveWith(changetype<T>(fn(source.__reason as Error)));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
class __FinallyJob<T> extends __Job {
|
|
371
|
+
constructor(public source: Promise<T>, public target: Promise<T>, public onFinally: () => void) {
|
|
372
|
+
super();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
run(): void {
|
|
376
|
+
const fn = this.onFinally;
|
|
377
|
+
fn();
|
|
378
|
+
this.target.__settleLike(this.source);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
class __AdoptJob extends __Job {
|
|
383
|
+
constructor(public source: PromiseBase, public target: PromiseBase) {
|
|
384
|
+
super();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
run(): void {
|
|
388
|
+
this.target.__settleLike(this.source);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Promise.all, allSettled, race and any. They are not members of Promise:
|
|
393
|
+
// the editor reads them from promise.types.d.ts, typed with TypeScript's
|
|
394
|
+
// tuples, and the compiler lowers a call to one of the functions here
|
|
395
|
+
// instead (Resolver.lowerPromiseCall) - a list, or a literal of one type, to
|
|
396
|
+
// __co_all and the rest; a literal of different types to __co_allN, whose
|
|
397
|
+
// value is a __TupleN.
|
|
398
|
+
|
|
399
|
+
class __AllState<T> {
|
|
400
|
+
results: T[];
|
|
401
|
+
remaining: i32;
|
|
402
|
+
|
|
403
|
+
constructor(public target: Promise<T[]>, count: i32) {
|
|
404
|
+
this.results = new Array<T>(count);
|
|
405
|
+
this.remaining = count;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
class __AllJob<T> extends __Job {
|
|
410
|
+
constructor(public state: __AllState<T>, public source: Promise<T>, public index: i32) {
|
|
411
|
+
super();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
run(): void {
|
|
415
|
+
const state = this.state;
|
|
416
|
+
const source = this.source;
|
|
417
|
+
if (source.__state == __REJECTED) {
|
|
418
|
+
state.target.__reject(source.__reason as Error);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
state.results[this.index] = source.__value();
|
|
422
|
+
if (--state.remaining == 0) state.target.__resolveWith(state.results);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** @hidden Promise.all: every value, in order, once all are; rejected with the first rejection. */
|
|
427
|
+
// @ts-ignore: decorator
|
|
428
|
+
@global function __co_all<T>(values: Promise<T>[]): Promise<T[]> {
|
|
429
|
+
const promise = __co_promise<T[]>();
|
|
430
|
+
const state = new __AllState<T>(promise, values.length);
|
|
431
|
+
if (values.length == 0) promise.__resolveWith(state.results);
|
|
432
|
+
for (let i = 0; i < values.length; i++) values[i].__react(new __AllJob<T>(state, values[i], i));
|
|
433
|
+
return promise;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** @hidden Promise.all of a list of values, not promises: settled with a copy of the list. */
|
|
437
|
+
// @ts-ignore: decorator
|
|
438
|
+
@global function __co_allValues<T>(values: T[]): Promise<T[]> {
|
|
439
|
+
const promise = __co_promise<T[]>();
|
|
440
|
+
promise.__resolveWith(values.slice());
|
|
441
|
+
return promise;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** A promise's outcome, as Promise.allSettled gives it for each. */
|
|
445
|
+
// @ts-ignore: decorator
|
|
446
|
+
@global export class PromiseSettledResult<T> {
|
|
447
|
+
/** @hidden made by allSettled, field by field: the constructor never runs. */
|
|
448
|
+
constructor(
|
|
449
|
+
/** The promise's outcome, either `"fulfilled"` or `"rejected"`. */
|
|
450
|
+
public status: "fulfilled" | "rejected",
|
|
451
|
+
/** The promise's value, when fulfilled. */
|
|
452
|
+
public value: T,
|
|
453
|
+
/** The rejection's reason, when rejected. */
|
|
454
|
+
public reason: Error
|
|
455
|
+
) {}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* @hidden an object of class T with every field zero, its constructor not
|
|
460
|
+
* run: a result or a tuple whose fields are filled in one by one.
|
|
461
|
+
*/
|
|
462
|
+
function __co_blank<T>(): T {
|
|
463
|
+
// @ts-ignore: the runtime's allocator, which the editor's typings leave out
|
|
464
|
+
return changetype<T>(__new(offsetof<T>(), idof<T>()));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
class __SettleJob<T> extends __Job {
|
|
468
|
+
constructor(public source: Promise<T>, public target: Promise<PromiseSettledResult<T>>) {
|
|
469
|
+
super();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
run(): void {
|
|
473
|
+
const source = this.source;
|
|
474
|
+
const result = __co_blank<PromiseSettledResult<T>>();
|
|
475
|
+
if (source.__state == __REJECTED) {
|
|
476
|
+
result.status = "rejected";
|
|
477
|
+
result.reason = source.__reason as Error;
|
|
478
|
+
} else {
|
|
479
|
+
result.status = "fulfilled";
|
|
480
|
+
result.value = source.__value();
|
|
481
|
+
}
|
|
482
|
+
this.target.__resolveWith(result);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** @hidden a promise always fulfilled, with how `promise` settled. */
|
|
487
|
+
// @ts-ignore: decorator
|
|
488
|
+
@global function __co_settle<T>(promise: Promise<T>): Promise<PromiseSettledResult<T>> {
|
|
489
|
+
const settled = __co_promise<PromiseSettledResult<T>>();
|
|
490
|
+
promise.__react(new __SettleJob<T>(promise, settled));
|
|
491
|
+
return settled;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** @hidden Promise.allSettled: how each settled, in order, once all have. */
|
|
495
|
+
// @ts-ignore: decorator
|
|
496
|
+
@global function __co_allSettled<T>(values: Promise<T>[]): Promise<PromiseSettledResult<T>[]> {
|
|
497
|
+
const settled: Promise<PromiseSettledResult<T>>[] = [];
|
|
498
|
+
for (let i = 0; i < values.length; i++) settled.push(__co_settle<T>(values[i]));
|
|
499
|
+
return __co_all<PromiseSettledResult<T>>(settled);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* @hidden Promise.race: settles like the first of `values` to settle. The
|
|
504
|
+
* value is copied as it is stored, so T may be a base class of theirs, or
|
|
505
|
+
* nullable where one of them is a Promise<void>.
|
|
506
|
+
*/
|
|
507
|
+
// @ts-ignore: decorator
|
|
508
|
+
@global function __co_race<T>(values: PromiseBase[]): Promise<T> {
|
|
509
|
+
const promise = __co_promise<T>();
|
|
510
|
+
for (let i = 0; i < values.length; i++) values[i].__react(new __AdoptJob(values[i], promise));
|
|
511
|
+
return promise;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** @hidden Promise.race of a list. */
|
|
515
|
+
// @ts-ignore: decorator
|
|
516
|
+
@global function __co_raceList<T>(values: Promise<T>[]): Promise<T> {
|
|
517
|
+
return __co_race<T>(changetype<PromiseBase[]>(values));
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/** The error Promise.any rejects with when every promise is rejected; their reasons are in `errors`. */
|
|
521
|
+
// @ts-ignore: decorator
|
|
522
|
+
@global export class AggregateError extends Error {
|
|
523
|
+
constructor(
|
|
524
|
+
/** The reason each promise was rejected, in the order the promises were given. */
|
|
525
|
+
public errors: Error[],
|
|
526
|
+
message: string = ""
|
|
527
|
+
) {
|
|
528
|
+
super(message);
|
|
529
|
+
this.name = "AggregateError";
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
class __AnyState {
|
|
534
|
+
remaining: i32;
|
|
535
|
+
errors: Error[];
|
|
536
|
+
|
|
537
|
+
constructor(public target: PromiseBase, count: i32) {
|
|
538
|
+
this.remaining = count;
|
|
539
|
+
this.errors = new Array<Error>(count);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
class __AnyJob extends __Job {
|
|
544
|
+
constructor(public state: __AnyState, public source: PromiseBase, public index: i32) {
|
|
545
|
+
super();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
run(): void {
|
|
549
|
+
const state = this.state;
|
|
550
|
+
const source = this.source;
|
|
551
|
+
if (source.__state == __FULFILLED) {
|
|
552
|
+
state.target.__settleLike(source);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
state.errors[this.index] = source.__reason as Error;
|
|
556
|
+
if (--state.remaining == 0) state.target.__reject(new AggregateError(state.errors, "All promises were rejected"));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/** @hidden Promise.any: the first value; an AggregateError once every promise is rejected. */
|
|
561
|
+
// @ts-ignore: decorator
|
|
562
|
+
@global function __co_any<T>(values: PromiseBase[]): Promise<T> {
|
|
563
|
+
const promise = __co_promise<T>();
|
|
564
|
+
const state = new __AnyState(promise, values.length);
|
|
565
|
+
if (values.length == 0) promise.__reject(new AggregateError([], "All promises were rejected"));
|
|
566
|
+
for (let i = 0; i < values.length; i++) values[i].__react(new __AnyJob(state, values[i], i));
|
|
567
|
+
return promise;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** @hidden Promise.any of a list. */
|
|
571
|
+
// @ts-ignore: decorator
|
|
572
|
+
@global function __co_anyList<T>(values: Promise<T>[]): Promise<T> {
|
|
573
|
+
return __co_any<T>(changetype<PromiseBase[]>(values));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
class __TupleState {
|
|
577
|
+
constructor(public target: PromiseBase, public tuple: __Tuple, public remaining: i32) {}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
class __TupleJob extends __Job {
|
|
581
|
+
constructor(public state: __TupleState, public source: PromiseBase, public index: i32) {
|
|
582
|
+
super();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
run(): void {
|
|
586
|
+
const state = this.state;
|
|
587
|
+
const source = this.source;
|
|
588
|
+
if (source.__state == __REJECTED) {
|
|
589
|
+
state.target.__reject(source.__reason as Error);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
state.tuple.__setRaw(this.index, changetype<usize>(source.__ref), source.__bits);
|
|
593
|
+
if (--state.remaining == 0) state.target.__fulfillRaw(state.tuple, 0, true);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function __co_allOf(target: PromiseBase, tuple: __Tuple, sources: PromiseBase[]): void {
|
|
598
|
+
const state = new __TupleState(target, tuple, sources.length);
|
|
599
|
+
for (let i = 0; i < sources.length; i++) sources[i].__react(new __TupleJob(state, sources[i], i));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** @hidden Promise.all of 2 promises of different types. */
|
|
603
|
+
// @ts-ignore: decorator
|
|
604
|
+
@global function __co_all2<A, B>(a: Promise<A>, b: Promise<B>): Promise<__Tuple2<A, B>> {
|
|
605
|
+
const promise = __co_promise<__Tuple2<A, B>>();
|
|
606
|
+
__co_allOf(promise, __co_blank<__Tuple2<A, B>>(), [a, b]);
|
|
607
|
+
return promise;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** @hidden Promise.allSettled of 2 promises of different types. */
|
|
611
|
+
// @ts-ignore: decorator
|
|
612
|
+
@global function __co_allSettled2<A, B>(a: Promise<A>, b: Promise<B>): Promise<__Tuple2<PromiseSettledResult<A>, PromiseSettledResult<B>>> {
|
|
613
|
+
return __co_all2<PromiseSettledResult<A>, PromiseSettledResult<B>>(__co_settle<A>(a), __co_settle<B>(b));
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** @hidden Promise.all of 3 promises of different types. */
|
|
617
|
+
// @ts-ignore: decorator
|
|
618
|
+
@global function __co_all3<A, B, C>(a: Promise<A>, b: Promise<B>, c: Promise<C>): Promise<__Tuple3<A, B, C>> {
|
|
619
|
+
const promise = __co_promise<__Tuple3<A, B, C>>();
|
|
620
|
+
__co_allOf(promise, __co_blank<__Tuple3<A, B, C>>(), [a, b, c]);
|
|
621
|
+
return promise;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** @hidden Promise.allSettled of 3 promises of different types. */
|
|
625
|
+
// @ts-ignore: decorator
|
|
626
|
+
@global function __co_allSettled3<A, B, C>(a: Promise<A>, b: Promise<B>, c: Promise<C>): Promise<__Tuple3<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>>> {
|
|
627
|
+
return __co_all3<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** @hidden Promise.all of 4 promises of different types. */
|
|
631
|
+
// @ts-ignore: decorator
|
|
632
|
+
@global function __co_all4<A, B, C, D>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>): Promise<__Tuple4<A, B, C, D>> {
|
|
633
|
+
const promise = __co_promise<__Tuple4<A, B, C, D>>();
|
|
634
|
+
__co_allOf(promise, __co_blank<__Tuple4<A, B, C, D>>(), [a, b, c, d]);
|
|
635
|
+
return promise;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** @hidden Promise.allSettled of 4 promises of different types. */
|
|
639
|
+
// @ts-ignore: decorator
|
|
640
|
+
@global function __co_allSettled4<A, B, C, D>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>): Promise<__Tuple4<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>>> {
|
|
641
|
+
return __co_all4<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c), __co_settle<D>(d));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** @hidden Promise.all of 5 promises of different types. */
|
|
645
|
+
// @ts-ignore: decorator
|
|
646
|
+
@global function __co_all5<A, B, C, D, E>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>): Promise<__Tuple5<A, B, C, D, E>> {
|
|
647
|
+
const promise = __co_promise<__Tuple5<A, B, C, D, E>>();
|
|
648
|
+
__co_allOf(promise, __co_blank<__Tuple5<A, B, C, D, E>>(), [a, b, c, d, e]);
|
|
649
|
+
return promise;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** @hidden Promise.allSettled of 5 promises of different types. */
|
|
653
|
+
// @ts-ignore: decorator
|
|
654
|
+
@global function __co_allSettled5<A, B, C, D, E>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>): Promise<__Tuple5<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>>> {
|
|
655
|
+
return __co_all5<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c), __co_settle<D>(d), __co_settle<E>(e));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** @hidden Promise.all of 6 promises of different types. */
|
|
659
|
+
// @ts-ignore: decorator
|
|
660
|
+
@global function __co_all6<A, B, C, D, E, F>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>): Promise<__Tuple6<A, B, C, D, E, F>> {
|
|
661
|
+
const promise = __co_promise<__Tuple6<A, B, C, D, E, F>>();
|
|
662
|
+
__co_allOf(promise, __co_blank<__Tuple6<A, B, C, D, E, F>>(), [a, b, c, d, e, f]);
|
|
663
|
+
return promise;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** @hidden Promise.allSettled of 6 promises of different types. */
|
|
667
|
+
// @ts-ignore: decorator
|
|
668
|
+
@global function __co_allSettled6<A, B, C, D, E, F>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>): Promise<__Tuple6<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>>> {
|
|
669
|
+
return __co_all6<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c), __co_settle<D>(d), __co_settle<E>(e), __co_settle<F>(f));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/** @hidden Promise.all of 7 promises of different types. */
|
|
673
|
+
// @ts-ignore: decorator
|
|
674
|
+
@global function __co_all7<A, B, C, D, E, F, G>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>, g: Promise<G>): Promise<__Tuple7<A, B, C, D, E, F, G>> {
|
|
675
|
+
const promise = __co_promise<__Tuple7<A, B, C, D, E, F, G>>();
|
|
676
|
+
__co_allOf(promise, __co_blank<__Tuple7<A, B, C, D, E, F, G>>(), [a, b, c, d, e, f, g]);
|
|
677
|
+
return promise;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/** @hidden Promise.allSettled of 7 promises of different types. */
|
|
681
|
+
// @ts-ignore: decorator
|
|
682
|
+
@global function __co_allSettled7<A, B, C, D, E, F, G>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>, g: Promise<G>): Promise<__Tuple7<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>, PromiseSettledResult<G>>> {
|
|
683
|
+
return __co_all7<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>, PromiseSettledResult<G>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c), __co_settle<D>(d), __co_settle<E>(e), __co_settle<F>(f), __co_settle<G>(g));
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/** @hidden Promise.all of 8 promises of different types. */
|
|
687
|
+
// @ts-ignore: decorator
|
|
688
|
+
@global function __co_all8<A, B, C, D, E, F, G, H>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>, g: Promise<G>, h: Promise<H>): Promise<__Tuple8<A, B, C, D, E, F, G, H>> {
|
|
689
|
+
const promise = __co_promise<__Tuple8<A, B, C, D, E, F, G, H>>();
|
|
690
|
+
__co_allOf(promise, __co_blank<__Tuple8<A, B, C, D, E, F, G, H>>(), [a, b, c, d, e, f, g, h]);
|
|
691
|
+
return promise;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/** @hidden Promise.allSettled of 8 promises of different types. */
|
|
695
|
+
// @ts-ignore: decorator
|
|
696
|
+
@global function __co_allSettled8<A, B, C, D, E, F, G, H>(a: Promise<A>, b: Promise<B>, c: Promise<C>, d: Promise<D>, e: Promise<E>, f: Promise<F>, g: Promise<G>, h: Promise<H>): Promise<__Tuple8<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>, PromiseSettledResult<G>, PromiseSettledResult<H>>> {
|
|
697
|
+
return __co_all8<PromiseSettledResult<A>, PromiseSettledResult<B>, PromiseSettledResult<C>, PromiseSettledResult<D>, PromiseSettledResult<E>, PromiseSettledResult<F>, PromiseSettledResult<G>, PromiseSettledResult<H>>(__co_settle<A>(a), __co_settle<B>(b), __co_settle<C>(c), __co_settle<D>(d), __co_settle<E>(e), __co_settle<F>(f), __co_settle<G>(g), __co_settle<H>(h));
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// ---------------------------------------------------------------- resolve and reject
|
|
701
|
+
|
|
702
|
+
// The functions `new Promise(executor)` hands out. Each is a copy of one of
|
|
703
|
+
// these with the promise in its `_env`, which the compiler puts in __env - the
|
|
704
|
+
// global of its library that closures read theirs from - on every indirect
|
|
705
|
+
// call (Compiler.makeCallIndirect).
|
|
706
|
+
|
|
707
|
+
function __co_envPromise(): PromiseBase {
|
|
708
|
+
// @ts-ignore: the compiler library's, unknown to the editor
|
|
709
|
+
return changetype<PromiseBase>(__env);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function __co_resolveI32(value: i32 = 0): void {
|
|
713
|
+
__co_envPromise().__fulfillRaw(null, <u64><i64>value, true);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function __co_resolveRef(value: usize = 0): void {
|
|
717
|
+
__co_envPromise().__resolveRef(changetype<Object | null>(value));
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function __co_resolveI64(value: i64 = 0): void {
|
|
721
|
+
__co_envPromise().__fulfillRaw(null, <u64>value, true);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function __co_resolveF32(value: f32 = 0): void {
|
|
725
|
+
__co_envPromise().__fulfillRaw(null, reinterpret<u64>(<f64>value), true);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function __co_resolveF64(value: f64 = 0): void {
|
|
729
|
+
__co_envPromise().__fulfillRaw(null, reinterpret<u64>(value), true);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function __co_rejectBound(reason: Error): void {
|
|
733
|
+
__co_envPromise().__reject(reason);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* @hidden A function object like `fn`, with `env` in its `_env`; the
|
|
738
|
+
* collector follows `_env`. The function reads it back from __env.
|
|
739
|
+
*/
|
|
740
|
+
// @ts-ignore: decorator
|
|
741
|
+
@global export function __co_bindEnv(fn: usize, env: Object): usize {
|
|
742
|
+
// The copy is of the original's own class - its header's rtId, 8 bytes
|
|
743
|
+
// before it - since a Function class nothing else instantiates is not in
|
|
744
|
+
// the runtime's type table, and the collector stops at an unknown id.
|
|
745
|
+
// @ts-ignore: the runtime's allocator, which the editor's typings leave out
|
|
746
|
+
const copy: usize = __new(offsetof<(value?: i32) => void>(), load<u32>(fn - 8));
|
|
747
|
+
store<u32>(copy, load<u32>(fn));
|
|
748
|
+
store<usize>(copy, changetype<usize>(env), sizeof<u32>());
|
|
749
|
+
// @ts-ignore: the collector's write barrier, likewise
|
|
750
|
+
__link(copy, changetype<usize>(env), false);
|
|
751
|
+
return copy;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function __co_resolver<T>(promise: Promise<T>): (value?: T) => void {
|
|
755
|
+
let fn: usize;
|
|
756
|
+
if (isReference<T>()) fn = changetype<usize>(__co_resolveRef);
|
|
757
|
+
else if (isFloat<T>() && sizeof<T>() == 8) fn = changetype<usize>(__co_resolveF64);
|
|
758
|
+
else if (isFloat<T>()) fn = changetype<usize>(__co_resolveF32);
|
|
759
|
+
else if (sizeof<T>() == 8) fn = changetype<usize>(__co_resolveI64);
|
|
760
|
+
else fn = changetype<usize>(__co_resolveI32);
|
|
761
|
+
return changetype<(value?: T) => void>(__co_bindEnv(fn, promise));
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function __co_rejecter(promise: PromiseBase): (reason: Error) => void {
|
|
765
|
+
return changetype<(reason: Error) => void>(__co_bindEnv(changetype<usize>(__co_rejectBound), promise));
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// ---------------------------------------------------------------- coroutines
|
|
769
|
+
|
|
770
|
+
class __Coroutine {
|
|
771
|
+
/** Managed arguments of the call, which only its locals hold otherwise. */
|
|
772
|
+
roots: Object[] = [];
|
|
773
|
+
/** Its shadow stack while parked, where the collector sees it. */
|
|
774
|
+
stack: StaticArray<Object | null> | null = null;
|
|
775
|
+
/** Asyncify's buffer: where the host unwinds its locals to. */
|
|
776
|
+
buffer: usize = 0;
|
|
777
|
+
/** The player whose handler started it: its awaits end when they leave. */
|
|
778
|
+
player: i32 = 0;
|
|
779
|
+
/** The abort signal it runs under, once asked for. */
|
|
780
|
+
signal: AbortSignal | null = null;
|
|
781
|
+
/** Parked, waiting for a job to resume it. */
|
|
782
|
+
parked: bool = false;
|
|
783
|
+
/** Counts its waits, so a resume meant for an earlier one is dropped. */
|
|
784
|
+
wait: i32 = 0;
|
|
785
|
+
/** Why it is to give up at the next resume: its signal aborted. */
|
|
786
|
+
abortReason: Error | null = null;
|
|
787
|
+
watch: __CoroutineAbort | null = null;
|
|
788
|
+
watched: AbortSignal | null = null;
|
|
789
|
+
|
|
790
|
+
constructor(public id: i32, public promise: PromiseBase) {}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// @ts-ignore: decorator
|
|
794
|
+
@lazy const __coroutines = new Map<i32, __Coroutine>();
|
|
795
|
+
// @ts-ignore: decorator
|
|
796
|
+
@lazy const __running: __Coroutine[] = [];
|
|
797
|
+
// @ts-ignore: decorator
|
|
798
|
+
@lazy let __nextCoroutine: i32 = 1;
|
|
799
|
+
// @ts-ignore: decorator
|
|
800
|
+
@lazy const __spawnArgs = new StaticArray<u32>(64);
|
|
801
|
+
// @ts-ignore: decorator
|
|
802
|
+
@lazy const __keep: Object[] = [];
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* @hidden The player whose command or event is being handled, set by the
|
|
806
|
+
* facade around the handler: a coroutine started there runs under that
|
|
807
|
+
* player's signal.
|
|
808
|
+
*/
|
|
809
|
+
// @ts-ignore: decorator
|
|
810
|
+
@global let __co_ambient_player: i32 = 0;
|
|
811
|
+
|
|
812
|
+
function __co_current(): __Coroutine {
|
|
813
|
+
if (__running.length == 0) {
|
|
814
|
+
console.error("await outside an async function");
|
|
815
|
+
unreachable();
|
|
816
|
+
}
|
|
817
|
+
return __running[__running.length - 1];
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/** @hidden whether the host is calling an async function as its coroutine right now. */
|
|
821
|
+
// @ts-ignore: decorator
|
|
822
|
+
@global function __co_entered(): bool {
|
|
823
|
+
return __co_host_entered() != 0;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/** @hidden where an async function writes its arguments for the host. */
|
|
827
|
+
// @ts-ignore: decorator
|
|
828
|
+
@global function __co_args(): usize {
|
|
829
|
+
return changetype<usize>(__spawnArgs);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/** @hidden a managed argument, held while the call lasts. */
|
|
833
|
+
// @ts-ignore: decorator
|
|
834
|
+
@global function __co_keep(value: usize): void {
|
|
835
|
+
if (value != 0) __keep.push(changetype<Object>(value));
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/** @hidden runs an async function's body as a coroutine - see the top of this file. */
|
|
839
|
+
// @ts-ignore: decorator
|
|
840
|
+
@global function __co_spawn(fn: i32, cells: i32, promise: PromiseBase): void {
|
|
841
|
+
const co = new __Coroutine(__nextCoroutine++, promise);
|
|
842
|
+
for (let i = 0; i < __keep.length; i++) co.roots.push(__keep[i]);
|
|
843
|
+
__keep.length = 0;
|
|
844
|
+
|
|
845
|
+
// A player's command or event runs it under that player's signal; started
|
|
846
|
+
// from another coroutine otherwise, it runs under the same one.
|
|
847
|
+
if (__co_ambient_player > 0) {
|
|
848
|
+
co.player = __co_ambient_player;
|
|
849
|
+
} else if (__running.length > 0) {
|
|
850
|
+
const parent = __running[__running.length - 1];
|
|
851
|
+
co.player = parent.player;
|
|
852
|
+
co.signal = parent.signal;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
const buffer = heap.alloc(<usize>__CO_BUFFER);
|
|
856
|
+
store<usize>(buffer, buffer + 2 * sizeof<usize>());
|
|
857
|
+
store<usize>(buffer + sizeof<usize>(), buffer + <usize>__CO_BUFFER);
|
|
858
|
+
co.buffer = buffer;
|
|
859
|
+
__coroutines.set(co.id, co);
|
|
860
|
+
|
|
861
|
+
__running.push(co);
|
|
862
|
+
const depth = __tryDepth;
|
|
863
|
+
__tryDepth = 0;
|
|
864
|
+
const outcome = __co_host_spawn(fn, changetype<usize>(__spawnArgs), cells, co.id, buffer);
|
|
865
|
+
__tryDepth = depth;
|
|
866
|
+
__running.pop();
|
|
867
|
+
__co_after(co, outcome);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/** What a call into a coroutine came back with. */
|
|
871
|
+
function __co_after(co: __Coroutine, outcome: i32): void {
|
|
872
|
+
if (outcome == __CO_PARKED) return;
|
|
873
|
+
if (outcome == __CO_TRAPPED) {
|
|
874
|
+
// The host has said why, with the plugin's name. Whoever awaits it
|
|
875
|
+
// hears it as a rejection, which is not reported a second time.
|
|
876
|
+
co.promise.__reject(__co_abortError("the async function crashed"));
|
|
877
|
+
}
|
|
878
|
+
__co_finish(co);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function __co_finish(co: __Coroutine): void {
|
|
882
|
+
if (!__coroutines.has(co.id)) return;
|
|
883
|
+
__coroutines.delete(co.id);
|
|
884
|
+
__co_unwatch(co);
|
|
885
|
+
co.roots.length = 0;
|
|
886
|
+
co.stack = null;
|
|
887
|
+
if (co.buffer != 0) heap.free(co.buffer);
|
|
888
|
+
co.buffer = 0;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function __co_settled(co: __Coroutine): usize {
|
|
892
|
+
__co_finish(co);
|
|
893
|
+
return changetype<usize>(co.promise);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/** @hidden `return;` in an async function. */
|
|
897
|
+
// @ts-ignore: decorator
|
|
898
|
+
@global function __co_return_void(): usize {
|
|
899
|
+
const co = __co_current();
|
|
900
|
+
co.promise.__fulfillVoid();
|
|
901
|
+
return __co_settled(co);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/** @hidden `return value` in an async function, by how the value is carried. */
|
|
905
|
+
// @ts-ignore: decorator
|
|
906
|
+
@global function __co_return_i32(value: i32): usize {
|
|
907
|
+
const co = __co_current();
|
|
908
|
+
co.promise.__fulfillRaw(null, <u64><i64>value, true);
|
|
909
|
+
return __co_settled(co);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// @ts-ignore: decorator
|
|
913
|
+
@global function __co_return_i64(value: i64): usize {
|
|
914
|
+
const co = __co_current();
|
|
915
|
+
co.promise.__fulfillRaw(null, <u64>value, true);
|
|
916
|
+
return __co_settled(co);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// @ts-ignore: decorator
|
|
920
|
+
@global function __co_return_f64(value: f64): usize {
|
|
921
|
+
const co = __co_current();
|
|
922
|
+
co.promise.__fulfillRaw(null, reinterpret<u64>(value), true);
|
|
923
|
+
return __co_settled(co);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// @ts-ignore: decorator
|
|
927
|
+
@global function __co_return_ref(value: usize): usize {
|
|
928
|
+
const co = __co_current();
|
|
929
|
+
co.promise.__resolveRef(changetype<Object | null>(value));
|
|
930
|
+
return __co_settled(co);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** @hidden `throw error` in an async function: its Promise rejects with the error. */
|
|
934
|
+
// @ts-ignore: decorator
|
|
935
|
+
@global function __co_throw(reason: Error): usize {
|
|
936
|
+
const co = __co_current();
|
|
937
|
+
co.promise.__reject(reason);
|
|
938
|
+
return __co_settled(co);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/** @hidden an error that unwound out of an async function: its Promise rejects with it. */
|
|
942
|
+
// @ts-ignore: decorator
|
|
943
|
+
@global function __co_throwPending(): usize {
|
|
944
|
+
return __co_throw(__catch());
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
class __ResumeJob extends __Job {
|
|
948
|
+
constructor(id: i32, wait: i32) {
|
|
949
|
+
super();
|
|
950
|
+
this.coroutine = id;
|
|
951
|
+
this.wait = wait;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** @hidden `await promise`: parks the coroutine until it settles, then gives back its value. */
|
|
956
|
+
// @ts-ignore: decorator
|
|
957
|
+
@global function __await<T>(promise: Promise<T>): T {
|
|
958
|
+
__co_wait(promise);
|
|
959
|
+
return promise.__value();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function __co_wait(promise: PromiseBase): void {
|
|
963
|
+
const co = __co_current();
|
|
964
|
+
const wait = ++co.wait;
|
|
965
|
+
|
|
966
|
+
// As in JavaScript, even a settled promise is awaited: the rest of the
|
|
967
|
+
// function runs as a job, after what is already queued.
|
|
968
|
+
promise.__react(new __ResumeJob(co.id, wait));
|
|
969
|
+
|
|
970
|
+
const signal = __co_signal(co);
|
|
971
|
+
if (signal) {
|
|
972
|
+
if (signal.aborted) {
|
|
973
|
+
co.abortReason = signal.reason;
|
|
974
|
+
} else {
|
|
975
|
+
const watch = new __CoroutineAbort(co.id);
|
|
976
|
+
co.watch = watch;
|
|
977
|
+
co.watched = signal;
|
|
978
|
+
signal.__watch(watch);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
co.parked = true;
|
|
983
|
+
const depth = __tryDepth;
|
|
984
|
+
__co_host_suspend(0);
|
|
985
|
+
|
|
986
|
+
// Resumed. The signal it runs under aborted - its player left: it ends
|
|
987
|
+
// here, quietly. The promise was rejected: a `try` around the await takes
|
|
988
|
+
// the reason to its catch.
|
|
989
|
+
__tryDepth = depth;
|
|
990
|
+
__co_unwatch(co);
|
|
991
|
+
const aborted = co.abortReason;
|
|
992
|
+
if (aborted) __co_giveUp(co, aborted);
|
|
993
|
+
if (promise.__state != __REJECTED) return;
|
|
994
|
+
const reason = promise.__reason as Error;
|
|
995
|
+
if (depth == 0) __co_giveUp(co, reason);
|
|
996
|
+
__thrown = reason;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/** The awaited promise was rejected, or the signal aborted: the coroutine rejects with it and ends here. */
|
|
1000
|
+
function __co_giveUp(co: __Coroutine, reason: Error): void {
|
|
1001
|
+
co.promise.__reject(reason);
|
|
1002
|
+
__co_host_suspend(1);
|
|
1003
|
+
unreachable();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function __co_unwatch(co: __Coroutine): void {
|
|
1007
|
+
const watch = co.watch;
|
|
1008
|
+
const signal = co.watched;
|
|
1009
|
+
co.watch = null;
|
|
1010
|
+
co.watched = null;
|
|
1011
|
+
if (watch && signal) signal.__unwatch(watch);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/** The signal a coroutine runs under: its player's, while that player is on the server. */
|
|
1015
|
+
function __co_signal(co: __Coroutine): AbortSignal | null {
|
|
1016
|
+
let signal = co.signal;
|
|
1017
|
+
if (!signal && co.player > 0) co.signal = signal = __co_player_signal(co.player);
|
|
1018
|
+
return signal;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/** @hidden the signal the running coroutine is under, for fetch and sleep. */
|
|
1022
|
+
// @ts-ignore: decorator
|
|
1023
|
+
@global function __co_context(): AbortSignal | null {
|
|
1024
|
+
if (__running.length == 0) return null;
|
|
1025
|
+
return __co_signal(__running[__running.length - 1]);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/** @hidden the next job to run, run here, or the coroutine the host is to resume; 0 when none are left. */
|
|
1029
|
+
// @ts-ignore: decorator
|
|
1030
|
+
@global function __co_next_job(): i32 {
|
|
1031
|
+
while (__jobHead < __jobs.length) {
|
|
1032
|
+
const job = __jobs[__jobHead] as __Job;
|
|
1033
|
+
__jobs[__jobHead] = null;
|
|
1034
|
+
__jobHead++;
|
|
1035
|
+
|
|
1036
|
+
const id = job.coroutine;
|
|
1037
|
+
if (id == 0) {
|
|
1038
|
+
job.run();
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
if (!__coroutines.has(id)) continue;
|
|
1042
|
+
const co = __coroutines.get(id);
|
|
1043
|
+
if (!co.parked || co.wait != job.wait) continue;
|
|
1044
|
+
co.parked = false;
|
|
1045
|
+
__running.push(co);
|
|
1046
|
+
return id;
|
|
1047
|
+
}
|
|
1048
|
+
__jobs.length = 0;
|
|
1049
|
+
__jobHead = 0;
|
|
1050
|
+
__woken = false;
|
|
1051
|
+
__co_reportUnhandled();
|
|
1052
|
+
return 0;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/** @hidden a resume has come back from the host. */
|
|
1056
|
+
// @ts-ignore: decorator
|
|
1057
|
+
@global function __co_resumed(outcome: i32): void {
|
|
1058
|
+
__tryDepth = 0;
|
|
1059
|
+
const co = __running.pop();
|
|
1060
|
+
__co_after(co, outcome);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function __co_reportUnhandled(): void {
|
|
1064
|
+
for (let i = 0; i < __unhandled.length; i++) {
|
|
1065
|
+
const promise = __unhandled[i];
|
|
1066
|
+
if (promise.__handled) continue;
|
|
1067
|
+
const reason = promise.__reason as Error;
|
|
1068
|
+
// An abort is expected - a player left, a request was cancelled.
|
|
1069
|
+
if (reason.name == "AbortError" || reason.name == "TimeoutError") continue;
|
|
1070
|
+
console.error(`Unhandled promise rejection: ${reason.message}`);
|
|
1071
|
+
}
|
|
1072
|
+
__unhandled.length = 0;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// The shadow stack of a parked coroutine. AssemblyScript keeps the managed
|
|
1076
|
+
// locals of every frame in linear memory below __stack_pointer, where the
|
|
1077
|
+
// collector scans them; a parked coroutine's frames would be overwritten by
|
|
1078
|
+
// whatever runs next, so they are copied here, into memory the collector
|
|
1079
|
+
// traces, and copied back to the same addresses when it resumes.
|
|
1080
|
+
|
|
1081
|
+
/** @hidden */
|
|
1082
|
+
// @ts-ignore: decorator
|
|
1083
|
+
@global function __co_sp(): usize {
|
|
1084
|
+
return __stack_pointer;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/** @hidden */
|
|
1088
|
+
// @ts-ignore: decorator
|
|
1089
|
+
@global function __co_set_sp(sp: usize): void {
|
|
1090
|
+
__stack_pointer = sp;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/** @hidden */
|
|
1094
|
+
// @ts-ignore: decorator
|
|
1095
|
+
@global function __co_park(id: i32, lo: usize, hi: usize): void {
|
|
1096
|
+
if (!__coroutines.has(id)) return;
|
|
1097
|
+
const saved = new StaticArray<Object | null>(<i32>((hi - lo) / sizeof<usize>()));
|
|
1098
|
+
memory.copy(changetype<usize>(saved), lo, hi - lo);
|
|
1099
|
+
__coroutines.get(id).stack = saved;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/** @hidden bytes of shadow stack a parked coroutine holds. */
|
|
1103
|
+
// @ts-ignore: decorator
|
|
1104
|
+
@global function __co_parked(id: i32): usize {
|
|
1105
|
+
if (!__coroutines.has(id)) return 0;
|
|
1106
|
+
const saved = __coroutines.get(id).stack;
|
|
1107
|
+
return saved ? <usize>saved.length * sizeof<usize>() : 0;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/** @hidden */
|
|
1111
|
+
// @ts-ignore: decorator
|
|
1112
|
+
@global function __co_unpark(id: i32, lo: usize): void {
|
|
1113
|
+
if (!__coroutines.has(id)) return;
|
|
1114
|
+
const co = __coroutines.get(id);
|
|
1115
|
+
const saved = co.stack;
|
|
1116
|
+
if (!saved) return;
|
|
1117
|
+
memory.copy(lo, changetype<usize>(saved), <usize>saved.length * sizeof<usize>());
|
|
1118
|
+
co.stack = null;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// ---------------------------------------------------------------- abort
|
|
1122
|
+
|
|
1123
|
+
/** The event an abort listener gets. */
|
|
1124
|
+
// @ts-ignore: decorator
|
|
1125
|
+
@global export class Event {
|
|
1126
|
+
constructor(
|
|
1127
|
+
/** The event's type; the only one here is `"abort"`. */
|
|
1128
|
+
public type: string
|
|
1129
|
+
) {}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** @hidden something the hood does when a signal aborts. */
|
|
1133
|
+
// @ts-ignore: decorator
|
|
1134
|
+
@global export class __AbortWatch {
|
|
1135
|
+
run(reason: Error): void {}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/** A signal to give something up: a request, a timer, everything a player started. */
|
|
1139
|
+
// @ts-ignore: decorator
|
|
1140
|
+
@global export class AbortSignal {
|
|
1141
|
+
private __aborted: bool = false;
|
|
1142
|
+
private __reason: Error | null = null;
|
|
1143
|
+
private __listeners: ((event: Event) => void)[] = [];
|
|
1144
|
+
private __watches: __AbortWatch[] = [];
|
|
1145
|
+
|
|
1146
|
+
/** `true` once the signal has aborted. */
|
|
1147
|
+
get aborted(): bool {
|
|
1148
|
+
return this.__aborted;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/** The abort's reason: an Error named `"AbortError"` unless `abort()` was given one. */
|
|
1152
|
+
get reason(): Error | null {
|
|
1153
|
+
return this.__reason;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/** Calls `listener` when the signal aborts. */
|
|
1157
|
+
addEventListener(type: "abort", listener: (event: Event) => void): void {
|
|
1158
|
+
this.__listeners.push(listener);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/** Takes back a `listener` given to `addEventListener`: it is not called any more. */
|
|
1162
|
+
removeEventListener(type: "abort", listener: (event: Event) => void): void {
|
|
1163
|
+
const at = this.__listeners.indexOf(listener);
|
|
1164
|
+
if (at >= 0) this.__listeners.splice(at, 1);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/** A signal that aborts by itself after `ms`, with an Error named `"TimeoutError"`. */
|
|
1168
|
+
static timeout(ms: number): AbortSignal {
|
|
1169
|
+
const signal = new AbortSignal();
|
|
1170
|
+
const id = __co_host_id();
|
|
1171
|
+
__timeouts.set(id, signal);
|
|
1172
|
+
__co_host_task(reinterpret<i32>(<f32>(ms / 1000.0)), __co_timeoutFired.index, id, 0);
|
|
1173
|
+
return signal;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/** A signal that has aborted already. */
|
|
1177
|
+
static abort(reason: Error | null = null): AbortSignal {
|
|
1178
|
+
const signal = new AbortSignal();
|
|
1179
|
+
signal.__abort(reason ? reason : __co_abortError("This operation was aborted"));
|
|
1180
|
+
return signal;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/** A signal that aborts when any of `signals` does. */
|
|
1184
|
+
static any(signals: AbortSignal[]): AbortSignal {
|
|
1185
|
+
const signal = new AbortSignal();
|
|
1186
|
+
for (let i = 0; i < signals.length; i++) {
|
|
1187
|
+
const one = signals[i];
|
|
1188
|
+
if (one.aborted) {
|
|
1189
|
+
signal.__abort(one.reason as Error);
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
one.__watch(new __AnyAbort(signal));
|
|
1193
|
+
}
|
|
1194
|
+
return signal;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
/** @hidden */
|
|
1198
|
+
__abort(reason: Error): void {
|
|
1199
|
+
if (this.__aborted) return;
|
|
1200
|
+
this.__aborted = true;
|
|
1201
|
+
this.__reason = reason;
|
|
1202
|
+
const watches = this.__watches;
|
|
1203
|
+
this.__watches = [];
|
|
1204
|
+
for (let i = 0; i < watches.length; i++) watches[i].run(reason);
|
|
1205
|
+
const listeners = this.__listeners.slice(0);
|
|
1206
|
+
const event = new Event("abort");
|
|
1207
|
+
for (let i = 0; i < listeners.length; i++) listeners[i](event);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/** @hidden */
|
|
1211
|
+
__watch(watch: __AbortWatch): void {
|
|
1212
|
+
this.__watches.push(watch);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/** @hidden */
|
|
1216
|
+
__unwatch(watch: __AbortWatch): void {
|
|
1217
|
+
const at = this.__watches.indexOf(watch);
|
|
1218
|
+
if (at >= 0) this.__watches.splice(at, 1);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/** A controller that aborts its `signal` on demand: `controller.abort()`. */
|
|
1223
|
+
// @ts-ignore: decorator
|
|
1224
|
+
@global export class AbortController {
|
|
1225
|
+
/** The controller's signal: hand it to fetch, sleep or anything else that takes one. */
|
|
1226
|
+
readonly signal: AbortSignal = new AbortSignal();
|
|
1227
|
+
|
|
1228
|
+
/** Aborts the signal, with `reason` or an Error named `"AbortError"`. */
|
|
1229
|
+
abort(reason: Error | null = null): void {
|
|
1230
|
+
this.signal.__abort(reason ? reason : __co_abortError("This operation was aborted"));
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/** @hidden an Error named as an abort: expected, never reported as unhandled. */
|
|
1235
|
+
// @ts-ignore: decorator
|
|
1236
|
+
@global function __co_abortError(message: string): Error {
|
|
1237
|
+
const error = new Error(message);
|
|
1238
|
+
error.name = "AbortError";
|
|
1239
|
+
return error;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
class __AnyAbort extends __AbortWatch {
|
|
1243
|
+
constructor(public signal: AbortSignal) {
|
|
1244
|
+
super();
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
run(reason: Error): void {
|
|
1248
|
+
this.signal.__abort(reason);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
class __CoroutineAbort extends __AbortWatch {
|
|
1253
|
+
constructor(public id: i32) {
|
|
1254
|
+
super();
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
run(reason: Error): void {
|
|
1258
|
+
if (!__coroutines.has(this.id)) return;
|
|
1259
|
+
const co = __coroutines.get(this.id);
|
|
1260
|
+
if (!co.parked) return;
|
|
1261
|
+
co.abortReason = reason;
|
|
1262
|
+
__co_queue(new __ResumeJob(co.id, co.wait));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// @ts-ignore: decorator
|
|
1267
|
+
@lazy const __timeouts = new Map<i32, AbortSignal>();
|
|
1268
|
+
|
|
1269
|
+
function __co_timeoutFired(id: i32): void {
|
|
1270
|
+
if (!__timeouts.has(id)) return;
|
|
1271
|
+
const signal = __timeouts.get(id);
|
|
1272
|
+
__timeouts.delete(id);
|
|
1273
|
+
const error = new Error("The operation was aborted due to timeout");
|
|
1274
|
+
error.name = "TimeoutError";
|
|
1275
|
+
signal.__abort(error);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* @hidden watches up to two signals for an operation - the one it was given
|
|
1280
|
+
* and the one its coroutine runs under - and lets go of both when it ends.
|
|
1281
|
+
*/
|
|
1282
|
+
// @ts-ignore: decorator
|
|
1283
|
+
@global export class __AbortGuard {
|
|
1284
|
+
private signals: AbortSignal[] = [];
|
|
1285
|
+
|
|
1286
|
+
constructor(public watch: __AbortWatch, given: AbortSignal | null) {
|
|
1287
|
+
this.add(given);
|
|
1288
|
+
const context = __co_context();
|
|
1289
|
+
if (context != given) this.add(context);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
private add(signal: AbortSignal | null): void {
|
|
1293
|
+
if (!signal) return;
|
|
1294
|
+
this.signals.push(signal);
|
|
1295
|
+
signal.__watch(this.watch);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/** The reason if a signal has aborted already. */
|
|
1299
|
+
get aborted(): Error | null {
|
|
1300
|
+
for (let i = 0; i < this.signals.length; i++) {
|
|
1301
|
+
if (this.signals[i].aborted) return this.signals[i].reason;
|
|
1302
|
+
}
|
|
1303
|
+
return null;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
release(): void {
|
|
1307
|
+
for (let i = 0; i < this.signals.length; i++) this.signals[i].__unwatch(this.watch);
|
|
1308
|
+
this.signals.length = 0;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// ---------------------------------------------------------------- sleep
|
|
1313
|
+
|
|
1314
|
+
class __Sleep extends __AbortWatch {
|
|
1315
|
+
guard: __AbortGuard | null = null;
|
|
1316
|
+
|
|
1317
|
+
constructor(public promise: Promise<void>, public id: i32) {
|
|
1318
|
+
super();
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
run(reason: Error): void {
|
|
1322
|
+
if (!__sleeps.has(this.id)) return;
|
|
1323
|
+
__sleeps.delete(this.id);
|
|
1324
|
+
__co_host_stop_task(this.id);
|
|
1325
|
+
(this.guard as __AbortGuard).release();
|
|
1326
|
+
this.promise.__reject(reason);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// @ts-ignore: decorator
|
|
1331
|
+
@lazy const __sleeps = new Map<i32, __Sleep>();
|
|
1332
|
+
|
|
1333
|
+
/** @hidden the facade's sleep(): fulfilled after `ms`, rejected if a signal aborts first. */
|
|
1334
|
+
// @ts-ignore: decorator
|
|
1335
|
+
@global export function __co_sleep(ms: f64, signal: AbortSignal | null): Promise<void> {
|
|
1336
|
+
const promise = __co_promise<void>();
|
|
1337
|
+
const id = __co_host_id();
|
|
1338
|
+
const sleep = new __Sleep(promise, id);
|
|
1339
|
+
const guard = new __AbortGuard(sleep, signal);
|
|
1340
|
+
sleep.guard = guard;
|
|
1341
|
+
|
|
1342
|
+
const already = guard.aborted;
|
|
1343
|
+
if (already) {
|
|
1344
|
+
guard.release();
|
|
1345
|
+
promise.__reject(already);
|
|
1346
|
+
return promise;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
__sleeps.set(id, sleep);
|
|
1350
|
+
__co_host_task(reinterpret<i32>(<f32>(ms / 1000.0)), __co_sleepFired.index, id, 0);
|
|
1351
|
+
return promise;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function __co_sleepFired(id: i32): void {
|
|
1355
|
+
if (!__sleeps.has(id)) return;
|
|
1356
|
+
const sleep = __sleeps.get(id);
|
|
1357
|
+
__sleeps.delete(id);
|
|
1358
|
+
(sleep.guard as __AbortGuard).release();
|
|
1359
|
+
sleep.promise.__fulfillVoid();
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// ---------------------------------------------------------------- players
|
|
1363
|
+
|
|
1364
|
+
// One controller per player slot, made when first asked for and aborted when
|
|
1365
|
+
// the player leaves; the next player in the slot gets a fresh one.
|
|
1366
|
+
// @ts-ignore: decorator
|
|
1367
|
+
@lazy const __playerControllers: (AbortController | null)[] = [];
|
|
1368
|
+
// @ts-ignore: decorator
|
|
1369
|
+
@lazy let __playersWatched = false;
|
|
1370
|
+
|
|
1371
|
+
/** @hidden player.signal. */
|
|
1372
|
+
// @ts-ignore: decorator
|
|
1373
|
+
@global export function __co_player_signal(id: i32): AbortSignal {
|
|
1374
|
+
if (!__playersWatched) {
|
|
1375
|
+
__playersWatched = true;
|
|
1376
|
+
// The stock forward, through the host like every event: shape 1 is
|
|
1377
|
+
// the wide handler, (id, drop, message, unused).
|
|
1378
|
+
__co_host_on("client_disconnected", __co_playerLeft.index, 1);
|
|
1379
|
+
}
|
|
1380
|
+
while (__playerControllers.length <= id) __playerControllers.push(null);
|
|
1381
|
+
let controller = __playerControllers[id];
|
|
1382
|
+
if (!controller) __playerControllers[id] = controller = new AbortController();
|
|
1383
|
+
return controller.signal;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
function __co_playerLeft(id: i32, drop: i32, message: i32, unused: i32): void {
|
|
1387
|
+
if (id < 0 || id >= __playerControllers.length) return;
|
|
1388
|
+
const controller = __playerControllers[id];
|
|
1389
|
+
if (!controller) return;
|
|
1390
|
+
__playerControllers[id] = null;
|
|
1391
|
+
controller.signal.__abort(__co_abortError("The player left"));
|
|
1392
|
+
}
|