Newer
Older
/*
* e-Knife, a program slicing tool for Erlang based on the EDG
* Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import edg.DotFactory;
import edg.EDGFactory;
import edg.constraint.AccessConstraint;
import eknife.erlang.ErlangCodeFactory;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.lang.reflect.InvocationTargetException;
public class EKnife {
public enum Language {Java, Erlang, Php}
// TODO: OUTPUT FILES PATH
// Graph Generation Time
public static final String baseDir = "JSS_Empirical_Evaluation/";
public static final String outputTimeFolder = baseDir + "Times/";
public static final String outputSizeFolder = baseDir + "Sizes/";
public static final String sliceTimeFolder = outputTimeFolder + "Slices/";
public static final String outputSliceGenerationFolder = baseDir + "Slices/";
public static void main(String[] args) {
if (args.length == 0) {
EKnife.printHelp();
return;
}
final Args arguments = EKnife.processArguments(args);
if (!arguments.isValid()) {
EKnife.printHelp();
System.exit(3);
}
EKnife.run(arguments);
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public static void generateAllSlices(String[] args, String setName) {
if (args.length == 0) {
EKnife.printHelp();
return;
}
final Args arguments = EKnife.processArguments(args);
if (!arguments.isValid()) {
EKnife.printHelp();
System.exit(3);
}
EKnife.sliceGenerationRun(arguments, setName);
}
public static void timedRun(String[] args, String setName) {
if (args.length == 0) {
EKnife.printHelp();
return;
}
final Args arguments = EKnife.processArguments(args);
if (!arguments.isValid()) {
EKnife.printHelp();
System.exit(3);
}
EKnife.timedRun(arguments,setName);
// EKnife.SCCounter(arguments);
}
private static Args processArguments(String[] args) {
for (int argIndex = 0; argIndex < args.length; argIndex++) {
switch (arg) {
case "-i":
case "--input":
case "-o":
case "--output":
case "-f":
case "--file":
case "-l":
case "--line":
case "-v":
case "--var":
case "-n":
case "--occurrence":
case "-G":
case "--print-graph":
if (argIndex == args.length - 1) {
System.out.printf("Parameter %s requires an argument\n", arg);
printHelp();
System.exit(1);
}
kArgs.inputPath = args[argIndex + 1];
break;
kArgs.outputFile = new File(args[argIndex + 1]);
break;
kArgs.line = Integer.parseInt(args[argIndex + 1]);
break;
kArgs.occurrence = Integer.parseInt(args[argIndex + 1]);
break;
kArgs.setGraphFile(new File(args[argIndex + 1]));
break;
case "--ignore-constraints":
kArgs.algClass = AdaptedStandardAlgorithm.class;
break;
}
}
kArgs.completeData();
return kArgs;
}
String help = "";
help += "Use the following options:\n";
help += " -i,--input <file/folder> The file/folder where the source code is\n";
help += " -o,--output <file/folder> The file/folder where the slice will be stored\n";
help += " -f,--file <file> The file (relative to -i) where the slicing criterion is\n";
help += " -l,--line <num> The line of the slicing criterion\n";
help += " -v,--var <name> The name of the slicing criterion (must be a variable)\n";
help += " -n,--occurrence <num> The occurrence of the slicing criterion in that line\n";
help += " -G,--print-graph <file.dot> Exports the graph as a dot file\n";
help += " -G,--print-graph <file.pdf> Exports the graph as a PDF file\n";
help += " --ignore-constraints Generates constraints but ignores when slicing\n";
help += " --help Show this message.\n";
System.out.print(help);
}
final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
final EDG edg = new EDGFactory(last).createEDG();
final SlicingCriterion slicingCriterion = new SlicingCriterion(a.file, a.line, a.name, a.occurrence);
if (SC == null) {
System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion);
System.exit(1);
}
} catch (IllegalArgumentException e) {
System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion);
System.exit(1);
final SlicingAlgorithm slicingAlgorithm = a.getAlgorithm(edg);
final Set<Node> slice = slicingAlgorithm.slice(SC);
if (slice.isEmpty()) {
System.out.println("Warning: no files will be written, as the slice is empty.");
System.exit(2);
}
if (a.graphFile != null) {
switch (a.graphFormat) {
case DOT:
DotFactory.createDot(a.graphFile, edg, SC, slice);
break;
case PDF:
PdfFactory.createPdf(a.graphFile, edg, SC, slice);
break;
}
}
CodeFactory.createCode(Language.Erlang, a.outputFile, edg, slice);
}
/**
* Executes and measures time for graph generation and slicing according to measured dimensions
*/
private static void timedRun(Args a, String setName) {
boolean measureGenerationTime = false;
boolean countSlices = false;
boolean measureSlicingTime = false;
boolean measureAccessConstraints = false;
boolean outputSlice = true;
/* ***************************************************** */
/* ** MEASUREMENT OF GENERATION TIME (100 executions) ** */
/* ***************************************************** */
int numberOfIterationsGen = Integer.parseInt(System.getProperty("iterGen", "100"));
int numberOfIterationsSlice = Integer.parseInt(System.getProperty("iterSlice", "1000"));
// GET MODULE NAME:
String moduleName = a.inputPath.lastIndexOf(File.separator) != -1 ?
a.inputPath.substring(a.inputPath.lastIndexOf("/") + 1) : a.inputPath;
moduleName = moduleName.lastIndexOf(".") != -1 ?
moduleName.substring(0, moduleName.lastIndexOf(".")) : moduleName;
if (measureAccessConstraints) {
LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
EDG edg = new EDGFactory(last).createEDG();
long accessConstraints = edg.edgeSet().stream()
.filter(e -> e.getConstraint() instanceof AccessConstraint)
.count();
printAccessConstraint(setName, moduleName, accessConstraints);
}
if (measureGenerationTime) {
double[] StructuralTime = new double[numberOfIterationsGen];
double[] CFGTime = new double[numberOfIterationsGen];
double[] ValueTime = new double[numberOfIterationsGen];
double[] CompositeTime = new double[numberOfIterationsGen];
double[] ControlTime = new double[numberOfIterationsGen];
double[] FlowTime = new double[numberOfIterationsGen];
double[] IPTime = new double[numberOfIterationsGen];
double[] standardTime = new double[numberOfIterationsGen];
double[] constrainedTime = new double[numberOfIterationsGen];
final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
final EDG edg = new EDGFactory(last).createEDG();
if (i == -1)
continue;
StructuralTime[i] = edg.getGenerationTime().getStructureTime();
CFGTime[i] = edg.getGenerationTime().getControlFlowTime();
ValueTime[i] = edg.getGenerationTime().getValueTime();
CompositeTime[i] = edg.getGenerationTime().getCompositeTime();
ControlTime[i] = edg.getGenerationTime().getControlTime();
FlowTime[i] = edg.getGenerationTime().getFlowTime();
IPTime[i] = edg.getGenerationTime().getInterproceduralTime();
standardTime[i] = StructuralTime[i] + CFGTime[i] + ValueTime[i] - CompositeTime[i]
+ ControlTime[i] + FlowTime[i] + IPTime[i];
constrainedTime[i] = StructuralTime[i] + CFGTime[i] + ValueTime[i]
+ ControlTime[i] + FlowTime[i] + IPTime[i];
// printArray("/Users/serperu/Desktop/SlicerOutput/Times/ArrayContent.txt",constrainedTime);
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// Print RESULTS:
try {
new File(outputTimeFolder).mkdirs();
File txtSDG = new File(outputTimeFolder + "graphGenerationPDG.txt");
File txtEDG = new File(outputTimeFolder + "graphGenerationCE-PDG.txt");
if (!txtSDG.exists())
txtSDG.createNewFile();
if (!txtEDG.exists())
txtEDG.createNewFile();
FileWriter fw = new FileWriter(txtSDG, true);
for (int i = 0; i < numberOfIterationsGen; i++) {
fw.append(moduleName + ";" + standardTime[i] + "\n");
}
fw.close();
FileWriter fw2 = new FileWriter(txtEDG, true);
for (int i = 0; i < numberOfIterationsGen; i++) {
fw2.append(moduleName + ";" + constrainedTime[i] + "\n");
}
fw2.close();
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
// printCSVGenerationTime(a.file, standardTime, constrainedTime);
}
final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
final EDG edg = new EDGFactory(last).createEDG();
/* ************************ */
/* ** MEASUREMENT OF SCs ** */
/* ************************ */
if (performAllSCs) {
// SC SELECTION
List<Node> clauses = edg.getNodes(Node.Type.Clause);
clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine);
// printHeadings("/Users/serperu/Desktop/SlicerOutput/Times/slicingStatistics.txt", a.file);
int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size();
String functionName = edg.getParent(clause).getName() + "/" + arity;
List<Node> resultNodes = extractSlicingCriteria(edg, clause, a.file);
final SlicingAlgorithm standardAlgorithm = new AdaptedStandardAlgorithm(edg);
final SlicingAlgorithm constrainedAlgorithm = outputSlice || measureSlicingTime ? new OnePassConstrainedAlgorithm(edg) : null;
final CounterOnePassConstrainedAlgorithm counterAlgorithm = countSlices ? new CounterOnePassConstrainedAlgorithm(edg) : null;
double[] standardTime = new double[numberOfIterationsSlice];
double[] constrainedTime = new double[numberOfIterationsSlice];
// MEASURE SIZE OF SLICE
if (countSlices) {
Set<Node> stdSlice = standardAlgorithm.slice(SC);
Set<Node> ctdSlice = counterAlgorithm.slice(SC);
printPrecisionData(setName, moduleName, functionName, scCounter, stdSlice, ctdSlice,
counterAlgorithm.getVisitedAccessCons());
}
if (outputSlice) {
Set<Node> stdSlice = standardAlgorithm.slice(SC);
Set<Node> ctdSlice = constrainedAlgorithm.slice(SC);
ErlangCodeFactory.createErlangFile(new File(moduleName + "_SC" + scCounter + ".erl"), edg, stdSlice);
ErlangCodeFactory.createErlangFile(new File(moduleName + "_CE_SC" + scCounter + ".erl"), edg, ctdSlice);
}
for (int j = -1; j < numberOfIterationsSlice; j++) {
long initialStandardTime = System.nanoTime();
standardAlgorithm.slice(SC);
long finalStandardTime = System.nanoTime();
if (j == -1)
continue;
standardTime[j] = (finalStandardTime - initialStandardTime) / 1000.0;
}
for (int j = -1; j < numberOfIterationsSlice; j++) {
long initialConstrainedTime = System.nanoTime();
constrainedAlgorithm.slice(SC);
long finalConstrainedTime = System.nanoTime();
if (j == -1)
continue;
constrainedTime[j] = (finalConstrainedTime - initialConstrainedTime) / 1000.0;
printEachSCData(setName, moduleName, functionName, scCounter, standardTime, constrainedTime);
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
/** Extracts the possible SCs from a clause considering different factors commented inside the method body */
public static List<Node> extractSlicingCriteria(EDG edg, Node clause, String fileName) {
List<Node> descendants = edg.getDescendants(clause);
descendants.add(clause);
List<Node> resultNodes = new LinkedList<>();
List<Node> nonResultNodes = new LinkedList<>();
for (Node n : descendants)
if (n.getType() == Node.Type.Result) {
if (edg.getNodeFromRes(n).getType() != Node.Type.Callee)
resultNodes.add(n);
} else
nonResultNodes.add(n);
nonResultNodes.add(clause);
resultNodes.add(edg.getResFromNode(clause));
// IGNORE FUNCTIONS WITHOUT COMPOSITE STRUCTURES
if (nonResultNodes.stream()
.noneMatch(
n -> {
if (n.getType() == Node.Type.DataConstructor)
return true;
if (n.getType() == Node.Type.List)
if (edg.getChildren(n).size() != 0 || edg.getParent(n).getType() == Node.Type.List)
return true;
return false;
})) {
// System.out.println("File Name: " + fileName);
// int functionArity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size();
// System.out.println("Function " + edg.getParent(clause).getName() + "/" + functionArity + " has no valid slicing criteria.");
return new LinkedList<>();
}
/* FILTER SC NODES BY A CRITERION (CONSIDER ONLY VARIABLES) */
resultNodes.removeIf(n -> edg.getNodeFromRes(n).getType() != Node.Type.Variable);
/* REMOVE NODES GENERATED BY HIGH ORDER FUNS (e.g., fun g/2) */
resultNodes.removeIf(n -> edg.getAncestor(n, Node.Type.AnonymousRoutine) != null &&
edg.getNodeFromRes(n).getName().startsWith("FreshVar"));
/* REMOVE DEAD CODE FROM BEING SLICING CRITERIA */
resultNodes.removeIf(n -> edg.incomingEdgesOf(edg.getNodeFromRes(n)).stream().noneMatch(Edge::isControlFlowEdge));
return resultNodes;
}
/** Generates the slice and store it for all possible slicing criteria */
private static void sliceGenerationRun(Args a, String setName)
{
final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
final EDG edg = new EDGFactory(last).createEDG();
int scFunctionCounter = 0;
/* ************************ */
/* ** MEASUREMENT OF SCs ** */
/* ************************ */
// SC SELECTION
List<Node> clauses = edg.getNodes(Node.Type.Clause);
clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine);
for (Node clause : clauses) {
Map<String,String> functionCriteria = new HashMap<>();
List<Node> descendants = edg.getDescendants(clause);
descendants.add(clause);
List<Node> resultNodes = extractSlicingCriteria(edg,clause,a.file);
int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size();
String functionName = edg.getParent(clause).getName() + "-" + arity;
String moduleName = edg.getParent(edg.getParent(clause)).getName();
moduleName = setName.equals("SetA") ? setName+"/"+moduleName + "-" + functionName : setName+"/"+moduleName;
if (!resultNodes.isEmpty())
new File(outputSliceGenerationFolder + moduleName).mkdirs();
final SlicingAlgorithm standardAlgorithm = new AdaptedStandardAlgorithm(edg);
final SlicingAlgorithm constrainedAlgorithm = new OnePassConstrainedAlgorithm(edg); // WITH LOOPS
if(setName.equals("SetA"))
scFunctionCounter = 0;
for (Node SC : resultNodes) {
Node normalNodeSC = edg.getNodeFromRes(SC);
String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">";
functionCriteria.put("#"+scFunctionCounter,scString);
// MEASURE PERFORMARNCE
final Set<Node> standardSlice = standardAlgorithm.slice(SC);
final Set<Node> constrainedSlice = constrainedAlgorithm.slice(SC);
if (constrainedSlice.size() > standardSlice.size())
throw new RuntimeException("The CE-PDG slice cannot be greater than the PDG slice");
// PDG PRINT
CodeFactory.createCode(Language.Erlang,
new File(outputSliceGenerationFolder+moduleName+"/PDG"+"#"+scFunctionCounter+".erl"),
edg, standardSlice);
// CE-PDG PRINT
CodeFactory.createCode(Language.Erlang,
new File(outputSliceGenerationFolder+moduleName+"/CE-PDG"+"#"+scFunctionCounter+".erl"),
edg, constrainedSlice);
scFunctionCounter++;
File txt = new File(outputSliceGenerationFolder + moduleName + "/criteria_map.txt");
txt.createNewFile();
FileWriter fw = new FileWriter(txt, true);
for (String k : functionCriteria.keySet()) {
fw.append(k + " => " + functionCriteria.get(k) + "\n");
}
fw.close();
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
/** Method to count the number of SCs in each program or pair program-function */
public static void SCCounter(Args a) {
final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
final EDG edg = new EDGFactory(last).createEDG();
// SC SELECTION
List<Node> clauses = edg.getNodes(Node.Type.Clause);
clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine);
int scFunctionCounter = 0;
for (Node clause : clauses) {
Map<String,String> functionCriteria = new HashMap<>();
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size();
String functionName = edg.getParent(clause).getName() + "-" + arity;
String moduleName = edg.getParent(edg.getParent(clause)).getName();
List<Node> descendants = edg.getDescendants(clause);
descendants.add(clause);
List<Node> resultNodes = extractSlicingCriteria(edg,clause,a.file);
for (Node SC : resultNodes ){
Node normalNodeSC = edg.getNodeFromRes(SC);
String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">";
functionCriteria.put("#"+scFunctionCounter,scString);
scFunctionCounter++;
}
//
// Map<String,String> functionCriteriaIncomplete = new HashMap<>();
// Map<String,String> InversefunctionCriteriaIncomplete = new HashMap<>();
// List<Node> resultNodesIncomplete = extractSlicingCriteriaIncomplete(edg,clause,a.file);
//
// for (Node SC : resultNodesIncomplete ){
// Node normalNodeSC = edg.getNodeFromRes(SC);
// String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">";
// functionCriteriaIncomplete.put("#"+scFunctionCounterIncomplete,scString);
// InversefunctionCriteriaIncomplete.put(scString,"#"+scFunctionCounterIncomplete);
// scFunctionCounterIncomplete++;
// }
//
// Collection<String> okValues = functionCriteria.values();
//
// for (String s : functionCriteriaIncomplete.values()) {
// if (!okValues.contains(s)) {
// System.out.println("Benchmark"+moduleName+"_"+functionName+"Remove SC "+InversefunctionCriteriaIncomplete.get(s)+" => "+s);
// }
// }
try {
new File(outputSliceGenerationFolder+"SlicingCriteriaMaps/"+moduleName+"/").mkdirs();
File txt = new File(outputSliceGenerationFolder+"SlicingCriteriaMaps/"+moduleName+"/criteria.txt");
txt.createNewFile();
FileWriter fw = new FileWriter(txt,true);
for(String k : functionCriteria.keySet()){
fw.append(k +" => "+ functionCriteria.get(k)+"\n");
}
fw.close();
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
System.out.println("\tBenchmark: "+ a.file + " => " + scFunctionCounter +" SCs");
/* *********************** *********************** *********************** *********************** */
/* *********************** ****************** PRINT METHODS ************** *********************** */
/* *********************** *********************** *********************** *********************** */
public static void printEachSCData(String setName, String file, String funName, int iteration,double[] stdTime, double[] consTime) {
new File(sliceTimeFolder + setName + "/").mkdirs();
File outputFile = new File(sliceTimeFolder + setName + "/" + file + ".txt");
if (!outputFile.exists())
outputFile.createNewFile();
// FILE; FunName; #SC; StandardTIME; ConstrainedTIME => ROW FORMAT
timeFileWriter.append(file + ";" + funName + ";" + "#"+ iteration + ";" + stdTime[i] + ";" + consTime[i] + "\n");
} catch (IOException e) {
System.out.println("FILE NOT FOUND ERROR");
}
}
public static void printPrecisionData(String setName, String file, String funName, int iteration, Set<Node> stdSlice, Set<Node> ctdSlice, int ctdArcs) {
new File(outputSizeFolder + setName + "/").mkdirs();
try (FileWriter out = new FileWriter(outputSizeFolder + setName + "/sizes.txt", true)) {
out.write(String.format("%s;%s;#%d;%d;%d;%d\n", file, funName, iteration, stdSlice.size(), ctdSlice.size(), ctdArcs));
} catch (IOException e) {
System.out.println("FILE NOT FOUND ERROR");
}
}
public static void printAccessConstraint(String setName, String file, long count) {
new File(outputSizeFolder + setName + "/").mkdirs();
try (FileWriter out = new FileWriter(outputSizeFolder + setName + "/accessConstraints.txt", true)) {
out.write(String.format("%s;%d\n", file, count));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/* *********************** *********************** *********************** *********************** */
/* *********************** *********************** *********************** *********************** */
/* *********************** *********************** *********************** *********************** */
String inputPath;
File outputFile;
String file;
int line;
String name;
int occurrence = 1;
Class<? extends SlicingAlgorithm> algClass = OnePassConstrainedAlgorithm.class;
void setGraphFile(File graphFile) {
if (graphFile.getName().endsWith(".dot"))
graphFormat = GraphFormat.DOT;
else if (graphFile.getName().endsWith(".pdf"))
graphFormat = GraphFormat.PDF;
else {
System.out.println("The graph file must end in .dot or .pdf, you set it to " + graphFile);
System.exit(1);
}
this.graphFile = graphFile;
}
void completeData() {
if (file == null && inputPath != null && new File(inputPath).isFile())
file = new File(inputPath).getName();
}
boolean isValid() {
List<String> errors = new LinkedList<>();
if (inputPath == null)
errors.add("You must specify a file to analyze with '-i'.");
else if (!new File(inputPath).exists())
errors.add("The input file you've specified does not exist or isn't readable(" + inputPath + ").");
else if (file == null)
errors.add("The input file is a folder, so you must specify a the file that contains the slicing criterion with '-f'.");
if (outputFile == null)
errors.add("You must specify a location for the output with '-o'.");
else if (!outputFile.exists() && !outputFile.getAbsoluteFile().getParentFile().isDirectory())
errors.add("The output file's parent folder does not exist, or the output folder does not exist.");
if (line <= 0)
errors.add("You must specify a line number greater than 0 with '-l'.");
if (name == null || name.isEmpty())
errors.add("You must specify a valid variable name with '-v'.");
if (occurrence <= 0)
errors.add("You must specify an occurrence greater than 0 with '-n'.");
for (String error : errors)
System.out.println(error);
return errors.isEmpty();
SlicingAlgorithm getAlgorithm(EDG edg) {
try {
return algClass.getConstructor(EDG.class).newInstance(edg);
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private final double slicingPerformance;
private final double slicingTime;
public SlicingCriterionState(double performance, double time) {
slicingPerformance = performance;
slicingTime = time;
}
public double getPerformance() { return this.slicingPerformance; }
public double getTime() { return this.slicingTime; }
}