Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat Dynamic Export Analysis #187

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Changes from all 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
@@ -29,6 +29,10 @@ export * from "./lib/constant/ConstantVisitor";
export * from "./lib/dependency/DependencyFactory";
export * from "./lib/dependency/DependencyVisitor";

export * from "./lib/dynamic/action/Action";
export * from "./lib/dynamic/action/ActionFactory";
export * from "./lib/dynamic/action/Executor";

export * from "./lib/target/export/Export";
export * from "./lib/target/export/ExportDefaultDeclaration";
export * from "./lib/target/export/ExportFactory";
40 changes: 39 additions & 1 deletion libraries/analysis-javascript/lib/RootContext.ts
Original file line number Diff line number Diff line change
@@ -29,6 +29,8 @@ import { ConstantPool } from "./constant/ConstantPool";
import { ConstantPoolFactory } from "./constant/ConstantPoolFactory";
import { ConstantPoolManager } from "./constant/ConstantPoolManager";
import { DependencyFactory } from "./dependency/DependencyFactory";
import { Action } from "./dynamic/action/Action";
import { ActionFactory } from "./dynamic/action/ActionFactory";
import { Events } from "./Events";
import { Export } from "./target/export/Export";
import { ExportFactory } from "./target/export/ExportFactory";
@@ -51,6 +53,8 @@ export class RootContext extends CoreRootContext<t.Node> {

protected _constantPoolFactory: ConstantPoolFactory;

protected _actionFactory: ActionFactory;

protected _targetFiles: Set<string>;
protected _analysisFiles: Set<string>;

@@ -66,6 +70,7 @@ export class RootContext extends CoreRootContext<t.Node> {

// Mapping: filepath -> target name -> Exports
protected _exportMap: Map<string, Export[]>;
protected _actionMap: Map<string, Map<string, Action>>;

constructor(
rootPath: string,
@@ -78,7 +83,8 @@ export class RootContext extends CoreRootContext<t.Node> {
exportFactory: ExportFactory,
typeExtractor: TypeExtractor,
typeResolver: TypeModelFactory,
constantPoolFactory: ConstantPoolFactory
constantPoolFactory: ConstantPoolFactory,
actionFactory: ActionFactory
) {
super(
rootPath,
@@ -95,6 +101,7 @@ export class RootContext extends CoreRootContext<t.Node> {
this._typeExtractor = typeExtractor;
this._typeResolver = typeResolver;
this._constantPoolFactory = constantPoolFactory;
this._actionFactory = actionFactory;
}

get rootPath(): string {
@@ -133,6 +140,11 @@ export class RootContext extends CoreRootContext<t.Node> {
return this._sources.get(absoluteTargetPath);
}

protected async getActions(filePath: string) {
const factory = new ActionFactory(1000);
return await factory.extract(filePath, this.getSource(filePath), this.getAbstractSyntaxTree(filePath));
}

getExports(filePath: string): Export[] {
const absolutePath = this.resolvePath(filePath);

@@ -159,6 +171,28 @@ export class RootContext extends CoreRootContext<t.Node> {
return this._exportMap.get(absolutePath);
}

async extractAllActions() {
if (!this._actionMap) {
this._actionMap = new Map();

for (const filepath of this._analysisFiles) {
this._actionMap.set(filepath, await this.getActions(filepath));
}

this._actionFactory.exit()
}

return this._actionMap;
}

getAllActions() {
if (!this._actionMap) {
throw new Error("First call extractAllActions before calling getAllActions")
}

return this._actionMap;
}

getAllExports(): Map<string, Export[]> {
if (!this._exportMap) {
this._exportMap = new Map();
@@ -354,4 +388,8 @@ export class RootContext extends CoreRootContext<t.Node> {
RootContext.LOGGER.info("Extracting constants done");
return constantPoolManager;
}

exit() {
this._actionFactory.exit();
}
}
Original file line number Diff line number Diff line change
@@ -15,33 +15,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { prng } from "@syntest/prng";

import { StatementPool } from "../../../StatementPool";
import { Getter } from "../../../statements/action/Getter";
export type Action = {
type: ActionType;

import { CallGenerator } from "./CallGenerator";
// identification
id: string;
filePath: string;
// location: Location; ??

export class GetterGenerator extends CallGenerator<Getter> {
override generate(
depth: number,
variableIdentifier: string,
typeIdentifier: string,
exportIdentifier: string,
name: string,
_statementPool: StatementPool
): Getter {
const constructor_ = this.sampler.sampleConstructorCall(
depth + 1,
exportIdentifier
);
// ancestory
children: {
[key: string]: Action;
};
parentId: string | undefined

return new Getter(
variableIdentifier,
typeIdentifier,
name,
prng.uniqueId(),
constructor_
);
}
}
// properties
constructable: boolean
name: string
};

export type ActionType =
| "function"
| "object"
| "constant" // maybe nice
106 changes: 106 additions & 0 deletions libraries/analysis-javascript/lib/dynamic/action/ActionFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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 { ChildProcess, fork } from "node:child_process";
import * as path from "node:path";

import * as t from "@babel/types";
import { getLogger, Logger } from "@syntest/logging";

import { Action } from "./Action";
import { ExecuteMessage, ResultMessage } from "./Executor";

export class ActionFactory {
protected static LOGGER: Logger;

private executionTimeout: number;
private _process: ChildProcess;

constructor(executionTimeout: number) {
ActionFactory.LOGGER = getLogger(ActionFactory.name);

this.executionTimeout = executionTimeout;
// eslint-disable-next-line unicorn/prefer-module
this._process = fork(path.join(__dirname, "Executor.js"));
}

exit() {
if (this._process) {
this._process.kill();
}
}

async extract(filePath: string, source: string, ast: t.Node) {
// try catch maybe?
return await this._extract(filePath, source, ast);
}

private async _extract(filePath: string, source: string, ast: t.Node): Promise<Map<string, Action>> {
if (!this._process.connected || this._process.killed) {
// eslint-disable-next-line unicorn/prefer-module
this._process = fork(path.join(__dirname, "Executor.js"));
}
const childProcess = this._process;

return await new Promise<Map<string, Action>>((resolve, reject) => {
const timeout = setTimeout(() => {
ActionFactory.LOGGER.warn(
`Execution timeout reached killing process, timeout: ${this.executionTimeout}`
);
childProcess.removeAllListeners();
childProcess.kill();
reject("timeout");
}, this.executionTimeout);

childProcess.on("message", (message: ResultMessage) => {
if (typeof message !== "object") {
return reject(
new TypeError("Invalid data received from child process")
);
}

if (message.message === "result") {
childProcess.removeAllListeners();
clearTimeout(timeout);

//
const actionMap = new Map<string, Action>()

for (const key of Object.keys(message.actions)) {
actionMap.set(key, message.actions[key])
}

return resolve(actionMap);
}
});

childProcess.on("error", (error) => {
reject(error);
});

const executeMessage: ExecuteMessage = {
message: "execute",
filePath: filePath,
source: source,
ast: ast
};

childProcess.send(executeMessage);
});
}
}
346 changes: 346 additions & 0 deletions libraries/analysis-javascript/lib/dynamic/action/Executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
/*
* 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, traverse } from "@babel/core";
import * as t from "@babel/types";

import { Action, ActionType } from "./Action";

export type ExecuteMessage = {
message: "execute";
filePath: string;
source: string;
ast: t.Node;
};

export type ResultMessage = {
message: "result";
actions: {[key: string]: Action};
};

// eslint-disable-next-line @typescript-eslint/no-misused-promises
process.on("message", async (data: ExecuteMessage) => {
if (typeof data !== "object") {
throw new TypeError("Invalid data received from child process");
}
if (data.message === "execute") {
await gatherIntel(data.filePath, data.source, data.ast);
}
});

function getLineAndColumn(text: string, index: number) {
let line = 1;
let column = 0;
for (let index_ = 0; index_ < index; index_++) {
column++;
if (text[index_] === "\n") {
column = 0;
line++;
}
if (text[index_] === "\r") {
// A line feed after a carriage return counts as part of the same newline
if (text[index_ + 1] === "\n") {
index_++;
}
column = 0;
line++;
}
}
return { line, column };
}

function isClass(object: any) {
if (object == undefined || object === undefined) {
return false;
}

const isCtorClass =
object.constructor && object.constructor.toString().slice(0, 5) === "class";
const isNativeCtorClass= object.constructor &&
object.constructor.name != "Function" &&
object.constructor.name in global;

if (object.prototype === undefined) {
return isCtorClass || isNativeCtorClass
}
const isPrototypeCtorClass =
object.prototype.constructor &&
object.prototype.constructor.toString &&
object.prototype.constructor.toString().slice(0, 5) === "class";

const isNativePrototypeCtorClass =
object.prototype.constructor.name in global &&
((<any>global)[object.prototype.constructor.name] == object.constructor ||
(<any>global)[object.prototype.constructor.name] == object);

const hasPrototypes =
object.prototype && Object.keys(object.prototype).length > 0;
return (
isCtorClass ||
isNativeCtorClass ||
isPrototypeCtorClass ||
isNativePrototypeCtorClass ||
hasPrototypes
);
}

// function getAllFuncs(toCheck: any) {
// const properties: string[] = [];
// let object = toCheck;
// do {
// properties.push(...Object.getOwnPropertyNames(object));
// } while (((object = Object.getPrototypeOf(object)) && object != Object.prototype));

// return properties.sort().filter((e, index, array) => {
// if (Object.prototype.hasOwnProperty(e)) {
// return false
// }
// if (e!=array[index+1] && typeof toCheck[e] == 'function') return true;
// return false
// });
// }

function isConstructable(object: any) {
if (object == undefined || object === undefined) {
return false;
}

const handler={construct(){return handler}} //Must return ANY object, so reuse one

// try{
// return !!(new (new Proxy(object, handler))())
// }catch{
// return false
// }

try {
Reflect.construct(String, [], object);
} catch {
return false;
}
return true;
// const properties: Set<string | symbol> = getAllMethodNames(
// object.prototype
// );
// return object.hasOwnProperty('prototype') && (Object.keys(object).length > 0 || properties.size > 0)
// return !!object.constructor
}

function getAllMethodNames(object: any) {
const methods = new Set<string | symbol>();

if (!(object instanceof Object)) {
return methods;
}

do {
const keys = Reflect.ownKeys(object);
for (const k of keys) methods.add(k);
} while (
(object = Reflect.getPrototypeOf(object)) &&
object != Object.prototype
);
return methods;
}

function getSourceCodeLocationId(filePath: string, source: string, ast: t.Node, parentAction: Action, key: string, object: any): string {
const text: string = object.toString();

if (key === 'constructor') {
let id: string
traverse(ast, {
ClassMethod: {
enter: (path: NodePath<t.ClassMethod>) => {
if (path.node.kind !== 'constructor') {
return
}
const classParent = path.parentPath.parentPath
const [start, end] = parentAction.id.split(':::')[2].split(':')
if (classParent.isClass() && classParent.node.start === Number.parseInt(start) && classParent.node.end === Number.parseInt(end)) {
id = `${filePath}:${path.node.loc.start.line}:${path.node.loc.start.column}:::${path.node.loc.end.line}:${path.node.loc.end.column}:::${path.node.loc.start.index}:${path.node.loc.end.index}`;
}
}
}
});


return id
// // terrible solution to extract a constructor
// // TODO this only prevents directly commented code
// // TODO /* \n\n\n constructor ... */ is also commented but will be picked up by this
// // TODO this regex also only allows white space between the ) and { at the end of a constructor
// const regx = /(?<!((\/\/[\S\s]*)|(\/\*[\S\s]*)))constructor[\S\s]*?\([\S\s]*?\)\s*?{/g;
// const match = regx.exec(text)

// console.log(match)
// if (!match) {
// return undefined
// }

// const startIndex = match.index
// const value = match[0]
// console.log(value)
// let endIndex = startIndex + value.length
// // find the closing bracket
// let depth = 1
// for (;depth > 0;endIndex++) {
// if (!source[endIndex]) {
// throw new Error("Cannot find closing bracket")
// }
// if (source[endIndex] === '{') {
// depth++
// } else if (source[endIndex] === '}') {
// depth--
// }
// }

// const { line: startLine, column: startColumn } = getLineAndColumn(
// source,
// startIndex
// );
// const { line: endLine, column: endColumn } = getLineAndColumn(
// source,
// endIndex
// );

// return `${filePath}:${startLine}:${startColumn}:::${endLine}:${endColumn}:::${startIndex}:${endIndex}`;
} else {
const startIndex = source.indexOf(text);

if (source.lastIndexOf(text) !== startIndex) {
console.warn("Multiple occurences of the same function/object found in export! We do not support this! (Additionally, duplicate code is not something you want!)")
}

let id: string
if (startIndex === -1) {
console.log('could not find the following:')
console.log(object.toString())
// its actually an object or class
id = isClass(object) ? "object" : "function";
} else {
const endIndex = startIndex + text.length;
const { line: startLine, column: startColumn } = getLineAndColumn(
source,
startIndex
);
const { line: endLine, column: endColumn } = getLineAndColumn(
source,
endIndex
);
id = `${filePath}:${startLine}:${startColumn}:::${endLine}:${endColumn}:::${startIndex}:${endIndex}`;
}
return id
}
}

async function gatherIntel(filePath: string, source: string, ast: t.Node) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const import_ = await import(filePath);

let rootAction: Action;
const actions: {[key: string]: Action} = {}

// queue of [parent, key, child]
const queue: [Action, string, unknown][] = [[undefined, "__root__", import_]];

while (queue.length > 0) {
const [parentAction, key, child] = queue.shift();

// console.log(child)
if (!(child instanceof Object)) {
// TODO could be an constant!
continue;
}

const id: string = getSourceCodeLocationId(filePath, source, ast, parentAction, key, child)

if (!id) {
// probably non-defined constructor
continue
}

if (actions[id]) {
// no repeats
continue
}

const type: ActionType = typeof child === 'function' ? 'function' : 'object'//typeof child === 'object' ? 'object' : 'constant'

const childAction: Action = {
type: type,

id: id,
filePath: filePath,

children: {},
parentId: parentAction ? parentAction.id : undefined,

constructable: isConstructable(child) && key !== 'constructor',
name: key
};

actions[id] = childAction
if (!rootAction) {
rootAction = childAction;
}

if (parentAction) {
parentAction.children[key] = childAction;
}

if (isConstructable(child) && key !== 'constructor') {
queue.push([childAction, 'constructor', (<any>child).prototype['constructor']])
}

if (key === 'constructor') {
continue
}

for (const key of Object.keys(child)) {
if (key === "prototype") {
continue;
}
if ((<any>child)[key] === child) {
continue
}
queue.push([childAction, key, (<never>child)[key]]);
}

const properties: Set<string | symbol> = getAllMethodNames(
(<any>child).prototype
);
for (const key of properties) {
if ((<any>child).prototype[key] === child) {
continue;
}
queue.push([childAction, key.toString(), (<any>child).prototype[key]]);
}
}

// console.log(rootAction);
// console.log(JSON.stringify(rootAction, undefined, 2));

const resultMessage: ResultMessage = {
message: "result",
actions: actions,
};
// console.log(resultMessage)
// console.log(JSON.stringify(resultMessage, undefined, 2));

process.send(resultMessage);
}
Original file line number Diff line number Diff line change
@@ -103,7 +103,6 @@ export function extractExportsFromRightAssignmentExpression(
});
}

console.log(exports);
return exports;
}

16 changes: 6 additions & 10 deletions libraries/search-javascript/index.ts
Original file line number Diff line number Diff line change
@@ -34,24 +34,20 @@ export * from "./lib/testcase/sampling/JavaScriptRandomSampler";
export * from "./lib/testcase/sampling/JavaScriptTestCaseSampler";

export * from "./lib/testcase/statements/action/ActionStatement";
export * from "./lib/testcase/statements/action/Getter";
export * from "./lib/testcase/statements/action/MethodCall";
export * from "./lib/testcase/statements/action/Setter";

export * from "./lib/testcase/statements/complex/ArrayStatement";
export * from "./lib/testcase/statements/complex/ArrowFunctionStatement";
export * from "./lib/testcase/statements/complex/ObjectStatement";

export * from "./lib/testcase/statements/primitive/BoolStatement";
export * from "./lib/testcase/statements/primitive/NullStatement";
export * from "./lib/testcase/statements/primitive/NumericStatement";
export * from "./lib/testcase/statements/primitive/PrimitiveStatement";
export * from "./lib/testcase/statements/primitive/StringStatement";
export * from "./lib/testcase/statements/primitive/UndefinedStatement";
export * from "./lib/testcase/statements/literal/BoolStatement";
export * from "./lib/testcase/statements/literal/NullStatement";
export * from "./lib/testcase/statements/literal/NumericStatement";
export * from "./lib/testcase/statements/literal/LiteralStatement";
export * from "./lib/testcase/statements/literal/StringStatement";
export * from "./lib/testcase/statements/literal/UndefinedStatement";

export * from "./lib/testcase/statements/action/ConstructorCall";
export * from "./lib/testcase/statements/action/FunctionCall";
export * from "./lib/testcase/statements/action/ConstantObject";

export * from "./lib/testcase/statements/Statement";

30 changes: 16 additions & 14 deletions libraries/search-javascript/lib/search/JavaScriptSubject.ts
Original file line number Diff line number Diff line change
@@ -15,8 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TargetType } from "@syntest/analysis";
import { RootContext, SubTarget, Target } from "@syntest/analysis-javascript";
import { Action, RootContext, Target } from "@syntest/analysis-javascript";
import { ControlFlowGraph, Edge, EdgeType } from "@syntest/cfg";
import {
ApproachLevel,
@@ -31,15 +30,18 @@ import { BranchDistance } from "../criterion/BranchDistance";
import { JavaScriptTestCase } from "../testcase/JavaScriptTestCase";

export class JavaScriptSubject extends SearchSubject<JavaScriptTestCase> {
protected _actions: Map<string, Action>
protected syntaxForgiving: boolean;
protected stringAlphabet: string;
constructor(
target: Target,
actions: Map<string, Action>,
rootContext: RootContext,
syntaxForgiving: boolean,
stringAlphabet: string
) {
super(target, rootContext);
this._actions = actions
this.syntaxForgiving = syntaxForgiving;
this.stringAlphabet = stringAlphabet;

@@ -181,19 +183,19 @@ export class JavaScriptSubject extends SearchSubject<JavaScriptTestCase> {
return childObjectives;
}

getActionableTargets(): SubTarget[] {
return this._target.subTargets.filter((t) => {
return (
t.type === TargetType.FUNCTION ||
t.type === TargetType.CLASS ||
t.type === TargetType.METHOD ||
t.type === TargetType.OBJECT ||
t.type === TargetType.OBJECT_FUNCTION
);
});
get actionsMap(): Map<string, Action> {
return this._actions
}

get actions(): Action[] {
return [...this._actions.values()]
}

get constructableActions(): Action[] {
return this.actions.filter((x) => x.constructable)
}

getActionableTargetsByType(type: TargetType): SubTarget[] {
return this.getActionableTargets().filter((t) => t.type === type);
get functionActions(): Action[] {
return this.actions.filter((x) => x.type === 'function')
}
}
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@ import { Crossover } from "@syntest/search";

import { JavaScriptTestCase } from "../../testcase/JavaScriptTestCase";
import { ActionStatement } from "../../testcase/statements/action/ActionStatement";
import { ConstantObject } from "../../testcase/statements/action/ConstantObject";
import { ConstructorCall } from "../../testcase/statements/action/ConstructorCall";
import { Statement } from "../../testcase/statements/Statement";

@@ -85,29 +84,7 @@ export class TreeCrossover extends Crossover<JavaScriptTestCase> {
if (
swapA.child instanceof ConstructorCall &&
swapB.child instanceof ConstructorCall &&
swapA.child.export.id !== swapB.child.export.id
) {
continue;
}

if (
swapA.child instanceof ConstantObject &&
!(swapB.child instanceof ConstantObject)
) {
continue;
}

if (
swapB.child instanceof ConstantObject &&
!(swapA.child instanceof ConstantObject)
) {
continue;
}

if (
swapA.child instanceof ConstantObject &&
swapB.child instanceof ConstantObject &&
swapA.child.export.id !== swapB.child.export.id
swapA.child.action.id !== swapB.child.action.id
) {
continue;
}
89 changes: 21 additions & 68 deletions libraries/search-javascript/lib/testbuilding/ContextBuilder.ts
Original file line number Diff line number Diff line change
@@ -18,33 +18,28 @@

import * as path from "node:path";

import { Export } from "@syntest/analysis-javascript";
import { Action } from "@syntest/analysis-javascript";
import {
globalVariables,
reservedKeywords,
} from "@syntest/ast-visitor-javascript";
import { getLogger, Logger } from "@syntest/logging";

import { ClassActionStatement } from "../testcase/statements/action/ClassActionStatement";
import { FunctionCall } from "../testcase/statements/action/FunctionCall";
import { ObjectFunctionCall } from "../testcase/statements/action/ObjectFunctionCall";
import { ImportStatement } from "../testcase/statements/action/ImportStatement";
import { Statement } from "../testcase/statements/Statement";

type Import = RegularImport | RenamedImport;

type RegularImport = {
name: string;
renamed: false;
module: boolean;
default: boolean;
};

type RenamedImport = {
name: string;
renamed: true;
renamedTo: string;
module: boolean;
default: boolean;
};

type Require = {
@@ -69,6 +64,7 @@ export class ContextBuilder {
// Statement -> variableName
private statementVariableNameMap: Map<Statement, string>;


constructor(targetRootDirectory: string, sourceDirectory: string) {
ContextBuilder.LOGGER = getLogger("ContextBuilder");
this.targetRootDirectory = targetRootDirectory;
@@ -87,6 +83,10 @@ export class ContextBuilder {
}

getOrCreateVariableName(statement: Statement): string {
if (statement instanceof ImportStatement) {
return this.getOrCreateImportName(statement.action)
}

if (this.statementVariableNameMap.has(statement)) {
return this.statementVariableNameMap.get(statement);
}
@@ -103,9 +103,7 @@ export class ContextBuilder {
: variableName;

if (
statement instanceof ClassActionStatement ||
statement instanceof FunctionCall ||
statement instanceof ObjectFunctionCall
statement instanceof FunctionCall
) {
variableName += "ReturnValue";
}
@@ -136,33 +134,31 @@ export class ContextBuilder {
return variableName;
}

getOrCreateImportName(export_: Export): string {
const import_ = this._addImport(export_);
getOrCreateImportName(action: Action): string {
const import_ = this._addImport(action);

return import_.renamed ? import_.renamedTo : import_.name;
}

private _addImport(export_: Export): Import {
const path_ = export_.filePath.replace(
protected _addImport(action: Action): Import {
const path_ = action.filePath.replace(
path.resolve(this.targetRootDirectory),
path.join(this.sourceDirectory, path.basename(this.targetRootDirectory))
);

const exportedName = export_.renamedTo;


const exportedName = action.name;
let import_: Import = {
name: exportedName === "default" ? "defaultExport" : exportedName,
name: exportedName === "__root__" ? "defaultExport" : exportedName,
renamed: false,
default: export_.default,
module: export_.module,
};
let newName: string = exportedName;

if (this.imports.has(path_)) {
const foundImport = this.imports.get(path_).find((value) => {
return (
value.name === import_.name &&
value.default === import_.default &&
value.module === import_.module
value.name === import_.name
);
});
if (foundImport !== undefined) {
@@ -197,9 +193,7 @@ export class ContextBuilder {
import_ = {
name: exportedName,
renamed: true,
renamedTo: newName,
default: export_.default,
module: export_.module,
renamedTo: newName
};
}

@@ -211,50 +205,13 @@ export class ContextBuilder {
return import_;
}

// TODO we could gather all the imports of a certain path together into one import
private _getImportString(_path: string, import_: Import): string {
if (import_.module) {
throw new Error("Only non module imports can use import statements");
}

// if (import_.renamed) {
// return import_.default
// ? `const ${import_.renamedTo} = require("${_path}";`
// : `const {${import_.name} as ${import_.renamedTo}} = equire("${_path}";`;
// } else {
// return import_.default
// ? `const ${import_.name} = require("${_path}";`
// : `const {${import_.name}} = require("${_path}";`;
// }

if (import_.renamed) {
return import_.default
? `import ${import_.renamedTo} from "${_path}";`
: `import {${import_.name} as ${import_.renamedTo}} from "${_path}";`;
} else {
return import_.default
? `import ${import_.name} from "${_path}";`
: `import {${import_.name}} from "${_path}";`;
}
}

private _getRequireString(_path: string, import_: Import): Require {
if (!import_.module) {
throw new Error("Only module imports can use require statements");
}

const require: Require = {
left: "",
right: `require("${_path}")`,
right: `import("${_path}")`,
};

if (import_.renamed) {
require.left = import_.default
? import_.renamedTo
: `{${import_.name}: ${import_.renamedTo}}`;
} else {
require.left = import_.default ? import_.name : `{${import_.name}}`;
}
require.left = import_.renamed && import_.renamedTo ? import_.renamedTo : import_.name;

return require;
}
@@ -266,11 +223,7 @@ export class ContextBuilder {
for (const [path_, imports_] of this.imports.entries()) {
// TODO remove unused imports
for (const import_ of imports_) {
if (import_.module) {
requires.push(this._getRequireString(path_, import_));
} else {
imports.push(this._getImportString(path_, import_));
}
requires.push(this._getRequireString(path_, import_));
}
}

22 changes: 12 additions & 10 deletions libraries/search-javascript/lib/testbuilding/JavaScriptDecoder.ts
Original file line number Diff line number Diff line change
@@ -20,9 +20,8 @@ import { Decoder } from "@syntest/search";

import { JavaScriptTestCase } from "../testcase/JavaScriptTestCase";
import { ActionStatement } from "../testcase/statements/action/ActionStatement";
import { ClassActionStatement } from "../testcase/statements/action/ClassActionStatement";
import { FunctionCall } from "../testcase/statements/action/FunctionCall";
import { ObjectFunctionCall } from "../testcase/statements/action/ObjectFunctionCall";
import { ImportStatement } from "../testcase/statements/action/ImportStatement";
import { Decoding } from "../testcase/statements/Statement";

import { assertionFunction } from "./assertionFunctionTemplate";
@@ -125,7 +124,6 @@ export class JavaScriptDecoder implements Decoder<JavaScriptTestCase, string> {

const lines = [
"// Imports",
"require = require('esm')(module)",
...imports,
gatherAssertionData ? assertionFunction : "",
`describe('SynTest Test Suite', function() {`,
@@ -167,23 +165,27 @@ export class JavaScriptDecoder implements Decoder<JavaScriptTestCase, string> {
}

for (const [index, value] of decodings.entries()) {
if (value.reference instanceof ImportStatement) {
// do not include import statements in the test lines
continue
}

const variableName = value.variableName
const asString = value.decoded;
if (testLines.includes(asString)) {
const fullString = `const ${variableName} = ${asString}`
if (testLines.includes(fullString)) {
// skip repeated statements
continue;
}

testLines.push(asString);
testLines.push(fullString);

if (gatherAssertionData) {
// add log per statement
const variableName = context.getOrCreateVariableName(value.reference);
testLines.push(`count = ${index + 1};`);

if (
value.reference instanceof FunctionCall ||
value.reference instanceof ObjectFunctionCall ||
value.reference instanceof ClassActionStatement
value.reference instanceof FunctionCall
) {
testLines.push(
`addAssertion('${testCase.id}', '${variableName}', ${variableName})`
@@ -256,7 +258,7 @@ export class JavaScriptDecoder implements Decoder<JavaScriptTestCase, string> {

assertions.push(
`await expect((async () => {`,
`\t${errorDecoding.decoded.split(" = ")[1]}`,
`\t${errorDecoding.decoded}`,
`})()).to.be.rejectedWith("${value}")`
);
}
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ export class JavaScriptTestCase extends Encoding {
if (choice < 0.33) {
// 33% chance to add a root on this position
const index = prng.nextInt(0, roots.length);
roots.splice(index, 0, sampler.sampleRoot());
roots.splice(index, 0, sampler.sampleFunctionCall(0));
} else if (choice < 0.66) {
// 33% chance to delete the root
const index = prng.nextInt(0, roots.length - 1);
@@ -79,7 +79,7 @@ export class JavaScriptTestCase extends Encoding {
if (choice < 0.5) {
// 50% chance to add a root on this position
const index = prng.nextInt(0, roots.length);
roots.splice(index, 0, sampler.sampleRoot());
roots.splice(index, 0, sampler.sampleFunctionCall(0));
} else {
// 50% chance to just mutate the root
const index = prng.nextInt(0, roots.length - 1);
38 changes: 7 additions & 31 deletions libraries/search-javascript/lib/testcase/StatementPool.ts
Original file line number Diff line number Diff line change
@@ -15,14 +15,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Action } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";

import { ActionStatement } from "./statements/action/ActionStatement";
import { ClassActionStatement } from "./statements/action/ClassActionStatement";
import { ConstantObject } from "./statements/action/ConstantObject";
import { ConstructorCall } from "./statements/action/ConstructorCall";
import { FunctionCall } from "./statements/action/FunctionCall";
import { ObjectFunctionCall } from "./statements/action/ObjectFunctionCall";
import { Statement } from "./statements/Statement";

export class StatementPool {
@@ -31,12 +29,10 @@ export class StatementPool {
// this is a bit out of scope for this class but otherwise we have to walk the tree multiple times
// we can solve this by making a singular tree walker class with visitors
private constructors: ConstructorCall[];
private objects: ConstantObject[];

constructor(roots: ActionStatement[]) {
this.pool = new Map();
this.constructors = [];
this.objects = [];
this._fillGenePool(roots);
}

@@ -50,9 +46,9 @@ export class StatementPool {
return prng.pickOne(statements);
}

public getRandomConstructor(exportId?: string): ConstructorCall {
const options = exportId
? this.constructors.filter((o) => exportId === o.export.id)
public getRandomConstructor(action?: Action): ConstructorCall {
const options = action
? this.constructors.filter((o) => action.id === o.action.id)
: this.constructors;

if (options.length === 0) {
@@ -61,16 +57,6 @@ export class StatementPool {
return prng.pickOne(options);
}

public getRandomConstantObject(exportId: string): ConstantObject {
const options = exportId
? this.objects.filter((o) => exportId === o.export.id)
: this.objects;
if (options.length === 0) {
return undefined;
}
return prng.pickOne(options);
}

private _fillGenePool(roots: ActionStatement[]) {
for (const action of roots) {
const queue: Statement[] = [action];
@@ -83,20 +69,10 @@ export class StatementPool {
}

// use type enum for primitives and arrays
let type: string = statement.ownType;
const type: string = statement.returnType;

if (statement instanceof ConstantObject) {
// use export identifier
type = statement.export.id;
this.objects.push(statement);
} else if (statement instanceof ConstructorCall) {
// use export identifier
type = statement.export.id;
this.constructors.push(statement);
} else if (
statement instanceof FunctionCall ||
statement instanceof ClassActionStatement ||
statement instanceof ObjectFunctionCall
if (
statement instanceof FunctionCall
) {
// TODO use return type
// type = statement.
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
*/

import { TypeModel } from "@syntest/analysis-javascript";
import { getLogger, Logger } from "@syntest/logging";
import Mocha = require("mocha");

import { JavaScriptTestCase } from "../JavaScriptTestCase";
@@ -25,9 +26,11 @@ import { Statement } from "../statements/Statement";
import { Test } from "./TestExecutor";

export class ExecutionInformationIntegrator {
protected static LOGGER: Logger
private _typeModel: TypeModel;

constructor(typeModel: TypeModel) {
ExecutionInformationIntegrator.LOGGER = getLogger(ExecutionInformationIntegrator.name)
this._typeModel = typeModel;
}

@@ -46,12 +49,13 @@ export class ExecutionInformationIntegrator {
if (
testResult.error &&
testResult.error.message &&
testResult.error.message.includes(child.name)
testResult.error.message.includes(child.name) && (child.name !== 'constructor')
) {
ExecutionInformationIntegrator.LOGGER.info(`Adding execution score to '${child.name}' with id '${child.variableIdentifier}'`)
this._typeModel.addExecutionScore(
child.variableIdentifier,
child.typeIdentifier,
child.ownType
child.type
);
}
queue.push(child);

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -16,43 +16,31 @@
* limitations under the License.
*/

import { ConstantPoolManager, RootContext } from "@syntest/analysis-javascript";
import { Action, ConstantPoolManager, ObjectType, RootContext } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";
import { EncodingSampler } from "@syntest/search";

import { JavaScriptSubject } from "../../search/JavaScriptSubject";
import { JavaScriptTestCase } from "../JavaScriptTestCase";
import { StatementPool } from "../StatementPool";
import { ActionStatement } from "../statements/action/ActionStatement";
import { ConstantObject } from "../statements/action/ConstantObject";
import { ConstructorCall } from "../statements/action/ConstructorCall";
import { FunctionCall } from "../statements/action/FunctionCall";
import { Getter } from "../statements/action/Getter";
import { MethodCall } from "../statements/action/MethodCall";
import { ObjectFunctionCall } from "../statements/action/ObjectFunctionCall";
import { Setter } from "../statements/action/Setter";
import { ImportStatement } from "../statements/action/ImportStatement";
import { MemberStatement } from "../statements/action/MemberStatement";
import { ArrayStatement } from "../statements/complex/ArrayStatement";
import { ArrowFunctionStatement } from "../statements/complex/ArrowFunctionStatement";
import { ObjectStatement } from "../statements/complex/ObjectStatement";
import { BoolStatement } from "../statements/primitive/BoolStatement";
import { IntegerStatement } from "../statements/primitive/IntegerStatement";
import { NullStatement } from "../statements/primitive/NullStatement";
import { NumericStatement } from "../statements/primitive/NumericStatement";
import { StringStatement } from "../statements/primitive/StringStatement";
import { UndefinedStatement } from "../statements/primitive/UndefinedStatement";
import { BoolStatement } from "../statements/literal/BoolStatement";
import { IntegerStatement } from "../statements/literal/IntegerStatement";
import { NullStatement } from "../statements/literal/NullStatement";
import { NumericStatement } from "../statements/literal/NumericStatement";
import { StringStatement } from "../statements/literal/StringStatement";
import { UndefinedStatement } from "../statements/literal/UndefinedStatement";
import { Statement } from "../statements/Statement";

import { ConstantObjectGenerator } from "./generators/action/ConstantObjectGenerator";
import { ConstructorCallGenerator } from "./generators/action/ConstructorCallGenerator";
import { FunctionCallGenerator } from "./generators/action/FunctionCallGenerator";
import { GetterGenerator } from "./generators/action/GetterGenerator";
import { MethodCallGenerator } from "./generators/action/MethodCallGenerator";
import { ObjectFunctionCallGenerator } from "./generators/action/ObjectFunctionCallGenerator";
import { SetterGenerator } from "./generators/action/SetterGenerator";

/**
* JavaScriptRandomSampler class
*
* @author Dimitri Stallenberg
*/
export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScriptTestCase> {
private _rootContext: RootContext;
@@ -82,16 +70,6 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScri

private _statementPool: StatementPool | null;

private _functionCallGenerator: FunctionCallGenerator;

private _constructorCallGenerator: ConstructorCallGenerator;
private _methodCallGenerator: MethodCallGenerator;
private _getterGenerator: GetterGenerator;
private _setterGenerator: SetterGenerator;

private _constantObjectGenerator: ConstantObjectGenerator;
private _objectFunctionCallGenerator: ObjectFunctionCallGenerator;

constructor(
subject: JavaScriptSubject,
constantPoolManager: ConstantPoolManager,
@@ -137,77 +115,6 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScri

set rootContext(rootContext: RootContext) {
this._rootContext = rootContext;

this._functionCallGenerator = new FunctionCallGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._constructorCallGenerator = new ConstructorCallGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._methodCallGenerator = new MethodCallGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._getterGenerator = new GetterGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._setterGenerator = new SetterGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._constantObjectGenerator = new ConstantObjectGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
this._objectFunctionCallGenerator = new ObjectFunctionCallGenerator(
this,
rootContext,
this._statementPoolEnabled,
this._statementPoolProbability
);
}

get functionCallGenerator() {
return this._functionCallGenerator;
}

get constructorCallGenerator() {
return this._constructorCallGenerator;
}

get methodCallGenerator() {
return this._methodCallGenerator;
}

get getterGenerator() {
return this._getterGenerator;
}

get setterGenerator() {
return this._setterGenerator;
}

get constantObjectGenerator() {
return this._constantObjectGenerator;
}

get objectFunctionCallGenerator() {
return this._objectFunctionCallGenerator;
}

get statementPool() {
@@ -218,24 +125,45 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScri
this._statementPool = statementPool;
}

abstract sampleRoot(): ActionStatement;
/**
* Samples an action
* Use when
* @param depth
* @param action
*/
abstract sampleParentAction(
depth: number,
action: Action
): ActionStatement;

abstract sampleFunctionCall(
depth: number
): FunctionCall;

abstract sampleFunctionCall(depth: number): FunctionCall;
abstract sampleSpecificFunctionCall(
depth: number,
action: string | Action
): FunctionCall;

abstract sampleConstructorCall(
depth: number
): ConstructorCall;

abstract sampleSpecificConstructorCall(
depth: number,
classId?: string
action: string | Action
): ConstructorCall;
abstract sampleClassAction(depth: number): MethodCall | Getter | Setter;
abstract sampleMethodCall(depth: number): MethodCall;
abstract sampleGetter(depth: number): Getter;
abstract sampleSetter(depth: number): Setter;

abstract sampleConstantObject(
abstract sampleMemberStatement(
depth: number,
objectId?: string
): ConstantObject;
abstract sampleObjectFunctionCall(depth: number): ObjectFunctionCall;
action: Action,
key: string
): MemberStatement

abstract sampleImportStatement(
depth: number,
action: Action
): ImportStatement

// TODO
// abstract sampleStaticMethodCall(depth: number): MethodCall;
@@ -255,7 +183,7 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScri
id: string,
typeId: string,
name: string
): ObjectStatement | ConstructorCall | ConstantObject | FunctionCall;
): ObjectStatement | ConstructorCall | FunctionCall;

abstract sampleArray(
depth: number,
@@ -301,6 +229,40 @@ export abstract class JavaScriptTestCaseSampler extends EncodingSampler<JavaScri
name: string
): UndefinedStatement;

sampleArguments(depth: number, type_: ObjectType): Statement[] {
const arguments_: Statement[] = [];

for (const [index, parameterId] of type_.parameters.entries()) {
const name = type_.parameterNames.get(index);
arguments_[index] = this.sampleArgument(
depth + 1,
parameterId,
name
);
}

// if some params are missing, fill them with fake params
const parameterIds = [...type_.parameters.values()];
for (let index = 0; index < arguments_.length; index++) {
if (!arguments_[index]) {
arguments_[index] = this.sampleArgument(
depth + 1,
prng.pickOne(parameterIds),
String(index)
);
}
}

for (let index = 0; index < 10; index++) {
if (prng.nextBoolean(0.05)) {
// TODO make this a config parameter
arguments_.push(this.sampleArgument(depth + 1, "anon", "anon"));
}
}

return arguments_;
}

get constantPoolManager(): ConstantPoolManager {
return this._constantPoolManager;
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 Reference {

}
32 changes: 21 additions & 11 deletions libraries/search-javascript/lib/testcase/statements/Statement.ts
Original file line number Diff line number Diff line change
@@ -22,13 +22,12 @@ import { Encoding, EncodingSampler } from "@syntest/search";
import { ContextBuilder } from "../../testbuilding/ContextBuilder";

/**
* @author Dimitri Stallenberg
* The abstract statement class
*/
export abstract class Statement {
private _variableIdentifier: string;
private _typeIdentifier: string;
private _name: string;
private _ownType: TypeEnum;
protected _uniqueId: string;

public get variableIdentifier(): string {
@@ -43,32 +42,32 @@ export abstract class Statement {
return this._name;
}

get ownType(): TypeEnum {
return this._ownType;
}

public get uniqueId(): string {
return this._uniqueId;
}

/**
* Constructor
* @param identifierDescription
* @param ownType
* @param uniqueId
* @param variableIdentifier the identifier that points to the variable this statement represents
* @param typeIdentifier the identifier used to choose the type of this statement
* @param name
* @param uniqueId the unique id of the statement
*/
protected constructor(
variableIdentifier: string,
typeIdentifier: string,
name: string,
ownType: TypeEnum,
uniqueId: string
) {
this._variableIdentifier = variableIdentifier;
this._typeIdentifier = typeIdentifier;
this._name = name;
this._ownType = ownType;
this._uniqueId = uniqueId;

if (!name) {
console.log(name)
throw new Error("Name must be given!")
}
}

/**
@@ -112,9 +111,20 @@ export abstract class Statement {
* Note: when implementing this function please always decode the children of the statement before making getOrCreateVariableName on the context object.
*/
abstract decode(context: ContextBuilder): Decoding[];

/**
* returns the return type of statement
*/
abstract get returnType(): string;

/**
* returns the type of statement
*/
abstract get type(): TypeEnum;
}

export interface Decoding {
variableName: string;
decoded: string;
reference: Statement;
}
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
* limitations under the License.
*/

import { Export, TypeEnum } from "@syntest/analysis-javascript";
import { Action } from "@syntest/analysis-javascript";
import { Encoding, EncodingSampler, shouldNeverHappen } from "@syntest/search";

import { Statement } from "../Statement";
@@ -25,21 +25,29 @@ import { Statement } from "../Statement";
* ActionStatement
*/
export abstract class ActionStatement extends Statement {
private _args: Statement[];
protected _export?: Export;
protected _action: Action;
private _children: Statement[];
private _parent: Statement | undefined

protected constructor(
variableIdentifier: string,
typeIdentifier: string,
name: string,
ownType: TypeEnum,
uniqueId: string,
arguments_: Statement[],
export_?: Export
action: Action,
children: Statement[],
parent?: Statement | undefined
) {
super(variableIdentifier, typeIdentifier, name, ownType, uniqueId);
this._args = arguments_;
this._export = export_;
super(variableIdentifier, typeIdentifier, name, uniqueId);
this._action = action;
this._children = children;
this._parent = parent

for (const child of children) {
if (!child) {
throw new Error("Invalid arg")
}
}
}

abstract override mutate(
@@ -49,31 +57,51 @@ export abstract class ActionStatement extends Statement {

abstract override copy(): ActionStatement;

setChild(index: number, newChild: Statement) {
hasChildren(): boolean {
return this._children.length > 0 || !this._parent;
}

getChildren(): Statement[] {
const children = [...this._children]
if (this._parent) {
children.push(this._parent)
}
return children;
}

override setChild(index: number, newChild: Statement) {
if (!newChild) {
throw new Error("Invalid new child!");
}

if (index < 0 || index >= this.args.length) {
if (index < 0 || index > this.children.length) {
throw new Error(shouldNeverHappen(`Invalid index used index: ${index}`));
}

if (index === this.children.length && !this.parent) {
throw new Error(shouldNeverHappen(`Invalid index used index: ${index}`));
}

this.args[index] = newChild;
if (index === this.children.length) {
this._parent = newChild;
} else {
this._children[index] = newChild;
}
}

hasChildren(): boolean {
return this._args.length > 0;
get action() {
return this._action;
}

getChildren(): Statement[] {
return [...this._args];
protected get parent(): Statement {
return this._parent
}

protected get args(): Statement[] {
return this._args;
protected get children(): Statement[] {
return this._children;
}

get export() {
return this._export;
override get returnType(): string {
return this.typeIdentifier
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
* limitations under the License.
*/

import { Export, TypeEnum } from "@syntest/analysis-javascript";
import { Action, TypeEnum } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";

import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
@@ -29,103 +29,97 @@ import { ActionStatement } from "./ActionStatement";
* ConstructorCall
*/
export class ConstructorCall extends ActionStatement {
private _classIdentifier: string;

get classIdentifier(): string {
return this._classIdentifier;
}

/**
* Constructor
* @param type the return identifierDescription of the constructor
* @param uniqueId optional argument
* @param args the arguments of the constructor
*/
constructor(
variableIdentifier: string,
typeIdentifier: string,
classIdentifier: string,
name: string,
uniqueId: string,
action: Action,
arguments_: Statement[],
export_: Export
parent: Statement
) {
super(
variableIdentifier,
typeIdentifier,
name,
TypeEnum.FUNCTION,
uniqueId,
action,
arguments_,
export_
parent
);
this._classIdentifier = classIdentifier;
}

mutate(sampler: JavaScriptTestCaseSampler, depth: number): ConstructorCall {
if (prng.nextBoolean(sampler.deltaMutationProbability)) {
const arguments_ = this.args.map((a: Statement) => a.copy());
let parent = this.parent.copy()
const arguments_ = this.children.map((a: Statement) => a.copy());

if (arguments_.length > 0) {
const index = prng.nextInt(0, arguments_.length - 1);
const index = prng.nextInt(0, arguments_.length);

if (index < arguments_.length) {
arguments_[index] = arguments_[index].mutate(sampler, depth + 1);
} else {
parent = parent.mutate(sampler, depth + 1)
}

return new ConstructorCall(
this.variableIdentifier,
this.typeIdentifier,
this._classIdentifier,
this.name,
prng.uniqueId(),
this.action,
arguments_,
this.export
parent
);
} else {
return sampler.constructorCallGenerator.generate(
return sampler.sampleSpecificConstructorCall(
depth,
this.variableIdentifier,
this.typeIdentifier,
this.export.id,
this.name,
sampler.statementPool
this.action
);
}
}

copy(): ConstructorCall {
const deepCopyArguments = this.args.map((a: Statement) => a.copy());
const deepCopyArguments = this.children.map((a: Statement) => a.copy());

return new ConstructorCall(
this.variableIdentifier,
this.typeIdentifier,
this._classIdentifier,
this.name,
this.uniqueId,
this.action,
deepCopyArguments,
this.export
this.parent.copy()
);
}

decode(context: ContextBuilder): Decoding[] {
const argumentsDecoding: Decoding[] = this.args.flatMap((a) =>
const parentDecoding = this.parent.decode(context)
const argumentsDecoding: Decoding[] = this.children.flatMap((a) =>
a.decode(context)
);

const arguments_ = this.args
const arguments_ = this.children
.map((a) => context.getOrCreateVariableName(a))
.join(", ");

const import_ = context.getOrCreateImportName(this.export);
const decoded = `const ${context.getOrCreateVariableName(
this
)} = new ${import_}(${arguments_})`;
const decoded = `new ${context.getOrCreateVariableName(this.parent)}(${arguments_})`;

return [
...parentDecoding,
...argumentsDecoding,
{
variableName: context.getOrCreateVariableName(
this
),
decoded: decoded,
reference: this,
},
];
}

override get type(): TypeEnum {
return TypeEnum.OBJECT
}
}
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
* limitations under the License.
*/

import { Export, TypeEnum } from "@syntest/analysis-javascript";
import { Action, TypeEnum } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";

import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
@@ -26,86 +26,96 @@ import { Decoding, Statement } from "../Statement";
import { ActionStatement } from "./ActionStatement";

/**
* @author Dimitri Stallenberg
* FunctionCall
*/
export class FunctionCall extends ActionStatement {
/**
* Constructor
* @param uniqueId id of the gene
* @param functionName the name of the function
* @param args the arguments of the function
*/

constructor(
variableIdentifier: string,
typeIdentifier: string,
name: string,
uniqueId: string,
action: Action,
arguments_: Statement[],
export_: Export
parent: Statement
) {
super(
variableIdentifier,
typeIdentifier,
name,
TypeEnum.FUNCTION,
uniqueId,
action,
arguments_,
export_
parent
);
}

mutate(sampler: JavaScriptTestCaseSampler, depth: number): FunctionCall {
// replace entire function call
const arguments_ = this.args.map((a: Statement) => a.copy());

if (arguments_.length > 0) {
const index = prng.nextInt(0, arguments_.length - 1);
arguments_[index] = arguments_[index].mutate(sampler, depth + 1);
}
const arguments_ = this.children.map((a: Statement) => a.copy());

return new FunctionCall(
this.variableIdentifier,
this.typeIdentifier,
this.name,
prng.uniqueId(),
arguments_,
this.export
);
let parent = this.parent.copy()
const index = prng.nextInt(0, arguments_.length)

if (index < arguments_.length) {
arguments_[index] = arguments_[index].mutate(sampler, depth + 1);
} else {
parent = parent.mutate(sampler, depth + 1)
}

return new FunctionCall(
this.variableIdentifier,
this.typeIdentifier,
this.name,
prng.uniqueId(),
this.action,
arguments_,
parent
);

}

copy(): FunctionCall {
const deepCopyArguments = this.args.map((a: Statement) => a.copy());
const deepCopyArguments = this.children.map((a: Statement) => a.copy());

return new FunctionCall(
this.variableIdentifier,
this.typeIdentifier,
this.name,
this.uniqueId,
this.action,
deepCopyArguments,
this.export
this.parent.copy()
);
}

decode(context: ContextBuilder): Decoding[] {
const argumentDecoding: Decoding[] = this.args.flatMap((a) =>
a.decode(context)
);
const objectDecoding = this.parent.decode(context)

const arguments_ = this.args
.map((a) => context.getOrCreateVariableName(a))
.join(", ");
const argumentDecoding: Decoding[] = this.children.flatMap((a) =>
a.decode(context)
);

const import_ = context.getOrCreateImportName(this.export);
const decoded = `const ${context.getOrCreateVariableName(
this
)} = await ${import_}(${arguments_})`;
const arguments_ = this.children
.map((a) => context.getOrCreateVariableName(a))
.join(", ");

const decoded = `await ${context.getOrCreateVariableName(this.parent)}(${arguments_})`;

return [
...objectDecoding,
...argumentDecoding,
{
variableName: context.getOrCreateVariableName(
this
),
decoded: decoded,
reference: this,
},
];
}

return [
...argumentDecoding,
{
decoded: decoded,
reference: this,
},
];
override get type(): TypeEnum {
return TypeEnum.FUNCTION
}
}
Original file line number Diff line number Diff line change
@@ -16,72 +16,69 @@
* limitations under the License.
*/

import { Export, TypeEnum } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";
import { Action, TypeEnum } from "@syntest/analysis-javascript";

import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Decoding } from "../Statement";
import { Decoding, Statement } from "../Statement";

import { ActionStatement } from "./ActionStatement";


/**
* @author Dimitri Stallenberg
* ImportStatement
*/
export class ConstantObject extends ActionStatement {
export class ImportStatement extends ActionStatement {

constructor(
variableIdentifier: string,
typeIdentifier: string,
name: string,
uniqueId: string,
export_: Export
action: Action,
) {
super(
variableIdentifier,
typeIdentifier,
name,
TypeEnum.OBJECT,
uniqueId,
[],
export_
action,
[]
);
}

mutate(sampler: JavaScriptTestCaseSampler, depth: number): ConstantObject {
// delta mutations are non existance here so we make a copy instead
return prng.nextBoolean(sampler.deltaMutationProbability)
? this.copy()
: sampler.constantObjectGenerator.generate(
depth,
this.variableIdentifier,
this.typeIdentifier,
this.export.id,
this.name,
sampler.statementPool
);
mutate(_sampler: JavaScriptTestCaseSampler, _depth: number): ImportStatement {
// nothing to mutate actually
return this.copy()
}

copy(): ConstantObject {
return new ConstantObject(
copy(): ImportStatement {
return new ImportStatement(
this.variableIdentifier,
this.typeIdentifier,
this.name,
this.uniqueId,
this.export
this.action
);
}

decode(context: ContextBuilder): Decoding[] {
const import_ = context.getOrCreateImportName(this.export);
const decoded = `const ${context.getOrCreateVariableName(
this
)} = ${import_}`;

return [
{
decoded: decoded,
variableName: context.getOrCreateVariableName(
this
),
decoded: `SHOULD NOT BE USED`,
reference: this,
},
];
}

override setChild(_index: number, _newChild: Statement): void {
throw new Error("Import statement does not have children")
}

override get type(): TypeEnum {
return TypeEnum.NULL
}
}
Original file line number Diff line number Diff line change
@@ -16,82 +16,85 @@
* limitations under the License.
*/

import { TypeEnum } from "@syntest/analysis-javascript";
import { Action, TypeEnum } from "@syntest/analysis-javascript";
import { prng } from "@syntest/prng";

import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Decoding } from "../Statement";
import { Decoding, Statement } from "../Statement";

import { ActionStatement } from "./ActionStatement";

import { ClassActionStatement } from "./ClassActionStatement";
import { ConstructorCall } from "./ConstructorCall";

/**
* @author Dimitri Stallenberg
* MemberStatement
*/
export class Getter extends ClassActionStatement {
/**
* Constructor
* @param identifierDescription the return type options of the function
* @param type the type of property
* @param uniqueId id of the gene
* @param property the name of the property
*/
export class MemberStatement extends ActionStatement {
private key: string

constructor(
variableIdentifier: string,
typeIdentifier: string,
name: string,
uniqueId: string,
constructor_: ConstructorCall
action: Action,
parent: Statement,
key: string
) {
super(
variableIdentifier,
typeIdentifier,
name,
TypeEnum.FUNCTION,
uniqueId,
action,
[],
constructor_
parent
);
this.key = key
}

mutate(sampler: JavaScriptTestCaseSampler, depth: number): Getter {
const constructor_ = this.constructor_.mutate(sampler, depth + 1);

return new Getter(
this.variableIdentifier,
this.typeIdentifier,
this.name,
prng.uniqueId(),
constructor_
);
mutate(sampler: JavaScriptTestCaseSampler, depth: number): MemberStatement {
return prng.nextBoolean(sampler.deltaMutationProbability) ? new MemberStatement(
this.variableIdentifier,
this.typeIdentifier,
this.name,
prng.uniqueId(),
this.action,
this.parent.mutate(sampler, depth + 1),
this.key
) : sampler.sampleMemberStatement(
depth,
this.action,
this.key
);
}

copy(): Getter {
return new Getter(
copy(): MemberStatement {
return new MemberStatement(
this.variableIdentifier,
this.typeIdentifier,
this.name,
this.uniqueId,
this.constructor_.copy()
this.action,
this.parent.copy(),
this.key
);
}

decode(context: ContextBuilder): Decoding[] {
const constructorDecoding = this.constructor_.decode(context);

const decoded = `const ${context.getOrCreateVariableName(
this
)} = await ${context.getOrCreateVariableName(this.constructor_)}.${
this.name
}`;

return [
...constructorDecoding,
...this.parent.decode(context),
{
decoded: decoded,
variableName: context.getOrCreateVariableName(
this
),
decoded: `${context.getOrCreateVariableName(this.parent)}.${this.name}`,
reference: this,
},
];
}

override get type(): TypeEnum {
return TypeEnum.NULL
}
}

This file was deleted.

This file was deleted.

121 changes: 0 additions & 121 deletions libraries/search-javascript/lib/testcase/statements/action/Setter.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSamp
import { Decoding, Statement } from "../Statement";

/**
* @author Dimitri Stallenberg
* ArrayStatement
*/
export class ArrayStatement extends Statement {
private _elements: Statement[];
@@ -37,7 +37,7 @@ export class ArrayStatement extends Statement {
uniqueId: string,
elements: Statement[]
) {
super(variableIdentifier, typeIdentifier, name, TypeEnum.ARRAY, uniqueId);
super(variableIdentifier, typeIdentifier, name, uniqueId);
this._elements = elements;

// check for circular
@@ -137,13 +137,14 @@ export class ArrayStatement extends Statement {
.map((a) => context.getOrCreateVariableName(a))
.join(", ");

const decoded = `const ${context.getOrCreateVariableName(
this
)} = [${elements}]`;
const decoded = `[${elements}]`;

return [
...elementStatements,
{
variableName: context.getOrCreateVariableName(
this
),
decoded: decoded,
reference: this,
},
@@ -173,4 +174,12 @@ export class ArrayStatement extends Statement {
protected get children(): Statement[] {
return this._elements;
}

override get returnType(): string {
return TypeEnum.ARRAY
}

override get type(): TypeEnum {
return <TypeEnum>this.returnType
}
}
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSamp
import { Decoding, Statement } from "../Statement";

/**
* @author Dimitri Stallenberg
* Arrow function statement
*/
export class ArrowFunctionStatement extends Statement {
private _parameters: string[];
@@ -43,7 +43,6 @@ export class ArrowFunctionStatement extends Statement {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.FUNCTION,
uniqueId
);
this._parameters = parameters;
@@ -59,7 +58,7 @@ export class ArrowFunctionStatement extends Statement {
this.name,
prng.uniqueId(),
this._parameters,
this.returnValue
this._returnValue
? this._returnValue.mutate(sampler, depth + 1)
: undefined
);
@@ -99,25 +98,27 @@ export class ArrowFunctionStatement extends Statement {
if (this._returnValue === undefined) {
return [
{
decoded: `const ${context.getOrCreateVariableName(
variableName: context.getOrCreateVariableName(
this
)} = (${this._parameters.join(", ")}) => {};`,
),
decoded: `(${this._parameters.join(", ")}) => {};`,
reference: this,
},
];
}

const returnStatement: Decoding[] = this._returnValue.decode(context);

const decoded = `const ${context.getOrCreateVariableName(
this
)} = (${this._parameters.join(
const decoded = `(${this._parameters.join(
", "
)}) => { return ${context.getOrCreateVariableName(this.returnValue)} };`;
)}) => { return ${context.getOrCreateVariableName(this._returnValue)} };`;

return [
...returnStatement,
{
variableName: context.getOrCreateVariableName(
this
),
decoded: decoded,
reference: this,
},
@@ -128,7 +129,7 @@ export class ArrowFunctionStatement extends Statement {
if (this._returnValue === undefined) {
return [];
}
return [this.returnValue];
return [this._returnValue];
}

hasChildren(): boolean {
@@ -147,7 +148,11 @@ export class ArrowFunctionStatement extends Statement {
this._returnValue = newChild;
}

get returnValue(): Statement {
return this._returnValue;
override get returnType(): string {
return TypeEnum.FUNCTION
}

override get type(): TypeEnum {
return <TypeEnum>this.returnType
}
}
Original file line number Diff line number Diff line change
@@ -24,12 +24,13 @@ import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Decoding, Statement } from "../Statement";

/**
* @author Dimitri Stallenberg
*/
type ObjectType = {
[key: string]: Statement | undefined;
};

/**
* Object Statement
*/
export class ObjectStatement extends Statement {
private _object: ObjectType;

@@ -40,7 +41,7 @@ export class ObjectStatement extends Statement {
uniqueId: string,
object: ObjectType
) {
super(variableIdentifier, typeIdentifier, name, TypeEnum.OBJECT, uniqueId);
super(variableIdentifier, typeIdentifier, name, uniqueId);
this._object = object;

// check for circular
@@ -179,13 +180,14 @@ export class ObjectStatement extends Statement {
)
.join(",");

const decoded = `const ${context.getOrCreateVariableName(
this
)} = {${children}${children.length > 0 ? "\n\t\t" : ""}}`;
const decoded = `{${children}${children.length > 0 ? "\n\t\t" : ""}}`;

return [
...childStatements,
{
variableName: context.getOrCreateVariableName(
this
),
decoded: decoded,
reference: this,
},
@@ -219,4 +221,12 @@ export class ObjectStatement extends Statement {

this._object[key] = newChild;
}

override get returnType(): string {
return TypeEnum.OBJECT
}

override get type(): TypeEnum {
return <TypeEnum>this.returnType
}
}
Original file line number Diff line number Diff line change
@@ -22,12 +22,12 @@ import { prng } from "@syntest/prng";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Statement } from "../Statement";

import { PrimitiveStatement } from "./PrimitiveStatement";
import { LiteralStatement } from "./LiteralStatement";

/**
* @author Dimitri Stallenberg
*/
export class BoolStatement extends PrimitiveStatement<boolean> {
export class BoolStatement extends LiteralStatement<boolean> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -39,7 +39,6 @@ export class BoolStatement extends PrimitiveStatement<boolean> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.BOOLEAN,
uniqueId,
value
);
@@ -74,4 +73,8 @@ export class BoolStatement extends PrimitiveStatement<boolean> {
this.value
);
}

get returnType() {
return TypeEnum.BOOLEAN
}
}
Original file line number Diff line number Diff line change
@@ -22,15 +22,13 @@ import { prng } from "@syntest/prng";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Statement } from "../Statement";

import { LiteralStatement } from "./LiteralStatement";
import { NumericStatement } from "./NumericStatement";
import { PrimitiveStatement } from "./PrimitiveStatement";

/**
* Generic number class
*
* @author Dimitri Stallenberg
*/
export class IntegerStatement extends PrimitiveStatement<number> {
export class IntegerStatement extends LiteralStatement<number> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -42,7 +40,6 @@ export class IntegerStatement extends PrimitiveStatement<number> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.INTEGER,
uniqueId,
Math.round(value)
);
@@ -118,4 +115,8 @@ export class IntegerStatement extends PrimitiveStatement<number> {
this.value
);
}

get returnType() {
return TypeEnum.INTEGER
}
}
Original file line number Diff line number Diff line change
@@ -22,10 +22,7 @@ import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Decoding, Statement } from "../Statement";

/**
* @author Dimitri Stallenberg
*/
export abstract class PrimitiveStatement<T> extends Statement {
export abstract class LiteralStatement<T> extends Statement {
get value(): T {
return this._value;
}
@@ -35,11 +32,10 @@ export abstract class PrimitiveStatement<T> extends Statement {
variableIdentifier: string,
typeIdentifier: string,
name: string,
type: TypeEnum,
uniqueId: string,
value: T
) {
super(variableIdentifier, typeIdentifier, name, type, uniqueId);
super(variableIdentifier, typeIdentifier, name, uniqueId);
this._value = value;
}

@@ -70,11 +66,18 @@ export abstract class PrimitiveStatement<T> extends Statement {
const asString = String(this.value);
return [
{
decoded: `const ${context.getOrCreateVariableName(
variableName: context.getOrCreateVariableName(
this
)} = ${asString};`,
),
decoded: `${asString};`,
reference: this,
},
];
}

abstract override get returnType(): TypeEnum;

override get type(): TypeEnum {
return this.returnType
}
}
Original file line number Diff line number Diff line change
@@ -22,12 +22,12 @@ import { prng } from "@syntest/prng";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Statement } from "../Statement";

import { PrimitiveStatement } from "./PrimitiveStatement";
import { LiteralStatement } from "./LiteralStatement";

/**
* @author Dimitri Stallenberg
* Null Statement
*/
export class NullStatement extends PrimitiveStatement<boolean> {
export class NullStatement extends LiteralStatement<boolean> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -38,7 +38,6 @@ export class NullStatement extends PrimitiveStatement<boolean> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.NULL,
uniqueId,
// eslint-disable-next-line unicorn/no-null
null
@@ -72,4 +71,8 @@ export class NullStatement extends PrimitiveStatement<boolean> {
this.uniqueId
);
}

get returnType() {
return TypeEnum.NULL
}
}
Original file line number Diff line number Diff line change
@@ -23,14 +23,12 @@ import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSamp
import { Statement } from "../Statement";

import { IntegerStatement } from "./IntegerStatement";
import { PrimitiveStatement } from "./PrimitiveStatement";
import { LiteralStatement } from "./LiteralStatement";

/**
* Generic number class
*
* @author Dimitri Stallenberg
*/
export class NumericStatement extends PrimitiveStatement<number> {
export class NumericStatement extends LiteralStatement<number> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -42,7 +40,6 @@ export class NumericStatement extends PrimitiveStatement<number> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.NUMERIC,
uniqueId,
value
);
@@ -118,4 +115,8 @@ export class NumericStatement extends PrimitiveStatement<number> {
this.value
);
}

get returnType() {
return TypeEnum.NUMERIC
}
}
Original file line number Diff line number Diff line change
@@ -23,12 +23,12 @@ import { ContextBuilder } from "../../../testbuilding/ContextBuilder";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Decoding, Statement } from "../Statement";

import { PrimitiveStatement } from "./PrimitiveStatement";
import { LiteralStatement } from "./LiteralStatement";

/**
* @author Dimitri Stallenberg
* StringStatement
*/
export class StringStatement extends PrimitiveStatement<string> {
export class StringStatement extends LiteralStatement<string> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -40,7 +40,6 @@ export class StringStatement extends PrimitiveStatement<string> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.STRING,
uniqueId,
value
);
@@ -207,9 +206,14 @@ export class StringStatement extends PrimitiveStatement<string> {

return [
{
decoded: `const ${context.getOrCreateVariableName(this)} = "${value}";`,
variableName: context.getOrCreateVariableName(this),
decoded: `"${value}";`,
reference: this,
},
];
}

get returnType() {
return TypeEnum.STRING
}
}
Original file line number Diff line number Diff line change
@@ -22,12 +22,12 @@ import { prng } from "@syntest/prng";
import { JavaScriptTestCaseSampler } from "../../sampling/JavaScriptTestCaseSampler";
import { Statement } from "../Statement";

import { PrimitiveStatement } from "./PrimitiveStatement";
import { LiteralStatement } from "./LiteralStatement";

/**
* @author Dimitri Stallenberg
* UndefinedStatement
*/
export class UndefinedStatement extends PrimitiveStatement<undefined> {
export class UndefinedStatement extends LiteralStatement<undefined> {
constructor(
variableIdentifier: string,
typeIdentifier: string,
@@ -38,7 +38,6 @@ export class UndefinedStatement extends PrimitiveStatement<undefined> {
variableIdentifier,
typeIdentifier,
name,
TypeEnum.UNDEFINED,
uniqueId,
// eslint-disable-next-line unicorn/no-useless-undefined
undefined
@@ -72,4 +71,8 @@ export class UndefinedStatement extends PrimitiveStatement<undefined> {
this.uniqueId
);
}

get returnType() {
return TypeEnum.UNDEFINED
}
}
25 changes: 19 additions & 6 deletions tools/javascript/lib/JavaScriptLauncher.ts
Original file line number Diff line number Diff line change
@@ -20,12 +20,12 @@ import * as path from "node:path";

import {
AbstractSyntaxTreeFactory,
ActionFactory,
ConstantPoolFactory,
ControlFlowGraphFactory,
DependencyFactory,
ExportFactory,
InferenceTypeModelFactory,
isExported,
RootContext,
Target,
TargetFactory,
@@ -201,6 +201,10 @@ export class JavaScriptLauncher extends Launcher {
this.arguments_.analysisExclude
);

const actionFactory = new ActionFactory(
(<JavaScriptArguments>this.arguments_).executionTimeout
);

this.rootContext = new RootContext(
this.arguments_.targetRootDirectory,
targetFiles,
@@ -212,7 +216,8 @@ export class JavaScriptLauncher extends Launcher {
exportFactory,
typeExtractor,
typeResolver,
constantPoolFactory
constantPoolFactory,
actionFactory
);

this.userInterface.printHeader("GENERAL INFO");
@@ -403,7 +408,7 @@ export class JavaScriptLauncher extends Launcher {

this.userInterface.printTable("DIRECTORY SETTINGS", directorySettings);

JavaScriptLauncher.LOGGER.info("Instrumenting targets");
JavaScriptLauncher.LOGGER.info("Instrumenting target files");
const startInstrumentation = Date.now();
const instrumenter = new Instrumenter();
await instrumenter.instrumentAll(
@@ -418,6 +423,9 @@ export class JavaScriptLauncher extends Launcher {
`${timeInMs}`
);

JavaScriptLauncher.LOGGER.info("Gathering actions");
await this.rootContext.extractAllActions();

const startTypeResolving = Date.now();
JavaScriptLauncher.LOGGER.info("Extracting types");
this.rootContext.getAllElements();
@@ -733,16 +741,16 @@ export class JavaScriptLauncher extends Launcher {
JavaScriptLauncher.LOGGER.info(
`Testing target ${target.name} in ${target.path}`
);
const actions = this.rootContext.getAllActions().get(target.path)
const currentSubject = new JavaScriptSubject(
target,
actions,
this.rootContext,
(<JavaScriptArguments>this.arguments_).syntaxForgiving,
this.arguments_.stringAlphabet
);

const rootTargets = currentSubject
.getActionableTargets()
.filter((target) => isExported(target));
const rootTargets = currentSubject.actions

if (rootTargets.length === 0) {
JavaScriptLauncher.LOGGER.info(
@@ -824,6 +832,7 @@ export class JavaScriptLauncher extends Launcher {
sampler: sampler,
});

console.log('generating algo')
const algorithm = (<SearchAlgorithmPlugin<JavaScriptTestCase>>(
this.moduleManager.getPlugin(
PluginType.SearchAlgorithm,
@@ -868,6 +877,7 @@ export class JavaScriptLauncher extends Launcher {
);
}

console.log('starting search')
// This searches for a covering population
const archive = await algorithm.search(
currentSubject,
@@ -917,6 +927,9 @@ export class JavaScriptLauncher extends Launcher {
if (this.runner && this.runner.process) {
this.runner.process.kill();
}
if (this.rootContext) {
this.rootContext.exit();
}
// TODO should be cleanup step in tool
// Finish
JavaScriptLauncher.LOGGER.info("Deleting temporary directories");