Skip to content

Commit 02f0aaf

Browse files
feat: constant pool (#160)
This PR adds a constant pool to the JavaScript testing module. It searches for constants in the context root and stores these in a pool. This pool is then used for seeding the sampler. --------- Co-authored-by: apanichella <a.panichella@tudelft.nl>
1 parent 533bc15 commit 02f0aaf

File tree

10 files changed

+437
-15
lines changed

10 files changed

+437
-15
lines changed

libraries/analysis-javascript/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export * from "./lib/ast/defaultBabelConfig";
2121
export * from "./lib/cfg/ControlFlowGraphFactory";
2222
export * from "./lib/cfg/ControlFlowGraphVisitor";
2323

24+
export * from "./lib/constant/ConstantPool";
25+
export * from "./lib/constant/ConstantPoolManager";
26+
export * from "./lib/constant/ConstantVisitor";
27+
2428
export * from "./lib/dependency/DependencyFactory";
2529
export * from "./lib/dependency/DependencyVisitor";
2630

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
3+
*
4+
* This file is part of SynTest Framework - SynTest JavaScript.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import { prng } from "@syntest/prng";
20+
21+
export class ConstantPool {
22+
protected _numericPool: Map<number, number>;
23+
protected _integerPool: Map<number, number>;
24+
protected _bigIntPool: Map<bigint, number>;
25+
protected _stringPool: Map<string, number>;
26+
protected _numericCount: number;
27+
protected _integerCount: number;
28+
protected _bigIntCount: number;
29+
protected _stringCount: number;
30+
31+
constructor() {
32+
this._numericPool = new Map();
33+
this.addNumeric(Math.PI);
34+
this.addNumeric(Math.E);
35+
this.addNumeric(-1);
36+
this.addNumeric(0);
37+
this.addNumeric(+1);
38+
39+
this._integerPool = new Map();
40+
this.addInteger(-1);
41+
this.addInteger(0);
42+
this.addInteger(+1);
43+
44+
this._bigIntPool = new Map();
45+
46+
this._stringPool = new Map();
47+
this.addString("");
48+
}
49+
50+
public addNumeric(value: number): void {
51+
if (this._numericPool.has(value)) {
52+
this._numericPool.set(value, this._numericPool.get(value) + 1);
53+
} else {
54+
this._numericPool.set(value, 1);
55+
}
56+
this._numericCount++;
57+
}
58+
59+
public addInteger(value: number): void {
60+
if (this._integerPool.has(value)) {
61+
this._integerPool.set(value, this._integerPool.get(value) + 1);
62+
} else {
63+
this._integerPool.set(value, 1);
64+
}
65+
this._integerCount++;
66+
}
67+
68+
public addBigInt(value: bigint): void {
69+
if (this._bigIntPool.has(value)) {
70+
this._bigIntPool.set(value, this._bigIntPool.get(value) + 1);
71+
} else {
72+
this._bigIntPool.set(value, 1);
73+
}
74+
this._bigIntCount++;
75+
}
76+
77+
public addString(value: string): void {
78+
if (this._stringPool.has(value)) {
79+
this._stringPool.set(value, this._stringPool.get(value) + 1);
80+
} else {
81+
this._stringPool.set(value, 1);
82+
}
83+
this._stringCount++;
84+
}
85+
86+
public getRandomNumeric(frequencyBased = false): number {
87+
if (this._numericPool.size === 0) {
88+
return undefined;
89+
}
90+
91+
if (frequencyBased) {
92+
let index = prng.nextDouble() * this._numericCount;
93+
for (const [value, frequency] of this._numericPool.entries()) {
94+
if (index >= frequency) {
95+
return value;
96+
} else {
97+
index -= frequency;
98+
}
99+
}
100+
return prng.pickOne([...this._numericPool.keys()]);
101+
} else {
102+
return prng.pickOne([...this._numericPool.keys()]);
103+
}
104+
}
105+
106+
public getRandomInteger(frequencyBased = false): number {
107+
if (this._integerPool.size === 0) {
108+
return undefined;
109+
}
110+
111+
if (frequencyBased) {
112+
let index = prng.nextDouble() * this._integerCount;
113+
for (const [value, frequency] of this._integerPool.entries()) {
114+
if (index >= frequency) {
115+
return value;
116+
} else {
117+
index -= frequency;
118+
}
119+
}
120+
return prng.pickOne([...this._integerPool.keys()]);
121+
} else {
122+
return prng.pickOne([...this._integerPool.keys()]);
123+
}
124+
}
125+
126+
public getRandomBigInt(frequencyBased = false): bigint {
127+
if (this._bigIntPool.size === 0) {
128+
return undefined;
129+
}
130+
131+
if (frequencyBased) {
132+
let index = prng.nextDouble() * this._bigIntCount;
133+
for (const [value, frequency] of this._bigIntPool.entries()) {
134+
if (index >= frequency) {
135+
return value;
136+
} else {
137+
index -= frequency;
138+
}
139+
}
140+
return prng.pickOne([...this._bigIntPool.keys()]);
141+
} else {
142+
return prng.pickOne([...this._bigIntPool.keys()]);
143+
}
144+
}
145+
146+
public getRandomString(frequencyBased = false): string {
147+
if (this._stringPool.size === 0) {
148+
return undefined;
149+
}
150+
151+
if (frequencyBased) {
152+
let index = prng.nextDouble() * this._stringCount;
153+
for (const [value, frequency] of this._stringPool.entries()) {
154+
if (index >= frequency) {
155+
return value;
156+
} else {
157+
index -= frequency;
158+
}
159+
}
160+
return prng.pickOne([...this._stringPool.keys()]);
161+
} else {
162+
return prng.pickOne([...this._stringPool.keys()]);
163+
}
164+
}
165+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
3+
*
4+
* This file is part of SynTest Framework - SynTest JavaScript.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import { ConstantPool } from "./ConstantPool";
20+
21+
export class ConstantPoolManager {
22+
protected _targetConstantPool: ConstantPool;
23+
protected _contextConstantPool: ConstantPool;
24+
protected _dynamicConstantPool: ConstantPool;
25+
26+
constructor() {
27+
this._targetConstantPool = new ConstantPool();
28+
this._contextConstantPool = new ConstantPool();
29+
this._dynamicConstantPool = new ConstantPool();
30+
}
31+
32+
public get targetConstantPool(): ConstantPool {
33+
return this._targetConstantPool;
34+
}
35+
36+
public get contextConstantPool(): ConstantPool {
37+
return this._contextConstantPool;
38+
}
39+
40+
public get dynamicConstantPool(): ConstantPool {
41+
return this._dynamicConstantPool;
42+
}
43+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
3+
*
4+
* This file is part of SynTest Framework - SynTest JavaScript.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
import { NodePath } from "@babel/core";
19+
import * as t from "@babel/types";
20+
import { AbstractSyntaxTreeVisitor } from "@syntest/ast-visitor-javascript";
21+
import { ConstantPool } from "./ConstantPool";
22+
23+
export class ConstantVisitor extends AbstractSyntaxTreeVisitor {
24+
protected _constantPool: ConstantPool;
25+
26+
constructor(filePath: string, constantPool: ConstantPool) {
27+
super(filePath);
28+
this._constantPool = constantPool;
29+
}
30+
31+
public Literal: (path: NodePath<t.Literal>) => void = (
32+
path: NodePath<t.Literal>
33+
) => {
34+
switch (path.node.type) {
35+
case "StringLiteral": {
36+
this._constantPool.addString(path.node.value);
37+
break;
38+
}
39+
case "NumericLiteral": {
40+
if (Number.isInteger(path.node.value)) {
41+
this._constantPool.addInteger(path.node.value);
42+
} else {
43+
this._constantPool.addNumeric(path.node.value);
44+
}
45+
break;
46+
}
47+
case "NullLiteral": {
48+
// Not useful for the constant pool
49+
break;
50+
}
51+
case "BooleanLiteral": {
52+
// Not useful for the constant pool
53+
break;
54+
}
55+
case "RegExpLiteral": {
56+
break;
57+
}
58+
case "TemplateLiteral": {
59+
break;
60+
}
61+
case "BigIntLiteral": {
62+
this._constantPool.addBigInt(BigInt(path.node.value));
63+
break;
64+
}
65+
case "DecimalLiteral": {
66+
this._constantPool.addNumeric(Number(path.node.value));
67+
break;
68+
}
69+
default: {
70+
// should never occur
71+
throw new Error(`Unknown literal type`);
72+
}
73+
}
74+
};
75+
}

libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import {
2020
ClassTarget,
21+
ConstantPoolManager,
2122
FunctionTarget,
2223
getRelationName,
2324
isExported,
@@ -59,6 +60,9 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
5960

6061
constructor(
6162
subject: JavaScriptSubject,
63+
constantPoolManager: ConstantPoolManager,
64+
constantPoolEnabled: boolean,
65+
constantPoolProbability: number,
6266
typeInferenceMode: string,
6367
randomTypeProbability: number,
6468
incorporateExecutionInformation: boolean,
@@ -71,6 +75,9 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
7175
) {
7276
super(
7377
subject,
78+
constantPoolManager,
79+
constantPoolEnabled,
80+
constantPoolProbability,
7481
typeInferenceMode,
7582
randomTypeProbability,
7683
incorporateExecutionInformation,
@@ -774,11 +781,21 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
774781
alphabet = this.stringAlphabet,
775782
maxlength = this.stringMaxLength
776783
): StringStatement {
777-
const valueLength = prng.nextInt(0, maxlength - 1);
778-
let value = "";
784+
let value: string;
785+
if (
786+
this.constantPoolEnabled &&
787+
prng.nextBoolean(this.constantPoolProbability)
788+
) {
789+
value = this.constantPoolManager.contextConstantPool.getRandomString();
790+
}
779791

780-
for (let index = 0; index < valueLength; index++) {
781-
value += prng.pickOne([...alphabet]);
792+
if (value === undefined) {
793+
value = "";
794+
const valueLength = prng.nextInt(0, maxlength - 1);
795+
796+
for (let index = 0; index < valueLength; index++) {
797+
value += prng.pickOne([...alphabet]);
798+
}
782799
}
783800

784801
return new StringStatement(
@@ -812,12 +829,21 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
812829
const max = 10;
813830
const min = -10;
814831

832+
const value =
833+
this.constantPoolEnabled && prng.nextBoolean(this.constantPoolProbability)
834+
? this.constantPoolManager.contextConstantPool.getRandomNumeric()
835+
: prng.nextDouble(min, max);
836+
837+
if (value === undefined) {
838+
prng.nextDouble(min, max);
839+
}
840+
815841
return new NumericStatement(
816842
id,
817843
name,
818844
TypeEnum.NUMERIC,
819845
prng.uniqueId(),
820-
prng.nextDouble(min, max)
846+
value
821847
);
822848
}
823849

@@ -826,12 +852,21 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
826852
const max = 10;
827853
const min = -10;
828854

855+
const value =
856+
this.constantPoolEnabled && prng.nextBoolean(this.constantPoolProbability)
857+
? this.constantPoolManager.contextConstantPool.getRandomInteger()
858+
: prng.nextInt(min, max);
859+
860+
if (value === undefined) {
861+
prng.nextInt(min, max);
862+
}
863+
829864
return new IntegerStatement(
830865
id,
831866
name,
832867
TypeEnum.INTEGER,
833868
prng.uniqueId(),
834-
prng.nextInt(min, max)
869+
value
835870
);
836871
}
837872

0 commit comments

Comments
 (0)