/** * 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.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Queue; import java.util.Set; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.ArrayType; 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.Modifier.ModifierKeyword; 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.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; 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.evosuite.testcarver.capture.CaptureLog; import org.evosuite.testcarver.capture.CaptureUtil; import org.evosuite.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class JUnitCodeGenerator implements ICodeGenerator<CompilationUnit> { private static final Logger logger = LoggerFactory.getLogger(JUnitCodeGenerator.class); //--- source generation private final TIntObjectHashMap<String> oidToVarMapping; private final TIntObjectHashMap<Class<?>> oidToTypeMapping; private int varCounter; private TIntHashSet failedRecords; private boolean postprocessing; private boolean isNewInstanceMethodNeeded; private boolean isCallMethodMethodNeeded; private boolean isSetFieldMethodNeeded; private boolean isGetFieldMethodNeeded; private boolean isXStreamNeeded; private String cuName; private String packageName; public JUnitCodeGenerator(final String cuName, final String packageName) { if(cuName == null || cuName.isEmpty()) { throw new IllegalArgumentException("Illegal compilation unit name: " + cuName); } if(packageName == null) { throw new NullPointerException("package name must not be null"); } this.packageName = packageName; this.cuName = cuName; this.oidToVarMapping = new TIntObjectHashMap<String>(); this.oidToTypeMapping = new TIntObjectHashMap<Class<?>>(); this.failedRecords = new TIntHashSet(); this.init(); } private void init() { this.oidToVarMapping.clear(); this.oidToTypeMapping.clear(); this.failedRecords.clear(); this.varCounter = 0; this.postprocessing = false; this.isNewInstanceMethodNeeded = false; this.isCallMethodMethodNeeded = false; this.isSetFieldMethodNeeded = false; this.isGetFieldMethodNeeded = false; this.isXStreamNeeded = false; } public void enablePostProcessingCodeGeneration() { this.postprocessing = true; } public void disablePostProcessingCodeGeneration(final TIntArrayList failedRecords) { this.postprocessing = false; this.failedRecords.addAll(failedRecords); } private AST ast; private CompilationUnit cu; private Block methodBlock; TypeDeclaration td; @SuppressWarnings("unchecked") @Override public void before(CaptureLog log) { ast = AST.newAST(AST.JLS3); 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 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); methodBlock = ast.newBlock(); md.setBody(methodBlock); } @SuppressWarnings("unchecked") @Override public void after(CaptureLog log) { if(this.isXStreamNeeded) { ImportDeclaration 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; } } @Override public CompilationUnit getCode() { return this.cu; } @Override public void clear() { this.init(); } private String createNewVarName(final int oid, final String typeName) { return this.createNewVarName(oid, typeName, false); } private String createNewVarName(final int oid, final String typeName, final boolean asObject) { if(this.oidToVarMapping.containsKey(oid)) { throw new IllegalStateException("There is already an oid to var mapping for oid " + oid); } // try // { if(asObject) { this.oidToTypeMapping.put(oid, Object.class); } else { this.oidToTypeMapping.put(oid, getClassForName(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\\.", ""); extractedType = extractedType.replace('$', '.'); return ast.newArrayType(this.createAstType(extractedType, ast), arrayDim); } else { // --- primitive type array if(type.contains("[I")) // int[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.INT), arrayDim); } else if(type.contains("[B")) // byte[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.BYTE), arrayDim); } else if(type.contains("[C")) // char[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.CHAR), arrayDim); } else if(type.contains("[D")) // double[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.DOUBLE), arrayDim); } else if(type.contains("[Z")) // boolean[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.BOOLEAN), arrayDim); } else if(type.contains("[F")) // float[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.FLOAT), arrayDim); } else if(type.contains("[S")) // short[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.SHORT), arrayDim); } else if(type.contains("[J")) // long[] { return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.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) { if(fragments[0].contains("$")) { return ast.newSimpleType(ast.newName(fragments[0].replace('$', '.'))); } else { return ast.newSimpleType(ast.newSimpleName(fragments[0])); } } if(type.startsWith("java.lang")) { return ast.newSimpleType(ast.newSimpleName(fragments[2])); } final String[] pkgArray = new String[fragments.length - 1]; System.arraycopy(fragments, 0, pkgArray, 0, pkgArray.length); final SimpleType pkgType = ast.newSimpleType(ast.newName(pkgArray)); final String clazzName = fragments[fragments.length - 1]; if(clazzName.contains("$")) { final String[] clazzSplit = clazzName.split("\\$"); final String[] newPkgType = new String[pkgArray.length + clazzSplit.length - 1]; System.arraycopy(pkgArray, 0, newPkgType, 0, pkgArray.length); System.arraycopy(clazzSplit, 0, newPkgType, pkgArray.length, clazzSplit.length - 1); if(clazzName.endsWith("[]")) { final QualifiedType t = ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1].replace("[]", ""))); return ast.newArrayType(t); } else { return ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1])); } } else { final String[] clazzSplit = clazzName.split("\\$"); final String[] newPkgType = new String[pkgArray.length + clazzSplit.length - 1]; System.arraycopy(pkgArray, 0, newPkgType, 0, pkgArray.length); System.arraycopy(clazzSplit, 0, newPkgType, pkgArray.length, clazzSplit.length - 1); if(clazzName.endsWith("[]")) { final QualifiedType t = ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1].replace("[]", ""))); return ast.newArrayType(t); } else { return ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.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[]{"org", "evosuite","testcarver","codegen", "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; } /* (non-Javadoc) * @see org.evosuite.testcarver.codegen.ICodeGenerator#createMethodCallStmt(org.evosuite.testcarver.capture.CaptureLog, int) */ @SuppressWarnings("unchecked") @Override public void createMethodCallStmt(CaptureLog log, int logRecNo) { PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo); // assumption: all necessary statements are created and there is one variable for reach referenced object final int oid = log.objectIds.get(logRecNo); Object[] methodArgs = log.params.get(logRecNo); final String methodName = log.methodNames.get(logRecNo); final String methodDesc = log.descList.get(logRecNo); org.objectweb.asm.Type[] methodParamTypes = org.objectweb.asm.Type.getArgumentTypes(methodDesc); Class<?>[] methodParamTypeClasses = new Class[methodParamTypes.length]; for(int i = 0; i < methodParamTypes.length; i++) { methodParamTypeClasses[i] = getClassForName(methodParamTypes[i].getClassName()); } final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); Class<?> type = getClassForName(typeName); 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); //FIXME this does not make any sense try { this.getConstructorModifiers(type,methodParamTypeClasses); } catch(Exception e) { logger.error(""+e,e); } 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(); final int recNo = log.oidRecMapping.get(oid); final int dependencyOID = log.dependencies.getQuick(recNo); if(dependencyOID == CaptureLog.NO_DEPENDENCY) { ci.setType(this.createAstType(typeName, ast)); } else { // final String varTypeName = oidToVarMapping.get(dependencyOID) + "." + typeName.substring(typeName.indexOf('$') + 1); // ci.setType(this.createAstType(varTypeName, ast)); /* * e.g. * OuterClass.InnerClass innerObject = outerObject.new InnerClass(); */ ci.setType(this.createAstType(typeName.substring(typeName.indexOf('$') + 1), ast)); ci.setExpression(ast.newSimpleName(oidToVarMapping.get(dependencyOID))); final int index = Arrays.binarySearch(methodArgs, dependencyOID); if(index > -1) { logger.debug(varName + " xxxx3 " + index); final Object[] newArgs = new Object[methodArgs.length - 1]; System.arraycopy(methodArgs, 0, newArgs, 0, index); System.arraycopy(methodArgs, index + 1, newArgs, index, methodArgs.length - index - 1); methodArgs = newArgs; final Class<?>[] newParamTypeClasses = new Class<?>[methodParamTypeClasses.length - 1]; System.arraycopy(methodParamTypeClasses, 0, newParamTypeClasses, 0, index); System.arraycopy(methodParamTypeClasses, index + 1, newParamTypeClasses, index, methodParamTypeClasses.length - index - 1); methodParamTypeClasses = newParamTypeClasses; final org.objectweb.asm.Type[] newParamTypes = new org.objectweb.asm.Type[methodParamTypes.length - 1]; System.arraycopy(methodParamTypes, 0, newParamTypes, 0, index); System.arraycopy(methodParamTypes, index + 1, newParamTypes, index, methodParamTypes.length - index - 1); methodParamTypes = newParamTypes; } } 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 = log.descList.get(logRecNo); final String returnType = org.objectweb.asm.Type.getReturnType(desc).getClassName(); final Object returnValue = 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( log.isStaticCallList.get(logRecNo)) { // can only happen, if this is a static method call (because constructor statement has been reported) final String tmpType = log.oidClassNames.get(log.oidRecMapping.get(oid)); mi.setExpression(ast.newName(tmpType.split("\\."))); } else { try { mi.setExpression(ast.newSimpleName(varName)); } catch(final IllegalArgumentException ex) { String msg = ""; msg += "--recno-- " + logRecNo + "\n"; msg += "--oid-- " + oid + "\n"; msg += "--method-- " + methodName + "\n"; msg += "--varName-- " + varName + "\n"; msg += "--oidToVarMap-- " + this.oidToVarMapping + "\n"; msg += (log) + "\n"; logger.error(msg); throw new RuntimeException(msg); } } 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) { 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); // TODO: Warten was Florian und Gordon dazu sagen. Siehe Mail 04.08.2012 if(argType == null) { logger.error("Call within constructor needs instance of enclosing object as parameter -> ignored: " + arg); methodBlock.statements().remove(methodBlock.statements().size() - 1); return; } final CastExpression cast = ast.newCastExpression(); if(methodParamType.isPrimitive()) { // cast to ensure that right method is called // --> see doSth(int) and doSth(Integer) if(methodParamType.equals(boolean.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.BOOLEAN)); } else if(methodParamType.equals(byte.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.BYTE)); } else if(methodParamType.equals(char.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.CHAR)); } else if(methodParamType.equals(double.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.DOUBLE)); } else if(methodParamType.equals(float.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.FLOAT)); } else if(methodParamType.equals(int.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.INT)); } else if(methodParamType.equals(long.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.LONG)); } else if(methodParamType.equals(short.class)) { cast.setType(ast.newPrimitiveType(PrimitiveType.SHORT)); } else { throw new RuntimeException("unknown primitive type: " + methodParamType); } } else { // we need an up-cast if(methodParamType.getName().contains(".")) { cast.setType(this.createAstType(methodParamType.getName(), ast)); } else { cast.setType(createAstType(methodParamType.getName(), ast)); } } cast.setExpression(ast.newSimpleName(this.oidToVarMapping.get(arg))); arguments.add(cast); } } } } @SuppressWarnings("unchecked") @Override public void createPlainInitStmt(CaptureLog log, int logRecNo) { PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo); // NOTE: PLAIN INIT: has always one non-null param // TODO: use primitives final int oid = 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 = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Object value = 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") @Override public void createUnobservedInitStmt(CaptureLog log, int logRecNo) { PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo); // NOTE: PLAIN INIT: has always one non-null param // TODO: use primitives final int oid = log.objectIds.get(logRecNo); // final String type = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Object value = 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, log.oidClassNames.get(log.oidRecMapping.get(oid)), true))); 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); vd.setInitializer(methodInvocation); final VariableDeclarationStatement vs = ast.newVariableDeclarationStatement(vd); vs.setType(this.createAstType("Object", ast)); methodBlock.statements().add(vs); } @SuppressWarnings("unchecked") @Override public void createFieldWriteAccessStmt(CaptureLog log, int logRecNo) { // assumption: all necessary statements are created and there is one variable for reach referenced object final Object[] methodArgs = log.params.get(logRecNo); final String methodName = log.methodNames.get(logRecNo); final int oid = log.objectIds.get(logRecNo); final int captureId = log.captureIds.get(logRecNo); final String fieldName = log.namesOfAccessedFields.get(captureId); final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Class<?> type = getClassForName(typeName); 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)) { fa.setExpression(ast.newName(typeName)); } 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 = log.descList.get(logRecNo); final Class<?> fieldType = CaptureUtil.getClassFromDesc(fieldDesc); if(fieldType.isAssignableFrom(argType) || fieldType.isPrimitive()) { 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") @Override public void createFieldReadAccessStmt(CaptureLog log, int logRecNo) { final String methodName = log.methodNames.get(logRecNo); final int oid = log.objectIds.get(logRecNo); final int captureId = log.captureIds.get(logRecNo); String returnVarName = null; final Object returnValue = log.returnValues.get(logRecNo); if(! CaptureLog.RETURN_TYPE_VOID.equals(returnValue)) { Integer returnValueOID = (Integer) returnValue; final String descriptor = log.descList.get(logRecNo); final String fieldTypeName = org.objectweb.asm.Type.getType(descriptor).getClassName(); final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); final String fieldName = log.namesOfAccessedFields.get(captureId); final String receiverVarName = this.oidToVarMapping.get(oid); final Class<?> type = getClassForName(typeName); 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 = log.oidClassNames.get(log.oidRecMapping.get(oid)); fa.setExpression(ast.newName(classType.replace('$', '.').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); } } @Override public void createArrayInitStmt(final CaptureLog log, final int logRecNo) { final int oid = log.objectIds.get(logRecNo); final Object[] params = log.params.get(logRecNo); final String arrTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Class<?> arrType = getClassForName(arrTypeName); // --- create array instance creation e.g. int[] var = new int[10]; final ArrayType arrAstType = (ArrayType) createAstArrayType(arrTypeName, ast); final ArrayCreation arrCreationExpr = ast.newArrayCreation(); arrCreationExpr.setType(arrAstType); arrCreationExpr.dimensions().add(ast.newNumberLiteral(String.valueOf(params.length))); final String arrVarName = this.createNewVarName(oid, arrTypeName); final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment(); final SimpleName arrVarNameExpr = ast.newSimpleName(arrVarName); vd.setName(arrVarNameExpr); vd.setInitializer(arrCreationExpr); final VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(vd); varDeclStmt.setType(this.createAstType(arrTypeName, ast)); methodBlock.statements().add(varDeclStmt); // create array access statements var[0] = var1; Integer paramOID; Assignment assign; ArrayAccess arrAccessExpr; for(int i = 0; i < params.length; i++) { assign = ast.newAssignment(); arrAccessExpr = ast.newArrayAccess(); arrAccessExpr.setIndex(ast.newNumberLiteral(String.valueOf(i))); arrAccessExpr.setArray(arrVarNameExpr); assign.setLeftHandSide(arrAccessExpr); paramOID = (Integer) params[i]; if(paramOID == null) { assign.setRightHandSide(ast.newNullLiteral()); } else { assign.setRightHandSide(ast.newSimpleName(this.oidToVarMapping.get(paramOID))); } methodBlock.statements().add(assign); } } @SuppressWarnings("unchecked") @Override public void createCollectionInitStmt(final CaptureLog log, final int logRecNo) { final int oid = log.objectIds.get(logRecNo); final Object[] params = log.params.get(logRecNo); String collTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Class<?> collType = getClassForName(collTypeName); final String varName; // -- determine if an alternative collection must be used for code generation final boolean isPrivate = java.lang.reflect.Modifier.isPrivate(collType.getModifiers()); if(isPrivate || ! hasDefaultConstructor(collType)) { if(Set.class.isAssignableFrom(collType)) { collTypeName = HashSet.class.getName(); } else if (List.class.isAssignableFrom(collType)) { collTypeName = ArrayList.class.getName(); } else if(Queue.class.isAssignableFrom(collType)) { collTypeName = ArrayDeque.class.getName(); } else { throw new RuntimeException("Collection " + collType + " is not supported"); } } // -- create code for instantiating collection varName = this.createNewVarName(oid, collTypeName); final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment(); final SimpleName varNameExpr = ast.newSimpleName(varName); vd.setName(varNameExpr); final ClassInstanceCreation ci = ast.newClassInstanceCreation(); ci.setType(this.createAstType(collTypeName, ast)); vd.setInitializer(ci); final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd); stmt.setType(this.createAstType(collTypeName, ast)); methodBlock.statements().add(stmt); // --- create code for filling the collection Integer paramOID; MethodInvocation mi; for(int i = 0; i < params.length; i++) { mi = ast.newMethodInvocation(); mi.setName(ast.newSimpleName("add")); paramOID = (Integer) params[i]; if(paramOID == null) { mi.arguments().add(ast.newNullLiteral()); } else { mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(paramOID))); } methodBlock.statements().add(mi); } } private boolean hasDefaultConstructor(final Class<?> clazz) { for(final Constructor<?> c : clazz.getConstructors()) { if(c.getParameterTypes().length == 0) { return true; } } return false; } @Override public void createMapInitStmt(final CaptureLog log, final int logRecNo) { final int oid = log.objectIds.get(logRecNo); final Object[] params = log.params.get(logRecNo); String collTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid)); final Class<?> collType = getClassForName(collTypeName); final String varName; // -- determine if an alternative collection must be used for code generation final boolean isPrivate = java.lang.reflect.Modifier.isPrivate(collType.getModifiers()); if(isPrivate || ! hasDefaultConstructor(collType)) { collTypeName = HashMap.class.getName(); } // -- create code for instantiating collection varName = this.createNewVarName(oid, collTypeName); final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment(); final SimpleName varNameExpr = ast.newSimpleName(varName); vd.setName(varNameExpr); final ClassInstanceCreation ci = ast.newClassInstanceCreation(); ci.setType(this.createAstType(collTypeName, ast)); vd.setInitializer(ci); final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd); stmt.setType(this.createAstType(collTypeName, ast)); methodBlock.statements().add(stmt); // --- create code for filling the collection Integer valueOID; Integer keyOID; MethodInvocation mi; for(int i = 0; i + 1< params.length; i+=2) { mi = ast.newMethodInvocation(); mi.setName(ast.newSimpleName("put")); keyOID = (Integer) params[i]; mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(keyOID))); valueOID = (Integer) params[i + 1]; if(valueOID == null) { mi.arguments().add(ast.newNullLiteral()); } else { mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(valueOID))); } methodBlock.statements().add(mi); } } @SuppressWarnings({ "rawtypes", "unchecked" }) private void createSetFieldMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast) { //-- 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<?> 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 = type.replace("[]", ""); final Class<?> baseClass = getClassForName(type); if(baseClass.isPrimitive()) { if(int.class.equals(baseClass)) { return int[].class; } else if(byte.class.equals(baseClass)) { return byte[].class; } else if(char.class.equals(baseClass)) { return char[].class; } else if(double.class.equals(baseClass)) { return double[].class; } else if(boolean.class.equals(baseClass)) { return boolean[].class; } else if(float.class.equals(baseClass)) { return float[].class; } else if(short.class.equals(baseClass)) { return short[].class; } else { return long[].class; } } else if(baseClass.isArray()) { return Class.forName("[" + type); } else { return Class.forName("[L" + type + ";"); } } else { return Class.forName(Utils.getClassNameFromResourcePath(type)); } } catch (final ClassNotFoundException e) { throw new RuntimeException(e); } } }