/**
* Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite 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
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
*/
package org.evosuite.testcarver.codegen;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.hash.TIntHashSet;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Assignment.Operator;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.evosuite.testcarver.capture.CaptureLog;
import org.evosuite.testcarver.capture.CaptureUtil;
@Deprecated
public class CodeGenerator
{
private final CaptureLog log;
//--- source generation
private final TIntObjectHashMap<String> oidToVarMapping;
private final TIntObjectHashMap<Class<?>> oidToTypeMapping;
private int varCounter;
private TIntHashSet failedRecords;
private boolean isNewInstanceMethodNeeded;
private boolean isCallMethodMethodNeeded;
private boolean isSetFieldMethodNeeded;
private boolean isGetFieldMethodNeeded;
private boolean isXStreamNeeded;
public CodeGenerator(final CaptureLog log)
{
if(log == null)
{
throw new NullPointerException();
}
this.log = log.clone();
this.oidToVarMapping = new TIntObjectHashMap<String>();
this.oidToTypeMapping = new TIntObjectHashMap<Class<?>>();
this.varCounter = 0;
this.isNewInstanceMethodNeeded = false;
this.isCallMethodMethodNeeded = false;
this.isSetFieldMethodNeeded = false;
this.isGetFieldMethodNeeded = false;
this.isXStreamNeeded = false;
}
public CompilationUnit generateCodeForPostProcessing(final String cuName, final String packageName, final Class<?>...observedClasses)
{
return this.generateCode(cuName, packageName, true, observedClasses);
}
public CompilationUnit generateFinalCode(final String cuName, final String packageName, final TIntArrayList failedRecords, final Class<?>...observedClasses)
{
if(failedRecords == null)
{
throw new NullPointerException("list of failed records must not be null");
}
this.failedRecords = new TIntHashSet(failedRecords);
return this.generateCode(cuName, packageName, false, observedClasses);
}
// FIXME specifying classes here might not be needed anymore, if we only instrument observed classes...
@SuppressWarnings("unchecked")
private CompilationUnit generateCode(final String cuName, final String packageName, final boolean postprocessing, final Class<?>...observedClasses)
{
if(cuName == null || cuName.isEmpty())
{
throw new IllegalArgumentException("Illegal compilation unit name: " + cuName);
}
if(observedClasses == null || observedClasses.length == 0)
{
throw new IllegalArgumentException("No observed classes specified");
}
if(packageName == null)
{
throw new NullPointerException("package name must not be null");
}
//--- 1. step: extract class names
final HashSet<String> observedClassNames = new HashSet<String>();
for(int i = 0; i < observedClasses.length; i++)
{
observedClassNames.add(observedClasses[i].getName());
}
//--- 2. step: get all oids of the instances of the observed classes
// NOTE: They are implicitly sorted by INIT_REC_NO because of the natural object creation order captured by the
// instrumentation
final TIntArrayList targetOIDs = new TIntArrayList();
final int numInfoRecs = this.log.oidClassNames.size();
for(int i = 0; i < numInfoRecs; i++)
{
if(observedClassNames.contains(this.log.oidClassNames.get(i)))
{
targetOIDs.add(this.log.oids.getQuick(i));
}
}
//--- 3. step: init compilation unit
final AST ast = AST.newAST(AST.JLS3);
CompilationUnit cu = ast.newCompilationUnit();
// package declaration
final PackageDeclaration p1 = ast.newPackageDeclaration();
p1.setName(ast.newName(packageName.split("\\.")));
cu.setPackage(p1);
// import specifications
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "org", "junit", "Test" }));
cu.imports().add(id);
// class declaration
final TypeDeclaration td = ast.newTypeDeclaration();
td.setName(ast.newSimpleName(cuName));
td.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));
cu.types().add(td);
// test method construction
final MethodDeclaration md = ast.newMethodDeclaration();
md.setName(ast.newSimpleName("test"));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
// sets @Test annotation to test method
final NormalAnnotation annotation = ast.newNormalAnnotation();
annotation.setTypeName(ast.newSimpleName("Test"));
md.modifiers().add(annotation);
// sets method to public
md.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));
td.bodyDeclarations().add(md);
final Block block = ast.newBlock();
md.setBody(block);
// no invocations on objects of observed classes -> return empty but compilable CompilationUnit
if(targetOIDs.isEmpty())
{
return cu;
}
//--- 4. step: generating code starting with OID with lowest log rec no.
final int numLogRecords = this.log.objectIds.size();
int currentOID = targetOIDs.getQuick(0);
try
{
// TODO knowing last logRecNo for termination criterion belonging to an observed instance would prevent processing unnecessary statements
for(int currentRecord = this.log.oidRecMapping.get(currentOID); currentRecord < numLogRecords; currentRecord++)
{
currentOID = this.log.objectIds.getQuick(currentRecord);
if(targetOIDs.contains(currentOID))
{
this.restorceCodeFromLastPosTo(packageName, currentOID, currentRecord, postprocessing, ast, block);
// forward to end of method call sequence
currentRecord = findEndOfMethod(currentRecord, currentOID);
// each method call is considered as object state modification -> so save last object modification
log.updateWhereObjectWasInitializedFirst(currentOID, currentRecord);
}
}
}
catch(Exception e)
{
// System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXX\n"+this.log);
throw new RuntimeException(e.getMessage(), e);
}
if(this.isXStreamNeeded)
{
id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "com", "thoughtworks", "xstream", "XStream" }));
cu.imports().add(id);
// create XSTREAM constant: private static final XStream XSTREAM = new XStream();
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName("XSTREAM"));
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
ci.setType(this.createAstType("XStream", ast));
vd.setInitializer(ci);
final FieldDeclaration xstreamConst = ast.newFieldDeclaration(vd);
xstreamConst.setType(this.createAstType("XStream", ast));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.STATIC_KEYWORD));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD));
td.bodyDeclarations().add(xstreamConst);
this.isXStreamNeeded = false;
}
//-- creates utility method to set field value via reflections
if(this.isSetFieldMethodNeeded)
{
this.createSetFieldMethod(td, cu, ast);
this.isSetFieldMethodNeeded = false;
}
//-- creates utility method to get field value via reflections
if(this.isGetFieldMethodNeeded)
{
this.createGetFieldMethod(td, cu, ast);
this.isGetFieldMethodNeeded = false;
}
//-- creates utility method to call method via reflections
if(this.isCallMethodMethodNeeded)
{
this.createCallMethod(td, cu, ast);
this.isCallMethodMethodNeeded = false;
}
//-- creates utility method to call constructor via reflections
if(this.isNewInstanceMethodNeeded)
{
this.createNewInstanceMethod(td, cu, ast);
this.isNewInstanceMethodNeeded = false;
}
return cu;
}
private void updateInitRec(final int currentOID, final int currentRecord)
{
if(currentRecord > log.getRecordIndexOfWhereObjectWasInitializedFirst(currentOID))
{
log.updateWhereObjectWasInitializedFirst(currentOID, currentRecord);
}
}
private int findEndOfMethod(final int currentRecord, final int currentOID)
{
int record = currentRecord;
final int captureId = this.log.captureIds.getQuick(currentRecord);
while(! (this.log.objectIds.getQuick(record) == currentOID &&
this.log.captureIds.getQuick(record) == captureId &&
this.log.methodNames.get(record).equals(CaptureLog.END_CAPTURE_PSEUDO_METHOD)))
{
record++;
}
return record;
}
@SuppressWarnings("unchecked")
private void restorceCodeFromLastPosTo(final String packageName, final int oid, final int end, final boolean postprocessing, final AST ast, final Block block)
{
final int oidInfoRecNo = this.log.oidRecMapping.get(oid);
// start from last OID modification point
int currentRecord = log.getRecordIndexOfWhereObjectWasInitializedFirst(oid);
if(currentRecord > 0)
{
// last modification of object happened here
// -> we start looking for interesting records after retrieved record
currentRecord++;
}
else
{
// object new instance statement
// -> retrieved loc record no is included
currentRecord = -currentRecord;
}
String methodName;
int currentOID;
Object[] methodArgs;
Integer methodArgOID;
Integer returnValue;
Object returnValueObj;
for(; currentRecord <= end; currentRecord++)
{
currentOID = this.log.objectIds.getQuick(currentRecord);
returnValueObj = this.log.returnValues.get(currentRecord);
returnValue = returnValueObj.equals(CaptureLog.RETURN_TYPE_VOID) ? -1 : (Integer) returnValueObj;
if(oid == currentOID || returnValue == oid)
{
methodName = this.log.methodNames.get(currentRecord);
if(CaptureLog.PLAIN_INIT.equals(methodName)) // e.g. String var = "Hello World";
{
PostProcessor.notifyRecentlyProcessedLogRecNo(currentRecord);
this.createPlainInitStmt(currentRecord, block, ast);
currentRecord = findEndOfMethod(currentRecord, currentOID);
this.updateInitRec(currentOID, currentRecord);
}
else if(CaptureLog.NOT_OBSERVED_INIT.equals(methodName)) // e.g. Person var = (Person) XSTREAM.fromXML("<xml/>");
{
PostProcessor.notifyRecentlyProcessedLogRecNo(currentRecord);
this.createUnobservedInitStmt(currentRecord, block, ast);
currentRecord = findEndOfMethod(currentRecord, currentOID);
}
else if(CaptureLog.PUTFIELD.equals(methodName) || CaptureLog.PUTSTATIC.equals(methodName) || // field write access such as p.id = id or Person.staticVar = "something"
CaptureLog.GETFIELD.equals(methodName) || CaptureLog.GETSTATIC.equals(methodName)) // field READ access such as "int a = p.id" or "String var = Person.staticVar"
{
if(CaptureLog.PUTFIELD.equals(methodName) || CaptureLog.PUTSTATIC.equals(methodName))
{
// a field assignment has always one argument
methodArgs = this.log.params.get(currentRecord);
methodArgOID = (Integer) methodArgs[0];
if(methodArgOID != null && methodArgOID != oid)
{
// create history of assigned value
this.restorceCodeFromLastPosTo(packageName, methodArgOID, currentRecord, postprocessing, ast, block);
}
this.createFieldWriteAccessStmt(packageName, currentRecord, block, ast);
}
else
{
this.createFieldReadAccessStmt(packageName, currentRecord, block, ast);
}
currentRecord = findEndOfMethod(currentRecord, currentOID);
if(CaptureLog.GETFIELD.equals(methodName) || CaptureLog.GETSTATIC.equals(methodName))
{
// GETFIELD and GETSTATIC should only happen, if we obtain an instance whose creation has not been observed
this.updateInitRec(currentOID, currentRecord);
if(returnValue != -1)
{
this.updateInitRec(returnValue, currentRecord);
}
}
}
else // var0.call(someArg) or Person var0 = new Person()
{
methodArgs = this.log.params.get(currentRecord);
for(int i = 0; i < methodArgs.length; i++)
{
// there can only be OIDs or null
methodArgOID = (Integer) methodArgs[i];
if(methodArgOID != null && methodArgOID != oid)
{
this.restorceCodeFromLastPosTo(packageName, methodArgOID, currentRecord, postprocessing, ast, block);
}
}
PostProcessor.notifyRecentlyProcessedLogRecNo(currentRecord);
this.createMethodCallStmt(packageName, currentRecord, postprocessing, block, ast);
block.statements().add(ast.newEmptyStatement());
// forward to end of method call sequence
currentRecord = findEndOfMethod(currentRecord, currentOID);
// each method call is considered as object state modification -> so save last object modification
this.updateInitRec(currentOID, currentRecord);
if(returnValue != -1)
{
// if returnValue has not type VOID, mark current log record as record where the return value instance was created
// --> if an object is created within an observed method, it would not be semantically correct
// (and impossible to handle properly) to create an extra instance of the return value type outside this method
this.updateInitRec(returnValue, currentRecord);
}
// consider each passed argument as being modified at the end of the method call sequence
for(int i = 0; i < methodArgs.length; i++)
{
// there can only be OIDs or null
methodArgOID = (Integer) methodArgs[i];
if(methodArgOID != null && methodArgOID != oid)
{
this.updateInitRec(methodArgOID, currentRecord);
}
}
}
}
}
}
private String createNewVarName(final int oid, final String typeName)
{
if(this.oidToVarMapping.containsKey(oid))
{
throw new IllegalStateException("There is already an oid to var mapping for oid " + oid);
}
try
{
this.oidToTypeMapping.put(oid, Class.forName(typeName));
}
catch(final ClassNotFoundException e)
{
throw new IllegalArgumentException(e);
}
final String varName = "var" + this.varCounter++;
this.oidToVarMapping.put(oid, varName);
return varName;
}
// TODO easy but inefficient implementation
private Type createAstArrayType(final String type, final AST ast)
{
// NOTE: see asm4 guide p. 11 for more insights about internal type representation
final int arrayDim = type.lastIndexOf('[') + 1;
if(type.contains("[L"))
{
// --- object array
// TODO use regex for extraction
String extractedType = type.substring(type.indexOf('L') + 1, type.length() - 1);
extractedType = extractedType.replaceFirst("java\\.lang\\.", "");
return ast.newArrayType(this.createAstType(extractedType, ast), arrayDim);
}
else
{
// --- primitive type array
if(type.contains("[I")) // int[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("int")), arrayDim);
}
else if(type.contains("[B")) // byte[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("byte")), arrayDim);
}
else if(type.contains("[C")) // char[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("char")), arrayDim);
}
else if(type.contains("[D")) // double[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("double")), arrayDim);
}
else if(type.contains("[Z")) // boolean[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("boolean")), arrayDim);
}
else if(type.contains("[F")) // float[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("float")), arrayDim);
}
else if(type.contains("[S")) // short[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("short")), arrayDim);
}
else if(type.contains("[J")) // long[]
{
return ast.newArrayType(ast.newSimpleType(ast.newSimpleName("long")), arrayDim);
}
else
{
throw new RuntimeException("Can not create array type for " + type);
}
}
}
private Type createAstType(final String type, final AST ast)
{
if(type.startsWith("[")) // does type specify an array?
{
return this.createAstArrayType(type, ast);
}
else
{
final String[] fragments = type.split("\\.");
if(fragments.length == 1)
{
return ast.newSimpleType(ast.newSimpleName(fragments[0]));
}
final String[] pkgArray = new String[fragments.length - 1];
System.arraycopy(fragments, 0, pkgArray, 0, pkgArray.length);
final SimpleType pkgType = ast.newSimpleType(ast.newName(pkgArray));
return ast.newQualifiedType( pkgType, ast.newSimpleName(fragments[fragments.length - 1]));
}
}
/*
* Needed to preserve program flow:
*
* try
* {
* var0.doSth();
* }
* catch(Throwable t) {}
*/
@SuppressWarnings("unchecked")
private TryStatement createTryStmtWithEmptyCatch(final AST ast, Statement stmt)
{
final TryStatement tryStmt = ast.newTryStatement();
tryStmt.getBody().statements().add(stmt);
final CatchClause cc = ast.newCatchClause();
SingleVariableDeclaration excDecl = ast.newSingleVariableDeclaration();
excDecl.setType(ast.newSimpleType(ast.newSimpleName("Throwable")));
excDecl.setName(ast.newSimpleName("t"));
cc.setException(excDecl);
tryStmt.catchClauses().add(cc);
return tryStmt;
}
/*
* try
* {
* var0.doSth();
* }
* catch(Throwable t)
* {
* org.uni.saarland.sw.prototype.capture.PostProcessor.captureException(<logRecNo>);
* }
*/
@SuppressWarnings("unchecked")
private TryStatement createTryStatementForPostProcessing(final AST ast, final Statement stmt, final int logRecNo)
{
final TryStatement tryStmt = this.createTryStmtWithEmptyCatch(ast, stmt);
final CatchClause cc = (CatchClause) tryStmt.catchClauses().get(0);
final MethodInvocation m = ast.newMethodInvocation();
m.setExpression(ast.newName(new String[]{"de","unisb", "cs","st","testcarver","capture", "PostProcessor"}));
m.setName(ast.newSimpleName("captureException"));
m.arguments().add(ast.newNumberLiteral(String.valueOf(logRecNo)));
cc.getBody().statements().add(ast.newExpressionStatement(m));
MethodInvocation m2 = ast.newMethodInvocation();
m2.setExpression(ast.newSimpleName("t"));
m2.setName(ast.newSimpleName("printStackTrace"));
cc.getBody().statements().add(ast.newExpressionStatement(m2));
return tryStmt;
}
@SuppressWarnings("unchecked")
private MethodInvocation createCallMethodOrNewInstanceCallStmt(final boolean isConstructorCall, final AST ast, final String varName, final String targetTypeName, final String methodName, final Object[] methodArgs, final org.objectweb.asm.Type[] paramTypes)
{
//-- construct getField() call
final MethodInvocation callMethodCall = ast.newMethodInvocation();
if(isConstructorCall)
{
callMethodCall.setName(ast.newSimpleName("newInstance"));
}
else
{
callMethodCall.setName(ast.newSimpleName("callMethod"));
}
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(targetTypeName);
callMethodCall.arguments().add(stringLiteral); // class name
if(! isConstructorCall)
{
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(methodName);
callMethodCall.arguments().add(stringLiteral); // method name
if(varName == null)
{
callMethodCall.arguments().add(ast.newNullLiteral()); // static call -> no receiver
}
else
{
callMethodCall.arguments().add(ast.newSimpleName(varName)); // receiver
}
}
// method arguments
ArrayCreation arrCreation = ast.newArrayCreation();
arrCreation.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
ArrayInitializer arrInit = ast.newArrayInitializer();
arrCreation.setInitializer(arrInit);
callMethodCall.arguments().add(arrCreation);
Integer arg; // is either an oid or null
for(int i = 0; i < methodArgs.length; i++)
{
arg = (Integer) methodArgs[i];
if(arg == null)
{
arrInit.expressions().add(ast.newNullLiteral());
}
else
{
arrInit.expressions().add(ast.newSimpleName(this.oidToVarMapping.get(arg)));
}
}
// paramTypes
arrCreation = ast.newArrayCreation();
arrCreation.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
arrInit = ast.newArrayInitializer();
arrCreation.setInitializer(arrInit);
callMethodCall.arguments().add(arrCreation);
org.objectweb.asm.Type type;
for(int i = 0; i < paramTypes.length; i++)
{
type = paramTypes[i];
if(type.equals(org.objectweb.asm.Type.BOOLEAN_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Boolean"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.BYTE_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Byte"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.CHAR_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Character"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.DOUBLE_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Double"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.FLOAT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Float"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.INT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Integer"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.LONG_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Long"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.SHORT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Short"));
arrInit.expressions().add(facc);
}
else
{
final TypeLiteral clazz = ast.newTypeLiteral();
clazz.setType(ast.newSimpleType(ast.newName(type.getClassName())));
arrInit.expressions().add(clazz);
}
}
return callMethodCall;
}
@SuppressWarnings("unchecked")
private void createMethodCallStmt(final String packageName, final int logRecNo, final boolean postprocessing, final Block methodBlock, final AST ast)
{
// assumption: all necessary statements are created and there is one variable for reach referenced object
final int oid = this.log.objectIds.get(logRecNo);
final Object[] methodArgs = this.log.params.get(logRecNo);
final String methodName = this.log.methodNames.get(logRecNo);
final String methodDesc = this.log.descList.get(logRecNo);
final org.objectweb.asm.Type[] methodParamTypes = org.objectweb.asm.Type.getArgumentTypes(methodDesc);
final Class<?>[] methodParamTypeClasses = new Class[methodParamTypes.length];
for(int i = 0; i < methodParamTypes.length; i++)
{
methodParamTypeClasses[i] = getClassForName(methodParamTypes[i].getClassName());
}
final String typeName = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
Class<?> type = getClassForName(typeName);
// Class<?> type;
// try {
// type = Class.forName(typeName);
// } catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
// }
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final Statement finalStmt;
@SuppressWarnings("rawtypes")
final List arguments;
if(CaptureLog.OBSERVED_INIT.equals(methodName))
{
/*
* Person var0 = null;
* {
* var0 = new Person();
* }
* catch(Throwable t)
* {
* org.uni.saarland.sw.prototype.capture.PostProcessor.captureException(<logRecNo>);
* }
*/
// e.g. Person var0 = null;
final String varName = this.createNewVarName(oid, typeName);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(varName));
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(typeName, ast));
vd.setInitializer(ast.newNullLiteral());
methodBlock.statements().add(stmt);
try
{
this.getConstructorModifiers(type,methodParamTypeClasses);
}
catch(Exception e)
{
e.printStackTrace();
}
final int constructorTypeModifiers = this.getConstructorModifiers(type,methodParamTypeClasses);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(constructorTypeModifiers);
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
if(isReflectionAccessNeeded)
{
this.isNewInstanceMethodNeeded = true;
final MethodInvocation mi = this.createCallMethodOrNewInstanceCallStmt(true, ast, varName, typeName, methodName, methodArgs, methodParamTypes);
arguments = null;
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(varName));
assignment.setOperator(Operator.ASSIGN);
final CastExpression cast = ast.newCastExpression();
cast.setType(this.createAstType(typeName, ast));
cast.setExpression(mi);
assignment.setRightHandSide(cast);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
// e.g. var0 = new Person();
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
ci.setType(this.createAstType(typeName, ast));
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(varName));
assignment.setOperator(Operator.ASSIGN);
assignment.setRightHandSide(ci);
finalStmt = ast.newExpressionStatement(assignment);
arguments = ci.arguments();
}
}
else //------------------ handling for ordinary method calls e.g. var1 = var0.doSth();
{
String returnVarName = null;
final String desc = this.log.descList.get(logRecNo);
final String returnType = org.objectweb.asm.Type.getReturnType(desc).getClassName();
final Object returnValue = this.log.returnValues.get(logRecNo);
if(! CaptureLog.RETURN_TYPE_VOID.equals(returnValue))
{
Integer returnValueOID = (Integer) returnValue;
// e.g. Person var0 = null;
returnVarName = this.createNewVarName(returnValueOID, returnType);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(returnVarName));
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(returnType, ast));
vd.setInitializer(ast.newNullLiteral());
methodBlock.statements().add(stmt);
}
final String varName = this.oidToVarMapping.get(oid);
final int methodTypeModifiers = this.getMethodModifiers(type, methodName, methodParamTypeClasses);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(methodTypeModifiers);
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
// e.g. Person var0 = var1.getPerson("Ben");
final MethodInvocation mi;
if(isReflectionAccessNeeded)
{
this.isCallMethodMethodNeeded = true;
mi = this.createCallMethodOrNewInstanceCallStmt(false, ast, varName, typeName, methodName, methodArgs, methodParamTypes);
arguments = null;
if(returnVarName != null)
{
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(returnVarName));
assignment.setOperator(Operator.ASSIGN);
final CastExpression cast = ast.newCastExpression();
cast.setType(this.createAstType(returnType, ast));
cast.setExpression(mi);
assignment.setRightHandSide(cast);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
finalStmt = ast.newExpressionStatement(mi);
}
}
else
{
mi = ast.newMethodInvocation();
if( this.log.isStaticCallList.get(logRecNo))
{
// can only happen, if this is a static method call (because constructor statement has been reported)
final String tmpType = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
mi.setExpression(ast.newName(tmpType.split("\\.")));
}
else
{
mi.setExpression(ast.newSimpleName(varName));
}
mi.setName(ast.newSimpleName(methodName));
arguments = mi.arguments();
if(returnVarName != null)
{
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(returnVarName));
assignment.setOperator(Operator.ASSIGN);
assignment.setRightHandSide(mi);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
finalStmt = ast.newExpressionStatement(mi);
}
}
}
if(postprocessing)
{
final TryStatement tryStmt = this.createTryStatementForPostProcessing(ast, finalStmt, logRecNo);
methodBlock.statements().add(tryStmt);
}
else
{
if(this.failedRecords.contains(logRecNo))
{
// we just need an empty catch block to preserve program flow
final TryStatement tryStmt = this.createTryStmtWithEmptyCatch(ast, finalStmt);
methodBlock.statements().add(tryStmt);
}
else
{
methodBlock.statements().add(finalStmt);
}
}
if(arguments != null)
{
// final String methodDesc = this.log.descList.get(logRecNo);
// final org.objectweb.asm.Type[] methodParamTypes = org.objectweb.asm.Type.getArgumentTypes(methodDesc);
Class<?> methodParamType;
Class<?> argType;
Integer arg; // is either an oid or null
for(int i = 0; i < methodArgs.length; i++)
{
arg = (Integer) methodArgs[i];
if(arg == null)
{
arguments.add(ast.newNullLiteral());
}
else
{
methodParamType = CaptureUtil.getClassFromDesc(methodParamTypes[i].getDescriptor());
argType = this.oidToTypeMapping.get(arg);
if(methodParamType.isAssignableFrom(argType))
{
arguments.add(ast.newSimpleName(this.oidToVarMapping.get(arg)));
}
else
{
// we need an up-cast
final CastExpression cast = ast.newCastExpression();
cast.setType(ast.newSimpleType(ast.newName(methodParamType.getName())));
cast.setExpression(ast.newSimpleName(this.oidToVarMapping.get(arg)));
arguments.add(cast);
}
}
}
}
}
@SuppressWarnings("unchecked")
private void createPlainInitStmt(final int logRecNo, final Block methodBlock, final AST ast)
{
// NOTE: PLAIN INIT: has always one non-null param
// TODO: use primitives
final int oid = this.log.objectIds.get(logRecNo);
if(this.oidToVarMapping.containsKey(oid))
{
// TODO this might happen because of Integer.valueOf o.รค. . Is this approach ok?
return;
}
final String type = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
final Object value = this.log.params.get(logRecNo)[0];
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(this.createNewVarName(oid, value.getClass().getName())));
final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
if(value instanceof Class)
{
stmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
}
else
{
stmt.setType(this.createAstType(type, ast));
}
if(value instanceof Number)
{
if(value instanceof Long)
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value) + 'l'));
}
else if(value instanceof Double)
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value) + 'd'));
}
else
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value)));
}
}
else if(value instanceof String)
{
final StringLiteral literal = ast.newStringLiteral();
literal.setLiteralValue((String) value);
vd.setInitializer(literal);
}
else if(value instanceof Character)
{
final CharacterLiteral literal = ast.newCharacterLiteral();
literal.setCharValue((Character) value);
vd.setInitializer(literal);
}
else if(value instanceof Boolean)
{
final BooleanLiteral literal = ast.newBooleanLiteral((Boolean)value);
vd.setInitializer(literal);
}
else if(value instanceof Class)
{
final TypeLiteral clazz = ast.newTypeLiteral();
clazz.setType(ast.newSimpleType(ast.newName(((Class<?>) value).getName())));
vd.setInitializer(clazz);
}
else
{
throw new IllegalStateException("An error occurred while creating a plain statement: unsupported type: " + value.getClass().getName());
}
methodBlock.statements().add(stmt);
}
@SuppressWarnings("unchecked")
private void createUnobservedInitStmt(final int logRecNo, final Block methodBlock, final AST ast)
{
// NOTE: PLAIN INIT: has always one non-null param
// TODO: use primitives
final int oid = this.log.objectIds.get(logRecNo);
final String type = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
final Object value = this.log.params.get(logRecNo)[0];
this.isXStreamNeeded = true;
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
// handling because there must always be a new instantiation statement for pseudo inits
this.oidToVarMapping.remove(oid);
vd.setName(ast.newSimpleName(this.createNewVarName(oid, type)));
final MethodInvocation methodInvocation = ast.newMethodInvocation();
final Name name = ast.newSimpleName("XSTREAM");
methodInvocation.setExpression(name);
methodInvocation.setName(ast.newSimpleName("fromXML"));
final StringLiteral xmlParam = ast.newStringLiteral();
xmlParam.setLiteralValue((String) value);
methodInvocation.arguments().add(xmlParam);
final CastExpression castExpr = ast.newCastExpression();
castExpr.setType(this.createAstType(type, ast));
castExpr.setExpression(methodInvocation);
vd.setInitializer(castExpr);
final VariableDeclarationStatement vs = ast.newVariableDeclarationStatement(vd);
vs.setType(this.createAstType(type, ast));
methodBlock.statements().add(vs);
}
@SuppressWarnings("unchecked")
private void createFieldWriteAccessStmt(final String packageName, final int logRecNo, final Block methodBlock, final AST ast)
{
// assumption: all necessary statements are created and there is one variable for reach referenced object
final Object[] methodArgs = this.log.params.get(logRecNo);
final String methodName = this.log.methodNames.get(logRecNo);
final int oid = this.log.objectIds.get(logRecNo);
final int captureId = this.log.captureIds.get(logRecNo);
final String fieldName = this.log.namesOfAccessedFields.get(captureId);
final String typeName = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
Class<?> type = getClassForName(typeName);
// try {
// type = Class.forName(typeName);
// } catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
// }
final int fieldTypeModifiers = this.getFieldModifiers(type, fieldName);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(fieldTypeModifiers);
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
if(isReflectionAccessNeeded)
{
this.isSetFieldMethodNeeded = true;
final String varName = this.oidToVarMapping.get(oid);
final MethodInvocation setFieldCall = ast.newMethodInvocation();
setFieldCall.setName(ast.newSimpleName("setField"));
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(typeName);
setFieldCall.arguments().add(stringLiteral); // class name
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldName);
setFieldCall.arguments().add(stringLiteral); // field name
setFieldCall.arguments().add(ast.newSimpleName(varName)); // receiver
final Integer arg = (Integer) methodArgs[0];
if(arg == null)
{
setFieldCall.arguments().add(ast.newNullLiteral()); // value
}
else
{
setFieldCall.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(arg))); // value
}
methodBlock.statements().add(ast.newExpressionStatement(setFieldCall));
}
else
{
FieldAccess fa = ast.newFieldAccess();
if(CaptureLog.PUTSTATIC.equals(methodName))
{
// final String type = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
fa.setExpression(ast.newName(typeName));//.split("\\.")));
}
else
{
final String varName = this.oidToVarMapping.get(oid);
fa.setExpression(ast.newSimpleName(varName));
}
fa.setName(ast.newSimpleName(fieldName));
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(fa);
final Integer arg = (Integer) methodArgs[0];
if(arg == null)
{
assignment.setRightHandSide(ast.newNullLiteral());
}
else
{
final Class<?> argType = this.oidToTypeMapping.get(arg);
final String fieldDesc = this.log.descList.get(logRecNo);
final Class<?> fieldType = CaptureUtil.getClassFromDesc(fieldDesc);
if(fieldType.isAssignableFrom(argType))
{
assignment.setRightHandSide(ast.newSimpleName(this.oidToVarMapping.get(arg)));
}
else
{
// we need an up-cast
final CastExpression cast = ast.newCastExpression();
cast.setType(ast.newSimpleType(ast.newName(fieldType.getName())));
cast.setExpression(ast.newSimpleName(this.oidToVarMapping.get(arg)));
assignment.setRightHandSide(cast);
}
}
methodBlock.statements().add(ast.newExpressionStatement(assignment));
}
}
@SuppressWarnings("unchecked")
private void createFieldReadAccessStmt(final String packageName, final int logRecNo, final Block methodBlock, final AST ast)
{
// assumption: all necessary statements are created and there is one variable for reach referenced object
final String methodName = this.log.methodNames.get(logRecNo);
final int oid = this.log.objectIds.get(logRecNo);
final int captureId = this.log.captureIds.get(logRecNo);
String returnVarName = null;
final Object returnValue = this.log.returnValues.get(logRecNo);
if(! CaptureLog.RETURN_TYPE_VOID.equals(returnValue))
{
Integer returnValueOID = (Integer) returnValue;
final String descriptor = this.log.descList.get(logRecNo);
final String fieldTypeName = org.objectweb.asm.Type.getType(descriptor).getClassName();
final String typeName = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
final String fieldName = this.log.namesOfAccessedFields.get(captureId);
final String receiverVarName = this.oidToVarMapping.get(oid);
final Class<?> type = getClassForName(typeName);
// try {
// type = Class.forName(typeName);
// } catch (ClassNotFoundException e) {
// throw new RuntimeException(e);
// }
final int fieldTypeModifiers = this.getFieldModifiers(type, fieldName);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(fieldTypeModifiers);
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
// e.g. Person var0 = Person.BEN;
returnVarName = this.createNewVarName(returnValueOID, fieldTypeName);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(returnVarName));
if(isReflectionAccessNeeded)
{
this.isGetFieldMethodNeeded = true;
//-- construct getField() call
final MethodInvocation getFieldCall = ast.newMethodInvocation();
getFieldCall.setName(ast.newSimpleName("getField"));
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldTypeName);
getFieldCall.arguments().add(stringLiteral); // class name
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldName);
getFieldCall.arguments().add(stringLiteral); // field name
if(receiverVarName == null)
{
getFieldCall.arguments().add(ast.newNullLiteral()); // static call -> no receiver
}
else
{
getFieldCall.arguments().add(ast.newSimpleName(receiverVarName)); // receiver
}
// cast from object to field type
final CastExpression cast = ast.newCastExpression();
cast.setType(ast.newSimpleType(ast.newName(fieldTypeName)));
cast.setExpression(getFieldCall);
vd.setInitializer(cast);
}
else
{
FieldAccess fa = ast.newFieldAccess();
if(CaptureLog.GETSTATIC.equals(methodName))
{
final String classType = this.log.oidClassNames.get(this.log.oidRecMapping.get(oid));
fa.setExpression(ast.newName(classType.split("\\.")));
}
else
{
fa.setExpression(ast.newSimpleName(receiverVarName));
}
fa.setName(ast.newSimpleName(fieldName));
vd.setInitializer(fa);
}
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(fieldTypeName, ast));
methodBlock.statements().add(stmt);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createSetFieldMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static void setField(final String clazzName, final String fieldName, final Object receiver, final Object value) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// f.set(receiver, value);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Field" }));
imports.add(id);
//-- create method frame: "public static void setProtectedField(final String clazzName, final String fieldName, final Object receiver, final Object value) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("setField"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("fieldName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("value"));
parameters.add(svd);
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// f.set(receiver, value);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Field f = clazz.getDeclaredField(fieldName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredField"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("fieldName"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("f"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Field")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// f.set(receiver, value);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("set"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newSimpleName("receiver"));
minv.arguments().add(ast.newSimpleName("value"));
methodStmts.add(ast.newExpressionStatement(minv));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createGetFieldMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static void setField(final String clazzName, final String fieldName, final Object receiver, final Object value) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// f.set(receiver, value);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Field" }));
imports.add(id);
//-- create method frame: "public static Object setProtectedField(final String clazzName, final String fieldName, final Object receiver) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("getField"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("fieldName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// return f.get(receiver);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Field f = clazz.getDeclaredField(fieldName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredField"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("fieldName"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("f"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Field")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return f.get(receiver);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("get"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newSimpleName("receiver"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createCallMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static Object callMethod(final String clazzName, final String methodName, final Object receiver, final Object...args) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Method m = clazz.getDeclaredMethod(methodName);
// m.setAccessible(true);
// return m.invoke(receiver, args);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Method" }));
imports.add(id);
//-- create method frame: "public static Object callMethod(final String clazzName, final String methodName, final Object receiver, final Object[] args, Class[] paramTypes) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("callMethod"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("methodName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
svd.setName(ast.newSimpleName("args"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
svd.setName(ast.newSimpleName("paramTypes"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Method m = clazz.getDeclaredMethod(methodName, paramTypes);
// m.setAccessible(true);
// return m.invoke(receiver, args);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Method m = clazz.getDeclaredMethod(methodName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredMethod"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("methodName"));
init.arguments().add(ast.newSimpleName("paramTypes"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("m"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Method")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("m"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return m.invoke(receiver, args);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("invoke"));
minv.setExpression(ast.newSimpleName("m"));
minv.arguments().add(ast.newSimpleName("receiver"));
minv.arguments().add(ast.newSimpleName("args"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createNewInstanceMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static Object newInstance(final String clazzName, final Object receiver, final Object[] args, final Class[] parameterTypes) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
// c.setAccessible(true);
//
// return c.newInstance(args);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Constructor" }));
imports.add(id);
//-- create method frame: "public static Object newInstance(final String clazzName, final Object[] args, Class[] paramTypes) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("newInstance"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
svd.setName(ast.newSimpleName("args"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
svd.setName(ast.newSimpleName("paramTypes"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
// c.setAccessible(true);
//
// return c.newInstance(args);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredConstructor"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("paramTypes"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("c"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Constructor")));
methodStmts.add(varDeclStmt);
// c.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("c"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return c.newInstance(args);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("newInstance"));
minv.setExpression(ast.newSimpleName("c"));
minv.arguments().add(ast.newSimpleName("args"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
private int getFieldModifiers(final Class<?> clazz, final String fieldName)
{
try
{
final Field f = clazz.getDeclaredField(fieldName);
return f.getModifiers();
}
catch(final NoSuchFieldException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getFieldModifiers(superClazz, fieldName);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
private int getMethodModifiers(final Class<?> clazz, final String methodName, final Class<?>[] paramTypes)
{
try
{
final Method m = clazz.getDeclaredMethod(methodName, paramTypes);
return m.getModifiers();
}
catch(final NoSuchMethodException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getMethodModifiers(superClazz, methodName, paramTypes);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
private int getConstructorModifiers(final Class<?> clazz, final Class<?>[] paramTypes)
{
try
{
final Constructor<?> c = clazz.getDeclaredConstructor(paramTypes);
return c.getModifiers();
}
catch(final NoSuchMethodException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getConstructorModifiers(superClazz, paramTypes);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
// private final Class<?> getClassFromType(final org.objectweb.asm.Type type)
// {
//
// if(type.equals(org.objectweb.asm.Type.BOOLEAN_TYPE))
// {
// return Boolean.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.BYTE_TYPE))
// {
// return Byte.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.CHAR_TYPE))
// {
// return Character.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.DOUBLE_TYPE))
// {
// return Double.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.FLOAT_TYPE))
// {
// return Float.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.INT_TYPE))
// {
// return Integer.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.LONG_TYPE))
// {
// return Long.TYPE;
// }
// else if(type.equals(org.objectweb.asm.Type.SHORT_TYPE))
// {
// return Short.TYPE;
// }
// else if(type.getSort() == org.objectweb.asm.Type.ARRAY)
// {
// final org.objectweb.asm.Type elementType = type.getElementType();
//
// if(elementType.equals(org.objectweb.asm.Type.BOOLEAN_TYPE))
// {
// return boolean[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.BYTE_TYPE))
// {
// return byte[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.CHAR_TYPE))
// {
// return char[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.DOUBLE_TYPE))
// {
// return double[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.FLOAT_TYPE))
// {
// return float[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.INT_TYPE))
// {
// return int[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.LONG_TYPE))
// {
// return long[].class;
// }
// else if(elementType.equals(org.objectweb.asm.Type.SHORT_TYPE))
// {
// return short[].class;
// }
// }
//
// try
// {
// return Class.forName(type.getClassName(), true, StaticTestCluster.classLoader);
// }
// catch (final ClassNotFoundException e)
// {
// throw new RuntimeException(e);
// }
// }
private final Class<?> getClassForName(String type)
{
try
{
if( type.equals("boolean"))
{
return Boolean.TYPE;
}
else if(type.equals("byte"))
{
return Byte.TYPE;
}
else if( type.equals("char"))
{
return Character.TYPE;
}
else if( type.equals("double"))
{
return Double.TYPE;
}
else if(type.equals("float"))
{
return Float.TYPE;
}
else if(type.equals("int"))
{
return Integer.TYPE;
}
else if( type.equals("long"))
{
return Long.TYPE;
}
else if(type.equals("short"))
{
return Short.TYPE;
}
else if(type.equals("String") ||type.equals("Boolean") ||type.equals("Boolean") || type.equals("Short") ||type.equals("Long") ||
type.equals("Integer") || type.equals("Float") || type.equals("Double") ||type.equals("Byte") ||
type.equals("Character") )
{
return Class.forName("java.lang." + type);
}
// if(type.endsWith(";") && ! type.startsWith("["))
// {
// type = type.replaceFirst("L", "");
// type = type.replace(";", "");
// }
if(type.endsWith("[]"))
{
type = type.replace("[]", "");
return Class.forName("[L" + type + ";");
}
else
{
return Class.forName(type);
}
}
catch (final ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
public static void main(String[] args)
{
System.out.println("$2CalculatorPanel$1".replaceFirst("\\$\\d+$", ""));
}
}