diff --git a/src/mono/wasm/runtime/guarded-promise.ts b/src/mono/wasm/runtime/guarded-promise.ts new file mode 100644 index 00000000000000..fe46ca43521ba2 --- /dev/null +++ b/src/mono/wasm/runtime/guarded-promise.ts @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/// A Promise that guards against multiple-resolve, multiple-reject, reject-after-accept and accept-after-reject. +class GuardedPromise extends Promise { + constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void) { + super((resolve, reject) => { + let resolved = false; + let rejected = false; + executor((value: T | PromiseLike) => { + if (resolved) { + throw new Error("Promise resolved more than once"); + } + if (rejected) { + throw new Error("Can not resolve a Promise after it has been rejected"); + } + resolved = true; + resolve(value); + }, (reason: any) => { + if (resolved) { + throw new Error("Can not reject a Promise after it has been resolved"); + } + if (rejected) { + throw new Error("Promise rejected more than once"); + } + rejected = true; + reject(reason); + }); + }); + } +} + +export default GuardedPromise; + diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 2920dd8de7b74f..c33549c2717cec 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -5,6 +5,7 @@ import { AllAssetEntryTypes, mono_assert, AssetEntry, CharPtrNull, DotnetModule, import { ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports"; import cwraps from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; +import GuardedPromise from "./guarded-promise"; import { mono_wasm_globalization_init, mono_wasm_load_icu_data } from "./icu"; import { toBase64StringImpl } from "./base64"; import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from "./profiler"; @@ -17,9 +18,9 @@ import { mono_on_abort } from "./run"; import { mono_wasm_new_root } from "./roots"; import { init_crypto } from "./crypto-worker"; -export let runtime_is_initialized_resolve: Function; -export let runtime_is_initialized_reject: Function; -export const mono_wasm_runtime_is_initialized = new Promise((resolve, reject) => { +export let runtime_is_initialized_resolve: () => void; +export let runtime_is_initialized_reject: (reason?: any) => void; +export const mono_wasm_runtime_is_initialized = new GuardedPromise((resolve, reject) => { runtime_is_initialized_resolve = resolve; runtime_is_initialized_reject = reject; });