diff --git a/libraries/analysis-javascript/lib/type/resolving/InferenceTypeModelFactory.ts b/libraries/analysis-javascript/lib/type/resolving/InferenceTypeModelFactory.ts index 1096e642..f6e211c7 100644 --- a/libraries/analysis-javascript/lib/type/resolving/InferenceTypeModelFactory.ts +++ b/libraries/analysis-javascript/lib/type/resolving/InferenceTypeModelFactory.ts @@ -60,7 +60,7 @@ export class InferenceTypeModelFactory extends TypeModelFactory { } createNewTypeProbability(id: string, bindingId: string) { - this._typeModel.addId(bindingId); + this._typeModel.createTypeNode(bindingId); if (id === bindingId) { // don't set if the id and binding are equal diff --git a/libraries/analysis-javascript/lib/type/resolving/Type.ts b/libraries/analysis-javascript/lib/type/resolving/Type.ts index 35940724..1828c36c 100644 --- a/libraries/analysis-javascript/lib/type/resolving/Type.ts +++ b/libraries/analysis-javascript/lib/type/resolving/Type.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -export interface ObjectType { +export class ObjectType { // name -> id properties: Map; @@ -31,6 +31,35 @@ export interface ObjectType { parameterNames: Map; // id return: Set; + + constructor() { + this.properties = new Map(); + this.elements = new Set(); + this.parameters = new Map(); + this.parameterNames = new Map(); + this.return = new Set(); + } + + public merge(other: ObjectType): ObjectType { + const combined = new ObjectType(); + + combined.properties = new Map([ + ...this.properties.entries(), + ...other.properties.entries(), + ]); + combined.elements = new Set(...this.elements, ...other.elements); + combined.parameters = new Map([ + ...this.parameters.entries(), + ...other.parameters.entries(), + ]); + combined.parameterNames = new Map([ + ...this.parameterNames.entries(), + ...other.parameterNames.entries(), + ]); + combined.return = new Set(...this.return, ...other.return); + + return combined; + } } export const functionProperties = new Set([ diff --git a/libraries/analysis-javascript/lib/type/resolving/TypeModel.ts b/libraries/analysis-javascript/lib/type/resolving/TypeModel.ts index ae01f952..ad86181c 100644 --- a/libraries/analysis-javascript/lib/type/resolving/TypeModel.ts +++ b/libraries/analysis-javascript/lib/type/resolving/TypeModel.ts @@ -16,149 +16,48 @@ * limitations under the License. */ -import { ImplementationError } from "@syntest/diagnostics"; import { prng } from "@syntest/prng"; -import { - arrayProperties, - functionProperties, - ObjectType, - stringProperties, -} from "./Type"; +import { ObjectType } from "./Type"; import { TypeEnum } from "./TypeEnum"; +import { TypeNode } from "./TypeNode"; export class TypeModel { - private _elements: Set; - // element1 -> element2 -> score - private _relationScoreMap: Map>; - // element -> type enum -> score - private _elementTypeScoreMap: Map>; - // element -> type enum -> score - private _typeExecutionScoreMap: Map>; + protected _typeNodes: Map; - // element -> type enum -> probability - private _elementTypeProbabilityMap: Map>; - - // element -> scoreHasChanged - private _scoreHasChangedMap: Map; - - // element -> object type - private _objectTypeDescription: Map; - - constructor() { - this._elements = new Set(); - - this._relationScoreMap = new Map(); - this._elementTypeScoreMap = new Map(); - this._typeExecutionScoreMap = new Map(); - - this._elementTypeProbabilityMap = new Map(); - - this._scoreHasChangedMap = new Map(); - - this._objectTypeDescription = new Map(); - - this.addId("anon"); // should be removed at some point - } - - get relationScoreMap() { - return this._relationScoreMap; + get typeNodes() { + return this._typeNodes; } - get elementTypeScoreMap() { - return this._elementTypeScoreMap; - } + constructor() { + this._typeNodes = new Map(); - get typeExecutionScoreMap() { - return this._typeExecutionScoreMap; + this.createTypeNode("anon"); // should be removed at some point } - getObjectDescription(element: string): ObjectType { - if (!this._objectTypeDescription.has(element)) { - throw new ImplementationError( - `Element ${element} does not have an object description` - ); + public createTypeNode(id: string) { + if (this._typeNodes.has(id)) { + return; } - - return this._objectTypeDescription.get(element); + this._typeNodes.set(id, new TypeNode(id)); } - addId(id: string) { - if (this._elements.has(id)) { - return; + public getTypeNode(id: string) { + if (!this._typeNodes.has(id)) { + throw new Error(`Element ${id} does not exist in type model`); } - - this._elements.add(id); - this._relationScoreMap.set(id, new Map()); - this._elementTypeScoreMap.set(id, new Map()); - this._elementTypeProbabilityMap.set(id, new Map()); - this._typeExecutionScoreMap.set(id, new Map()); - this._scoreHasChangedMap.set(id, true); - - this._objectTypeDescription.set(id, { - properties: new Map(), - elements: new Set(), - parameters: new Map(), - parameterNames: new Map(), - return: new Set(), - }); + return this._typeNodes.get(id); } - setEqual(id1: string, id2: string) { - //TODO maybe merge - for (const [key, value] of this._relationScoreMap.get(id2).entries()) - this._relationScoreMap.get(id1).has(key) - ? this._relationScoreMap - .get(id1) - .set(key, this._relationScoreMap.get(id1).get(key) + value) - : this._relationScoreMap.get(id1).set(key, value); - for (const [key, value] of this._elementTypeScoreMap.get(id2).entries()) - this._elementTypeScoreMap.get(id1).has(key) - ? this._elementTypeScoreMap - .get(id1) - .set(key, this._elementTypeScoreMap.get(id1).get(key) + value) - : this._elementTypeScoreMap.get(id1).set(key, value); - for (const [key, value] of this._elementTypeProbabilityMap - .get(id2) - .entries()) - this._elementTypeProbabilityMap.get(id1).has(key) - ? this._elementTypeProbabilityMap - .get(id1) - .set(key, this._elementTypeProbabilityMap.get(id1).get(key) + value) - : this._elementTypeProbabilityMap.get(id1).set(key, value); - for (const [key, value] of this._typeExecutionScoreMap.get(id2).entries()) - this._typeExecutionScoreMap.get(id1).has(key) - ? this._typeExecutionScoreMap - .get(id1) - .set(key, this._typeExecutionScoreMap.get(id1).get(key) + value) - : this._typeExecutionScoreMap.get(id1).set(key, value); - - this._relationScoreMap.set(id2, this._relationScoreMap.get(id1)); - this._elementTypeScoreMap.set(id2, this._elementTypeScoreMap.get(id1)); - this._elementTypeProbabilityMap.set( - id2, - this._elementTypeProbabilityMap.get(id1) - ); - this._typeExecutionScoreMap.set(id2, this._typeExecutionScoreMap.get(id1)); - this._scoreHasChangedMap.set(id2, this._scoreHasChangedMap.get(id1)); - // TODO maybe this should be merged too? - // or should we keep them separate? - this._objectTypeDescription.set(id2, this._objectTypeDescription.get(id1)); + getObjectDescription(element: string): ObjectType { + return this.getTypeNode(element).objectType; } - private _addRelationScore(id1: string, id2: string, score: number) { - if (!this._relationScoreMap.has(id1)) { - throw new ImplementationError(`Element ${id1} does not exist`); - } - if (!this._relationScoreMap.get(id1).has(id2)) { - this._relationScoreMap.get(id1).set(id2, 0); - } - - const currentScore1 = this._relationScoreMap.get(id1).get(id2); - - this._relationScoreMap.get(id1).set(id2, currentScore1 + score); + setEqual(id1: string, id2: string) { + const node1 = this.getTypeNode(id1); + const node2 = this.getTypeNode(id2); - this._scoreHasChangedMap.set(id1, true); + node1.merge(node2); } addWeakRelation(id1: string, id2: string) { @@ -175,8 +74,10 @@ export class TypeModel { return; // throw new ImplementationError(`ids should not be equal to add a relation id: ${id1}`); } - this._addRelationScore(id1, id2, score); - this._addRelationScore(id2, id1, score); + const node1 = this.getTypeNode(id1); + const node2 = this.getTypeNode(id2); + node1.addDependencyScore(node2, score); + node2.addDependencyScore(node1, score); } addStrongTypeScore(id: string, type: TypeEnum) { @@ -184,90 +85,32 @@ export class TypeModel { } addTypeScore(id: string, type: TypeEnum, score = 1) { - if (!this._elementTypeScoreMap.has(id)) { - throw new ImplementationError(`Element ${id} does not exist`); - } - if (!this._elementTypeScoreMap.get(id).has(type)) { - this._elementTypeScoreMap.get(id).set(type, 0); - } - if (!this._typeExecutionScoreMap.get(id).has(type)) { - this._typeExecutionScoreMap.get(id).set(type, 0); - } - - const currentScore = this._elementTypeScoreMap.get(id).get(type); - - this._elementTypeScoreMap.get(id).set(type, currentScore + score); - this._scoreHasChangedMap.set(id, true); - - if (type === TypeEnum.NUMERIC) { - this.addTypeScore(id, TypeEnum.INTEGER, score); - } + this.getTypeNode(id).addTypeScore(type, score); } addPropertyType(element: string, property: string, id: string) { - // check if the property is from a string/array/function - - if (functionProperties.has(property)) { - this.addTypeScore(element, TypeEnum.FUNCTION); - } - - if (arrayProperties.has(property)) { - this.addTypeScore(element, TypeEnum.ARRAY); - } - - if (stringProperties.has(property)) { - this.addTypeScore(element, TypeEnum.STRING); - } - - this.addTypeScore(element, TypeEnum.OBJECT); - - this.getObjectDescription(element).properties.set(property, id); + this.getTypeNode(element).addPropertyType(property, id); } addParameterType(element: string, index: number, id: string, name: string) { - this.addTypeScore(element, TypeEnum.FUNCTION); - this.getObjectDescription(element).parameters.set(index, id); - this.getObjectDescription(element).parameterNames.set(index, name); + this.getTypeNode(element).addParameterType(index, id, name); } addReturnType(element: string, returnId: string) { - this.addTypeScore(element, TypeEnum.FUNCTION); - this.getObjectDescription(element).return.add(returnId); + this.getTypeNode(element).addReturnType(returnId); } addElementType(element: string, id: string) { - this.addTypeScore(element, TypeEnum.ARRAY); - this.getObjectDescription(element).elements.add(id); + this.getTypeNode(element).addElementType(id); } - // TODO should also add scores to the relations when relevant addExecutionScore( id: string, typeId: string, typeEnum: TypeEnum, score = -1 ) { - if (!this._typeExecutionScoreMap.has(id)) { - throw new ImplementationError(`Element ${id} does not exist`); - } - - let type: string = typeEnum; - - if (id !== typeId) { - type = `${typeId}<>${typeEnum}`; - } - - if (!this._typeExecutionScoreMap.get(id).has(type)) { - this._typeExecutionScoreMap.get(id).set(type, 0); - } - - if (!this._elementTypeScoreMap.get(id).has(type)) { - this._elementTypeScoreMap.get(id).set(type, 0); - } - - const currentScore = this._typeExecutionScoreMap.get(id).get(type); - this._typeExecutionScoreMap.get(id).set(type, currentScore + score); - this._scoreHasChangedMap.set(id, true); + this.getTypeNode(id).addExecutionScore(typeId, typeEnum, score); } private _sum(iterable: Iterable) { @@ -278,30 +121,12 @@ export class TypeModel { } /** - * - * @param incorporateExecutionScore wether the execution score should be weighted in + * Gets a random type * @param id the id we want to get a random type for - * @param matchType (optional) the type enum you want to get (there can be multiple object/function/array types) * @returns a string describing the type */ - getRandomType( - incorporateExecutionScore: boolean, - randomTypeProbability: number, - id: string - ): string { - let probabilities; - - // This allows caching of type scores. The only problem is that when a score of a relation changes it will not recalculate for the current element (only for the relation element) - if (this._scoreHasChangedMap.get(id)) { - probabilities = this.calculateProbabilitiesForElement( - incorporateExecutionScore, - id - ); - this._scoreHasChangedMap.set(id, false); - } else { - // prevent recalculation of probabilities without score changes - probabilities = this._elementTypeProbabilityMap.get(id); - } + getRandomType(id: string) { + const probabilities = this.getTypeNode(id).getTypeProbabilities(); const genericTypes = [ TypeEnum.ARRAY, @@ -316,17 +141,21 @@ export class TypeModel { TypeEnum.UNDEFINED, ]; - if (probabilities.size === 0) { - return prng.pickOne(genericTypes); - } + return prng.pickOne([ + ...new Set([...probabilities.keys(), ...genericTypes]), + ]); + } + + /** + * Gets a random type proportional to its likelihood + * @param id the id we want to get a random type for + * @returns a string describing the type + */ + getRandomTypeProportional(id: string): string { + const probabilities = this.getTypeNode(id).getTypeProbabilities(); - if ( - this._sum(probabilities.values()) === 0 || - prng.nextBoolean(randomTypeProbability) - ) { - return prng.pickOne([ - ...new Set([...probabilities.keys(), ...genericTypes]), - ]); + if (probabilities.size === 0 || this._sum(probabilities.values()) === 0) { + return this.getRandomType(id); } const matchingTypes = [...probabilities.entries()]; @@ -348,46 +177,16 @@ export class TypeModel { return chosenType; } - getHighestProbabilityType( - incorporateExecutionScore: boolean, - randomTypeProbability: number, - id: string - ): string { - let probabilities; - - // This allows caching of type scores. The only problem is that when a score of a relation changes it will not recalculate for the current element (only for the relation element) - if (this._scoreHasChangedMap.get(id)) { - probabilities = this.calculateProbabilitiesForElement( - incorporateExecutionScore, - id - ); - this._scoreHasChangedMap.set(id, false); - } else { - // prevent recalculation of probabilities without score changes - probabilities = this._elementTypeProbabilityMap.get(id); - } - - const genericTypes = [ - TypeEnum.ARRAY, - TypeEnum.BOOLEAN, - TypeEnum.FUNCTION, - TypeEnum.NULL, - TypeEnum.NUMERIC, - TypeEnum.INTEGER, - TypeEnum.OBJECT, - TypeEnum.REGEX, - TypeEnum.STRING, - TypeEnum.UNDEFINED, - ]; - - if (probabilities.size === 0) { - return prng.pickOne(genericTypes); - } + /** + * Gets a the most likely type + * @param id the id we want to get the most likely type for + * @returns a string describing the type + */ + getMostLikelyType(id: string): string { + const probabilities = this.getTypeNode(id).getTypeProbabilities(); - if (prng.nextBoolean(randomTypeProbability)) { - return prng.pickOne([ - ...new Set([...probabilities.keys(), ...genericTypes]), - ]); + if (probabilities.size === 0 || this._sum(probabilities.values()) === 0) { + return this.getRandomType(id); } const matchingTypes = probabilities; @@ -404,235 +203,16 @@ export class TypeModel { } calculateProbabilitiesForFile( - incorporateExecutionScore: boolean, filepath: string ): Map> { const map = new Map>(); - for (const id of this._elements) { + for (const [id, typeNode] of this._typeNodes.entries()) { if (!id.startsWith(filepath)) { continue; } - map.set( - id, - this.calculateProbabilitiesForElement(incorporateExecutionScore, id) - ); + map.set(id, typeNode.getTypeProbabilities()); } return map; } - - calculateProbabilitiesForElement( - incorporateExecutionScore: boolean, - id: string, - relationPairsVisited?: Map> - ): Map { - let probabilityMap = new Map(); - - if (id === "anon") { - return probabilityMap; - } - - const typeScoreMap = this._elementTypeScoreMap.get(id); - const relationMap = this._relationScoreMap.get(id); - - if (typeScoreMap === undefined) { - throw new ImplementationError(`Cannot get typescoreMap of ${id}`); - } - - if (!relationPairsVisited) { - relationPairsVisited = new Map(); - } - - let totalScore = this._sum(typeScoreMap.values()); - - const usableRelations = new Set(); - - for (const [relation, score] of relationMap.entries()) { - if (relation === id) { - // ignore self references - continue; - } - - if ( - (relationPairsVisited.has(id) && - relationPairsVisited.get(id).has(relation)) || - (relationPairsVisited.has(relation) && - relationPairsVisited.get(relation).has(id)) - ) { - // we have already visited this relation pair - // this means that we have a cycle in the graph - // we can safely ignore this relation - continue; - } - usableRelations.add(relation); - totalScore += score; - } - - if (totalScore === 0) { - totalScore = 1; - } - - for (const [type, score] of typeScoreMap.entries()) { - probabilityMap.set(type, score / totalScore); - } - - for (const relation of usableRelations) { - probabilityMap = this.incorporateRelation( - id, - probabilityMap, - relation, - relationMap, - relationPairsVisited, - totalScore, - incorporateExecutionScore - ); - } - - // incorporate execution scores - probabilityMap = this.incorporateExecutionScores( - id, - probabilityMap, - incorporateExecutionScore - ); - - return this.normalizeProbabilities(probabilityMap); - } - - incorporateRelation( - id: string, - probabilityMap: Map, - relation: string, - relationMap: Map, - relationPairsVisited: Map>, - totalScore: number, - incorporateExecutionScore: boolean - ): Map { - const score = relationMap.get(relation); - - if (!relationPairsVisited.has(id)) { - relationPairsVisited.set(id, new Set()); - } - if (!relationPairsVisited.has(relation)) { - relationPairsVisited.set(relation, new Set()); - } - - relationPairsVisited.get(id).add(relation); - relationPairsVisited.get(relation).add(id); - - const probabilityOfRelation = score / totalScore; - - const probabilityMapOfRelation = this.calculateProbabilitiesForElement( - incorporateExecutionScore, - relation, - relationPairsVisited - ); - - for (const [type, probability] of probabilityMapOfRelation.entries()) { - let finalType = type; - - if (!type.includes("<>")) { - // maybe should check for includes (or the inverse by checking for primitive types) - // this will only add only the final relation id - // the other method will add all relation id from the element to the final relation - finalType = `${relation}<>${type}`; - } - - if (finalType.includes("<>") && finalType.split("<>")[0] === id) { - // skip this is a self loop - continue; - } - - if (!probabilityMap.has(finalType)) { - probabilityMap.set(finalType, 0); - } - - probabilityMap.set( - finalType, - probabilityMap.get(finalType) + probability * probabilityOfRelation - ); - } - - return probabilityMap; - } - - incorporateExecutionScores( - id: string, - probabilityMap: Map, - incorporateExecutionScore: boolean - ): Map { - const executionScoreMap = this._typeExecutionScoreMap.get(id); - - if (!incorporateExecutionScore || executionScoreMap.size <= 1) { - return probabilityMap; - } - - const combinedProbabilityMap = new Map(); - - let minValue = 0; - for (const score of executionScoreMap.values()) { - minValue = Math.min(minValue, score); - } - - let totalScore = 0; - for (const type of probabilityMap.keys()) { - let score = executionScoreMap.get(type) ?? 0; - score -= minValue; - score += 1; - totalScore += score; - } - - if (totalScore < 0) { - throw new ImplementationError( - "Total score should be positive but is negative" - ); - } - - if (totalScore === 0) { - throw new ImplementationError( - "Total score should be positive but is zero" - ); - } - - if (Number.isNaN(totalScore)) { - throw new ImplementationError( - "Total score should be positive but is NaN" - ); - } - - // incorporate execution score - for (const type of probabilityMap.keys()) { - let score = executionScoreMap.has(type) ? executionScoreMap.get(type) : 0; - score -= minValue; - score += 1; - - const executionScoreDiscount = score / totalScore; - const probability = probabilityMap.get(type); - const newProbability = executionScoreDiscount * probability; - - combinedProbabilityMap.set(type, newProbability); - } - - return combinedProbabilityMap; - } - - normalizeProbabilities( - probabilityMap: Map - ): Map { - // normalize to 1 - let totalProbability = 0; - for (const probability of probabilityMap.values()) { - totalProbability += probability; - } - - if (totalProbability === 0 || totalProbability === 1) { - return probabilityMap; - } - - const normalizedProbabilityMap = new Map(); - for (const [type, probability] of probabilityMap.entries()) { - normalizedProbabilityMap.set(type, probability / totalProbability); - } - - return normalizedProbabilityMap; - } } diff --git a/libraries/analysis-javascript/lib/type/resolving/TypeNode.ts b/libraries/analysis-javascript/lib/type/resolving/TypeNode.ts new file mode 100644 index 00000000..2900718c --- /dev/null +++ b/libraries/analysis-javascript/lib/type/resolving/TypeNode.ts @@ -0,0 +1,376 @@ +/* + * 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 { + arrayProperties, + functionProperties, + ObjectType, + stringProperties, +} from "./Type"; +import { TypeEnum } from "./TypeEnum"; + +/** + * TypeNode class + */ +export class TypeNode { + protected _id: string; + protected _typeScores: Map; + protected _dependencyScores: Map; + protected _executionScores: Map; + protected _objectType: ObjectType; + + protected probabilities: Map; + + constructor(id: string) { + this._id = id; + + this._typeScores = new Map(); + this._dependencyScores = new Map(); + this._executionScores = new Map(); + this._objectType = new ObjectType(); + } + + // type scores + public addTypeScore(type: TypeEnum, score = 1) { + const currentScore = this._typeScores.get(type) ?? 0; + this._typeScores.set(type, currentScore + score); + this.probabilities = undefined; + + if (type === TypeEnum.NUMERIC) { + this.addTypeScore(TypeEnum.INTEGER, score); + } + } + + // complex type scores + public addPropertyType(property: string, propertyId: string) { + // check if the property is from a string/array/function + if (functionProperties.has(property)) { + this.addTypeScore(TypeEnum.FUNCTION); + } + + if (arrayProperties.has(property)) { + this.addTypeScore(TypeEnum.ARRAY); + } + + if (stringProperties.has(property)) { + this.addTypeScore(TypeEnum.STRING); + } + + this.addTypeScore(TypeEnum.OBJECT); + + this._objectType.properties.set(property, propertyId); + } + + public addParameterType(index: number, parameterId: string, name: string) { + this.addTypeScore(TypeEnum.FUNCTION); + this._objectType.parameters.set(index, parameterId); + this._objectType.parameterNames.set(index, name); + } + + public addReturnType(returnId: string) { + this.addTypeScore(TypeEnum.FUNCTION); + this._objectType.return.add(returnId); + } + + public addElementType(elementId: string) { + this.addTypeScore(TypeEnum.ARRAY); + this._objectType.elements.add(elementId); + } + + // dependency score + public addDependencyScore(dependency: TypeNode, score: number) { + if (dependency === this) { + // not allowed self loop + return; + // throw new Error(`ids should not be equal to add a relation id: ${id1}`); + } + + const currentScore = this._dependencyScores.get(dependency) ?? 0; + this._dependencyScores.set(dependency, currentScore + score); + this.probabilities = undefined; + } + + // execution score + public addExecutionScore(typeId: string, typeEnum: TypeEnum, score = -1) { + let type: string = typeEnum; + + if (this._id !== typeId) { + type = `${typeId}<>${typeEnum}`; + } + + const currentScore = this._executionScores.get(type) ?? 0; + this._executionScores.set(type, currentScore + score); + this.probabilities = undefined; + } + + // merging + public merge(other: TypeNode) { + // first add all entries in 'this's maps to the 'other's maps + // then set all 'this' maps equal to 'other' maps + + for (const [type, score] of this._typeScores.entries()) { + const currentScore = other._typeScores.get(type) ?? 0; + other._typeScores.set(type, currentScore + score); + } + + for (const [dependency, score] of this._dependencyScores.entries()) { + other.addDependencyScore(dependency, score); + } + + for (const [type, score] of this._executionScores.entries()) { + const currentScore = other._executionScores.get(type) ?? 0; + other._executionScores.set(type, currentScore + score); + } + + const mergedObjectType = other._objectType.merge(this._objectType); + this._objectType = mergedObjectType; + other._objectType = mergedObjectType; + + this._typeScores = other._typeScores; + this._dependencyScores = other._dependencyScores; + this._executionScores = other._executionScores; + + this.probabilities = undefined; + other.probabilities = undefined; + } + + // calculation + getTypeProbabilities() { + // This allows caching of type scores. The only problem is that when a score of a relation changes it will not recalculate for the current element (only for the relation element) + if (!this.probabilities) { + this.probabilities = this._calculateProbabilities(); + } + + return this.probabilities; + } + + protected _calculateProbabilities( + visitedDependencyPairs?: Map> + ) { + if (!visitedDependencyPairs) { + visitedDependencyPairs = new Map(); + } + + let probabilities = new Map(); + + let totalScore = this._sum(this._typeScores.values()); + + const relevantDependencies = new Set(); + + for (const [dependency, score] of this._dependencyScores.entries()) { + if (dependency === this) { + throw new Error("should never happen (self reference)"); + } + + if ( + (visitedDependencyPairs.has(this) && + visitedDependencyPairs.get(this).has(dependency)) || + (visitedDependencyPairs.has(dependency) && + visitedDependencyPairs.get(dependency).has(this)) + ) { + // we have already visited this relation pair + // this means that we have a cycle in the graph + // we can safely ignore this relation + continue; + } + + relevantDependencies.add(dependency); + totalScore += score; + } + + if (totalScore === 0) { + totalScore = 1; + } + + for (const [type, score] of this._typeScores.entries()) { + probabilities.set(type, score / totalScore); + } + + // incorporate dependency scores + probabilities = this._incorporateDependencyScores( + probabilities, + visitedDependencyPairs, + relevantDependencies, + totalScore + ); + + // incorporate execution scores + probabilities = this._incorporateExecutionScores(probabilities); + + // normalize + probabilities = this._normalizeProbabilities(probabilities); + + return probabilities; + } + + protected _incorporateDependencyScores( + probabilities: Map, + visitedDependencyPairs: Map>, + relevantDependencies: Set, + totalScore: number + ): Map { + for (const dependency of relevantDependencies) { + // add to visited pairs + visitedDependencyPairs.has(this) + ? undefined + : visitedDependencyPairs.set(this, new Set()); + visitedDependencyPairs.has(dependency) + ? undefined + : visitedDependencyPairs.set(dependency, new Set()); + visitedDependencyPairs.get(this).add(dependency); + visitedDependencyPairs.get(dependency).add(this); + + // calculate probabilties + const dependencyProbabilities = dependency._calculateProbabilities( + visitedDependencyPairs + ); + const dependencyScore = this._dependencyScores.get(dependency); + const dependencyScalar = dependencyScore / totalScore; + + // add probabilities + for (const [type, probability] of dependencyProbabilities.entries()) { + let finalType = type; + + if (!type.includes("<>")) { + // maybe should check for includes (or the inverse by checking for primitive types) + // this will only add only the final relation id + // the other method will add all relation id from the element to the final relation + finalType = `${dependency._id}<>${type}`; + } + + if (finalType.includes("<>") && finalType.split("<>")[0] === this._id) { + // skip this is a self loop + continue; + } + + const currentValue: number = probabilities.get(finalType) ?? 0; + + probabilities.set( + finalType, + currentValue + probability * dependencyScalar + ); + } + } + + return probabilities; + } + + protected _incorporateExecutionScores( + probabilities: Map + ): Map { + // if there are no executionscores return + if (this._executionScores.size <= 1) { + return probabilities; + } + + // ignore execution scores when probabilities are not available + if (probabilities.size === 0) { + return probabilities; + } + + const combinedProbabilityMap = new Map(); + + let minValue = 0; + for (const score of this._executionScores.values()) { + minValue = Math.min(minValue, score); + } + + let totalScore = 0; + for (const type of probabilities.keys()) { + let score = this._executionScores.get(type) ?? 0; + score -= minValue; + score += 1; + totalScore += score; + } + + if (totalScore < 0) { + throw new Error("Total score should be positive but is negative"); + } + + if (totalScore === 0) { + throw new Error("Total score should be positive but is zero"); + } + + if (Number.isNaN(totalScore)) { + throw new TypeError("Total score should be positive but is NaN"); + } + + // incorporate execution score + for (const type of probabilities.keys()) { + let score = this._executionScores.get(type) ?? 0; + score -= minValue; + score += 1; + + const executionScoreDiscount = score / totalScore; + const probability = probabilities.get(type); + const newProbability = executionScoreDiscount * probability; + + combinedProbabilityMap.set(type, newProbability); + } + + return combinedProbabilityMap; + } + + protected _normalizeProbabilities( + probabilities: Map + ): Map { + // normalize to 1 + let totalProbability = 0; + for (const probability of probabilities.values()) { + totalProbability += probability; + } + + if (totalProbability === 0 || totalProbability === 1) { + return probabilities; + } + + const normalizedProbabilities = new Map(); + for (const [type, probability] of probabilities.entries()) { + normalizedProbabilities.set(type, probability / totalProbability); + } + + return normalizedProbabilities; + } + + private _sum(iterable: Iterable) { + return [...iterable].reduce( + (total, currentValue) => total + currentValue, + 0 + ); + } + + get id() { + return this._id; + } + + get typeScores() { + return this._typeScores; + } + + get dependencyScores() { + return this._dependencyScores; + } + + get executionScores() { + return this._executionScores; + } + + get objectType() { + return this._objectType; + } +} diff --git a/libraries/search-javascript/index.ts b/libraries/search-javascript/index.ts index 40e7a784..5fbdb6d7 100644 --- a/libraries/search-javascript/index.ts +++ b/libraries/search-javascript/index.ts @@ -26,7 +26,7 @@ export * from "./lib/search/JavaScriptSubject"; export * from "./lib/testbuilding/JavaScriptDecoder"; export * from "./lib/testbuilding/JavaScriptSuiteBuilder"; -export * from "./lib/testcase/execution/ExecutionInformationIntegrator"; +export * from "./lib/testcase/execution/ExecutionInformationProcessor"; export * from "./lib/testcase/execution/JavaScriptRunner"; export * from "./lib/testcase/execution/SilentMochaReporter"; diff --git a/libraries/search-javascript/lib/testcase/execution/ExecutionInformationIntegrator.ts b/libraries/search-javascript/lib/testcase/execution/ExecutionInformationProcessor.ts similarity index 84% rename from libraries/search-javascript/lib/testcase/execution/ExecutionInformationIntegrator.ts rename to libraries/search-javascript/lib/testcase/execution/ExecutionInformationProcessor.ts index 2f40b953..c44165af 100644 --- a/libraries/search-javascript/lib/testcase/execution/ExecutionInformationIntegrator.ts +++ b/libraries/search-javascript/lib/testcase/execution/ExecutionInformationProcessor.ts @@ -24,11 +24,13 @@ import { Statement } from "../statements/Statement"; import { Test } from "./TestExecutor"; -export class ExecutionInformationIntegrator { +export class ExecutionInformationProcessor { private _typeModel: TypeModel; + private _incorporateExecutionInformation: boolean; - constructor(typeModel: TypeModel) { + constructor(typeModel: TypeModel, incorporateExecutionInformation: boolean) { this._typeModel = typeModel; + this._incorporateExecutionInformation = incorporateExecutionInformation; } process(testCase: JavaScriptTestCase, testResult: Test, stats: Mocha.Stats) { @@ -44,6 +46,7 @@ export class ExecutionInformationIntegrator { for (const child of children) { if ( + this._incorporateExecutionInformation && testResult.error && testResult.error.message && testResult.error.message.includes(child.name) diff --git a/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts b/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts index 02534cc0..9159a434 100644 --- a/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts +++ b/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts @@ -40,7 +40,7 @@ import { import { JavaScriptDecoder } from "../../testbuilding/JavaScriptDecoder"; import { JavaScriptTestCase } from "../JavaScriptTestCase"; -import { ExecutionInformationIntegrator } from "./ExecutionInformationIntegrator"; +import { ExecutionInformationProcessor } from "./ExecutionInformationProcessor"; import { DoneMessage, Message } from "./TestExecutor"; export class JavaScriptRunner implements EncodingRunner { @@ -48,7 +48,7 @@ export class JavaScriptRunner implements EncodingRunner { protected storageManager: StorageManager; protected decoder: JavaScriptDecoder; - protected executionInformationIntegrator: ExecutionInformationIntegrator; + protected executionInformationProcessor: ExecutionInformationProcessor; protected tempTestDirectory: string; @@ -62,7 +62,7 @@ export class JavaScriptRunner implements EncodingRunner { constructor( storageManager: StorageManager, decoder: JavaScriptDecoder, - executionInformationIntergrator: ExecutionInformationIntegrator, + executionInformationProcessor: ExecutionInformationProcessor, temporaryTestDirectory: string, executionTimeout: number, testTimeout: number, @@ -71,7 +71,7 @@ export class JavaScriptRunner implements EncodingRunner { JavaScriptRunner.LOGGER = getLogger(JavaScriptRunner.name); this.storageManager = storageManager; this.decoder = decoder; - this.executionInformationIntegrator = executionInformationIntergrator; + this.executionInformationProcessor = executionInformationProcessor; this.tempTestDirectory = temporaryTestDirectory; this.executionTimeout = executionTimeout; this.testTimeout = testTimeout; @@ -174,7 +174,7 @@ export class JavaScriptRunner implements EncodingRunner { const test = suites[0].tests[0]; // only one test in this case // If one of the executions failed, log it - this.executionInformationIntegrator.process(testCase, test, stats); + this.executionInformationProcessor.process(testCase, test, stats); const traces: Trace[] = this._extractTraces( instrumentationData, diff --git a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts index e938d214..a27c204c 100644 --- a/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts +++ b/libraries/search-javascript/lib/testcase/sampling/JavaScriptRandomSampler.ts @@ -530,34 +530,27 @@ export class JavaScriptRandomSampler extends JavaScriptTestCaseSampler { return this.sampleBool(id, id, name); } + let mode = this.typeInferenceMode; let chosenType: string; - switch (this.typeInferenceMode) { + if (prng.nextBoolean(this.randomTypeProbability)) { + mode = "none"; + } + + switch (mode) { case "none": { - chosenType = this.rootContext - .getTypeModel() - .getRandomType(false, 1, id); + chosenType = this.rootContext.getTypeModel().getRandomType(id); break; } case "proportional": { chosenType = this.rootContext .getTypeModel() - .getRandomType( - this.incorporateExecutionInformation, - this.randomTypeProbability, - id - ); + .getRandomTypeProportional(id); break; } case "ranked": { - chosenType = this.rootContext - .getTypeModel() - .getHighestProbabilityType( - this.incorporateExecutionInformation, - this.randomTypeProbability, - id - ); + chosenType = this.rootContext.getTypeModel().getMostLikelyType(id); break; } diff --git a/package-lock.json b/package-lock.json index 23e1747f..5d0718ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -79,8 +79,7 @@ }, "libraries/analysis-javascript/node_modules/@babel/core": { "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -106,6 +105,26 @@ "url": "https://opencollective.com/babel" } }, + "libraries/analysis-javascript/node_modules/@babel/traverse": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "libraries/analysis-javascript/node_modules/@babel/types": { "version": "7.23.0", "license": "MIT", @@ -118,10 +137,17 @@ "node": ">=6.9.0" } }, + "libraries/analysis-javascript/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, "libraries/analysis-javascript/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -146,8 +172,7 @@ }, "libraries/ast-visitor-javascript/node_modules/@babel/core": { "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -173,6 +198,34 @@ "url": "https://opencollective.com/babel" } }, + "libraries/ast-visitor-javascript/node_modules/@babel/traverse": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "libraries/ast-visitor-javascript/node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, "libraries/ast-visitor-javascript/node_modules/@babel/types": { "version": "7.23.0", "license": "MIT", @@ -187,8 +240,7 @@ }, "libraries/ast-visitor-javascript/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -217,8 +269,7 @@ }, "libraries/instrumentation-javascript/node_modules/@babel/core": { "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -244,6 +295,26 @@ "url": "https://opencollective.com/babel" } }, + "libraries/instrumentation-javascript/node_modules/@babel/traverse": { + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", + "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "libraries/instrumentation-javascript/node_modules/@babel/types": { "version": "7.23.0", "license": "MIT", @@ -256,10 +327,17 @@ "node": ">=6.9.0" } }, + "libraries/instrumentation-javascript/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, "libraries/instrumentation-javascript/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -298,8 +376,7 @@ }, "libraries/search-javascript/node_modules/@babel/core": { "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -339,8 +416,7 @@ }, "libraries/search-javascript/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -479,39 +555,11 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/core/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, - "node_modules/@babel/core/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -886,34 +934,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helpers/node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/highlight": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", @@ -2230,19 +2250,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz", - "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "debug": "^4.1.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { diff --git a/plugins/plugin-javascript-event-listener-state-storage/lib/StateStorage.ts b/plugins/plugin-javascript-event-listener-state-storage/lib/StateStorage.ts index 37e6ce28..3ce64e04 100644 --- a/plugins/plugin-javascript-event-listener-state-storage/lib/StateStorage.ts +++ b/plugins/plugin-javascript-event-listener-state-storage/lib/StateStorage.ts @@ -108,8 +108,11 @@ export class StateStorage { edges: [], }; - for (const [id, typeScores] of typeModel.elementTypeScoreMap.entries()) { - const executionScores = typeModel.typeExecutionScoreMap.get(id); + for (const [id, typeNode] of typeModel.typeNodes.entries()) { + const typeScores = typeNode.typeScores; + const executionScores = typeNode.executionScores; + const relationScores = typeNode.dependencyScores; + const scores: Score[] = []; for (const [type, score] of typeScores.entries()) { @@ -127,20 +130,13 @@ export class StateStorage { id: id, scores: scores, }); - } - - for (const [id1, relationScores] of typeModel.relationScoreMap.entries()) { - const executionScores = typeModel.typeExecutionScoreMap.get(id1); for (const [id2, score] of relationScores) { graph.edges.push({ - source: id1, - target: id2, + source: id, + target: id2.id, relationScore: score, - relationExecutionScore: - executionScores && executionScores.has(id2) - ? executionScores.get(id2) - : 0, + relationExecutionScore: executionScores.get(id2.id) ?? 0, }); } } diff --git a/tools/javascript/lib/JavaScriptLauncher.ts b/tools/javascript/lib/JavaScriptLauncher.ts index 77e56322..f6c8912a 100644 --- a/tools/javascript/lib/JavaScriptLauncher.ts +++ b/tools/javascript/lib/JavaScriptLauncher.ts @@ -74,7 +74,7 @@ import { } from "@syntest/search"; import { BranchDistanceCalculator, - ExecutionInformationIntegrator, + ExecutionInformationProcessor, JavaScriptDecoder, JavaScriptRandomSampler, JavaScriptRunner, @@ -407,13 +407,14 @@ export class JavaScriptLauncher extends Launcher { ); this.decoder = new JavaScriptDecoder(this.arguments_.targetRootDirectory); - const executionInformationIntegrator = new ExecutionInformationIntegrator( - this.rootContext.getTypeModel() + const executionInformationProcessor = new ExecutionInformationProcessor( + this.rootContext.getTypeModel(), + this.arguments_.incorporateExecutionInformation ); this.runner = new JavaScriptRunner( this.storageManager, this.decoder, - executionInformationIntegrator, + executionInformationProcessor, this.arguments_.testDirectory, this.arguments_.executionTimeout, this.arguments_.testTimeout,