@@ -2,14 +2,15 @@ import { WASI } from "node:wasi";
2
2
import { promises } from "node:fs" ;
3
3
import { ensureDirSync } from "fs-extra" ;
4
4
import { instantiate , Imports as ASImports } from "@assemblyscript/loader" ;
5
- import { ExecutionResult } from "../executionResult.js" ;
5
+ import { ExecutionResultSummary } from "../executionResult.js" ;
6
6
import { Imports , ImportsArgument , InstrumentResult } from "../interface.js" ;
7
7
import { mockInstrumentFunc } from "../utils/import.js" ;
8
8
import { supplyDefaultFunction } from "../utils/index.js" ;
9
9
import { parseImportFunctionInfo } from "../utils/wasmparser.js" ;
10
- import { ExecutionRecorder , SingleExecutionResult } from "./executionRecorder.js" ;
10
+ import { ExecutionRecorder , ExecutionResult } from "./executionRecorder.js" ;
11
11
import { CoverageRecorder } from "./covRecorder.js" ;
12
12
import assert from "node:assert" ;
13
+ import { ExecutionError , handleWebAssemblyError } from "../utils/errorTraceHandler.js" ;
13
14
14
15
const readFile = promises . readFile ;
15
16
@@ -18,7 +19,7 @@ async function nodeExecutor(
18
19
outFolder : string ,
19
20
matchedTestNames : string [ ] ,
20
21
imports ?: Imports
21
- ) : Promise < SingleExecutionResult > {
22
+ ) : Promise < ExecutionResult > {
22
23
const wasi = new WASI ( {
23
24
args : [ "node" , instrumentResult . baseName ] ,
24
25
env : process . env ,
@@ -48,36 +49,51 @@ async function nodeExecutor(
48
49
importsArg . module = ins . module ;
49
50
importsArg . instance = ins . instance ;
50
51
importsArg . exports = ins . exports ;
52
+
53
+ let isCrashed = false ; // we don't want to crash any code after crash. AS' heap may be broken.
54
+
55
+ const exceptionHandler = async ( error : unknown ) => {
56
+ if ( error instanceof WebAssembly . RuntimeError ) {
57
+ isCrashed = true ;
58
+ const errorMessage : ExecutionError = await handleWebAssemblyError ( error , instrumentResult . instrumentedWasm ) ;
59
+ executionRecorder . notifyTestCrash ( errorMessage ) ;
60
+ return ;
61
+ }
62
+ // unrecoverable error, rethrow
63
+ if ( error instanceof Error ) {
64
+ console . error ( error . stack ) ;
65
+ }
66
+ throw new Error ( "node executor abort" ) ;
67
+ } ;
68
+
51
69
try {
70
+ executionRecorder . startTestFunction ( `${ instrumentResult . baseName } - init` ) ;
52
71
wasi . start ( ins ) ;
53
- const execTestFunction = ins . exports [ "executeTestFunction" ] ;
54
- assert ( typeof execTestFunction === "function" ) ;
55
- if ( matchedTestNames . length === 0 ) {
56
- // By default, all testcases are executed
57
- for ( const functionInfo of executionRecorder . registerFunctions ) {
58
- const [ testCaseName , functionIndex ] = functionInfo ;
59
- executionRecorder . startTestFunction ( testCaseName ) ;
72
+ } catch ( error ) {
73
+ await exceptionHandler ( error ) ;
74
+ }
75
+ executionRecorder . finishTestFunction ( ) ;
76
+
77
+ const execTestFunction = ins . exports [ "executeTestFunction" ] ;
78
+ assert ( typeof execTestFunction === "function" ) ;
79
+
80
+ for ( const functionInfo of executionRecorder . registerFunctions ) {
81
+ if ( isCrashed ) {
82
+ break ;
83
+ }
84
+ const [ testCaseName , functionIndex ] = functionInfo ;
85
+ if ( matchedTestNames . length === 0 || matchedTestNames . includes ( testCaseName ) ) {
86
+ executionRecorder . startTestFunction ( testCaseName ) ;
87
+ try {
60
88
( execTestFunction as ( a : number ) => void ) ( functionIndex ) ;
61
- executionRecorder . finishTestFunction ( ) ;
62
- mockInstrumentFunc [ "mockFunctionStatus.clear" ] ( ) ;
89
+ } catch ( error ) {
90
+ await exceptionHandler ( error ) ;
63
91
}
64
- } else {
65
- for ( const functionInfo of executionRecorder . registerFunctions ) {
66
- const [ testCaseName , functionIndex ] = functionInfo ;
67
- if ( matchedTestNames . includes ( testCaseName ) ) {
68
- executionRecorder . startTestFunction ( testCaseName ) ;
69
- ( execTestFunction as ( a : number ) => void ) ( functionIndex ) ;
70
- executionRecorder . finishTestFunction ( ) ;
71
- mockInstrumentFunc [ "mockFunctionStatus.clear" ] ( ) ;
72
- }
73
- }
74
- }
75
- } catch ( error ) {
76
- if ( error instanceof Error ) {
77
- console . error ( error . stack ) ;
92
+ executionRecorder . finishTestFunction ( ) ;
93
+ mockInstrumentFunc [ "mockFunctionStatus.clear" ] ( ) ;
78
94
}
79
- throw new Error ( "node executor abort." ) ;
80
95
}
96
+
81
97
coverageRecorder . outputTrace ( instrumentResult . traceFile ) ;
82
98
return executionRecorder . result ;
83
99
}
@@ -87,12 +103,12 @@ export async function execWasmBinaries(
87
103
instrumentResults : InstrumentResult [ ] ,
88
104
matchedTestNames : string [ ] ,
89
105
imports ?: Imports
90
- ) : Promise < ExecutionResult > {
91
- const assertRes = new ExecutionResult ( ) ;
106
+ ) : Promise < ExecutionResultSummary > {
107
+ const assertRes = new ExecutionResultSummary ( ) ;
92
108
ensureDirSync ( outFolder ) ;
93
109
await Promise . all < void > (
94
110
instrumentResults . map ( async ( instrumentResult ) : Promise < void > => {
95
- const result : SingleExecutionResult = await nodeExecutor ( instrumentResult , outFolder , matchedTestNames , imports ) ;
111
+ const result : ExecutionResult = await nodeExecutor ( instrumentResult , outFolder , matchedTestNames , imports ) ;
96
112
await assertRes . merge ( result , instrumentResult . expectInfo ) ;
97
113
} )
98
114
) ;
0 commit comments