/*
* Copyright (C) 2013 Nastaran Shafiei and Franck van Breugel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You can find a copy of the GNU General Public License at
* <http://www.gnu.org/licenses/>.
*/
package nhandler.peerGen;
import gov.nasa.jpf.vm.MethodInfo;
import java.io.*;
import org.apache.bcel.generic.Type;
/**
* Creates the source code for the clases that are created on the fly. To
* make this feature work, the property nhandler.genSource has to be set
* to true.
*
* @author Nastaran Shafiei
* @author Franck van Breugel
*/
public class PeerSourceGen {
private File file;
private String name;
private String path;
private StringBuilder content;
protected static boolean addComment = false;
protected static boolean genSource = false;
protected PeerSourceGen (String name) throws IOException {
this.name = name;
this.path = PeerClassGen.peersLocation + name + ".java";
this.file = new File(this.path);
setContent();
}
protected File setContent () throws IOException{
content = new StringBuilder();
if (!this.file.exists()){
this.file.createNewFile();
System.out.println("Create Peer File " + file);
generateMinimalPeerSource();
} else{
loadContent();
}
return this.file;
}
private void generateMinimalPeerSource () throws FileNotFoundException{
printImport();
printClassHeader();
printDefaultConstructor();
printClassFooter();
}
private void printImport (){
append("import gov.nasa.jpf.vm.NativePeer;");
gotoNextLine();
append("import gov.nasa.jpf.vm.MJIEnv;");
gotoNextLine();
append("import ConversionException;");
gotoNextLine();
append("import ConverterBase;");
gotoNextLine();
append("import JVM2JPFConverter;");
gotoNextLine();
append("import JPF2JVMConverter;");
gotoNextLine();
append("import java.lang.reflect.InvocationTargetException;");
gotoNextLine();
append("import java.lang.reflect.Method;");
gotoNextLine();
append("import java.lang.reflect.Constructor;");
addBlankLine();
}
private void printClassHeader (){
append("public class " + this.name);
append(" extends NativePeer");
append(" {");
addBlankLine();
}
private void printDefaultConstructor (){
append(" public " + this.name + "()");
append(" {");
gotoNextLine();
append(" }");
addBlankLine();
}
protected void printClassFooter (){
append("}");
}
private void append (String s){
content.append(s);
}
private void loadContent (){
BufferedReader reader = null;
try{
reader = new BufferedReader(new FileReader(path));
} catch (FileNotFoundException e1){
e1.printStackTrace();
}
String line = null;
String ls = System.getProperty("line.separator");
try{
while ((line = reader.readLine()) != null){
content.append(line);
content.append(ls);
}
} catch (IOException e){
e.printStackTrace();
}
}
protected void removeClassFooter (){
if (content.indexOf("}") > 0) {
content.deleteCharAt(content.lastIndexOf("}"));
}
}
private void addDoubleIndent (){
append(" ");
}
private void addMultipleDoubleIndent (int n){
while(n>0) {
append(" ");
n--;
}
}
private void gotoNextLine (){
append("\n");
}
private void addBlankLine (){
append("\n\n");
}
private void addComment (String comment){
if(addComment) {
addDoubleIndent();
append("// " + comment);
gotoNextLine();
}
}
protected class MethodGen {
private MethodInfo mi;
protected MethodGen (MethodInfo mi) {
this.mi = mi;
}
protected void printMethodHeader (Type returnType, String name, Type[] argsType){
removeClassFooter();
append(" public static");
append(" " + ((returnType.equals(Type.OBJECT)) ? "int" : returnType.toString()));
append(" " + name);
if (mi.isStatic()){
append(" (MJIEnv env, int rcls");
} else{
append(" (MJIEnv env, int robj");
}
for (int i = 3; i < argsType.length-1; i++){
append(", " + argsType[i].toString());
append(" arg" + (i - 3));
}
// Add "FeatureExpr ctx" to the end of the argument list
append(", FeatureExpr ctx");
append(")");
}
protected void printThrowsExceptions (boolean isCtor){
gotoNextLine();
addMultipleDoubleIndent(2);
append("throws java.lang.IllegalArgumentException,");
gotoNextLine();
addMultipleDoubleIndent(3);
append("SecurityException, NoSuchMethodException, IllegalAccessException,");
gotoNextLine();
addMultipleDoubleIndent(4);
append(" ClassNotFoundException, ConversionException, InvocationTargetException");
if(isCtor) {
append(",");
gotoNextLine();
addMultipleDoubleIndent(5);
append(" InstantiationException");
}
completeHeader();
}
protected void completeHeader (){
append(" {");
gotoNextLine();
}
protected void printConvertorPart (){
gotoNextLine();
addComment("Resest the Converter class used to converting objects/classes between JPF & JVM");
addDoubleIndent();
append("ConverterBase.reset(env);");
addBlankLine();
}
protected void printCallerPart (){
if (mi.isStatic()){
addComment("Captures the class that invokes the static method to be handled in JVM");
addDoubleIndent();
/* Append the ctx */
append("Class<?> caller = JPF2JVMConverter.obtainJVMCls(rcls, env, ctx);");
} else{
addComment("Captures the object that invokes the method to be handled in JVM");
addDoubleIndent();
append("Object caller = JPF2JVMConverter.obtainJVMObj(robj, env);");
}
addBlankLine();
}
protected void printCreateArgsVal (int nArgs){
addComment("Captures the input parameters of the method to be handled in JVM");
addDoubleIndent();
append("Object argValue[] = new Object[" + nArgs + "];");
gotoNextLine();
}
protected void printSetObjArgVal (int index){
addDoubleIndent();
append("argValue[" + index + "] = JPF2JVMConverter.obtainJVMObj(arg" + index + ", env);");
gotoNextLine();
}
protected void printSetPrimitiveArgVal (String wrapper, int index){
addDoubleIndent();
append("argValue[" + index + "] = new " + wrapper + "(arg" + index + ");");
gotoNextLine();
}
protected void printCreateArgsType (int nArgs){
gotoNextLine();
addComment("Captures the input parameters types of the method to be hanlded in JVM");
addDoubleIndent();
append("Class<?> argType[] = new Class[" + nArgs + "];");
gotoNextLine();
}
protected void printSetObjArgType (String type, int index){
addDoubleIndent();
append("argType[" + index + "] = Class.forName(\"" + type + "\");");
gotoNextLine();
}
protected void printSetArrArgType (int index){
addDoubleIndent();
append("argType[" + index + "] = argValue[" + index + "].getClass();");
gotoNextLine();
}
protected void printSetPrimitiveArgType (String wrapper, int index){
addDoubleIndent();
append("argType[" + index + "] = " + wrapper + ".TYPE;");
gotoNextLine();
}
protected void printGetCallerClass (String type){
gotoNextLine();
addComment("Obtains the class of the object that invokes the method to be handled");
addDoubleIndent();
append("Class<?> callerClass = Class.forName(\"" + type + "\");");
gotoNextLine();
}
protected void printGetMethod (String mname, boolean isStatic){
gotoNextLine();
addComment("Obtains the Method instance of the method to be handled");
addDoubleIndent();
if (isStatic){
append("Method method = caller.getDeclaredMethod(\"" + mname + "\", argType);");
} else{
append("Method method = callerClass.getDeclaredMethod(\"" + mname + "\", argType);");
}
addBlankLine();
}
protected void printGetCtor (String mname){
gotoNextLine();
addComment("Obtains the instance of the constructor to be handled");
addDoubleIndent();
append("Constructor ctor = callerClass.getDeclaredConstructor(argType);");
addBlankLine();
}
protected void printSetAccessible (boolean isCtor){
addComment("Makes the method with any access modifier accessible");
addDoubleIndent();
String mname = isCtor? "ctor" : "method";
append(mname + ".setAccessible(true);");
addBlankLine();
}
protected void printInvokeMethod (boolean isStatic){
addComment("Invokes the method to be handled in JVM");
addDoubleIndent();
if (isStatic){
append("Object returnValue = method.invoke(null, argValue);");
} else{
append("Object returnValue = method.invoke(caller, argValue);");
}
addBlankLine();
}
protected void creatNewInstance (){
addComment("Creates an instance by invoking the constructor to be handled in JVM");
addDoubleIndent();
append("Object returnValue = ctor.newInstance(argValue);");
addBlankLine();
}
protected void printConvertReturnValue (){
addComment("Converts the return value from JVM to JPF");
addDoubleIndent();
append("int JPFObj = JVM2JPFConverter.obtainJPFObj(returnValue, env);");
addBlankLine();
}
protected void printUpdateCaller (boolean isStatic){
if (isStatic){
addComment("Updates the class that invokes the method to be handle in JPF");
addDoubleIndent();
/* Append the ctx */
append("JVM2JPFConverter.obtainJPFCls(caller, env, ctx);");
} else{
addComment("Updates the object that invokes the method to be handled in JPF");
addDoubleIndent();
append("JVM2JPFConverter.updateJPFObj(caller, robj, env);");
}
addBlankLine();
addUpdateArgsComment = true;
}
protected void printUpdateCtorCaller (){
addComment("Updates the object that invokes the method to be handled in JPF");
addDoubleIndent();
append("JVM2JPFConverter.updateJPFObj(returnValue, robj, env);");
addBlankLine();
addUpdateArgsComment = true;
}
private boolean addUpdateArgsComment = false;
protected void printUpdateJPFArgs (int index, int nArgs){
if (addUpdateArgsComment){
addComment("Updates the input parameters objects of the method to be handled");
addUpdateArgsComment = false;
}
addDoubleIndent();
append("JVM2JPFConverter.updateJPFObj(argValue[" + index + "], arg" + index + ", env);");
if(index == nArgs-1) {
addBlankLine();
} else {
gotoNextLine();
}
}
protected void printReturn (){
addDoubleIndent();
append("return;");
gotoNextLine();
}
protected void printReturnObj (){
addComment("Returns the return value that is converted to a JPF object");
addDoubleIndent();
append("return JPFObj;");
gotoNextLine();
}
protected void printReturnPrimitive (String wrapper, String methodName){
addDoubleIndent();
append("return ((" + wrapper + ")returnValue)." + methodName + "();");
gotoNextLine();
}
protected void printDummyReturnStatement (){
String returnType = this.mi.getReturnTypeName();
addDoubleIndent();
if (!PeerMethodGen.isPrimitiveType(returnType)){
append("return -1;");
} else if ("void".equals(returnType)){
append("return;");
} else{
if ("boolean".equals(returnType)){
append("return false;");
} else if ("int".equals(returnType)){
append("return 0;");
} else if ("long".equals(returnType)){
append("return 0;");
} else if ("byte".equals(returnType)){
append("return 0;");
} else if ("char".equals(returnType)){
append("return 0;");
} else if ("short".equals(returnType)){
append("return 0;");
} else if ("float".equals(returnType)){
append("return 0.0;");
} else if ("double".equals(returnType)){
append("return 0.0;");
}
}
gotoNextLine();
}
protected void wrapUpSource (){
printFooter();
writeToFile();
}
private void printFooter (){
append(" }");
gotoNextLine();
append("}");
gotoNextLine();
}
private void writeToFile (){
PrintWriter pw = null;
try{
pw = new PrintWriter(file);
} catch (FileNotFoundException e){
e.printStackTrace();
}
pw.append(content);
pw.flush();
}
}
}