//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.vm;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.function.BiFunction;
import cmu.conditional.Conditional;
import cmu.conditional.One;
import de.fosd.typechef.featureexpr.FeatureExpr;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.annotation.MJI;
/**
* MJI NativePeer class for java.lang.Class library abstraction
*/
@SuppressWarnings("deprecation")
public class JPF_java_lang_Class extends NativePeer {
static final String FIELD_CLASSNAME = "java.lang.reflect.Field";
static final String METHOD_CLASSNAME = "java.lang.reflect.Method";
static final String CONSTRUCTOR_CLASSNAME = "java.lang.reflect.Constructor";
public static boolean init(Config conf) {
// we create Method and Constructor objects, so we better make sure these
// classes are initialized (they already might be)
JPF_java_lang_reflect_Method.init(conf);
JPF_java_lang_reflect_Constructor.init(conf);
return true;
}
@MJI
public boolean isArray____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return ci.isArray();
}
@MJI
public int getComponentType____Ljava_lang_Class_2(MJIEnv env, int robj, FeatureExpr ctx) {
if (isArray____Z(env, robj, ctx)) {
ThreadInfo ti = env.getThreadInfo();
// Instruction insn = ti.getPC().getValue();
ClassInfo ci = env.getReferredClassInfo(ctx, robj).getComponentClassInfo();
if (ci.pushRequiredClinits(ctx, ti)) {
env.repeatInvocation();
return MJIEnv.NULL;
}
return ci.getClassObjectRef();
}
return MJIEnv.NULL;
}
@MJI
public boolean isInstance__Ljava_lang_Object_2__Z(MJIEnv env, int robj, int r1, FeatureExpr ctx) {
ElementInfo sei = env.getStaticElementInfo(robj);
ClassInfo ci = sei.getClassInfo();
ClassInfo ciOther = env.getClassInfo(r1);
return (ciOther.isInstanceOf(ci.getName()));
}
@MJI
public boolean isInterface____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return ci.isInterface();
}
@MJI
public Conditional<Boolean> isAssignableFrom__Ljava_lang_Class_2__Z(final MJIEnv env, int rcls, Conditional<Integer> r1, FeatureExpr ctx) {
ElementInfo sei1 = env.getStaticElementInfo(rcls);
final ClassInfo ci1 = sei1.getClassInfo();
return r1.mapf(ctx, new BiFunction<FeatureExpr, Integer, Conditional<Boolean>>() {
@Override
public Conditional<Boolean> apply(FeatureExpr ctx, Integer r1) {
ElementInfo sei2 = env.getStaticElementInfo(r1);
ClassInfo ci2 = sei2.getClassInfo();
return new One<>(ci2.isInstanceOf(ci1.getName()));
}
});
}
@MJI
public int getAnnotations_____3Ljava_lang_annotation_Annotation_2(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
AnnotationInfo[] ai = ci.getAnnotations();
try {
return env.newAnnotationProxies(ctx, ai);
} catch (ClinitRequired x) {
env.handleClinitRequest(ctx, x.getRequiredClassInfo());
return MJIEnv.NULL;
}
}
@MJI
public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2(MJIEnv env, int robj, int annoClsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
ClassInfo aci = env.getReferredClassInfo(ctx, annoClsRef);
AnnotationInfo ai = ci.getAnnotation(aci.getName());
if (ai != null) {
ClassInfo aciProxy = aci.getAnnotationProxy();
try {
return env.newAnnotationProxy(ctx, aciProxy, ai);
} catch (ClinitRequired x) {
env.handleClinitRequest(ctx, x.getRequiredClassInfo());
return MJIEnv.NULL;
}
} else {
return MJIEnv.NULL;
}
}
@MJI
public int getPrimitiveClass__Ljava_lang_String_2__Ljava_lang_Class_2(MJIEnv env, int rcls, Conditional<Integer> stringRef, FeatureExpr ctx) {
// we don't really have to check for a valid class name here, since
// this is a package default method that just gets called from
// the clinit of box classes
// note this does NOT return the box class (e.g. java.lang.Integer), which
// is a normal, functional class, but a primitive class (e.g. 'int') that
// is rather a strange beast (not even Object derived)
ClassLoaderInfo scli = env.getSystemClassLoaderInfo(); // this is the one responsible for builtin classes
String primClsName = env.getStringObject(ctx, stringRef.getValue()); // always initialized
ClassInfo ci = scli.getResolvedClassInfo(ctx, primClsName);
return ci.getClassObjectRef();
}
@MJI
public boolean desiredAssertionStatus____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return ci.desiredAssertionStatus();
}
public static int getClassObject(MJIEnv env, ClassInfo ci, FeatureExpr ctx) {
ThreadInfo ti = env.getThreadInfo();
// Instruction insn = ti.getPC().getValue();
if (ci.pushRequiredClinits(ctx, ti)) {
env.repeatInvocation();
return MJIEnv.NULL;
}
StaticElementInfo ei = ci.getStaticElementInfo();
int ref = ei.getClassObjectRef();
return ref;
}
@MJI
public int forName__Ljava_lang_String_2__Ljava_lang_Class_2(MJIEnv env, int rcls, Conditional<Integer> clsNameRef, FeatureExpr ctx) {
if (clsNameRef.getValue().intValue() == MJIEnv.NULL) {
env.throwException(ctx, "java.lang.NullPointerException", "no class name provided");
return MJIEnv.NULL;
}
String clsName = env.getStringObject(ctx, clsNameRef.getValue());
if (clsName.isEmpty()) {
env.throwException(ctx, "java.lang.ClassNotFoundException", "empty class name");
return MJIEnv.NULL;
}
ThreadInfo ti = env.getThreadInfo();
MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
// class of the method that includes the invocation of Class.forName()
ClassInfo cls = mi.getClassInfo();
String name;
// for array type, the component terminal must be resolved
if (clsName.charAt(0) == '[') {
name = Types.getComponentTerminal(clsName);
} else {
name = clsName;
}
// make the classloader of the class including the invocation of
// Class.forName() resolve the class with the given name
try {
cls.resolveReferencedClass(ctx, name);
} catch (LoadOnJPFRequired lre) {
env.repeatInvocation();
return MJIEnv.NULL;
}
// The class obtained here is the same as the resolved one, except
// if it represents an array type
ClassInfo ci = cls.getClassLoaderInfo().getResolvedClassInfo(ctx, clsName);
return getClassObject(env, ci, ctx);
}
/**
* this is an example of a native method issuing direct calls - otherwise known
* as a round trip.
* We don't have to deal with class init here anymore, since this is called
* via the class object of the class to instantiate
*/
@MJI
public int newInstance____Ljava_lang_Object_2(MJIEnv env, int robj, FeatureExpr ctx) {
ThreadInfo ti = env.getThreadInfo();
DirectCallStackFrame frame = ti.getReturnedDirectCall();
ClassInfo ci = env.getReferredClassInfo(ctx, robj); // what are we
MethodInfo miCtor = ci.getMethod("<init>()V", true); // note there always is one since something needs to call Object()
if (frame == null) { // first time around
if (ci.isAbstract()) { // not allowed to instantiate
env.throwException(ctx, "java.lang.InstantiationException");
return MJIEnv.NULL;
}
// <2do> - still need to handle protected
if (miCtor.isPrivate()) {
env.throwException(ctx, "java.lang.IllegalAccessException", "cannot access non-public member of class " + ci.getName());
return MJIEnv.NULL;
}
int objRef = env.newObjectOfUncheckedClass(ctx, ci); // create the thing
frame = miCtor.createDirectCallStackFrame(ctx, ti, 1);
// note that we don't set this as a reflection call since it is supposed to propagate exceptions
frame.setReferenceArgument(ctx, 0, objRef, null);
frame.setLocalReferenceVariable(ctx, 0, objRef); // (1) store ref for retrieval during re-exec
ti.pushFrame(frame);
// check if we have to push clinits
ci.pushRequiredClinits(ctx, ti);
env.repeatInvocation();
return MJIEnv.NULL;
} else { // re-execution
int objRef = frame.getLocalVariable(ctx, 0).getValue(); // that's the object ref we set in (1)
return objRef;
}
}
@MJI
public int getSuperclass____Ljava_lang_Class_2(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
ClassInfo sci = ci.getSuperClass();
if (sci != null) {
return sci.getClassObjectRef();
} else {
return MJIEnv.NULL;
}
}
int getMethod(MJIEnv env, int clsRef, ClassInfo ciMethod, String mname, int argTypesRef, boolean isRecursiveLookup, boolean publicOnly, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
StringBuilder sb = new StringBuilder(mname);
sb.append('(');
int nParams = argTypesRef != MJIEnv.NULL ? env.getArrayLength(ctx, argTypesRef) : 0;
for (int i = 0; i < nParams; i++) {
int cRef = env.getReferenceArrayElement(argTypesRef, i).simplify(ctx).getValue();
if (cRef == MJIEnv.NULL) {
String tcode = "java.lang.Object";
tcode = Types.getTypeSignature(tcode, false);
sb.append(tcode);
} else {
ClassInfo cit = env.getReferredClassInfo(ctx, cRef);
String tcode = cit.getName();
tcode = Types.getTypeSignature(tcode, false);
sb.append(tcode);
}
}
sb.append(')');
String fullMthName = sb.toString();
MethodInfo mi = ci.getReflectionMethod(fullMthName, isRecursiveLookup);
if (mi == null || (publicOnly && !mi.isPublic())) {
env.throwException(ctx, "java.lang.NoSuchMethodException", ci.getName() + '.' + fullMthName);
return MJIEnv.NULL;
} else {
return createMethodObject(env, ciMethod, mi, ctx);
}
}
int createMethodObject(MJIEnv env, ClassInfo objectCi, MethodInfo mi, FeatureExpr ctx) {
// NOTE - we rely on Constructor and Method peers being initialized
if (mi.isCtor()) {
return JPF_java_lang_reflect_Constructor.createConstructorObject(env, objectCi, mi, ctx);
} else {
return JPF_java_lang_reflect_Method.createMethodObject(env, objectCi, mi, ctx);
}
}
@MJI
public int getDeclaredMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2(MJIEnv env, int clsRef, int nameRef, int argTypesRef, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
String mname = env.getStringObject(ctx, nameRef);
return getMethod(env, clsRef, mci, mname, argTypesRef, false, false, ctx);
}
@MJI
public int getDeclaredConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2(MJIEnv env, int clsRef, int argTypesRef, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
int ctorRef = getMethod(env, clsRef, mci, "<init>", argTypesRef, false, false, ctx);
return ctorRef;
}
@MJI
public Conditional<Integer> getMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2(final MJIEnv env, final int clsRef, Conditional<Integer> nameRef,
final Conditional<Integer> argTypesRef, FeatureExpr ctx) {
final ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return One.MJIEnvNULL;
}
Conditional<String> mname = env.getStringObject(ctx, nameRef);
return mname.mapf(ctx, new BiFunction<FeatureExpr, String, Conditional<Integer>>() {
@Override
public Conditional<Integer> apply(FeatureExpr ctx, String mname) {
return new One<>(getMethod(env, clsRef, mci, mname, argTypesRef.getValue(), true, true, ctx));
}
});
}
private void addDeclaredMethodsRec(HashMap<String, MethodInfo> methods, ClassInfo ci, FeatureExpr ctx) {
ClassInfo sci = ci.getSuperClass();
if (sci != null) {
addDeclaredMethodsRec(methods, sci, ctx);
}
ClassLoaderInfo cl = ci.getClassLoaderInfo();
for (String ifcName : ci.getDirectInterfaceNames()) {
ClassInfo ici = cl.getResolvedClassInfo(ctx, ifcName); // has to be already defined, so no exception
addDeclaredMethodsRec(methods, ici, ctx);
}
for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
// filter out non-public, <clinit> and <init>
if (mi.isPublic() && (mi.getName().charAt(0) != '<')) {
String mname = mi.getUniqueName();
if (!(ci.isInterface() && methods.containsKey(mname))) {
methods.put(mname, mi);
}
}
}
}
@MJI
public int getMethods_____3Ljava_lang_reflect_Method_2(MJIEnv env, int objref, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, objref);
// collect all the public, non-ctor instance methods
if (!ci.isPrimitive()) {
HashMap<String, MethodInfo> methods = new HashMap<>();
addDeclaredMethodsRec(methods, ci, ctx);
int n = methods.size();
int aref = env.newObjectArray("Ljava/lang/reflect/Method;", n);
int i = 0;
for (MethodInfo mi : methods.values()) {
int mref = createMethodObject(env, mci, mi, ctx);
env.setReferenceArrayElement(ctx, aref, i++, new One<>(mref));
}
return aref;
} else {
return env.newObjectArray("Ljava/lang/reflect/Method;", 0);
}
}
@MJI
public int getDeclaredMethods_____3Ljava_lang_reflect_Method_2(MJIEnv env, int objref, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, objref);
MethodInfo[] methodInfos = ci.getDeclaredMethodInfos();
// we have to filter out the ctors and the static init
int nMth = methodInfos.length;
for (int i = 0; i < methodInfos.length; i++) {
if (methodInfos[i].getName().charAt(0) == '<') {
methodInfos[i] = null;
nMth--;
}
}
int aref = env.newObjectArray("Ljava/lang/reflect/Method;", nMth);
for (int i = 0, j = 0; i < methodInfos.length; i++) {
if (methodInfos[i] != null) {
int mref = createMethodObject(env, mci, methodInfos[i], ctx);
env.setReferenceArrayElement(ctx, aref, j++, new One<>(mref));
}
}
return aref;
}
int getConstructors(MJIEnv env, int objref, boolean publicOnly, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, objref);
ArrayList<MethodInfo> ctors = new ArrayList<>();
// we have to filter out the ctors and the static init
for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
if (mi.getName().equals("<init>")) {
if (!publicOnly || mi.isPublic()) {
ctors.add(mi);
}
}
}
int nCtors = ctors.size();
int aref = env.newObjectArray("Ljava/lang/reflect/Constructor;", nCtors);
for (int i = 0; i < nCtors; i++) {
env.setReferenceArrayElement(ctx, aref, i, new One<>(createMethodObject(env, mci, ctors.get(i), ctx)));
}
return aref;
}
@MJI
public int getConstructors_____3Ljava_lang_reflect_Constructor_2(MJIEnv env, int objref, FeatureExpr ctx) {
return getConstructors(env, objref, true, ctx);
}
@MJI
public int getDeclaredConstructors_____3Ljava_lang_reflect_Constructor_2(MJIEnv env, int objref, FeatureExpr ctx) {
return getConstructors(env, objref, false, ctx);
}
@MJI
public int getConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2(MJIEnv env, int clsRef, int argTypesRef, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
// <2do> should only return a public ctor
return getMethod(env, clsRef, mci, "<init>", argTypesRef, false, true, ctx);
}
// this is only used for system classes such as java.lang.reflect.Method
ClassInfo getInitializedClassInfo(MJIEnv env, String clsName, FeatureExpr ctx) {
ThreadInfo ti = env.getThreadInfo();
// Instruction insn = ti.getPC().getValue();
ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(clsName);
if (ci.pushRequiredClinits(ctx, ti)) {
return null;
} else {
return ci;
}
}
@MJI
public void initialize0____V(MJIEnv env, int clsObjRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsObjRef);
ci.pushRequiredClinits(ctx, ThreadInfo.currentThread);
}
Set<ClassInfo> getInitializedInterfaces(MJIEnv env, ClassInfo ci, FeatureExpr ctx) {
ThreadInfo ti = env.getThreadInfo();
// Instruction insn = ti.getPC().getValue();
Set<ClassInfo> ifcs = ci.getAllInterfaceClassInfos();
for (ClassInfo ciIfc : ifcs) {
if (ciIfc.pushRequiredClinits(ctx, ti)) {
return null;
}
}
return ifcs;
}
static int createFieldObject(MJIEnv env, FieldInfo fi, ClassInfo fci, FeatureExpr ctx) {
int regIdx = JPF_java_lang_reflect_Field.registerFieldInfo(fi);
int eidx = env.newObject(ctx, fci);
ElementInfo ei = env.getModifiableElementInfo(eidx);
ei.setIntField(ctx, "regIdx", new One<>(regIdx));
return eidx;
}
@MJI
public int getDeclaredFields_____3Ljava_lang_reflect_Field_2(MJIEnv env, int objRef, FeatureExpr ctx) {
ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME, ctx);
if (fci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, objRef);
int nInstance = ci.getNumberOfDeclaredInstanceFields();
int nStatic = ci.getNumberOfStaticFields();
int aref = env.newObjectArray("Ljava/lang/reflect/Field;", nInstance + nStatic);
int i, j = 0;
for (i = 0; i < nStatic; i++) {
FieldInfo fi = ci.getStaticField(i);
env.setReferenceArrayElement(ctx, aref, j++, new One<>(createFieldObject(env, fi, fci, ctx)));
}
for (i = 0; i < nInstance; i++) {
FieldInfo fi = ci.getDeclaredInstanceField(i);
env.setReferenceArrayElement(ctx, aref, j++, new One<>(createFieldObject(env, fi, fci, ctx)));
}
return aref;
}
@MJI
public int getFields_____3Ljava_lang_reflect_Field_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME, ctx);
if (fci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
// interfaces might not be initialized yet, so we have to check first
Set<ClassInfo> ifcs = getInitializedInterfaces(env, ci, ctx);
if (ifcs == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ArrayList<FieldInfo> fiList = new ArrayList<>();
for (; ci != null; ci = ci.getSuperClass()) {
// the host VM returns them in order of declaration, but the spec says there is no guaranteed order so we keep it simple
for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
if (fi.isPublic()) {
fiList.add(fi);
}
}
for (FieldInfo fi : ci.getDeclaredStaticFields()) {
if (fi.isPublic()) {
fiList.add(fi);
}
}
}
for (ClassInfo ciIfc : ifcs) {
for (FieldInfo fi : ciIfc.getDeclaredStaticFields()) {
fiList.add(fi); // there are no non-public fields in interfaces
}
}
int aref = env.newObjectArray("Ljava/lang/reflect/Field;", fiList.size());
int j = 0;
for (FieldInfo fi : fiList) {
env.setReferenceArrayElement(ctx, aref, j++, new One<>(createFieldObject(env, fi, fci, ctx)));
}
return aref;
}
int getField(MJIEnv env, int clsRef, int nameRef, boolean isRecursiveLookup, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
String fname = env.getStringObject(ctx, nameRef);
FieldInfo fi = null;
if (isRecursiveLookup) {
fi = ci.getInstanceField(fname);
if (fi == null) {
fi = ci.getStaticField(fname);
}
} else {
fi = ci.getDeclaredInstanceField(fname);
if (fi == null) {
fi = ci.getDeclaredStaticField(fname);
}
}
if (fi == null) {
env.throwException(ctx, "java.lang.NoSuchFieldException", fname);
return MJIEnv.NULL;
}
// if (!fi.isPublic()) {// TODO
// env.throwException(ctx, NoSuchFieldException.class.getName(), fi.getName());
// }
// don't do a Field clinit before we know there is such a field
ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME, ctx);
if (fci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
return createFieldObject(env, fi, fci, ctx);
}
@MJI
public int getDeclaredField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2(MJIEnv env, int clsRef, int nameRef, FeatureExpr ctx) {
return getField(env, clsRef, nameRef, false, ctx);
}
@MJI
public int getField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2(MJIEnv env, int clsRef, int nameRef, FeatureExpr ctx) {
return getField(env, clsRef, nameRef, true, ctx);
}
@MJI
public int getModifiers____I(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
return ci.getModifiers();
}
@MJI
public int getEnumConstants_____3Ljava_lang_Object_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
if (env.requiresClinitExecution(ctx, ci)) {
env.repeatInvocation();
return 0;
}
if (ci.getSuperClass().getName().equals("java.lang.Enum")) {
ArrayList<FieldInfo> list = new ArrayList<>();
String cName = ci.getName();
for (FieldInfo fi : ci.getDeclaredStaticFields()) {
if (fi.isFinal() && cName.equals(fi.getType())) {
list.add(fi);
}
}
int aRef = env.newObjectArray(cName, list.size());
StaticElementInfo sei = ci.getStaticElementInfo();
int i = 0;
for (FieldInfo fi : list) {
env.setReferenceArrayElement(ctx, aRef, i++, sei.getReferenceField(fi));
}
return aRef;
}
return MJIEnv.NULL;
}
@MJI
public int getInterfaces_____3Ljava_lang_Class_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
int aref = MJIEnv.NULL;
// ThreadInfo ti = env.getThreadInfo();
// contrary to the API doc, this only returns the interfaces directly
// implemented by this class, not it's bases
// <2do> this is not exactly correct, since the interfaces should be ordered
Set<ClassInfo> interfaces = ci.getInterfaceClassInfos();
aref = env.newObjectArray("Ljava/lang/Class;", interfaces.size());
int i = 0;
for (ClassInfo ifc : interfaces) {
env.setReferenceArrayElement(ctx, aref, i++, new One<>(ifc.getClassObjectRef()));
}
return aref;
}
/**
* <2do> needs to load from the classfile location, NOT the MJIEnv (native) class
*
* @author Sebastian Gfeller (sebastian.gfeller@gmail.com)
* @author Tihomir Gvero (tihomir.gvero@gmail.com)
*/
@MJI
public int getByteArrayFromResourceStream__Ljava_lang_String_2___3B(MJIEnv env, int clsRef, int nameRef, FeatureExpr ctx) {
String name = env.getStringObject(ctx, nameRef);
// <2do> this is not loading from the classfile location! fix it
InputStream is = env.getClass().getResourceAsStream(name);
if (is == null) {
return MJIEnv.NULL;
}
// We assume that the entire input stream can be read at the moment,
// although this could break.
byte[] content = null;
try {
content = new byte[is.available()];
is.read(content);
} catch (IOException e) {
throw new RuntimeException(e);
}
// Now if everything worked, the content should be in the byte buffer.
// We put this buffer into the JPF VM.
return env.newByteArray(ctx, content);
}
@MJI
public int getEnclosingClass____Ljava_lang_Class_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ciEncl = env.getReferredClassInfo(ctx, clsRef).getEnclosingClassInfo();
if (ciEncl == null) {
return MJIEnv.NULL;
}
if (!ciEncl.isRegistered()) {
ThreadInfo ti = env.getThreadInfo();
ciEncl.registerClass(ctx, ti);
if (!ciEncl.isInitialized()) {
if (ciEncl.pushRequiredClinits(ctx, ti)) {
env.repeatInvocation();
return 0;
}
}
}
return ciEncl.getClassObjectRef();
}
@MJI
public int getDeclaredClasses_____3Ljava_lang_Class_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
String[] innerClassNames = ci.getInnerClasses();
int aref = MJIEnv.NULL;
ThreadInfo ti = env.getThreadInfo();
MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
// class of the method that includes the invocation of Class.getDeclaredClasses
ClassInfo cls = mi.getClassInfo();
// first resolve all the inner classes
int length = innerClassNames.length;
ClassInfo[] resolvedInnerClass = new ClassInfo[length];
for (int i = 0; i < length; i++) {
try {
resolvedInnerClass[i] = cls.resolveReferencedClass(ctx, innerClassNames[i]);
} catch (LoadOnJPFRequired lre) {
env.repeatInvocation();
return MJIEnv.NULL;
}
}
aref = env.newObjectArray("Ljava/lang/Class;", innerClassNames.length);
for (int i = 0; i < length; i++) {
ClassInfo ici = resolvedInnerClass[i];
if (!ici.isRegistered()) {
ici.registerClass(ctx, ti);
}
env.setReferenceArrayElement(ctx, aref, i, new One<>(ici.getClassObjectRef()));
}
return aref;
}
private String getCanonicalName(ClassInfo ci) {
if (ci.isArray()) {
String canonicalName = getCanonicalName(ci.getComponentClassInfo());
if (canonicalName != null) {
return canonicalName + "[]";
} else {
return null;
}
}
if (isLocalOrAnonymousClass(ci)) {
return null;
}
if (ci.getEnclosingClassInfo() == null) {
return ci.getName();
} else {
String enclosingName = getCanonicalName(ci.getEnclosingClassInfo());
if (enclosingName == null) {
return null;
}
return enclosingName + "." + ci.getSimpleName();
}
}
@MJI
public int getCanonicalName____Ljava_lang_String_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
return env.newString(ctx, getCanonicalName(ci));
}
@MJI
public boolean isAnnotation____Z(MJIEnv env, int clsObjRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsObjRef);
return (ci.getModifiers() & 0x2000) != 0;
}
@MJI
public boolean isAnnotationPresent__Ljava_lang_Class_2__Z(MJIEnv env, int clsObjRef, int annoClsObjRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsObjRef);
ClassInfo ciAnno = env.getReferredClassInfo(ctx, annoClsObjRef);
return ci.getAnnotation(ciAnno.getName()) != null;
}
@MJI
public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
try {
return env.newAnnotationProxies(ctx, ci.getDeclaredAnnotations());
} catch (ClinitRequired x) {
env.handleClinitRequest(ctx, x.getRequiredClassInfo());
return MJIEnv.NULL;
}
}
@MJI
public int getEnclosingConstructor____Ljava_lang_reflect_Constructor_2(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
if ((enclosingMethod != null) && enclosingMethod.isCtor()) {
return createMethodObject(env, mci, enclosingMethod, ctx);
}
return MJIEnv.NULL;
}
@MJI
public int getEnclosingMethod____Ljava_lang_reflect_Method_2(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME, ctx);
if (mci == null) {
env.repeatInvocation();
return MJIEnv.NULL;
}
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
if ((enclosingMethod != null) && !enclosingMethod.isCtor()) {
return createMethodObject(env, mci, enclosingMethod, ctx);
}
return MJIEnv.NULL;
}
@MJI
public boolean isAnonymousClass____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
String cname = null;
if (ci.getName().contains("$")) {
cname = ci.getName().substring(ci.getName().lastIndexOf('$') + 1);
}
return (cname == null) ? false : cname.matches("\\d+?");
}
@MJI
public boolean isEnum____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return ci.isEnum();
}
// Similar to getEnclosingClass() except it returns null for the case of
// anonymous class.
@MJI
public int getDeclaringClass____Ljava_lang_Class_2(MJIEnv env, int clsRef, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, clsRef);
if (isLocalOrAnonymousClass(ci)) {
return MJIEnv.NULL;
} else {
return getEnclosingClass____Ljava_lang_Class_2(env, clsRef, ctx);
}
}
@MJI
public boolean isLocalClass____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return isLocalOrAnonymousClass(ci) && !isAnonymousClass____Z(env, robj, ctx);
}
private boolean isLocalOrAnonymousClass(ClassInfo ci) {
return (ci.getEnclosingMethodInfo() != null);
}
@MJI
public boolean isMemberClass____Z(MJIEnv env, int robj, FeatureExpr ctx) {
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
return (ci.getEnclosingClassInfo() != null) && !isLocalOrAnonymousClass(ci);
}
/**
* Append the package name prefix of the class represented by robj, if the name is not
* absolute. OW, remove leading "/".
*/
@MJI
public int getResolvedName__Ljava_lang_String_2__Ljava_lang_String_2(MJIEnv env, int robj, int resRef, FeatureExpr ctx) {
String rname = env.getStringObject(ctx, resRef);
ClassInfo ci = env.getReferredClassInfo(ctx, robj);
if (rname == null) {
return MJIEnv.NULL;
}
if (!rname.startsWith("/")) {
ClassInfo c = ci;
while (c.isArray()) {
c = c.getComponentClassInfo();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
rname = baseName.substring(0, index).replace('.', '/') + "/" + rname;
}
} else {
rname = rname.substring(1);
}
return env.newString(ctx, rname);
}
}