package tracer;
import java.util.ArrayList;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.expr.FieldAccess;
import javassist.expr.NewArray;
/**
* トレース出力を行う実行文を生成するクラス(フォーマット依存部分はITraceGeneratorに移譲)
*
* @author Nitta
*
*/
public class OutputStatementsGenerator {
private ITraceGenerator generator;
public OutputStatementsGenerator(ITraceGenerator generator) {
this.generator = generator;
}
public ITraceGenerator getGenerator() {
return generator;
}
public String generateReplaceStatementsForFieldSet(CtClass cc, FieldAccess f) {
String fieldName = "\"" + f.getClassName() + "." + f.getFieldName() + "\"";
String containerClass = "(($0 != null)?$0.getClass().getName():\"" + cc.getName() + "\")";
String containerObject = "(($0 != null)?System.identityHashCode($0):0)";
String valueClass = "(($1 != null)?$1.getClass().getName():\"---\")";
String valueObject = "(($1 != null)?System.identityHashCode($1):0)";
String threadId = "Thread.currentThread().getId()";
String timeStamp = "System.nanoTime()";
return generator.generateReplaceStatementsForFieldSet(fieldName, containerClass, containerObject, valueClass, valueObject, threadId, timeStamp);
}
public String generateReplaceStatementsForFieldGet(CtClass cc, CtBehavior m, FieldAccess f) {
String fieldName = "\"" + f.getClassName() + "." + f.getFieldName() + "\"";
String thisClass;
String thisObject;
if ((m.getModifiers() & Modifier.STATIC) == 0 && m instanceof CtMethod) {
thisClass = "this.getClass().getName()";
thisObject = "System.identityHashCode(this)";
} else {
// staticメソッドかコンストラクタの場合
thisClass = "\"" + cc.getName() + "\"";
thisObject = "\"0\"";
}
String containerClass = "(($0 != null)?$0.getClass().getName():\"---\")";
String containerObject = "(($0 != null)?System.identityHashCode($0):0)";
String valueClass = "(($_ != null)?$_.getClass().getName():\"---\")";
String valueObject = "(($_ != null)?System.identityHashCode($_):0)";
String threadId = "Thread.currentThread().getId()";
String timeStamp = "System.nanoTime()";
return generator.generateReplaceStatementsForFieldGet(fieldName, thisClass, thisObject, containerClass, containerObject, valueClass, valueObject, threadId, timeStamp);
}
public String generateReplaceStatementsForNewArray(NewArray a) {
String arrayClass = "(($_ != null)?$_.getClass().getName():\"---\")";
String arrayObject = "(($_ != null)?System.identityHashCode($_):0)";
String dimension = "\"" + Integer.toString(a.getDimension()) + "\"";
String threadId = "Thread.currentThread().getId()";
String timeStamp = "System.nanoTime()";
return generator.generateReplaceStatementsForNewArray(arrayClass, arrayObject, dimension, threadId, timeStamp);
}
public String generateReplaceStatementsForCall(CtClass cls, CtBehavior m) throws NotFoundException {
return "{" + generateInsertBeforeStatements(cls, m, true) + " $_ = $proceed($$); " + generateInsertAfterStatements(cls, m, true) + "}";
}
public String generateInsertBeforeStatementsForMethodBody(CtClass cls, CtBehavior m) throws NotFoundException {
return "{" + generateInsertBeforeStatements(cls, m, false) + "}";
}
public String generateInsertAfterStatementsForMethodBody(CtClass cls, CtBehavior m) throws NotFoundException {
return "{" + generateInsertAfterStatements(cls, m, false) + "}";
}
/**
* トレース出力用の命令列を生成する
* @param cls 対象クラス
* @param m 対象メソッド(コンストラクタ)
* @param isCallerSideInstrumentation 命令列を呼び出し側に挿入するか呼び出される側に挿入するか?
* @return
* @throws NotFoundException
*/
private String generateInsertBeforeStatements(CtClass cls, CtBehavior m, boolean isCallerSideInstrumentation) throws NotFoundException {
// メソッドシグニチャの構成
String declaredClassName = cls.getName();
String modifiers = "";
if ((m.getModifiers() & Modifier.PUBLIC) != 0) {
modifiers = "public ";
} else if ((m.getModifiers() & Modifier.PRIVATE) != 0) {
modifiers = "private ";
} else if ((m.getModifiers() & Modifier.PROTECTED) != 0) {
modifiers = "protected ";
}
if ((m.getModifiers() & Modifier.STATIC) != 0) {
modifiers += "static ";
}
if ((m.getModifiers() & Modifier.FINAL) != 0) {
modifiers += "final ";
}
if ((m.getModifiers() & Modifier.SYNCHRONIZED) != 0) {
modifiers += "synchronized ";
}
String thisClass;
String thisObject;
String methodSignature = null;
if ((m.getModifiers() & Modifier.STATIC) != 0) {
// staticメソッドの場合
methodSignature = "\"" + modifiers + ((CtMethod)m).getReturnType().getName() + " " + m.getLongName().replace('$', '.') + "\""; // AspectJではメソッドシグニチャ内では無名クラスはドットで区切られる
thisClass = "\"" + declaredClassName + "\"";
thisObject = "\"0\"";
} else if (m instanceof CtConstructor) {
// コンストラクタの場合
methodSignature = "\"" + modifiers + m.getLongName().replace('$', '.') + "\""; // AspectJではメソッドシグニチャ内では無名クラスはドットで区切られる
thisClass = "\"" + declaredClassName + "\"";
thisObject = "\"0\"";
} else {
// 通常メソッドの場合
methodSignature = "\"" + modifiers + ((CtMethod)m).getReturnType().getName() + " " + m.getLongName().replace('$', '.') + "\""; // AspectJではメソッドシグニチャ内では無名クラスはドットで区切られる
if (!isCallerSideInstrumentation) {
// 呼び出し先に埋め込む場合(通常)
thisClass = "this.getClass().getName()";
thisObject = "System.identityHashCode(this)";
} else {
// 呼出し元に埋め込む場合(標準クラスの呼出し)
thisClass = "$0.getClass().getName()";
thisObject = "System.identityHashCode($0)";
}
}
// 引数の出力式の構成
int p = 0;
CtClass parameterClasses[] = m.getParameterTypes();
ArrayList<String> argClasses = new ArrayList<>();
ArrayList<String> argObjects = new ArrayList<>();
for (CtClass c : parameterClasses) {
if (!c.isPrimitive()) {
argClasses.add("$args[" + p + "].getClass().getName()");
argObjects.add("System.identityHashCode($" + (p + 1) + ")");
} else {
argClasses.add("$args[" + p + "].getClass().getName()");
argObjects.add("$" + (p + 1));
}
p++;
}
String threadId = "Thread.currentThread().getId()";
String timeStamp = "System.nanoTime()";
return generator.generateInsertBeforeStatements(m, methodSignature, thisClass, thisObject, argClasses, argObjects, threadId, timeStamp);
}
private String generateInsertAfterStatements(CtClass cls, CtBehavior m, boolean isCallerSideInstrumentation) throws NotFoundException {
String declaredClassName = cls.getName();
String returnedClass;
String returnedObject;
String thisClass;
String thisObject;
if ((m.getModifiers() & Modifier.STATIC) != 0) {
// staticメソッドの場合
if (!((CtMethod)m).getReturnType().isPrimitive() || ((CtMethod)m).getReturnType() == CtClass.voidType) {
returnedClass = "(($_ != null)?$_.getClass().getName():\"void\")";
returnedObject = "(($_ != null)?System.identityHashCode($_):0)";
thisClass = "\"" + declaredClassName + "\"";
thisObject = "\"0\"";
} else {
returnedClass = "\"" + ((CtMethod)m).getReturnType().getName() +"\"";
returnedObject = "$_";
thisClass = "\"" + declaredClassName + "\"";
thisObject = "\"0\"";
}
} else if (m instanceof CtConstructor) {
// コンストラクタの場合
if (!isCallerSideInstrumentation) {
// 呼び出し先に埋め込む場合(通常)
returnedClass = "$0.getClass().getName()";
returnedObject = "System.identityHashCode($0)";
thisClass = "\"" + declaredClassName + "\"";
thisObject = "System.identityHashCode($0)";
} else {
// 呼出し元に埋め込む場合(標準クラスもしくはデフォルトコンストラクタの呼出し、または親コンストラクタの呼出し)
returnedClass = "(($_ != null)?$_.getClass().getName():$0.getClass().getName())";
returnedObject = "(($_ != null)?System.identityHashCode($_):System.identityHashCode($0))";
thisClass = "\"" + declaredClassName + "\"";
thisObject = "(($_ != null)?System.identityHashCode($_):System.identityHashCode($0))";
}
} else {
// 通常のメソッドの場合
if (!isCallerSideInstrumentation) {
// 呼び出し先に埋め込む場合(通常)
if (!((CtMethod)m).getReturnType().isPrimitive() || ((CtMethod)m).getReturnType() == CtClass.voidType) {
returnedClass = "(($_ != null)?$_.getClass().getName():\"void\")";
returnedObject = "(($_ != null)?System.identityHashCode($_):0)";
thisClass = "this.getClass().getName()";
thisObject = "System.identityHashCode(this)";
} else {
returnedClass = "\"" + ((CtMethod)m).getReturnType().getName() +"\"";
returnedObject = "$_";
thisClass = "this.getClass().getName()";
thisObject = "System.identityHashCode(this)";
}
} else {
// 呼出し元に埋め込む場合(標準クラスの呼出し)
if (!((CtMethod)m).getReturnType().isPrimitive() || ((CtMethod)m).getReturnType() == CtClass.voidType) {
returnedClass = "(($_ != null)?$_.getClass().getName():\"void\")";
returnedObject = "(($_ != null)?System.identityHashCode($_):0)";
thisClass = "$0.getClass().getName()";
thisObject = "System.identityHashCode($0)";
} else {
returnedClass = "\"" + ((CtMethod)m).getReturnType().getName() +"\"";
returnedObject = "$_";
thisClass = "$0.getClass().getName()";
thisObject = "System.identityHashCode($0)";
}
}
}
String threadId = "Thread.currentThread().getId()";
String timeStamp = "System.nanoTime()";
return generator.generateInsertAfterStatements(cls, m, thisClass, thisObject, returnedClass, returnedObject, threadId, timeStamp, isCallerSideInstrumentation);
}
}