-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Nadro/4857/wasm panic catching errors (#4901)
* chore: skeleton code to initialize and detect the global WASM panic * chore: implementing a reimport method to fix the wasm instance being bricked * fix: cleaning up tsc/lint * fix: renaming file to be more accurate * fix: added toast message * fix: types... * fix: typed the functions with arg spreads
- Loading branch information
Showing
5 changed files
with
186 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { kclManager } from 'lib/singletons' | ||
import { reloadModule, getModule } from 'lib/wasm_lib_wrapper' | ||
import toast from 'react-hot-toast' | ||
import { reportRejection } from './trap' | ||
|
||
let initialized = false | ||
|
||
/** | ||
* WASM/Rust runtime can panic and the original try/catch/finally blocks will not trigger | ||
* on the await promise. The interface will killed. This means we need to catch the error at | ||
* the global/DOM level. This will have to interface with whatever controlflow that needs to be picked up | ||
* within the error branch in the typescript to cover the application state. | ||
*/ | ||
export const initializeWindowExceptionHandler = () => { | ||
if (window && !initialized) { | ||
window.addEventListener('error', (event) => { | ||
void (async () => { | ||
if (matchImportExportErrorCrash(event.message)) { | ||
// do global singleton cleanup | ||
kclManager.executeAstCleanUp() | ||
toast.error( | ||
'You have hit a KCL execution bug! Put your KCL code in a github issue to help us resolve this bug.' | ||
) | ||
try { | ||
await reloadModule() | ||
await getModule().default() | ||
} catch (e) { | ||
console.error('Failed to initialize wasm_lib') | ||
console.error(e) | ||
} | ||
} | ||
})().catch(reportRejection) | ||
}) | ||
// Make sure we only initialize this event listener once | ||
initialized = true | ||
} else { | ||
console.error( | ||
`Failed to initialize, window: ${window}, initialized:${initialized}` | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Specifically match a substring of the message error to detect an import export runtime issue | ||
* when the WASM runtime panics | ||
*/ | ||
const matchImportExportErrorCrash = (message: string): boolean => { | ||
// called `Result::unwrap_throw()` on an `Err` value | ||
const substringError = '`Result::unwrap_throw()` on an `Err` value' | ||
return message.indexOf(substringError) !== -1 ? true : false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* This wrapper file is to enable reloading of the wasm_lib.js file. | ||
* When the wasm instance bricks there is no API or interface to restart, | ||
* restore, or re init the WebAssembly instance. The entire application would need | ||
* to restart. | ||
* A way to bypass this is by reloading the entire .js file so the global wasm variable | ||
* gets reinitialized and we do not use that old reference | ||
*/ | ||
|
||
import { | ||
parse_wasm as ParseWasm, | ||
recast_wasm as RecastWasm, | ||
execute as Execute, | ||
kcl_lint as KclLint, | ||
modify_ast_for_sketch_wasm as ModifyAstForSketch, | ||
is_points_ccw as IsPointsCcw, | ||
get_tangential_arc_to_info as GetTangentialArcToInfo, | ||
program_memory_init as ProgramMemoryInit, | ||
make_default_planes as MakeDefaultPlanes, | ||
coredump as CoreDump, | ||
toml_stringify as TomlStringify, | ||
default_app_settings as DefaultAppSettings, | ||
parse_app_settings as ParseAppSettings, | ||
parse_project_settings as ParseProjectSettings, | ||
default_project_settings as DefaultProjectSettings, | ||
base64_decode as Base64Decode, | ||
clear_scene_and_bust_cache as ClearSceneAndBustCache, | ||
} from '../wasm-lib/pkg/wasm_lib' | ||
|
||
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib') | ||
|
||
// Stores the result of the import of the wasm_lib file | ||
let data: ModuleType | ||
|
||
// Imports the .js file again which will clear the old import | ||
// This allows us to reinitialize the wasm instance | ||
export async function reloadModule() { | ||
data = await import(`../wasm-lib/pkg/wasm_lib`) | ||
} | ||
|
||
export function getModule(): ModuleType { | ||
return data | ||
} | ||
|
||
export async function init(module_or_path: any) { | ||
return await getModule().default(module_or_path) | ||
} | ||
export const parse_wasm: typeof ParseWasm = (...args) => { | ||
return getModule().parse_wasm(...args) | ||
} | ||
export const recast_wasm: typeof RecastWasm = (...args) => { | ||
return getModule().recast_wasm(...args) | ||
} | ||
export const execute: typeof Execute = (...args) => { | ||
return getModule().execute(...args) | ||
} | ||
export const kcl_lint: typeof KclLint = (...args) => { | ||
return getModule().kcl_lint(...args) | ||
} | ||
export const modify_ast_for_sketch_wasm: typeof ModifyAstForSketch = ( | ||
...args | ||
) => { | ||
return getModule().modify_ast_for_sketch_wasm(...args) | ||
} | ||
export const is_points_ccw: typeof IsPointsCcw = (...args) => { | ||
return getModule().is_points_ccw(...args) | ||
} | ||
export const get_tangential_arc_to_info: typeof GetTangentialArcToInfo = ( | ||
...args | ||
) => { | ||
return getModule().get_tangential_arc_to_info(...args) | ||
} | ||
export const program_memory_init: typeof ProgramMemoryInit = (...args) => { | ||
return getModule().program_memory_init(...args) | ||
} | ||
export const make_default_planes: typeof MakeDefaultPlanes = (...args) => { | ||
return getModule().make_default_planes(...args) | ||
} | ||
export const coredump: typeof CoreDump = (...args) => { | ||
return getModule().coredump(...args) | ||
} | ||
export const toml_stringify: typeof TomlStringify = (...args) => { | ||
return getModule().toml_stringify(...args) | ||
} | ||
export const default_app_settings: typeof DefaultAppSettings = (...args) => { | ||
return getModule().default_app_settings(...args) | ||
} | ||
export const parse_app_settings: typeof ParseAppSettings = (...args) => { | ||
return getModule().parse_app_settings(...args) | ||
} | ||
export const parse_project_settings: typeof ParseProjectSettings = ( | ||
...args | ||
) => { | ||
return getModule().parse_project_settings(...args) | ||
} | ||
export const default_project_settings: typeof DefaultProjectSettings = ( | ||
...args | ||
) => { | ||
return getModule().default_project_settings(...args) | ||
} | ||
export const base64_decode: typeof Base64Decode = (...args) => { | ||
return getModule().base64_decode(...args) | ||
} | ||
export const clear_scene_and_bust_cache: typeof ClearSceneAndBustCache = ( | ||
...args | ||
) => { | ||
return getModule().clear_scene_and_bust_cache(...args) | ||
} |