Newer
Older
TracerOnJavassist / TracerOnJavassist / src / tracer / OutputStatementsGenerator.java
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);
	}
}