package fr.inria.diversify.transformation.bytecode;
import fr.inria.diversify.transformation.SingleTransformation;
import fr.inria.diversify.util.Log;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.*;
import org.apache.commons.io.FileUtils;
import spoon.reflect.cu.SourcePosition;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* User: Simon
* Date: 11/6/13
* Time: 5:28 PM
*/
public abstract class BytecodeTransformation extends SingleTransformation {
protected CtMethod methodLocation;
protected int opcodeIndex;
protected List<CtMethod> methods;
protected File backupClassFile;
protected CtClass backupClass;
public void apply(String targetDir) throws Exception {
backupClass = methodLocation.getDeclaringClass();
String destination = targetDir+ "/"+backupClass.getName().replace(".","/") + ".originalClass";
backupClassFile = new File(destination);
backupClassFile.createNewFile();
FileUtils.copyFile(new File(backupClass.getURL().getFile()),backupClassFile);
Log.info("bytecode transformation: {} in method: {}",this.getType(),methodLocation.getLongName());
Log.debug("method bytecode before: \n{}", methodToString(methodLocation));
apply();
Log.debug("method bytecode after: \n{}",methodToString(methodLocation));
Log.debug("write new class in: {}",targetDir);
methodLocation.getDeclaringClass().writeFile(targetDir);
}
protected abstract void apply() throws BadBytecode;
public void applyWithParent(String srcDir) throws BadBytecode {}
public void restore(String targetDir) throws Exception {
String destination = targetDir+ "/"+backupClass.getName().replace(".","/") + ".class";
Log.debug("restore file: " + backupClassFile + " -> " + destination);
for(CtMethod method : methodLocation.getDeclaringClass().getDeclaredMethods())
if(!method.isEmpty())
methods.remove(method);
methodLocation.getDeclaringClass().detach();
FileUtils.copyFile(backupClassFile, new File(destination));
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(destination);
pool.get(backupClass.getName());
for(CtMethod method : pool.get(backupClass.getName()).getDeclaredMethods())
if(!method.isEmpty())
methods.add(method);
}
protected List<Integer> opCodeIndexList(CodeAttribute ca) throws BadBytecode {
List<Integer> list = new ArrayList<Integer>();
CodeIterator i = ca.iterator();
while (i.hasNext()) {
list.add(i.next());
}
return list;
}
public String methodToString(CtMethod method) throws BadBytecode {
MethodInfo minfo = method.getMethodInfo();
CodeAttribute ca = minfo.getCodeAttribute();
CodeIterator i = ca.iterator();
String ret = "";
int oldIndex = i.next();
while(i.hasNext()) {
int index = i.next();
int op = i.byteAt(oldIndex);
ret += "index: "+oldIndex+", opCode: "+Mnemonic.OPCODE[op]+ "\n";
for(int j = oldIndex+1; j < index; j++)
ret += "index: "+j+", byteCode: "+ i.byteAt(j) +"\n";
oldIndex = index;
}
return ret;
}
protected int byteCodeSize(CodeAttribute ca, List<Integer> opCodeIndexList, int index) {
if(index + 1 == opCodeIndexList.size())
return ca.getCodeLength() - opCodeIndexList.get(index);
else
return opCodeIndexList.get(index+1) - opCodeIndexList.get(index);
}
public String classLocationName() {
return methodLocation.getDeclaringClass().getName();
}
public String packageLocationName() {
return methodLocation.getDeclaringClass().getPackageName();
}
public String methodLocationName() {
return methodLocation.getLongName();
}
public void setOpcodeIndex(int opcodeIndex) {
this.opcodeIndex = opcodeIndex;
}
public void setMethodLocation(CtMethod methodLocation) {
this.methodLocation = methodLocation;
}
public int line() {return 0;}
@Override
public String getTransformationString() throws Exception {
return null;
}
@Override
public void printJavaFile(String srcDir) throws IOException {
}
@Override
public SourcePosition getPosition() {
throw new NotImplementedException();
}
}