From 021f4f7a01e84efc435b8bb8746a53991aab8eba Mon Sep 17 00:00:00 2001 From: Mitchell Olsthoorn Date: Thu, 6 Jul 2023 17:53:21 +0200 Subject: [PATCH 1/6] feat: constant pool --- .../lib/constant/ConstantPool.ts | 67 +++++++++++++++++ .../lib/constant/ConstantPoolManager.ts | 29 +++++++ .../lib/constant/ConstantVisitor.ts | 75 +++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 libraries/analysis-javascript/lib/constant/ConstantPool.ts create mode 100644 libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts create mode 100644 libraries/analysis-javascript/lib/constant/ConstantVisitor.ts diff --git a/libraries/analysis-javascript/lib/constant/ConstantPool.ts b/libraries/analysis-javascript/lib/constant/ConstantPool.ts new file mode 100644 index 000000000..614bb8194 --- /dev/null +++ b/libraries/analysis-javascript/lib/constant/ConstantPool.ts @@ -0,0 +1,67 @@ +/* + * 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. + */ + +export class ConstantPool { + protected _numericPool: Map; + protected _integerPool: Map; + protected _bigIntPool: Map; + protected _stringPool: Map; + + constructor() { + this._numericPool = new Map(); + this._integerPool = new Map(); + this._stringPool = new Map(); + } + + 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); + } + } + + 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); + } + } + + 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); + } + } + + 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); + } + } + + public getRandomNumeric(): number { + const index = Math.floor(Math.random() * this._numericPool.size); + return [...this._numericPool.keys()][index]; + } +} diff --git a/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts b/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts new file mode 100644 index 000000000..9617795c5 --- /dev/null +++ b/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts @@ -0,0 +1,29 @@ +/* + * 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 { + constructor() { + this._constantPools = []; + } + + public addConstantPool(constantPool: ConstantPool): void { + this._constantPools.push(constantPool); + } +} diff --git a/libraries/analysis-javascript/lib/constant/ConstantVisitor.ts b/libraries/analysis-javascript/lib/constant/ConstantVisitor.ts new file mode 100644 index 000000000..2598b9b8d --- /dev/null +++ b/libraries/analysis-javascript/lib/constant/ConstantVisitor.ts @@ -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) => void = ( + path: NodePath + ) => { + 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`); + } + } + }; +} From 67c4e9689e63b389e8109ffd199974d95b72567b Mon Sep 17 00:00:00 2001 From: Mitchell Olsthoorn Date: Tue, 11 Jul 2023 11:44:11 +0200 Subject: [PATCH 2/6] feat: constants pool --- libraries/analysis-javascript/index.ts | 4 + .../lib/constant/ConstantPool.ts | 76 ++++++++++++++++++- .../lib/constant/ConstantPoolManager.ts | 20 ++++- .../sampling/JavaScriptRandomSampler.ts | 16 +++- tools/javascript/lib/JavaScriptLauncher.ts | 44 +++++++++-- .../plugins/sampler/RandomSamplerPlugin.ts | 1 + 6 files changed, 145 insertions(+), 16 deletions(-) diff --git a/libraries/analysis-javascript/index.ts b/libraries/analysis-javascript/index.ts index 533f08979..0dcfc0d69 100644 --- a/libraries/analysis-javascript/index.ts +++ b/libraries/analysis-javascript/index.ts @@ -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"; diff --git a/libraries/analysis-javascript/lib/constant/ConstantPool.ts b/libraries/analysis-javascript/lib/constant/ConstantPool.ts index 614bb8194..f47c6a146 100644 --- a/libraries/analysis-javascript/lib/constant/ConstantPool.ts +++ b/libraries/analysis-javascript/lib/constant/ConstantPool.ts @@ -16,15 +16,22 @@ * limitations under the License. */ +import { prng } from "@syntest/prng"; + export class ConstantPool { protected _numericPool: Map; protected _integerPool: Map; protected _bigIntPool: Map; protected _stringPool: Map; + protected _numericCount: number; + protected _integerCount: number; + protected _bigIntCount: number; + protected _stringCount: number; constructor() { this._numericPool = new Map(); this._integerPool = new Map(); + this._bigIntPool = new Map(); this._stringPool = new Map(); } @@ -34,6 +41,7 @@ export class ConstantPool { } else { this._numericPool.set(value, 1); } + this._numericCount++; } public addInteger(value: number): void { @@ -42,6 +50,7 @@ export class ConstantPool { } else { this._integerPool.set(value, 1); } + this._integerCount++; } public addBigInt(value: bigint): void { @@ -50,6 +59,7 @@ export class ConstantPool { } else { this._bigIntPool.set(value, 1); } + this._bigIntCount++; } public addString(value: string): void { @@ -58,10 +68,70 @@ export class ConstantPool { } else { this._stringPool.set(value, 1); } + this._stringCount++; + } + + public getRandomNumeric(frequencyBased = false): number { + 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 getRandomNumeric(): number { - const index = Math.floor(Math.random() * this._numericPool.size); - return [...this._numericPool.keys()][index]; + public getRandomInteger(frequencyBased = false): number { + 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 (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 (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()]); + } } } diff --git a/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts b/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts index 9617795c5..60f375301 100644 --- a/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts +++ b/libraries/analysis-javascript/lib/constant/ConstantPoolManager.ts @@ -19,11 +19,25 @@ import { ConstantPool } from "./ConstantPool"; export class ConstantPoolManager { + protected _targetConstantPool: ConstantPool; + protected _contextConstantPool: ConstantPool; + protected _dynamicConstantPool: ConstantPool; + constructor() { - this._constantPools = []; + 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 addConstantPool(constantPool: ConstantPool): void { - this._constantPools.push(constantPool); + public get dynamicConstantPool(): ConstantPool { + return this._dynamicConstantPool; } } diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts index ef280eb67..807cb34d0 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts @@ -18,6 +18,7 @@ import { ClassTarget, + ConstantPoolManager, FunctionTarget, getRelationName, isExported, @@ -56,9 +57,11 @@ import { IntegerStatement } from "../statements/primitive/IntegerStatement"; export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { private _rootContext: RootContext; + private _constantPoolManager: ConstantPoolManager; constructor( subject: JavaScriptSubject, + constantPoolManager: ConstantPoolManager, typeInferenceMode: string, randomTypeProbability: number, incorporateExecutionInformation: boolean, @@ -81,6 +84,7 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { deltaMutationProbability, exploreIllegalValues ); + this._constantPoolManager = constantPoolManager; } /** @@ -774,11 +778,15 @@ 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 (prng.nextDouble() >= 0.5) { + value = this._constantPoolManager.contextConstantPool.getRandomString(); + } else { + const valueLength = prng.nextInt(0, maxlength - 1); - for (let index = 0; index < valueLength; index++) { - value += prng.pickOne([...alphabet]); + for (let index = 0; index < valueLength; index++) { + value += prng.pickOne([...alphabet]); + } } return new StringStatement( diff --git a/tools/javascript/lib/JavaScriptLauncher.ts b/tools/javascript/lib/JavaScriptLauncher.ts index ce7ddd3c6..662feb497 100644 --- a/tools/javascript/lib/JavaScriptLauncher.ts +++ b/tools/javascript/lib/JavaScriptLauncher.ts @@ -33,6 +33,9 @@ import { DependencyFactory, TypeExtractor, isExported, + ConstantPoolManager, + ConstantVisitor, + getAllFiles, } from "@syntest/analysis-javascript"; import { ArgumentsObject, @@ -79,6 +82,7 @@ import { getLogger, Logger } from "@syntest/logging"; import { TargetType } from "@syntest/analysis"; import { MetricManager } from "@syntest/metric"; import { StorageManager } from "@syntest/storage"; +import traverse from "@babel/traverse"; export type JavaScriptArguments = ArgumentsObject & TestCommandOptions; export class JavaScriptLauncher extends Launcher { @@ -182,13 +186,15 @@ export class JavaScriptLauncher extends Launcher { async preprocess(): Promise { JavaScriptLauncher.LOGGER.info("Preprocessing started"); - const start = Date.now(); + const startPreProcessing = Date.now(); + + const startTargetSelection = Date.now(); const targetSelector = new TargetSelector(this.rootContext); this.targets = targetSelector.loadTargets( this.arguments_.include, this.arguments_.exclude ); - let timeInMs = (Date.now() - start) / 1000; + let timeInMs = (Date.now() - startTargetSelection) / 1000; this.metricManager.recordProperty( PropertyName.TARGET_LOAD_TIME, `${timeInMs}` @@ -346,19 +352,18 @@ export class JavaScriptLauncher extends Launcher { this.rootContext.extractTypes(); JavaScriptLauncher.LOGGER.info("Resolving types"); this.rootContext.resolveTypes(); - JavaScriptLauncher.LOGGER.info("Preprocessing done"); - timeInMs = (Date.now() - startTypeResolving) / 1000; this.metricManager.recordProperty( PropertyName.TYPE_RESOLVE_TIME, `${timeInMs}` ); - timeInMs = (Date.now() - start) / 1000; + timeInMs = (Date.now() - startPreProcessing) / 1000; this.metricManager.recordProperty( PropertyName.PREPROCESS_TIME, `${timeInMs}` ); + JavaScriptLauncher.LOGGER.info("Preprocessing done"); } async process(): Promise { @@ -643,10 +648,37 @@ export class JavaScriptLauncher extends Launcher { this.arguments_.testDirectory ); - // TODO constant pool + JavaScriptLauncher.LOGGER.info("Extracting constants"); + const constantPoolManager = new ConstantPoolManager(); + const targetAbstractSyntaxTree = this.rootContext.getAbstractSyntaxTree( + target.path + ); + const constantVisitor = new ConstantVisitor( + target.path, + constantPoolManager.targetConstantPool + ); + traverse(targetAbstractSyntaxTree, constantVisitor); + + const files = getAllFiles(this.rootContext.rootPath, ".js").filter( + (x) => + !x.includes("/test/") && + !x.includes(".test.js") && + !x.includes("node_modules") + ); + + for (const file of files) { + const abstractSyntaxTree = this.rootContext.getAbstractSyntaxTree(file); + const constantVisitor = new ConstantVisitor( + file, + constantPoolManager.contextConstantPool + ); + traverse(abstractSyntaxTree, constantVisitor); + } + JavaScriptLauncher.LOGGER.info("Extracting constants done"); const sampler = new JavaScriptRandomSampler( currentSubject, + constantPoolManager, (this.arguments_).typeInferenceMode, (this.arguments_).randomTypeProbability, (this.arguments_).incorporateExecutionInformation, diff --git a/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts b/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts index caef0e680..86e1b5dc4 100644 --- a/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts +++ b/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts @@ -39,6 +39,7 @@ export class RandomSamplerPlugin extends SamplerPlugin { ): EncodingSampler { return new JavaScriptRandomSampler( options.subject as unknown as JavaScriptSubject, + undefined, ((this.args)).typeInferenceMode, ((this.args)).randomTypeProbability, (( From 56112d651fda7d3cfd46d1c42ced99d0f75c733c Mon Sep 17 00:00:00 2001 From: Mitchell Olsthoorn Date: Tue, 11 Jul 2023 12:01:44 +0200 Subject: [PATCH 3/6] feat: add config parameters --- .../sampling/JavaScriptRandomSampler.ts | 27 ++++++++++++++++--- tools/javascript/lib/JavaScriptLauncher.ts | 2 ++ .../plugins/sampler/RandomSamplerPlugin.ts | 2 ++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts index 807cb34d0..743e686b7 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts @@ -58,10 +58,14 @@ import { IntegerStatement } from "../statements/primitive/IntegerStatement"; export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { private _rootContext: RootContext; private _constantPoolManager: ConstantPoolManager; + private _constantPoolEnabled: boolean; + private _constantPoolProbability: number; constructor( subject: JavaScriptSubject, constantPoolManager: ConstantPoolManager, + constantPoolEnabled: boolean, + constantPoolProbability: number, typeInferenceMode: string, randomTypeProbability: number, incorporateExecutionInformation: boolean, @@ -85,6 +89,8 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { exploreIllegalValues ); this._constantPoolManager = constantPoolManager; + this._constantPoolEnabled = constantPoolEnabled; + this._constantPoolProbability = constantPoolProbability; } /** @@ -779,7 +785,10 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { maxlength = this.stringMaxLength ): StringStatement { let value: string; - if (prng.nextDouble() >= 0.5) { + if ( + prng.nextDouble() >= this._constantPoolProbability && + this._constantPoolEnabled + ) { value = this._constantPoolManager.contextConstantPool.getRandomString(); } else { const valueLength = prng.nextInt(0, maxlength - 1); @@ -820,12 +829,18 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { const max = 10; const min = -10; + const value = + prng.nextDouble() >= this._constantPoolProbability && + this._constantPoolEnabled + ? this._constantPoolManager.contextConstantPool.getRandomNumeric() + : prng.nextDouble(min, max); + return new NumericStatement( id, name, TypeEnum.NUMERIC, prng.uniqueId(), - prng.nextDouble(min, max) + value ); } @@ -834,12 +849,18 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { const max = 10; const min = -10; + const value = + prng.nextDouble() >= this._constantPoolProbability && + this._constantPoolEnabled + ? this._constantPoolManager.contextConstantPool.getRandomInteger() + : prng.nextInt(min, max); + return new IntegerStatement( id, name, TypeEnum.INTEGER, prng.uniqueId(), - prng.nextInt(min, max) + value ); } diff --git a/tools/javascript/lib/JavaScriptLauncher.ts b/tools/javascript/lib/JavaScriptLauncher.ts index 662feb497..a78fc8d49 100644 --- a/tools/javascript/lib/JavaScriptLauncher.ts +++ b/tools/javascript/lib/JavaScriptLauncher.ts @@ -679,6 +679,8 @@ export class JavaScriptLauncher extends Launcher { const sampler = new JavaScriptRandomSampler( currentSubject, constantPoolManager, + this.arguments_.constantPool, + this.arguments_.constantPoolProbability, (this.arguments_).typeInferenceMode, (this.arguments_).randomTypeProbability, (this.arguments_).incorporateExecutionInformation, diff --git a/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts b/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts index 86e1b5dc4..fb5021e41 100644 --- a/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts +++ b/tools/javascript/lib/plugins/sampler/RandomSamplerPlugin.ts @@ -40,6 +40,8 @@ export class RandomSamplerPlugin extends SamplerPlugin { return new JavaScriptRandomSampler( options.subject as unknown as JavaScriptSubject, undefined, + undefined, + undefined, ((this.args)).typeInferenceMode, ((this.args)).randomTypeProbability, (( From 2f60446fbaa5819b4491fc2042592d7d63f56022 Mon Sep 17 00:00:00 2001 From: apanichella Date: Tue, 11 Jul 2023 16:41:25 +0200 Subject: [PATCH 4/6] fix: initializing the constant pool with some standard values --- .../analysis-javascript/lib/constant/ConstantPool.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/analysis-javascript/lib/constant/ConstantPool.ts b/libraries/analysis-javascript/lib/constant/ConstantPool.ts index f47c6a146..aab88493d 100644 --- a/libraries/analysis-javascript/lib/constant/ConstantPool.ts +++ b/libraries/analysis-javascript/lib/constant/ConstantPool.ts @@ -30,9 +30,21 @@ export class ConstantPool { 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 { From 372ccc4451eec3bb56e6f30e2020a3204daed98f Mon Sep 17 00:00:00 2001 From: Mitchell Olsthoorn Date: Wed, 2 Aug 2023 12:44:56 +0200 Subject: [PATCH 5/6] feat: move constant configuration to javascript module --- .../lib/constant/ConstantPool.ts | 16 ++++++++ .../sampling/JavaScriptRandomSampler.ts | 37 +++++++++++-------- .../sampling/JavaScriptTestCaseSampler.ts | 22 +++++++++++ tools/javascript/lib/JavaScriptLauncher.ts | 22 +++++++++-- tools/javascript/lib/commands/test.ts | 22 +++++++++++ 5 files changed, 99 insertions(+), 20 deletions(-) diff --git a/libraries/analysis-javascript/lib/constant/ConstantPool.ts b/libraries/analysis-javascript/lib/constant/ConstantPool.ts index aab88493d..1e7098093 100644 --- a/libraries/analysis-javascript/lib/constant/ConstantPool.ts +++ b/libraries/analysis-javascript/lib/constant/ConstantPool.ts @@ -84,6 +84,10 @@ export class ConstantPool { } 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()) { @@ -100,6 +104,10 @@ export class ConstantPool { } 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()) { @@ -116,6 +124,10 @@ export class ConstantPool { } 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()) { @@ -132,6 +144,10 @@ export class ConstantPool { } 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()) { diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts index 743e686b7..f05d661ef 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts @@ -57,9 +57,6 @@ import { IntegerStatement } from "../statements/primitive/IntegerStatement"; export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { private _rootContext: RootContext; - private _constantPoolManager: ConstantPoolManager; - private _constantPoolEnabled: boolean; - private _constantPoolProbability: number; constructor( subject: JavaScriptSubject, @@ -78,6 +75,9 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { ) { super( subject, + constantPoolManager, + constantPoolEnabled, + constantPoolProbability, typeInferenceMode, randomTypeProbability, incorporateExecutionInformation, @@ -88,9 +88,6 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { deltaMutationProbability, exploreIllegalValues ); - this._constantPoolManager = constantPoolManager; - this._constantPoolEnabled = constantPoolEnabled; - this._constantPoolProbability = constantPoolProbability; } /** @@ -786,11 +783,13 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { ): StringStatement { let value: string; if ( - prng.nextDouble() >= this._constantPoolProbability && - this._constantPoolEnabled + this.constantPoolEnabled && + prng.nextBoolean(this.constantPoolProbability) ) { - value = this._constantPoolManager.contextConstantPool.getRandomString(); - } else { + value = this.constantPoolManager.contextConstantPool.getRandomString(); + } + + if (value === undefined) { const valueLength = prng.nextInt(0, maxlength - 1); for (let index = 0; index < valueLength; index++) { @@ -830,11 +829,14 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { const min = -10; const value = - prng.nextDouble() >= this._constantPoolProbability && - this._constantPoolEnabled - ? this._constantPoolManager.contextConstantPool.getRandomNumeric() + 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, @@ -850,11 +852,14 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { const min = -10; const value = - prng.nextDouble() >= this._constantPoolProbability && - this._constantPoolEnabled - ? this._constantPoolManager.contextConstantPool.getRandomInteger() + 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, diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptTestCaseSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptTestCaseSampler.ts index 9c81dd17d..f63462e04 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptTestCaseSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptTestCaseSampler.ts @@ -36,6 +36,7 @@ import { ArrowFunctionStatement } from "../statements/complex/ArrowFunctionState import { ArrayStatement } from "../statements/complex/ArrayStatement"; import { ObjectStatement } from "../statements/complex/ObjectStatement"; import { IntegerStatement } from "../statements/primitive/IntegerStatement"; +import { ConstantPoolManager } from "@syntest/analysis-javascript"; /** * JavaScriptRandomSampler class @@ -43,6 +44,9 @@ import { IntegerStatement } from "../statements/primitive/IntegerStatement"; * @author Dimitri Stallenberg */ export abstract class JavaScriptTestCaseSampler extends EncodingSampler { + private _constantPoolManager: ConstantPoolManager; + private _constantPoolEnabled: boolean; + private _constantPoolProbability: number; private _typeInferenceMode: string; private _randomTypeProbability: number; private _incorporateExecutionInformation: boolean; @@ -55,6 +59,9 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSamplerthis.arguments_).constantPool.toString()}` + ); + this.metricManager.recordProperty( + PropertyName.CONSTANT_POOL_PROBABILITY, + `${(( + this.arguments_ + )).constantPoolProbability.toString()}` + ); + this.storageManager.deleteTemporaryDirectories([ [this.arguments_.testDirectory], [this.arguments_.logDirectory], @@ -287,10 +298,13 @@ export class JavaScriptLauncher extends Launcher { "Sample Output Values", `${this.arguments_.sampleFunctionOutputAsArgument}`, ], - ["Use Constant Pool Values", `${this.arguments_.constantPool}`], + [ + "Use Constant Pool Values", + `${(this.arguments_).constantPool}`, + ], [ "Use Constant Pool Probability", - `${this.arguments_.constantPoolProbability}`, + `${(this.arguments_).constantPoolProbability}`, ], ], footers: ["", ""], @@ -679,8 +693,8 @@ export class JavaScriptLauncher extends Launcher { const sampler = new JavaScriptRandomSampler( currentSubject, constantPoolManager, - this.arguments_.constantPool, - this.arguments_.constantPoolProbability, + (this.arguments_).constantPool, + (this.arguments_).constantPoolProbability, (this.arguments_).typeInferenceMode, (this.arguments_).randomTypeProbability, (this.arguments_).incorporateExecutionInformation, diff --git a/tools/javascript/lib/commands/test.ts b/tools/javascript/lib/commands/test.ts index c208827b4..bc8076c3b 100644 --- a/tools/javascript/lib/commands/test.ts +++ b/tools/javascript/lib/commands/test.ts @@ -33,6 +33,7 @@ export function getTestCommand( const options = new Map(); const commandGroup = "Type Inference Options:"; + const samplingGroup = "Sampling Options:"; options.set("incorporate-execution-information", { alias: [], @@ -62,6 +63,25 @@ export function getTestCommand( type: "number", }); + options.set("constant-pool", { + alias: [], + default: false, + description: "Enable constant pool.", + group: samplingGroup, + hidden: false, + type: "boolean", + }); + + options.set("constant-pool-probability", { + alias: [], + default: 0.5, + description: + "Probability to sample from the constant pool instead creating random values", + group: samplingGroup, + hidden: false, + type: "number", + }); + return new Command( moduleManager, tool, @@ -85,4 +105,6 @@ export type TestCommandOptions = { incorporateExecutionInformation: boolean; typeInferenceMode: string; randomTypeProbability: number; + constantPool: boolean; + constantPoolProbability: number; }; From a0e8025f2eeb16db06226239671529673944401c Mon Sep 17 00:00:00 2001 From: Mitchell Olsthoorn Date: Wed, 2 Aug 2023 14:36:38 +0200 Subject: [PATCH 6/6] feat: fix unescaped input from constant pool --- .../lib/testcase/sampling/JavaScriptRandomSampler.ts | 1 + .../lib/testcase/statements/primitive/StringStatement.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts index f05d661ef..01bbd91a2 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts @@ -790,6 +790,7 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { } if (value === undefined) { + value = ""; const valueLength = prng.nextInt(0, maxlength - 1); for (let index = 0; index < valueLength; index++) { diff --git a/libraries/search-javascript/lib/testcase/statements/primitive/StringStatement.ts b/libraries/search-javascript/lib/testcase/statements/primitive/StringStatement.ts index 4baef5bae..227d9d0df 100644 --- a/libraries/search-javascript/lib/testcase/statements/primitive/StringStatement.ts +++ b/libraries/search-javascript/lib/testcase/statements/primitive/StringStatement.ts @@ -193,9 +193,14 @@ export class StringStatement extends PrimitiveStatement { } override decode(): Decoding[] { + let value = this.value; + value = value.replace(/\n/g, "\\n"); + value = value.replace(/\r/g, "\\r"); + value = value.replace(/\t/g, "\\t"); + value = value.replace(/"/g, '\\"'); return [ { - decoded: `const ${this.varName} = "${this.value}";`, + decoded: `const ${this.varName} = "${value}";`, reference: this, }, ];