/*
* 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.MJIEnv;
import gov.nasa.jpf.vm.MethodInfo;
import gov.nasa.jpf.vm.NativeMethodInfo;
import gov.nasa.jpf.vm.Types;
import org.apache.bcel.Constants;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
/**
* Creates the body of the methods within the native peer class using the Byte
* Code Engineering Library (BCEL).
*
* @author Nastaran Shafiei
* @author Franck van Breugel
*/
public class PeerMethodGen {
private InstructionList il;
private NativeMethodInfo mi;
private MethodGen nativeMth;
private String name;
private PeerClassGen peerClassGen;
private static final int methodAcc = Constants.ACC_PUBLIC | Constants.ACC_STATIC;
private static final String conversionPkg = "nhandler.conversion";
private static final String jpf2jvmConverter = conversionPkg + ".jpf2jvm.JPF2JVMConverter";
private static final String jvm2jpfConverter = conversionPkg + ".jvm2jpf.JVM2JPFConverter";
private static final String mjiEnvCls = "gov.nasa.jpf.vm.MJIEnv";
private static final Type mjiEnvType = new ObjectType(mjiEnvCls);
private PeerSourceGen.MethodGen sourceGen;
MJIEnv env;
public static boolean updateJPFState;
/**
* Creates a new instance of the PeerMethodCreator class.
*
* @param mi
* an object that represents a native method in JPF
*
* @param pcg
* a bytecode generator for to the class of the given method
*
* @param psg
* a source code generator for the class of the given method
*/
public PeerMethodGen (NativeMethodInfo mi, MJIEnv env, PeerClassGen pcg, PeerSourceGen psg) {
this.peerClassGen = pcg;
this.il = new InstructionList();
this.mi = mi;
this.name = getJNIName(mi);
this.env = env;
Type returnType = PeerMethodGen.getType(mi.getReturnTypeName());
Type[] argsType = PeerMethodGen.getArgumentsType(mi);
this.nativeMth = new MethodGen(methodAcc, (returnType.equals(Type.OBJECT)) ? Type.INT : returnType, argsType, PeerMethodGen.getArgumentsName(mi), name, PeerClassGen.getNativePeerClsName(mi.getClassName()), il, peerClassGen._cp);
if(genSource()) {
this.sourceGen = psg.new MethodGen(mi);
this.sourceGen.printMethodHeader(returnType, name, argsType);
}
}
private boolean genSource() {
return(PeerSourceGen.genSource);
}
/**
* Creates bytecode for the body of the method, and adds it to the peer class
* of this method.
*/
public void create() {
if(mi.isClinit()) {
this.createClinit();
} else if(mi.isCtor()) {
this.createCtor();
} else {
this.createMethod();
}
if(genSource()) {
sourceGen.wrapUpSource();
}
this.nativeMth.setMaxStack();
this.nativeMth.setMaxLocals();
peerClassGen._cg.addMethod(this.nativeMth.getMethod());
this.il.dispose();
}
private void createMethod (){
this.addException();
this.createResetConverterBase();
int caller = this.createCaller();
int argValue = this.createArgValue();
int argType = this.createArgType(argValue);
int callerClass = this.getCallerClass(caller);
int method = this.getMethod(callerClass, argType);
this.setAccessible(method);
int returnValue = this.invokeMethod(caller, method, argValue);
int jpfReturnValue = -1;
if (!mi.getReturnTypeName().equals("void")){
if (!PeerMethodGen.isPrimitiveType(this.mi.getReturnTypeName())){
// If the method is not of type void, converts returnValue to a JPF
// object
jpfReturnValue = this.convertJVM2JPF(returnValue);
} else {
jpfReturnValue = returnValue;
}
}
if(updateJPFState) {
if (mi.isStatic())
this.updateJPFClass(callerClass);
else
this.updateJPFObj(caller, 1);
this.updateJPFArguments(argValue);
}
this.addReturnStatement(jpfReturnValue);
}
private void createCtor (){
this.addException();
this.createResetConverterBase();
int argValue = this.createArgValue();
int argType = this.createArgType(argValue);
int callerClass = this.getCallerClass(MJIEnv.NULL);
int ctor = this.getConstructor(callerClass, argType);
this.setAccessible(ctor);
int returnValue = this.createNewInstance(ctor, argValue);
this.updateJPFObj(returnValue, 1);
this.updateJPFArguments(argValue);
this.addReturnStatement(MJIEnv.NULL);
}
private void createClinit (){
this.addException();
this.createResetConverterBase();
int caller = this.createCaller();
int callerClass = this.getCallerClass(caller);
this.updateJPFClass(callerClass);
// just set it to some dummy value, since the method is of type of void
this.addReturnStatement(MJIEnv.NULL);
}
/**
* Creates bytecode for the empty method, and adds it to the peer class
* of this method.
*/
public void createEmpty (){
if(genSource()) {
sourceGen.completeHeader();
}
this.addDummyReturnStatement();
this.nativeMth.setMaxStack();
this.nativeMth.setMaxLocals();
peerClassGen._cg.addMethod(this.nativeMth.getMethod());
this.il.dispose();
if(genSource()) {
sourceGen.wrapUpSource();
}
}
/**
* Adds bytecode to the body of the method for exceptions that are possibly
* thrown by this method. Adds "throws SecurityException,
* NoSuchMethodException, IllegalAccessException" to the declaration of the
* method.
*/
private void addException (){
this.nativeMth.addException("java.lang.IllegalArgumentException");
this.nativeMth.addException("java.lang.SecurityException");
this.nativeMth.addException("java.lang.NoSuchMethodException");
this.nativeMth.addException("java.lang.IllegalAccessException");
this.nativeMth.addException("java.lang.ClassNotFoundException");
this.nativeMth.addException(conversionPkg + ".ConversionException");
this.nativeMth.addException("java.lang.ClassNotFoundException");
this.nativeMth.addException("java.lang.reflect.InvocationTargetException");
if(mi.isCtor()) {
this.nativeMth.addException("java.lang.InstantiationException");
}
if(genSource()) {
sourceGen.printThrowsExceptions(mi.isCtor());
}
}
/**
* Adds bytecode to the body of the method that resets the Converter Class.
* Adds "Converter.reset(env)" to the body of the method.
*/
private void createResetConverterBase (){
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
il.append(peerClassGen._factory.createInvoke(conversionPkg + ".ConverterBase", "reset", Type.VOID, new Type[] { new ObjectType("gov.nasa.jpf.vm.MJIEnv") }, Constants.INVOKESTATIC));
if(genSource()) {
this.sourceGen.printConvertorPart();
}
}
/**
* For the non-static method, adds bytecode to the body of the method that
* creates a new instance of the object invoking the native method. For static
* method, adds bytecode to the body of the method that creates an instance of
* the Class class representing the class invoking the native method.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @return an index of the local variable that represents the caller object
* (non-static method) or class (static method)
*/
private int createCaller (){
LocalVariableGen lg;
this.il.append(InstructionFactory.createLoad(Type.INT, 1));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
Type[] types = { Type.INT, mjiEnvType };
if (this.mi.isStatic()){
this.il.append(peerClassGen._factory.createInvoke(jpf2jvmConverter, "obtainJVMCls", new ObjectType("java.lang.Class"), types, Constants.INVOKESTATIC));
lg = this.nativeMth.addLocalVariable("caller", new ObjectType("java.lang.Class"), null, null);
} else{
this.il.append(peerClassGen._factory.createInvoke(jpf2jvmConverter, "obtainJVMObj", Type.OBJECT, types, Constants.INVOKESTATIC));
lg = this.nativeMth.addLocalVariable("caller", Type.OBJECT, null, null);
}
int caller = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, caller));
if(genSource()) {
this.sourceGen.printCallerPart();
}
return caller;
}
/**
* Adds bytecode to the body of the method that creates an array of type
* Object including the arguments values of the method.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @return an index of the local variable which is an array of type Object
* including the arguments values of the method
*/
private int createArgValue (){
String[] argTypes = this.mi.getArgumentTypeNames();
int nArgs = argTypes.length;
/** Create an array of objects (Object[] args = new Objects[nArgs]) */
this.il.append(new PUSH(peerClassGen._cp, nArgs));
this.il.append(peerClassGen._factory.createNewArray(Type.OBJECT, (short) 1));
LocalVariableGen lg = this.nativeMth.addLocalVariable("argValue", new ArrayType(Type.OBJECT, 1), null, null);
int argValue = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, argValue));
if(genSource()) {
this.sourceGen.printCreateArgsVal(nArgs);
}
/** Setting args elements to the arguments of the native method */
// To skip the first and second arguements (MJIEnv & objRef/clsRef)
int j = 2;
for (int i = 0; i < nArgs; i++){
// Loading the array element args[i];
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argValue));
this.il.append(new PUSH(peerClassGen._cp, i));
// if the current argument representing an object
if (!PeerMethodGen.isPrimitiveType(argTypes[i])){
Type[] types = { Type.INT, mjiEnvType };
this.il.append(InstructionFactory.createLoad(Type.INT, j));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
this.il.append(peerClassGen._factory.createInvoke(jpf2jvmConverter, "obtainJVMObj", Type.OBJECT, types, Constants.INVOKESTATIC));
j++;
if(genSource()) {
this.sourceGen.printSetObjArgVal(i);
}
} else {
String wrapperName = null;
// if the current argument representing a primitive type we create the
// corresponding wrapper class
if ("boolean".equals(argTypes[i])){
wrapperName = "Boolean";
this.il.append(peerClassGen._factory.createNew("java.lang.Boolean"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.BOOLEAN, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Boolean", "<init>", Type.VOID, new Type[] { Type.BOOLEAN }, Constants.INVOKESPECIAL));
j++;
} else if ("int".equals(argTypes[i])){
wrapperName = "Integer";
this.il.append(peerClassGen._factory.createNew("java.lang.Integer"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.INT, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Integer", "<init>", Type.VOID, new Type[] { Type.INT }, Constants.INVOKESPECIAL));
j++;
} else if ("long".equals(argTypes[i])){
wrapperName = "Long";
this.il.append(peerClassGen._factory.createNew("java.lang.Long"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.LONG, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Long", "<init>", Type.VOID, new Type[] { Type.LONG }, Constants.INVOKESPECIAL));
j += 2;
} else if ("byte".equals(argTypes[i])){
wrapperName = "Byte";
this.il.append(peerClassGen._factory.createNew("java.lang.Byte"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.BYTE, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Byte", "<init>", Type.VOID, new Type[] { Type.BYTE }, Constants.INVOKESPECIAL));
j++;
} else if ("char".equals(argTypes[i])){
wrapperName = "Character";
this.il.append(peerClassGen._factory.createNew("java.lang.Character"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.CHAR, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Character", "<init>", Type.VOID, new Type[] { Type.CHAR }, Constants.INVOKESPECIAL));
j++;
} else if ("short".equals(argTypes[i])){
wrapperName = "Short";
this.il.append(peerClassGen._factory.createNew("java.lang.Short"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.SHORT, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Short", "<init>", Type.VOID, new Type[] { Type.SHORT }, Constants.INVOKESPECIAL));
j++;
} else if ("float".equals(argTypes[i])){
wrapperName = "Float";
this.il.append(peerClassGen._factory.createNew("java.lang.Float"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.FLOAT, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Float", "<init>", Type.VOID, new Type[] { Type.FLOAT }, Constants.INVOKESPECIAL));
j++;
} else if ("double".equals(argTypes[i])){
wrapperName = "Double";
this.il.append(peerClassGen._factory.createNew("java.lang.Double"));
this.il.append(InstructionConstants.DUP);
this.il.append(InstructionFactory.createLoad(Type.DOUBLE, j));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Double", "<init>", Type.VOID, new Type[] { Type.DOUBLE }, Constants.INVOKESPECIAL));
j += 2;
}
if(genSource()) {
sourceGen.printSetPrimitiveArgVal(wrapperName, i);
}
}
this.il.append(InstructionConstants.AASTORE);
}
return argValue;
}
/**
* Adds bytecode to the body of the method that creates an array of type
* Class<?> including the type of the arguments of the method
*
* @param argValue
* an index of the local variable which is an array of type Object
* including the arguments of the method
*
* @return an index of the local variable which is an array of type Class<?>
* including the type of the arguments of the method
*/
private int createArgType (int argValue){
String[] argTypes = this.mi.getArgumentTypeNames();
int nArgs = argTypes.length;
/** Create an array of Class<?> (Class<?>[] argType = new Class<?>[nArgs]) */
this.il.append(new PUSH(peerClassGen._cp, nArgs));
this.il.append(peerClassGen._factory.createNewArray(new ObjectType("java.lang.Class"), (short) 1));
LocalVariableGen lg = this.nativeMth.addLocalVariable("argType", new ArrayType(new ObjectType("java.lang.Class"), 1), null, null);
int argType = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, argType));
if(genSource()) {
sourceGen.printCreateArgsType(nArgs);
}
for (int i = 0; i < nArgs; i++){
// loading the element argType[i]
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argType));
this.il.append(new PUSH(peerClassGen._cp, i));
if (!PeerMethodGen.isPrimitiveType(argTypes[i])){
if(argTypes[i].contains("[]")) {
il.append(InstructionFactory.createLoad(Type.OBJECT, argValue));
il.append(new PUSH(peerClassGen._cp, i));
il.append(InstructionConstants.AALOAD);
il.append(peerClassGen._factory.createInvoke("java.lang.Object", "getClass", new ObjectType("java.lang.Class"), Type.NO_ARGS, Constants.INVOKEVIRTUAL));
if(genSource()) {
sourceGen.printSetArrArgType(i);
}
} else{
il.append(new PUSH(peerClassGen._cp, argTypes[i]));
il.append(peerClassGen._factory.createInvoke("java.lang.Class", "forName", new ObjectType("java.lang.Class"), new Type[] { Type.STRING }, Constants.INVOKESTATIC));
if(genSource()) {
sourceGen.printSetObjArgType(argTypes[i], i);
}
}
} else{
String wrapperType = getWrapperType(argTypes[i]);
il.append(peerClassGen._factory.createFieldAccess( wrapperType, "TYPE", new ObjectType("java.lang.Class"), Constants.GETSTATIC));
if(genSource()) {
String wrapperName = wrapperType.substring(wrapperType.lastIndexOf('.') + 1);
sourceGen.printSetPrimitiveArgType(wrapperName, i);
}
}
this.il.append(InstructionConstants.AASTORE);
}
return argType;
}
private String getWrapperType(String primitiveType) {
String wrapperType = null;
if ("boolean".equals(primitiveType))
wrapperType = "java.lang.Boolean";
else if ("int".equals(primitiveType))
wrapperType = "java.lang.Integer";
else if ("long".equals(primitiveType))
wrapperType = "java.lang.Long";
else if ("byte".equals(primitiveType))
wrapperType = "java.lang.Byte";
else if ("char".equals(primitiveType))
wrapperType = "java.lang.Character";
else if ("short".equals(primitiveType))
wrapperType = "java.lang.Short";
else if ("float".equals(primitiveType))
wrapperType = "java.lang.Float";
else if ("double".equals(primitiveType))
wrapperType = "java.lang.Double";
return wrapperType;
}
/**
* Adds bytecode to the body of the method that gets the class of the native
* method. For the static method, "caller" that has been already obtained from
* createCaller is what we want. But for non-static method, we need to get the
* class of object invoking the method (Class<?> callerClass =
* caller.getClass())
*
* @param caller
* an index of the local variable that represents the caller object
* (non-static method) or class (static method)
*
* @return an index of the local variable that represents the caller class
* (static method) or the class of the caller object (non-static
* method)
*/
private int getCallerClass (int caller){
int callerClass;
if (this.mi.isStatic())
callerClass = caller;
else{
// the class of the last invoked method
String className = env.getVM().getCurrentThread().getTopFrameMethodInfo().getClassName();
this.il.append(new PUSH(peerClassGen._cp, className));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Class", "forName", new ObjectType("java.lang.Class"), new Type[] { Type.STRING }, Constants.INVOKESTATIC));
LocalVariableGen lg = this.nativeMth.addLocalVariable("callerClass", new ObjectType("java.lang.Class"), null, null);
callerClass = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, callerClass));
if(genSource()) {
sourceGen.printGetCallerClass(className);
}
}
return callerClass;
}
/**
* Adds bytecode to the body of the method that uses reflection to get the
* method from the class
*
* @param callerClass
* an index of the local variable that represents the caller class
* (static method) or the class of the caller object (non-static
* method)
* @param argType
* an index of the local variable which is an array of type Class<?>
* including the type of the arguments of the method
*
* @return an index of the local variable that represents the Method instance
* representing this native method
*/
private int getMethod (int callerClass, int argType){
String name = this.mi.getName();
this.il.append(InstructionFactory.createLoad(Type.OBJECT, callerClass));
this.il.append(new PUSH(peerClassGen._cp, name));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argType));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Class", "getDeclaredMethod", new ObjectType("java.lang.reflect.Method"), new Type[] { Type.STRING, new ArrayType(new ObjectType("java.lang.Class"), 1) }, Constants.INVOKEVIRTUAL));
LocalVariableGen lg = this.nativeMth.addLocalVariable("method", new ObjectType("java.lang.reflect.Method"), null, null);
int method = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, method));
if(genSource()) {
sourceGen.printGetMethod(name, mi.isStatic());
}
return method;
}
private int getConstructor (int callerClass, int argType){
String name = this.mi.getName();
this.il.append(InstructionFactory.createLoad(Type.OBJECT, callerClass));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argType));
this.il.append(peerClassGen._factory.createInvoke("java.lang.Class", "getDeclaredConstructor", new ObjectType("java.lang.reflect.Constructor"), new Type[] { new ArrayType(new ObjectType("java.lang.Class"), 1) }, Constants.INVOKEVIRTUAL));
LocalVariableGen lg = this.nativeMth.addLocalVariable("ctor", new ObjectType("java.lang.reflect.Constructor"), null, null);
int method = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, method));
if(genSource()) {
sourceGen.printGetCtor(name);
}
return method;
}
/**
* Adds bytecode to the body of the method that provides access to a private
* method.
*
* @param method
* an index of the local variable that represents the Method instance
* representing this native method
*/
private void setAccessible (int method){
this.il.append(InstructionFactory.createLoad(Type.OBJECT, method));
this.il.append(new PUSH(peerClassGen._cp, 1));
String type = mi.isCtor() ? "java.lang.reflect.Constructor": "java.lang.reflect.Method";
this.il.append(peerClassGen._factory.createInvoke(type, "setAccessible", Type.VOID, new Type[] { Type.BOOLEAN }, Constants.INVOKEVIRTUAL));
if(genSource()) {
sourceGen.printSetAccessible(mi.isCtor());
}
}
/**
* Adds bytecode to the body of the method that uses reflection to invoke the
* method. For the static method, adds method.invoke(null, argValue); For the
* non-static method, adds method.invoke(caller, argValue);
*
* @param caller
* an index of the local variable that represents the caller object
* (non-static method) or class (static method)
* @param method
* an index of the local variable that represents the Method instance
* representing this native method
* @param argValue
* an index of the local variable which is an array of type Object
* including the arguments of the method
*
* @return an index of the local variable that represents the return value of
* the method
*/
private int invokeMethod (int caller, int method, int argValue){
int returnValue = -1;
this.il.append(InstructionFactory.createLoad(Type.OBJECT, method));
if (this.mi.isStatic())
// loading the value NULL
this.il.append(InstructionConstants.ACONST_NULL);
else
// loading the caller object
this.il.append(InstructionFactory.createLoad(Type.OBJECT, caller));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argValue));
this.il.append(peerClassGen._factory.createInvoke("java.lang.reflect.Method", "invoke", Type.OBJECT, new Type[] { Type.OBJECT, new ArrayType(Type.OBJECT, 1) }, Constants.INVOKEVIRTUAL));
if (!mi.getReturnTypeName().equals("void")){
LocalVariableGen lg = this.nativeMth.addLocalVariable("returnValue", Type.OBJECT, null, null);
returnValue = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, returnValue));
} else{
this.il.append(InstructionConstants.POP);
}
if(genSource()) {
sourceGen.printInvokeMethod(mi.isStatic());
}
return returnValue;
}
private int createNewInstance (int method, int argValue){
int returnValue = -1;
this.il.append(InstructionFactory.createLoad(Type.OBJECT, method));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argValue));
this.il.append(peerClassGen._factory.createInvoke("java.lang.reflect.Constructor", "newInstance", Type.OBJECT, new Type[] { new ArrayType(Type.OBJECT, 1) }, Constants.INVOKEVIRTUAL));
LocalVariableGen lg = this.nativeMth.addLocalVariable("returnValue", Type.OBJECT, null, null);
returnValue = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.OBJECT, returnValue));
if(genSource()) {
sourceGen.creatNewInstance();
}
return returnValue;
}
/**
* Adds bytecode to the body of the method that converts a given JVM object to
* a JPF object.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @param JVMObj
* an index of the local variable that represents a JVM object
*
* @return an index of the local variable that represents the JPF object
* corresponding to the given JVM object
*/
private int convertJVM2JPF (int JVMObj){
Type[] types = { Type.OBJECT, mjiEnvType };
this.il.append(InstructionFactory.createLoad(Type.OBJECT, JVMObj));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
this.il.append(peerClassGen._factory.createInvoke(jvm2jpfConverter, "obtainJPFObj", Type.INT, types, Constants.INVOKESTATIC));
LocalVariableGen lg = this.nativeMth.addLocalVariable("JPFObj", Type.INT, null, null);
int JPFObj = lg.getIndex();
this.il.append(InstructionFactory.createStore(Type.INT, JPFObj));
if(genSource()) {
sourceGen.printConvertReturnValue();
}
return JPFObj;
}
/**
* Adds bytecode to the body of the method that updates a JPF object according
* to the given JVM object.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @param JVMObj
* an index of the local variable that represents a JVM object
*
* @param JPFObj
* an index of the local variable that represents a JPF object
*/
private void updateJPFObj (int JVMObj, int JPFObj){
this.il.append(InstructionFactory.createLoad(Type.OBJECT, JVMObj));
this.il.append(InstructionFactory.createLoad(Type.INT, JPFObj));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
Type[] types = { Type.OBJECT, Type.INT, mjiEnvType };
this.il.append(peerClassGen._factory.createInvoke(jvm2jpfConverter, "updateJPFObj", Type.VOID, types, Constants.INVOKESTATIC));
if(genSource()) {
if(!mi.isCtor()) {
sourceGen.printUpdateCaller(mi.isStatic());
} else {
sourceGen.printUpdateCtorCaller();
}
}
}
/**
* Adds bytecode to the body of the method that updates a JPF class according
* to the given JVM object.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @param JPFCls
* an index of the local variable that represents a JPF class
*/
private void updateJPFClass (int JVMCls){
this.il.append(InstructionFactory.createLoad(Type.OBJECT, JVMCls));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
Type[] types = { new ObjectType("java.lang.Class"), mjiEnvType};
this.il.append(peerClassGen._factory.createInvoke(jvm2jpfConverter, "obtainJPFCls", new ObjectType("gov.nasa.jpf.vm.ClassInfo"), types, Constants.INVOKESTATIC));
this.il.append(InstructionConstants.POP);
if(genSource()) {
sourceGen.printUpdateCaller(mi.isStatic());
}
}
/**
* Adds bytecode to the body of the method that updates the arguments of the
* JPF method according to the given JVM values.
*
* @param converter
* an index of the local variable that represents the Converter
* object
*
* @param argValue
* an index of the local variable that represents a JVM array that
* holds the value of input parameters
*/
private void updateJPFArguments (int argValue){
String[] type = mi.getArgumentTypeNames();
int nArgs = type.length;
int j = 2;
for (int i = 0; i < nArgs; i++){
if (!PeerMethodGen.isPrimitiveType(type[i])){
// Loading the array element argsValue[i];
this.il.append(InstructionFactory.createLoad(Type.OBJECT, argValue));
this.il.append(new PUSH(peerClassGen._cp, i));
this.il.append(InstructionConstants.AALOAD);
// Loading the nth input parameter
this.il.append(InstructionFactory.createLoad(Type.INT, j));
this.il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
Type[] types = { Type.OBJECT, Type.INT, mjiEnvType };
// Invoking the method "updateJPFObj"
this.il.append(peerClassGen._factory.createInvoke(jvm2jpfConverter, "updateJPFObj", Type.VOID, types, Constants.INVOKESTATIC));
j++;
if(genSource()) {
sourceGen.printUpdateJPFArgs(i, nArgs);
}
} else if (type[i].equals("long") || type[i].equals("double")) {
j += 2;
} else {
j++;
}
}
}
/**
* Adds bytecode to the body of the method for return statement
*
* @param returnValue
* an index of the local variable that represents the return value of
* the method
*/
private void addReturnStatement (int returnValue){
String returnType = this.mi.getReturnTypeName();
// if the return type is Object
if (!PeerMethodGen.isPrimitiveType(returnType)){
this.il.append(InstructionFactory.createLoad(Type.INT, returnValue));
this.il.append(InstructionFactory.createReturn(Type.INT));
if(genSource()) {
sourceGen.printReturnObj();
}
} else if ("void".equals(returnType)){
this.il.append(InstructionFactory.createReturn(Type.VOID));
if(genSource()) {
sourceGen.printReturn();
}
} else{
String wrapper = null;
String methodName = null;
Type type = null;
if ("boolean".equals(returnType)){
wrapper = "Boolean";
methodName = "booleanValue";
type = Type.BOOLEAN;
} else if ("int".equals(returnType)){
wrapper = "Integer";
methodName = "intValue";
type = Type.INT;
} else if ("long".equals(returnType)){
wrapper = "Long";
methodName = "longValue";
type = Type.LONG;
} else if ("byte".equals(returnType)){
wrapper = "Byte";
methodName = "byteValue";
type = Type.BYTE;
} else if ("char".equals(returnType)){
wrapper = "Character";
methodName = "charValue";
type = Type.CHAR;
} else if ("short".equals(returnType)){
wrapper = "Short";
methodName = "shortValue";
type = Type.SHORT;
} else if ("float".equals(returnType)){
wrapper = "Float";
methodName = "floatValue";
type = Type.FLOAT;
} else if ("double".equals(returnType)){
wrapper = "Double";
methodName = "doubleValue";
type = Type.DOUBLE;
}
String className = "java.lang." + wrapper;
this.il.append(InstructionFactory.createLoad(Type.OBJECT, returnValue));
this.il.append(peerClassGen._factory.createCheckCast(new ObjectType(className)));
this.il.append(peerClassGen._factory.createInvoke(className, methodName, type, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
this.il.append(InstructionFactory.createReturn(type));
if(genSource()) {
sourceGen.printReturnPrimitive(wrapper, methodName);
}
}
}
/**
* Adds bytecode to the body of the method for a return statement that returns
* a dummy value
*
* @param returnValue
* an index of the local variable that represents the return value of
* the method
*/
private void addDummyReturnStatement (){
String returnType = this.mi.getReturnTypeName();
// if the return type is Object
if (!PeerMethodGen.isPrimitiveType(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, MJIEnv.NULL));
this.il.append(InstructionFactory.createReturn(Type.INT));
} else if ("void".equals(returnType)) {
this.il.append(InstructionFactory.createReturn(Type.VOID));
} else {
if ("boolean".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.BOOLEAN));
} else if ("int".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.INT));
} else if ("long".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.LONG));
} else if ("byte".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.BYTE));
} else if ("char".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.CHAR));
} else if ("short".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0));
this.il.append(InstructionFactory.createReturn(Type.SHORT));
} else if ("float".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0.0));
this.il.append(InstructionFactory.createReturn(Type.FLOAT));
} else if ("double".equals(returnType)) {
this.il.append(new PUSH(peerClassGen._cp, 0.0));
this.il.append(InstructionFactory.createReturn(Type.DOUBLE));
}
}
sourceGen.printDummyReturnStatement();
}
/**
* Returns the type corresponding to the given string.
*
* @param typeName
* a string that stores a type name
*
* @return the type corresponding to the given string
*/
private static Type getType (String typeName){
Type returnType = null;
if ("int".equals(typeName)) {
returnType = Type.INT;
} else if ("long".equals(typeName)) {
returnType = Type.LONG;
} else if ("boolean".equals(typeName)) {
returnType = Type.BOOLEAN;
} else if ("void".equals(typeName)) {
returnType = Type.VOID;
} else if ("byte".equals(typeName)) {
returnType = Type.BYTE;
} else if ("char".equals(typeName)) {
returnType = Type.CHAR;
} else if ("short".equals(typeName)) {
returnType = Type.SHORT;
} else if ("float".equals(typeName)) {
returnType = Type.FLOAT;
} else if ("double".equals(typeName)) {
returnType = Type.DOUBLE;
}
// The type should be a type of an object
else {
returnType = Type.OBJECT;
}
return returnType;
}
/**
* Checks if the given string represents a primitive type.
*
* @param t
* a string that stores a type name
*
* @return true of the given string stores a primitive type. OW, it returns
* false.
*/
protected static boolean isPrimitiveType (String t){
return ("int".equals(t) || "long".equals(t) || "boolean".equals(t) || "void".equals(t) || "byte".equals(t) || "char".equals(t) || "short".equals(t) || "float".equals(t) || "double".equals(t));
}
/**
* Creates an array of type Type including the type of the arguments of the
* method.
*
* @param mi
* an object that represents a method in JPF
*
* @return an array of type Type including the type of the arguments of the
* method
*/
private static Type[] getArgumentsType (NativeMethodInfo mi){
Type[] argTypes = new Type[mi.getNumberOfArguments() + 2];
argTypes[0] = new ObjectType(PeerClassGen.MJIEnvCls);
argTypes[1] = Type.INT;
String[] argTypesName = mi.getArgumentTypeNames();
for (int i = 2; i < mi.getNumberOfArguments() + 2; i++){
Type type = PeerMethodGen.getType(argTypesName[i - 2]);
argTypes[i] = (type == Type.OBJECT) ? Type.INT : type;
}
return argTypes;
}
/**
* Creates an array of type String including the arguments names of the
* method.
*
* @param mi
* an object that represents a method in JPF
*
* @return an array of type String including the arguments names of the method
*/
private static String[] getArgumentsName (NativeMethodInfo mi){
String[] argName = new String[mi.getNumberOfArguments() + 2];
argName[0] = "env";
if (mi.isStatic()) {
argName[1] = "rcls";
} else {
argName[1] = "robj";
}
for (int i = 2; i < mi.getNumberOfArguments() + 2; i++) {
argName[i] = "arg" + (i - 2);
}
return argName;
}
protected static String getJNIName(MethodInfo mi) {
String mname = mi.getName();
if(mname.startsWith("<") && mname.endsWith(">")) {
mname = "$" + mname.substring(1, mname.lastIndexOf(">"));
}
return (Types.getJNIMangledMethodName(null, mname, mi.getSignature()));
}
}