Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libraries/analysis-javascript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export * from "./lib/ast/defaultBabelConfig";
export * from "./lib/cfg/ControlFlowGraphFactory";
export * from "./lib/cfg/ControlFlowGraphVisitor";

export * from "./lib/constant/ConstantPool";
export * from "./lib/constant/ConstantPoolManager";
export * from "./lib/constant/ConstantVisitor";

export * from "./lib/dependency/DependencyFactory";
export * from "./lib/dependency/DependencyVisitor";

Expand Down
165 changes: 165 additions & 0 deletions libraries/analysis-javascript/lib/constant/ConstantPool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
*
* This file is part of SynTest Framework - SynTest JavaScript.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { prng } from "@syntest/prng";

export class ConstantPool {
protected _numericPool: Map<number, number>;
protected _integerPool: Map<number, number>;
protected _bigIntPool: Map<bigint, number>;
protected _stringPool: Map<string, number>;
protected _numericCount: number;
protected _integerCount: number;
protected _bigIntCount: number;
protected _stringCount: number;

constructor() {
this._numericPool = new Map();
this.addNumeric(Math.PI);
this.addNumeric(Math.E);
this.addNumeric(-1);
this.addNumeric(0);
this.addNumeric(+1);

this._integerPool = new Map();
this.addInteger(-1);
this.addInteger(0);
this.addInteger(+1);

this._bigIntPool = new Map();

this._stringPool = new Map();
this.addString("");
}

public addNumeric(value: number): void {
if (this._numericPool.has(value)) {
this._numericPool.set(value, this._numericPool.get(value) + 1);
} else {
this._numericPool.set(value, 1);
}
this._numericCount++;
}

public addInteger(value: number): void {
if (this._integerPool.has(value)) {
this._integerPool.set(value, this._integerPool.get(value) + 1);
} else {
this._integerPool.set(value, 1);
}
this._integerCount++;
}

public addBigInt(value: bigint): void {
if (this._bigIntPool.has(value)) {
this._bigIntPool.set(value, this._bigIntPool.get(value) + 1);
} else {
this._bigIntPool.set(value, 1);
}
this._bigIntCount++;
}

public addString(value: string): void {
if (this._stringPool.has(value)) {
this._stringPool.set(value, this._stringPool.get(value) + 1);
} else {
this._stringPool.set(value, 1);
}
this._stringCount++;
}

public getRandomNumeric(frequencyBased = false): number {
if (this._numericPool.size === 0) {
return undefined;
}

if (frequencyBased) {
let index = prng.nextDouble() * this._numericCount;
for (const [value, frequency] of this._numericPool.entries()) {
if (index >= frequency) {
return value;
} else {
index -= frequency;
}
}
return prng.pickOne([...this._numericPool.keys()]);
} else {
return prng.pickOne([...this._numericPool.keys()]);
}
}

public getRandomInteger(frequencyBased = false): number {
if (this._integerPool.size === 0) {
return undefined;
}

if (frequencyBased) {
let index = prng.nextDouble() * this._integerCount;
for (const [value, frequency] of this._integerPool.entries()) {
if (index >= frequency) {
return value;
} else {
index -= frequency;
}
}
return prng.pickOne([...this._integerPool.keys()]);
} else {
return prng.pickOne([...this._integerPool.keys()]);
}
}

public getRandomBigInt(frequencyBased = false): bigint {
if (this._bigIntPool.size === 0) {
return undefined;
}

if (frequencyBased) {
let index = prng.nextDouble() * this._bigIntCount;
for (const [value, frequency] of this._bigIntPool.entries()) {
if (index >= frequency) {
return value;
} else {
index -= frequency;
}
}
return prng.pickOne([...this._bigIntPool.keys()]);
} else {
return prng.pickOne([...this._bigIntPool.keys()]);
}
}

public getRandomString(frequencyBased = false): string {
if (this._stringPool.size === 0) {
return undefined;
}

if (frequencyBased) {
let index = prng.nextDouble() * this._stringCount;
for (const [value, frequency] of this._stringPool.entries()) {
if (index >= frequency) {
return value;
} else {
index -= frequency;
}
}
return prng.pickOne([...this._stringPool.keys()]);
} else {
return prng.pickOne([...this._stringPool.keys()]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
*
* This file is part of SynTest Framework - SynTest JavaScript.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ConstantPool } from "./ConstantPool";

export class ConstantPoolManager {
protected _targetConstantPool: ConstantPool;
protected _contextConstantPool: ConstantPool;
protected _dynamicConstantPool: ConstantPool;

constructor() {
this._targetConstantPool = new ConstantPool();
this._contextConstantPool = new ConstantPool();
this._dynamicConstantPool = new ConstantPool();
}

public get targetConstantPool(): ConstantPool {
return this._targetConstantPool;
}

public get contextConstantPool(): ConstantPool {
return this._contextConstantPool;
}

public get dynamicConstantPool(): ConstantPool {
return this._dynamicConstantPool;
}
}
75 changes: 75 additions & 0 deletions libraries/analysis-javascript/lib/constant/ConstantVisitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
*
* This file is part of SynTest Framework - SynTest JavaScript.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NodePath } from "@babel/core";
import * as t from "@babel/types";
import { AbstractSyntaxTreeVisitor } from "@syntest/ast-visitor-javascript";
import { ConstantPool } from "./ConstantPool";

export class ConstantVisitor extends AbstractSyntaxTreeVisitor {
protected _constantPool: ConstantPool;

constructor(filePath: string, constantPool: ConstantPool) {
super(filePath);
this._constantPool = constantPool;
}

public Literal: (path: NodePath<t.Literal>) => void = (
path: NodePath<t.Literal>
) => {
switch (path.node.type) {
case "StringLiteral": {
this._constantPool.addString(path.node.value);
break;
}
case "NumericLiteral": {
if (Number.isInteger(path.node.value)) {
this._constantPool.addInteger(path.node.value);
} else {
this._constantPool.addNumeric(path.node.value);
}
break;
}
case "NullLiteral": {
// Not useful for the constant pool
break;
}
case "BooleanLiteral": {
// Not useful for the constant pool
break;
}
case "RegExpLiteral": {
break;
}
case "TemplateLiteral": {
break;
}
case "BigIntLiteral": {
this._constantPool.addBigInt(BigInt(path.node.value));
break;
}
case "DecimalLiteral": {
this._constantPool.addNumeric(Number(path.node.value));
break;
}
default: {
// should never occur
throw new Error(`Unknown literal type`);
}
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import {
ClassTarget,
ConstantPoolManager,
FunctionTarget,
getRelationName,
isExported,
Expand Down Expand Up @@ -59,6 +60,9 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {

constructor(
subject: JavaScriptSubject,
constantPoolManager: ConstantPoolManager,
constantPoolEnabled: boolean,
constantPoolProbability: number,
typeInferenceMode: string,
randomTypeProbability: number,
incorporateExecutionInformation: boolean,
Expand All @@ -71,6 +75,9 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
) {
super(
subject,
constantPoolManager,
constantPoolEnabled,
constantPoolProbability,
typeInferenceMode,
randomTypeProbability,
incorporateExecutionInformation,
Expand Down Expand Up @@ -774,11 +781,20 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
alphabet = this.stringAlphabet,
maxlength = this.stringMaxLength
): StringStatement {
const valueLength = prng.nextInt(0, maxlength - 1);
let value = "";
let value: string;
if (
this.constantPoolEnabled &&
prng.nextBoolean(this.constantPoolProbability)
) {
value = this.constantPoolManager.contextConstantPool.getRandomString();
}

for (let index = 0; index < valueLength; index++) {
value += prng.pickOne([...alphabet]);
if (value === undefined) {
const valueLength = prng.nextInt(0, maxlength - 1);

for (let index = 0; index < valueLength; index++) {
value += prng.pickOne([...alphabet]);
}
}

return new StringStatement(
Expand Down Expand Up @@ -812,12 +828,21 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
const max = 10;
const min = -10;

const value =
this.constantPoolEnabled && prng.nextBoolean(this.constantPoolProbability)
? this.constantPoolManager.contextConstantPool.getRandomNumeric()
: prng.nextDouble(min, max);

if (value === undefined) {
prng.nextDouble(min, max);
}

return new NumericStatement(
id,
name,
TypeEnum.NUMERIC,
prng.uniqueId(),
prng.nextDouble(min, max)
value
);
}

Expand All @@ -826,12 +851,21 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler {
const max = 10;
const min = -10;

const value =
this.constantPoolEnabled && prng.nextBoolean(this.constantPoolProbability)
? this.constantPoolManager.contextConstantPool.getRandomInteger()
: prng.nextInt(min, max);

if (value === undefined) {
prng.nextInt(min, max);
}

return new IntegerStatement(
id,
name,
TypeEnum.INTEGER,
prng.uniqueId(),
prng.nextInt(min, max)
value
);
}

Expand Down
Loading