package fr.inria.diversify.sosie.logger.processor;
import fr.inria.diversify.transformation.Transformation;
import spoon.reflect.code.*;
import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.*;
import spoon.reflect.visitor.CtAbstractVisitor;
import spoon.reflect.visitor.QueryVisitor;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.reflect.code.CtIfImpl;
import java.util.*;
/**
* Adds basic logging before each conditionals (if, loops).
* Use basic scope inference (the real one is hard due to the complex semantics of "static" and "final"
* (w.r.t. init, anonymous classes, etc.)
*/
@Deprecated
public class VariableLoggingInstrumenter extends AbstractLoggingInstrumenter<CtStatement> {
protected int tmpVarCount = 0;
public VariableLoggingInstrumenter(List<Transformation> transformations) {
super(transformations);
}
@Override
public boolean isToBeProcessed(CtStatement candidate) {
if (candidate.getParent(CtCase.class) != null)
return false;
return
(CtIf.class.isAssignableFrom(candidate.getClass())
|| CtLoop.class.isAssignableFrom(candidate.getClass()))
&& !hasLabelAndGoto(candidate)
&& !containsGoto(candidate);
}
public boolean hasStaticParent(CtElement el) {
if (el instanceof CtModifiable) {
if (((CtModifiable) el).getModifiers().contains(ModifierKind.STATIC)) {
return true;
}
}
if (el.getParent() != null) {
return hasStaticParent(el.getParent());
}
return false;
}
public void process(CtStatement statement) {
try {
//Now instrument.
instruLoopOrIf(statement);
} catch (Exception e) {
}
}
/**
* Instruments the loop of If
*
* @param statement Statement to instrument
*/
private void instruLoopOrIf(CtStatement statement) {
boolean inStaticCode = hasStaticParent(statement);
String id = idFor(getClass(statement).getQualifiedName() + "." + getMethod(statement).getSignature());
String tmpVar = "tmpVarWrite" + tmpVarCount++;
String snippet;
if(containsTransformation(statement.getParent(CtExecutable.class))) {
snippet = getLogName()+".startLogging(Thread.currentThread(),\""+id+"\");\n\tObject[] " + tmpVar + " = {";
} else {
snippet = "Object[] " + tmpVar + " = {";
}
int nVisibleVariables = 0;
for (CtVariable<?> var : getVariablesInScope(statement)) {
boolean isStaticVar = var.getModifiers().contains(ModifierKind.STATIC);
// we only add if the code is non static
// or if code is static and the variable as well
if (!inStaticCode || (inStaticCode && isStaticVar)) {
// if the local var is not initialized, it might be a compilation problem
// because of "not initialized"
if (var instanceof CtLocalVariable) {
CtLocalVariable lvar = (CtLocalVariable) var;
if (lvar.getDefaultExpression() == null) {
continue;
}
}
nVisibleVariables++;
// we remove the "final" for solving "may have not been in initialized" in constructor code
// this does not work for case statements
// var.getModifiers().remove(ModifierKind.FINAL);
snippet += "\"" + idForVar(var,statement) + "\"," + var.getSimpleName() + ",";
}
}
snippet = snippet.substring(0, snippet.length() - 1);
snippet += "};\n\t" + getLogName() + ".writeVar(" + getCount(statement) + ",Thread.currentThread(),\""
+ id + "\"," + tmpVar + ");\n";
if (
nVisibleVariables > 0 // do not add the monitoring if nothing to ignore
&&
// too many variables and too many ifs
// may cause the following:
// The code of method populateFromBibtex(BibtexEntry) is exceeding the 65535 bytes limit
nVisibleVariables < 50
) {
CompilationUnit compileUnit = statement.getPosition().getCompilationUnit();
SourcePosition sp = statement.getPosition();
int index = sp.getSourceStart();
//Check for else if ( ... ) statements to wrap them inside brackets. This is an special case
boolean isElseIfSpecialCase = statement instanceof CtIfImpl && statement.getParent() instanceof CtIfImpl &&
((CtIfImpl) statement.getParent()).getElseStatement().equals(statement);
if (isElseIfSpecialCase) {
//Open the bracket at the "else"
// compileUnit.addSourceCodeFragment(new SourceCodeFragment(index - 1, "{", 0));
}
//Insert the snippet
// compileUnit.addSourceCodeFragment(new SourceCodeFragment(index, snippet, 0));
if (isElseIfSpecialCase) {
//Close the previously inserted "else" bracket
int endIndex = sp.getSourceEnd();
// compileUnit.addSourceCodeFragment(new SourceCodeFragment(endIndex, "}", 0));
}
}
}
protected String idForVar(CtVariable<?> var, CtStatement statement) {
String varName;
if(var instanceof CtField) {
varName = getClass(statement).getQualifiedName() +" "+var.getSimpleName();
} else {
varName = getMethod(statement).getSignature()+"."+var.getSimpleName();
}
return idFor(varName);
}
private Collection<CtVariable<?>> getVariablesInScope(final CtElement el) {
return _getVariablesInScope(el, new TreeSet());
}
/**
* Returns all variables in this scope
* if el does not define a scope, returns an empty set
*/
private Collection<CtVariable<?>> _getVariablesInScope(final CtElement el, final Set<CtElement> children) {
final Set<CtVariable<?>> variables = new TreeSet<CtVariable<?>>();
// we add all variables in the scope of el
variables.addAll(getVariablesInLocalScope(el, children));
// recursion: we collect all variables in this scope
// and in the scope of its parent
if (
// if we have parent
el.getParent() != null
// but a package does not define a scope
&& !CtPackage.class.isAssignableFrom(el.getParent().getClass())
// there are complex compilation rules with final fields
// and anonymous classes, skip parents of anonymous classes
&& !(el instanceof CtNewClass)
// constructor and "final" errors
&& !(el instanceof CtConstructor)
// static blocks and "may not have been initialized", skip
&& !(el instanceof CtAnonymousExecutable)
// Cannot refer to a non-final variable initial inside an inner class defined in a different method
&& !(el instanceof CtType && el.getParent() instanceof CtBlock)
) {
// here is the recursion
children.add(el);
variables.addAll(_getVariablesInScope(el.getParent(), children));
}
return variables;
}
private Collection<CtVariable<?>> getVariablesInLocalScope(final CtElement el, final Set<CtElement> stoppers) {
final Set<CtVariable<?>> variables = new TreeSet();
// we will visit some elements children of "el" to add the variables
CtAbstractVisitor visitor = new CtAbstractVisitor() {
// for a block we add the local variables
@Override
public <R> void visitCtBlock(CtBlock<R> block) {
for (CtStatement stmt : block.getStatements()) {
// we can not add variables that are declared after the stoppers
if (stoppers.contains(stmt)) {
return;
}
// we only add the new local variables
if (stmt instanceof CtLocalVariable) {
variables.add((CtVariable<?>) stmt);
}
}
}
// for a method we add the parameters
@Override
public <T> void visitCtMethod(CtMethod<T> m) {
for (CtParameter<?> param : m.getParameters()) {
variables.add(param);
}
}
// for a class we add the fields
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
for (CtField<?> field : ctClass.getFields()) {
variables.add(field);
}
}
@Override
public <T> void visitCtThisAccess(CtThisAccess<T> tCtThisAccess) {
}
/*
@Override
public <T> void visitCtCatchVariableReference(CtCatchVariableReference<T> tCtCatchVariableReference) {
}*/
};
visitor.scan(el);
return variables;
}
public boolean hasLabelAndGoto(CtStatement stmt) {
CtExecutable parent = stmt.getParent(CtExecutable.class);
if (parent == null)
return false;
QueryVisitor query = new QueryVisitor(new TypeFilter(CtContinue.class));
parent.accept(query);
return query.getResult().stream()
.anyMatch(cnt -> ((CtContinue) cnt).getTargetLabel() != null);
}
}