as-test 1.0.11 → 1.0.13
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/CHANGELOG.md +9 -1
- package/README.md +15 -0
- package/as-test.config.schema.json +1 -3
- package/assembly/__fuzz__/seed-perf.fuzz.ts +68 -0
- package/assembly/src/fuzz.ts +156 -55
- package/assembly/util/wipc.ts +30 -1
- package/bin/commands/fuzz-core.js +110 -15
- package/bin/commands/fuzz.js +2 -1
- package/bin/commands/init-core.js +0 -1
- package/bin/commands/run-core.js +321 -7
- package/bin/commands/run.js +2 -1
- package/bin/commands/test.js +3 -1
- package/bin/index.js +102 -29
- package/bin/reporters/default.js +61 -52
- package/bin/types.js +1 -1
- package/bin/util.js +2 -2
- package/package.json +10 -8
- package/transform/lib/builder.js +14 -15
- package/transform/lib/coverage.js +11 -12
- package/transform/lib/index.js +0 -1
- package/transform/lib/linker.js +3 -4
- package/transform/lib/location.js +0 -1
- package/transform/lib/log.js +0 -1
- package/transform/lib/mock.js +15 -9
- package/transform/lib/range.js +0 -1
- package/transform/lib/types.js +0 -1
- package/transform/lib/util.js +0 -1
- package/transform/lib/visitor.js +64 -65
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2025-05-03 - v1.0.13
|
|
4
|
+
|
|
5
|
+
- feat: add `--fuzzer` / `--fuzzers` filtering for `ast fuzz` and `ast test --fuzz`, accept `--suite` / `--suites` as fuzz aliases, and include target-specific repro commands in fuzz failure output.
|
|
6
|
+
- feat: add `--suite` / `--suites` filtering for `ast run` and `ast test`, and print suite-specific repro commands on failing test assertions.
|
|
7
|
+
|
|
8
|
+
## 2026-04-28 - v1.0.12
|
|
9
|
+
|
|
10
|
+
- perf: faster seed generation
|
|
11
|
+
- feat: make fuzz campaigns use a random base seed by default when `fuzz.seed` and CLI seed overrides are not set, while keeping deterministic replay via `--seed` / `--fuzz-seed`.
|
|
4
12
|
|
|
5
13
|
## 2026-04-18 - v1.0.11
|
|
6
14
|
|
package/README.md
CHANGED
|
@@ -134,6 +134,13 @@ Run one matching file:
|
|
|
134
134
|
npx ast test math
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
Re-run one suite inside a matching file:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx ast run math --suite array-check
|
|
141
|
+
npx ast run math --suite array-manipulation/array-check
|
|
142
|
+
```
|
|
143
|
+
|
|
137
144
|
You do not need to learn every CLI flag to get started. Most projects can begin with `npx ast test`, then add more configuration only when they need it.
|
|
138
145
|
|
|
139
146
|
## Mocking
|
|
@@ -276,6 +283,8 @@ If you used `npx ast init` with a fuzzer example, the config is already there. O
|
|
|
276
283
|
|
|
277
284
|
`ast fuzz` runs fuzz files across the selected modes, reports one result per file, and keeps the final summary separate from the normal test totals. If you want one combined command, use `ast test --fuzz`.
|
|
278
285
|
|
|
286
|
+
By default, each fuzz run campaign picks a new random base seed. Pin a seed with `--seed <n>` (or `--fuzz-seed <n>` on `ast test`) when you want deterministic replay.
|
|
287
|
+
|
|
279
288
|
When a fuzzer fails, `as-test` now prints the exact failing seeds and one-run repro commands such as `ast fuzz ... --seed <seed+n> --runs 1`. Crash records in `.as-test/crashes` also include the captured inputs passed to `run(...)`, which helps when the generator itself has side effects.
|
|
280
289
|
|
|
281
290
|
Run only fuzzers:
|
|
@@ -284,6 +293,12 @@ Run only fuzzers:
|
|
|
284
293
|
npx ast fuzz
|
|
285
294
|
```
|
|
286
295
|
|
|
296
|
+
Run one matching fuzz target:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
npx ast fuzz string --fuzzer ascii-strings-survive-concatenation-boundaries
|
|
300
|
+
```
|
|
301
|
+
|
|
287
302
|
Run tests and fuzzers together:
|
|
288
303
|
|
|
289
304
|
```bash
|
|
@@ -259,8 +259,7 @@
|
|
|
259
259
|
},
|
|
260
260
|
"seed": {
|
|
261
261
|
"type": "number",
|
|
262
|
-
"description": "
|
|
263
|
-
"default": 1337
|
|
262
|
+
"description": "Optional base seed for input mutation. If omitted, a random seed is generated each run."
|
|
264
263
|
},
|
|
265
264
|
"maxInputBytes": {
|
|
266
265
|
"type": "number",
|
|
@@ -287,7 +286,6 @@
|
|
|
287
286
|
"default": {
|
|
288
287
|
"input": ["./assembly/__fuzz__/*.fuzz.ts"],
|
|
289
288
|
"runs": 1000,
|
|
290
|
-
"seed": 1337,
|
|
291
289
|
"maxInputBytes": 4096,
|
|
292
290
|
"target": "bindings",
|
|
293
291
|
"corpusDir": "./.as-test/fuzz/corpus",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fuzz,
|
|
3
|
+
FuzzSeed,
|
|
4
|
+
IntegerOptions,
|
|
5
|
+
FloatOptions,
|
|
6
|
+
StringOptions,
|
|
7
|
+
BytesOptions,
|
|
8
|
+
ArrayOptions,
|
|
9
|
+
} from "as-test";
|
|
10
|
+
|
|
11
|
+
const PERF_I32 = new IntegerOptions<i32>();
|
|
12
|
+
PERF_I32.min = -100000;
|
|
13
|
+
PERF_I32.max = 100000;
|
|
14
|
+
const PERF_U32 = new IntegerOptions<u32>();
|
|
15
|
+
PERF_U32.min = 0;
|
|
16
|
+
PERF_U32.max = 1000000;
|
|
17
|
+
const PERF_F64 = new FloatOptions<f64>();
|
|
18
|
+
PERF_F64.min = -1.0;
|
|
19
|
+
PERF_F64.max = 1.0;
|
|
20
|
+
const PERF_ASCII = new StringOptions();
|
|
21
|
+
PERF_ASCII.charset = "ascii";
|
|
22
|
+
PERF_ASCII.min = 4;
|
|
23
|
+
PERF_ASCII.max = 24;
|
|
24
|
+
const PERF_BYTES = new BytesOptions();
|
|
25
|
+
PERF_BYTES.min = 8;
|
|
26
|
+
PERF_BYTES.max = 32;
|
|
27
|
+
const PERF_ARRAY = new ArrayOptions();
|
|
28
|
+
PERF_ARRAY.min = 4;
|
|
29
|
+
PERF_ARRAY.max = 16;
|
|
30
|
+
|
|
31
|
+
fuzz("seed perf i32/u32/f64", (_n: i32): bool => true).generate(
|
|
32
|
+
(seed: FuzzSeed, run: (n: i32) => bool): void => {
|
|
33
|
+
let accI: i32 = 0;
|
|
34
|
+
let accU: u32 = 0;
|
|
35
|
+
let accF: f64 = 0.0;
|
|
36
|
+
for (let i = 0; i < 128; i++) {
|
|
37
|
+
accI += seed.i32(PERF_I32);
|
|
38
|
+
accU ^= seed.u32(PERF_U32);
|
|
39
|
+
accF += seed.f64(PERF_F64);
|
|
40
|
+
}
|
|
41
|
+
run(accI + <i32>accU + <i32>accF);
|
|
42
|
+
},
|
|
43
|
+
20000,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
fuzz("seed perf strings", (_n: i32): bool => true).generate(
|
|
47
|
+
(seed: FuzzSeed, run: (n: i32) => bool): void => {
|
|
48
|
+
let total = 0;
|
|
49
|
+
for (let i = 0; i < 96; i++) {
|
|
50
|
+
total += seed.string(PERF_ASCII).length;
|
|
51
|
+
}
|
|
52
|
+
run(total);
|
|
53
|
+
},
|
|
54
|
+
20000,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
fuzz("seed perf bytes/array", (_n: i32): bool => true).generate(
|
|
58
|
+
(seed: FuzzSeed, run: (n: i32) => bool): void => {
|
|
59
|
+
let score = 0;
|
|
60
|
+
for (let i = 0; i < 64; i++) {
|
|
61
|
+
score += seed.bytes(PERF_BYTES).length;
|
|
62
|
+
score += seed.array<i32>((s) => s.i32({ min: -9, max: 9 }), PERF_ARRAY)
|
|
63
|
+
.length;
|
|
64
|
+
}
|
|
65
|
+
run(score);
|
|
66
|
+
},
|
|
67
|
+
20000,
|
|
68
|
+
);
|
package/assembly/src/fuzz.ts
CHANGED
|
@@ -34,8 +34,28 @@ export class ArrayOptions {
|
|
|
34
34
|
max: i32 = 16;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const DEFAULT_I32_OPTIONS = new IntegerOptions<i32>();
|
|
38
|
+
const DEFAULT_U32_OPTIONS = new IntegerOptions<u32>();
|
|
39
|
+
const DEFAULT_F32_OPTIONS = new FloatOptions<f32>();
|
|
40
|
+
const DEFAULT_F64_OPTIONS = new FloatOptions<f64>();
|
|
41
|
+
const DEFAULT_STRING_OPTIONS = new StringOptions();
|
|
42
|
+
const DEFAULT_BYTES_OPTIONS = new BytesOptions();
|
|
43
|
+
const DEFAULT_ARRAY_OPTIONS = new ArrayOptions();
|
|
44
|
+
|
|
37
45
|
export class FuzzSeed {
|
|
38
|
-
|
|
46
|
+
private state: u32;
|
|
47
|
+
|
|
48
|
+
constructor(seed: u64) {
|
|
49
|
+
this.reseed(seed);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
reseed(seed: u64): void {
|
|
53
|
+
const lo = <u32>seed;
|
|
54
|
+
const hi = <u32>(seed >> 32);
|
|
55
|
+
let mixed = lo ^ (hi * 0x9e3779b9) ^ 0xa341316c;
|
|
56
|
+
if (mixed == 0) mixed = 0x6d2b79f5;
|
|
57
|
+
this.state = mixed;
|
|
58
|
+
}
|
|
39
59
|
|
|
40
60
|
boolean(): bool {
|
|
41
61
|
return (this.nextU32() & 1) == 1;
|
|
@@ -46,59 +66,108 @@ export class FuzzSeed {
|
|
|
46
66
|
return unchecked(values[this.nextRange(0, values.length - 1)]);
|
|
47
67
|
}
|
|
48
68
|
|
|
49
|
-
i32(options: IntegerOptions<i32> =
|
|
50
|
-
|
|
69
|
+
i32(options: IntegerOptions<i32> | null = null): i32 {
|
|
70
|
+
const config = options != null ? options : DEFAULT_I32_OPTIONS;
|
|
71
|
+
return this.nextI32InRange(config.min, config.max, config.exclude);
|
|
51
72
|
}
|
|
52
73
|
|
|
53
|
-
u32(options: IntegerOptions<u32> =
|
|
54
|
-
|
|
74
|
+
u32(options: IntegerOptions<u32> | null = null): u32 {
|
|
75
|
+
const config = options != null ? options : DEFAULT_U32_OPTIONS;
|
|
76
|
+
return this.nextU32InRange(config.min, config.max, config.exclude);
|
|
55
77
|
}
|
|
56
78
|
|
|
57
|
-
f32(options: FloatOptions<f32> =
|
|
79
|
+
f32(options: FloatOptions<f32> | null = null): f32 {
|
|
80
|
+
const config = options != null ? options : DEFAULT_F32_OPTIONS;
|
|
58
81
|
return <f32>(
|
|
59
|
-
this.nextF64InRange<f32>(
|
|
82
|
+
this.nextF64InRange<f32>(config.min, config.max, config.exclude)
|
|
60
83
|
);
|
|
61
84
|
}
|
|
62
85
|
|
|
63
|
-
f64(options: FloatOptions<f64> =
|
|
64
|
-
|
|
86
|
+
f64(options: FloatOptions<f64> | null = null): f64 {
|
|
87
|
+
const config = options != null ? options : DEFAULT_F64_OPTIONS;
|
|
88
|
+
return this.nextF64InRange<f64>(config.min, config.max, config.exclude);
|
|
65
89
|
}
|
|
66
90
|
|
|
67
|
-
bytes(options: BytesOptions =
|
|
68
|
-
|
|
69
|
-
|
|
91
|
+
bytes(options: BytesOptions | null = null): Uint8Array {
|
|
92
|
+
const config = options != null ? options : DEFAULT_BYTES_OPTIONS;
|
|
93
|
+
validateLengthRange("seed.bytes()", config.min, config.max);
|
|
94
|
+
const length = this.nextRange(config.min, config.max);
|
|
70
95
|
const out = new Uint8Array(length);
|
|
96
|
+
const include = config.include;
|
|
97
|
+
const exclude = config.exclude;
|
|
98
|
+
if (include.length) {
|
|
99
|
+
if (!exclude.length) {
|
|
100
|
+
for (let i = 0; i < length; i++) {
|
|
101
|
+
unchecked(
|
|
102
|
+
(out[i] = <u8>unchecked(include[this.nextRange(0, include.length - 1)])),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
for (let i = 0; i < length; i++) {
|
|
108
|
+
unchecked((out[i] = this.byteFromOptions(config)));
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
if (!exclude.length) {
|
|
113
|
+
for (let i = 0; i < length; i++) {
|
|
114
|
+
unchecked((out[i] = <u8>(this.nextU32() & 0xff)));
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
71
118
|
for (let i = 0; i < length; i++) {
|
|
72
|
-
out[i] = this.byteFromOptions(
|
|
119
|
+
unchecked((out[i] = this.byteFromOptions(config)));
|
|
73
120
|
}
|
|
74
121
|
return out;
|
|
75
122
|
}
|
|
76
123
|
|
|
77
|
-
buffer(options: BytesOptions =
|
|
124
|
+
buffer(options: BytesOptions | null = null): ArrayBuffer {
|
|
78
125
|
return this.bytes(options).buffer;
|
|
79
126
|
}
|
|
80
127
|
|
|
81
|
-
string(options: StringOptions =
|
|
82
|
-
|
|
83
|
-
|
|
128
|
+
string(options: StringOptions | null = null): string {
|
|
129
|
+
const config = options != null ? options : DEFAULT_STRING_OPTIONS;
|
|
130
|
+
validateLengthRange("seed.string()", config.min, config.max);
|
|
131
|
+
const alphabet = buildAlphabet(config);
|
|
84
132
|
if (!alphabet.length) {
|
|
85
133
|
panic();
|
|
86
134
|
}
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
135
|
+
const coreLength = this.nextRange(config.min, config.max);
|
|
136
|
+
const prefixLength = config.prefix.length;
|
|
137
|
+
const suffixLength = config.suffix.length;
|
|
138
|
+
const totalLength = prefixLength + coreLength + suffixLength;
|
|
139
|
+
if (!totalLength) return "";
|
|
140
|
+
|
|
141
|
+
// Allocate UTF-16 payload directly and fill code units in one pass.
|
|
142
|
+
const outPtr = __new(<usize>(totalLength << 1), idof<string>());
|
|
143
|
+
let cursor: usize = outPtr;
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
146
|
+
store<u16>(cursor, <u16>config.prefix.charCodeAt(i));
|
|
147
|
+
cursor += 2;
|
|
91
148
|
}
|
|
92
|
-
|
|
93
|
-
|
|
149
|
+
|
|
150
|
+
const last = alphabet.length - 1;
|
|
151
|
+
for (let i = 0; i < coreLength; i++) {
|
|
152
|
+
store<u16>(cursor, <u16>unchecked(alphabet[this.nextRange(0, last)]));
|
|
153
|
+
cursor += 2;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (let i = 0; i < suffixLength; i++) {
|
|
157
|
+
store<u16>(cursor, <u16>config.suffix.charCodeAt(i));
|
|
158
|
+
cursor += 2;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return changetype<string>(outPtr);
|
|
94
162
|
}
|
|
95
163
|
|
|
96
164
|
array<T>(
|
|
97
165
|
item: (seed: FuzzSeed) => T,
|
|
98
|
-
options: ArrayOptions =
|
|
166
|
+
options: ArrayOptions | null = null,
|
|
99
167
|
): Array<T> {
|
|
100
|
-
|
|
101
|
-
|
|
168
|
+
const config = options != null ? options : DEFAULT_ARRAY_OPTIONS;
|
|
169
|
+
validateLengthRange("seed.array()", config.min, config.max);
|
|
170
|
+
const length = this.nextRange(config.min, config.max);
|
|
102
171
|
const out = new Array<T>(length);
|
|
103
172
|
for (let i = 0; i < length; i++) {
|
|
104
173
|
unchecked((out[i] = item(this)));
|
|
@@ -110,17 +179,23 @@ export class FuzzSeed {
|
|
|
110
179
|
const include = options.include;
|
|
111
180
|
const exclude = options.exclude;
|
|
112
181
|
if (include.length) {
|
|
182
|
+
if (!exclude.length) {
|
|
183
|
+
return <u8>unchecked(include[this.nextRange(0, include.length - 1)]);
|
|
184
|
+
}
|
|
113
185
|
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
114
186
|
const picked = unchecked(
|
|
115
187
|
include[this.nextRange(0, include.length - 1)],
|
|
116
188
|
);
|
|
117
|
-
if (!exclude
|
|
189
|
+
if (!containsValue<u8>(exclude, picked)) return picked;
|
|
118
190
|
}
|
|
119
191
|
panic();
|
|
120
192
|
}
|
|
193
|
+
if (!exclude.length) {
|
|
194
|
+
return <u8>(this.nextU32() & 0xff);
|
|
195
|
+
}
|
|
121
196
|
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
122
197
|
const value = <u8>(this.nextU32() & 0xff);
|
|
123
|
-
if (!exclude
|
|
198
|
+
if (!containsValue<u8>(exclude, value)) return value;
|
|
124
199
|
}
|
|
125
200
|
panic();
|
|
126
201
|
return 0;
|
|
@@ -128,6 +203,9 @@ export class FuzzSeed {
|
|
|
128
203
|
|
|
129
204
|
private nextI32InRange(min: i32, max: i32, exclude: i32[]): i32 {
|
|
130
205
|
if (max < min) panic();
|
|
206
|
+
if (!exclude.length) {
|
|
207
|
+
return max <= min ? min : min + <i32>(this.nextU32() % <u32>(max - min + 1));
|
|
208
|
+
}
|
|
131
209
|
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
132
210
|
const value =
|
|
133
211
|
max <= min ? min : min + <i32>(this.nextU32() % <u32>(max - min + 1));
|
|
@@ -139,6 +217,9 @@ export class FuzzSeed {
|
|
|
139
217
|
|
|
140
218
|
private nextU32InRange(min: u32, max: u32, exclude: u32[]): u32 {
|
|
141
219
|
if (max < min) panic();
|
|
220
|
+
if (!exclude.length) {
|
|
221
|
+
return max <= min ? min : min + (this.nextU32() % (max - min + 1));
|
|
222
|
+
}
|
|
142
223
|
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
143
224
|
const value = max <= min ? min : min + (this.nextU32() % (max - min + 1));
|
|
144
225
|
if (!containsValue<u32>(exclude, value)) return value;
|
|
@@ -151,6 +232,9 @@ export class FuzzSeed {
|
|
|
151
232
|
const left = <f64>min;
|
|
152
233
|
const right = <f64>max;
|
|
153
234
|
if (right < left) panic();
|
|
235
|
+
if (!exclude.length) {
|
|
236
|
+
return left + (right - left) * this.nextUnit();
|
|
237
|
+
}
|
|
154
238
|
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
155
239
|
const value = left + (right - left) * this.nextUnit();
|
|
156
240
|
if (!containsFloatValue<T>(exclude, changetype<T>(value))) return value;
|
|
@@ -169,11 +253,14 @@ export class FuzzSeed {
|
|
|
169
253
|
}
|
|
170
254
|
|
|
171
255
|
private nextU32(): u32 {
|
|
172
|
-
|
|
173
|
-
let
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
256
|
+
// mulberry32: very fast integer-only PRNG suitable for fuzz input generation.
|
|
257
|
+
let x = this.state;
|
|
258
|
+
x += 0x6d2b79f5;
|
|
259
|
+
this.state = x;
|
|
260
|
+
let z = x;
|
|
261
|
+
z = <u32>Math.imul(z ^ (z >> 15), z | 1);
|
|
262
|
+
z ^= z + <u32>Math.imul(z ^ (z >> 7), z | 61);
|
|
263
|
+
return z ^ (z >> 14);
|
|
177
264
|
}
|
|
178
265
|
|
|
179
266
|
private nextU64(): u64 {
|
|
@@ -183,6 +270,21 @@ export class FuzzSeed {
|
|
|
183
270
|
}
|
|
184
271
|
}
|
|
185
272
|
|
|
273
|
+
const ASCII_ALPHABET: i32[] = rangeChars(32, 126);
|
|
274
|
+
const ALPHA_ALPHABET: i32[] = rangeChars(65, 90).concat(rangeChars(97, 122));
|
|
275
|
+
const DIGIT_ALPHABET: i32[] = rangeChars(48, 57);
|
|
276
|
+
const HEX_ALPHABET: i32[] = DIGIT_ALPHABET.concat(rangeChars(97, 102));
|
|
277
|
+
const ALNUM_ALPHABET: i32[] = ALPHA_ALPHABET.concat(DIGIT_ALPHABET);
|
|
278
|
+
const BASE64_ALPHABET: i32[] = ALPHA_ALPHABET.concat(DIGIT_ALPHABET).concat([
|
|
279
|
+
43,
|
|
280
|
+
47,
|
|
281
|
+
61,
|
|
282
|
+
]);
|
|
283
|
+
const IDENTIFIER_ALPHABET: i32[] = [95].concat(ALPHA_ALPHABET).concat(
|
|
284
|
+
DIGIT_ALPHABET,
|
|
285
|
+
);
|
|
286
|
+
const WHITESPACE_ALPHABET: i32[] = [9, 10, 13, 32];
|
|
287
|
+
|
|
186
288
|
export abstract class FuzzerBase {
|
|
187
289
|
public name: string;
|
|
188
290
|
public skipped: bool;
|
|
@@ -409,12 +511,13 @@ export class Fuzzer0<R> extends FuzzerBase {
|
|
|
409
511
|
run(seedBase: u64, runs: i32): FuzzerResult {
|
|
410
512
|
if (this.skipped) return createSkippedResult(this.name);
|
|
411
513
|
const result = createResult(this.name, runs);
|
|
514
|
+
const seed = new FuzzSeed(seedBase);
|
|
412
515
|
__fuzz_callback0 = changetype<() => usize>(this.callback);
|
|
413
516
|
__fuzz_returns_bool = this.returnsBool;
|
|
414
517
|
for (let i = 0; i < runs; i++) {
|
|
415
518
|
prepareFuzzIteration();
|
|
416
519
|
__fuzz_calls = 0;
|
|
417
|
-
|
|
520
|
+
seed.reseed(seedBase + <u64>i);
|
|
418
521
|
if (this.generator) {
|
|
419
522
|
this.generator(seed, changetype<() => R>(__fuzz_run0));
|
|
420
523
|
} else {
|
|
@@ -469,12 +572,13 @@ export class Fuzzer1<A, R> extends FuzzerBase {
|
|
|
469
572
|
run(seedBase: u64, runs: i32): FuzzerResult {
|
|
470
573
|
if (this.skipped) return createSkippedResult(this.name);
|
|
471
574
|
const result = createResult(this.name, runs);
|
|
575
|
+
const seed = new FuzzSeed(seedBase);
|
|
472
576
|
__fuzz_callback1 = changetype<(a: usize) => usize>(this.callback);
|
|
473
577
|
__fuzz_returns_bool = this.returnsBool;
|
|
474
578
|
for (let i = 0; i < runs; i++) {
|
|
475
579
|
prepareFuzzIteration();
|
|
476
580
|
__fuzz_calls = 0;
|
|
477
|
-
|
|
581
|
+
seed.reseed(seedBase + <u64>i);
|
|
478
582
|
if (!this.generator) {
|
|
479
583
|
failFuzzIteration(
|
|
480
584
|
"generate",
|
|
@@ -538,12 +642,13 @@ export class Fuzzer2<A, B, R> extends FuzzerBase {
|
|
|
538
642
|
run(seedBase: u64, runs: i32): FuzzerResult {
|
|
539
643
|
if (this.skipped) return createSkippedResult(this.name);
|
|
540
644
|
const result = createResult(this.name, runs);
|
|
645
|
+
const seed = new FuzzSeed(seedBase);
|
|
541
646
|
__fuzz_callback2 = changetype<(a: usize, b: usize) => usize>(this.callback);
|
|
542
647
|
__fuzz_returns_bool = this.returnsBool;
|
|
543
648
|
for (let i = 0; i < runs; i++) {
|
|
544
649
|
prepareFuzzIteration();
|
|
545
650
|
__fuzz_calls = 0;
|
|
546
|
-
|
|
651
|
+
seed.reseed(seedBase + <u64>i);
|
|
547
652
|
if (!this.generator) {
|
|
548
653
|
failFuzzIteration(
|
|
549
654
|
"generate",
|
|
@@ -606,6 +711,7 @@ export class Fuzzer3<A, B, C, R> extends FuzzerBase {
|
|
|
606
711
|
run(seedBase: u64, runs: i32): FuzzerResult {
|
|
607
712
|
if (this.skipped) return createSkippedResult(this.name);
|
|
608
713
|
const result = createResult(this.name, runs);
|
|
714
|
+
const seed = new FuzzSeed(seedBase);
|
|
609
715
|
__fuzz_callback3 = changetype<(a: usize, b: usize, c: usize) => usize>(
|
|
610
716
|
this.callback,
|
|
611
717
|
);
|
|
@@ -613,7 +719,7 @@ export class Fuzzer3<A, B, C, R> extends FuzzerBase {
|
|
|
613
719
|
for (let i = 0; i < runs; i++) {
|
|
614
720
|
prepareFuzzIteration();
|
|
615
721
|
__fuzz_calls = 0;
|
|
616
|
-
|
|
722
|
+
seed.reseed(seedBase + <u64>i);
|
|
617
723
|
if (!this.generator) {
|
|
618
724
|
failFuzzIteration(
|
|
619
725
|
"generate",
|
|
@@ -701,13 +807,13 @@ export function createFuzzer<T extends Function>(
|
|
|
701
807
|
}
|
|
702
808
|
|
|
703
809
|
function buildAlphabet(options: StringOptions): i32[] {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
out.length = 0;
|
|
810
|
+
if (!options.include.length && !options.exclude.length) {
|
|
811
|
+
return baseAlphabet(options.charset);
|
|
707
812
|
}
|
|
813
|
+
const out = baseAlphabet(options.charset).slice(0);
|
|
708
814
|
for (let i = 0; i < options.include.length; i++) {
|
|
709
815
|
const value = unchecked(options.include[i]);
|
|
710
|
-
if (!out
|
|
816
|
+
if (!containsValue<i32>(out, value)) out.push(value);
|
|
711
817
|
}
|
|
712
818
|
for (let i = 0; i < options.exclude.length; i++) {
|
|
713
819
|
removeFirst(out, unchecked(options.exclude[i]));
|
|
@@ -716,21 +822,15 @@ function buildAlphabet(options: StringOptions): i32[] {
|
|
|
716
822
|
}
|
|
717
823
|
|
|
718
824
|
function baseAlphabet(charset: string): i32[] {
|
|
719
|
-
if (charset == "alpha") return
|
|
720
|
-
if (charset == "alnum")
|
|
721
|
-
|
|
722
|
-
if (charset == "
|
|
723
|
-
if (charset == "
|
|
724
|
-
if (charset == "
|
|
725
|
-
|
|
726
|
-
.concat(rangeChars(97, 122))
|
|
727
|
-
.concat(rangeChars(48, 57))
|
|
728
|
-
.concat([43, 47, 61]);
|
|
729
|
-
if (charset == "identifier")
|
|
730
|
-
return [95].concat(baseAlphabet("alpha")).concat(rangeChars(48, 57));
|
|
731
|
-
if (charset == "whitespace") return [9, 10, 13, 32];
|
|
825
|
+
if (charset == "alpha") return ALPHA_ALPHABET;
|
|
826
|
+
if (charset == "alnum") return ALNUM_ALPHABET;
|
|
827
|
+
if (charset == "digit") return DIGIT_ALPHABET;
|
|
828
|
+
if (charset == "hex") return HEX_ALPHABET;
|
|
829
|
+
if (charset == "base64") return BASE64_ALPHABET;
|
|
830
|
+
if (charset == "identifier") return IDENTIFIER_ALPHABET;
|
|
831
|
+
if (charset == "whitespace") return WHITESPACE_ALPHABET;
|
|
732
832
|
if (charset == "custom") return [];
|
|
733
|
-
return
|
|
833
|
+
return ASCII_ALPHABET;
|
|
734
834
|
}
|
|
735
835
|
|
|
736
836
|
function rangeChars(start: i32, end: i32): i32[] {
|
|
@@ -746,6 +846,7 @@ function removeFirst(values: i32[], needle: i32): void {
|
|
|
746
846
|
if (index >= 0) values.splice(index, 1);
|
|
747
847
|
}
|
|
748
848
|
|
|
849
|
+
|
|
749
850
|
function validateLengthRange(label: string, min: i32, max: i32): void {
|
|
750
851
|
if (min < 0 || max < 0) panic();
|
|
751
852
|
if (max < min) panic();
|
package/assembly/util/wipc.ts
CHANGED
|
@@ -33,6 +33,7 @@ const MAGIC_C: u8 = 0x43; // C
|
|
|
33
33
|
const HEADER_SIZE: i32 = 9;
|
|
34
34
|
const IOV_SIZE: usize = sizeof<usize>() * 2;
|
|
35
35
|
const U32_SIZE: usize = sizeof<u32>();
|
|
36
|
+
const REPORT_CHUNK_BYTES: i32 = 65536;
|
|
36
37
|
|
|
37
38
|
// @ts-ignore
|
|
38
39
|
const IS_BINDINGS: bool = isDefined(AS_TEST_BINDINGS);
|
|
@@ -162,7 +163,35 @@ export function requestFuzzConfig(): FuzzConfigReply {
|
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
export function sendReport(report: string): void {
|
|
165
|
-
|
|
166
|
+
const payload = String.UTF8.encode(report);
|
|
167
|
+
if (payload.byteLength <= REPORT_CHUNK_BYTES) {
|
|
168
|
+
sendFrame(MessageType.DATA, payload);
|
|
169
|
+
sendFrame(MessageType.CLOSE, new ArrayBuffer(0));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const totalBytes = payload.byteLength;
|
|
174
|
+
const chunkCount = (totalBytes + REPORT_CHUNK_BYTES - 1) / REPORT_CHUNK_BYTES;
|
|
175
|
+
sendJson(
|
|
176
|
+
MessageType.CALL,
|
|
177
|
+
`{"kind":"report:start","encoding":"utf8-chunks","totalBytes":${totalBytes.toString()},"chunkCount":${chunkCount.toString()},"chunkBytes":${REPORT_CHUNK_BYTES.toString()}}`,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const ptr = changetype<usize>(payload);
|
|
181
|
+
let offset = 0;
|
|
182
|
+
while (offset < totalBytes) {
|
|
183
|
+
const size = min<i32>(REPORT_CHUNK_BYTES, totalBytes - offset);
|
|
184
|
+
const chunk = new ArrayBuffer(size);
|
|
185
|
+
memory.copy(changetype<usize>(chunk), ptr + offset, size);
|
|
186
|
+
sendFrame(MessageType.DATA, chunk);
|
|
187
|
+
offset += size;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
sendJson(
|
|
191
|
+
MessageType.CALL,
|
|
192
|
+
`{"kind":"report:end","totalBytes":${totalBytes.toString()},"chunkCount":${chunkCount.toString()}}`,
|
|
193
|
+
);
|
|
194
|
+
sendFrame(MessageType.CLOSE, new ArrayBuffer(0));
|
|
166
195
|
}
|
|
167
196
|
|
|
168
197
|
export function sendWarning(message: string): void {
|