forked from swiftwasm/JavaScriptKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecl-smith.mjs
More file actions
108 lines (99 loc) · 3.04 KB
/
Copy pathdecl-smith.mjs
File metadata and controls
108 lines (99 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// @ts-check
/**
* @typedef {import('../types.mjs').DeclEnv} DeclEnv
* @typedef {import('../types.mjs').ImportedClass} ImportedClass
* @typedef {import('../types.mjs').ImportedFunc} ImportedFunc
* @typedef {import('../types.mjs').Param} Param
* @typedef {import('../types.mjs').BridgeType} BridgeType
*/
import { Random } from './random.mjs';
import { TypeSmith } from './type-smith.mjs';
/** @type {string[]} */
const PARAM_NAMES = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
/**
* Generates a DeclEnv with random imported classes and free functions.
*/
export class DeclSmith {
/** @type {Random} */
#rng;
/** @type {TypeSmith} */
#typeSmith;
/** @type {number} */
#maxClasses;
/** @type {number} */
#maxFuncs;
/** @type {number} */
#maxParams;
/**
* @param {Random} rng
* @param {TypeSmith} typeSmith
* @param {{ maxClasses?: number, maxFuncs?: number, maxParams?: number }} [options]
*/
constructor(rng, typeSmith, options = {}) {
this.#rng = rng;
this.#typeSmith = typeSmith;
this.#maxClasses = options.maxClasses ?? 2;
this.#maxFuncs = options.maxFuncs ?? 4;
this.#maxParams = options.maxParams ?? 4;
}
/**
* Generate a complete DeclEnv.
* @returns {DeclEnv}
*/
generate() {
const importedClasses = this.#generateClasses();
const classNames = importedClasses.map((c) => c.name);
const importedFuncs = this.#generateFuncs(classNames);
return { importedFuncs, importedClasses };
}
/**
* @returns {ImportedClass[]}
*/
#generateClasses() {
const count = this.#rng.nextInt(0, this.#maxClasses);
/** @type {ImportedClass[]} */
const classes = [];
for (let i = 0; i < count; i++) {
const name = `FuzzObj${i}`;
const paramCount = this.#rng.nextInt(1, 3);
const params = this.#makeParams(paramCount, []);
classes.push({ name, constructorParams: params });
}
return classes;
}
/**
* @param {string[]} availableClasses
* @returns {ImportedFunc[]}
*/
#generateFuncs(availableClasses) {
const count = this.#rng.nextInt(1, this.#maxFuncs);
/** @type {ImportedFunc[]} */
const funcs = [];
for (let i = 0; i < count; i++) {
const name = `jsFuzz${i}`;
const paramCount = this.#rng.nextInt(0, this.#maxParams);
const params = this.#makeParams(paramCount, availableClasses);
const returnType = this.#typeSmith.randomReturnType(0, availableClasses);
funcs.push({ name, params, returnType });
}
return funcs;
}
/**
* Generate a list of Param objects.
* @param {number} count
* @param {string[]} availableClasses
* @returns {Param[]}
*/
#makeParams(count, availableClasses) {
/** @type {Param[]} */
const params = [];
for (let i = 0; i < count; i++) {
const name = PARAM_NAMES[i] ?? `p${i}`;
const type = this.#typeSmith.randomParamType(0, availableClasses);
// 30% chance of a label that differs from the parameter name
const label = this.#rng.next() < 0.3 ? `_` : null;
params.push({ label, name, type });
}
return params;
}
}