// // 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.lang.reflect.Modifier; 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; public class JPF_java_lang_reflect_Field extends NativePeer { // the registry is rather braindead, let's hope we don't have many lookups - // using Fields is fine, but creating them is not efficient until we fix this static final int NREG = 64; static FieldInfo[] registered; static int nRegistered; public static boolean init (Config conf){ registered = new FieldInfo[NREG]; nRegistered = 0; return true; } static int registerFieldInfo (FieldInfo fi) { int idx; for (idx=0; idx < nRegistered; idx++) { if (registered[idx] == fi) { return idx; } } if (idx == registered.length) { FieldInfo[] newReg = new FieldInfo[registered.length+NREG]; System.arraycopy(registered, 0, newReg, 0, registered.length); registered = newReg; } registered[idx] = fi; nRegistered++; return idx; } static FieldInfo getRegisteredFieldInfo (int idx) { return registered[idx]; } /** * >2do> that doesn't take care of class init yet */ @MJI public int getType____Ljava_lang_Class_2 (MJIEnv env, int objRef, FeatureExpr ctx) { ThreadInfo ti = env.getThreadInfo(); FieldInfo fi = getFieldInfo(ctx, env, objRef); ClassInfo ci = fi.getTypeClassInfo(); if (!ci.isRegistered()) { ci.registerClass(ctx, ti); } return ci.getClassObjectRef(); } @MJI public int getModifiers____I (MJIEnv env, int objRef, FeatureExpr ctx){ FieldInfo fi = getFieldInfo(ctx, env, objRef); return fi.getModifiers(); } static ElementInfo getCheckedElementInfo(MJIEnv env, int objRef, FieldInfo fi, int fobjRef, Class<?> fiType, String type, boolean isWrite, FeatureExpr ctx) { ElementInfo ei; if (!isAvailable(env, fi, fobjRef, ctx)) { return null; } if (fi.isStatic()) { ClassInfo fci = fi.getClassInfo(); ei = isWrite ? fci.getModifiableStaticElementInfo() : fci.getStaticElementInfo(); } else { // instance field ei = isWrite ? env.getModifiableElementInfo(fobjRef) : env.getElementInfo(fobjRef); } // our guards (still need IllegalAccessException) if (fi.isPrivate() && !env.getBooleanField(objRef, "isAccessible").getValue()) { env.throwException(ctx, IllegalAccessException.class.getName(), fi + ""); return null; } if (ei == null) { env.throwException(ctx, "java.lang.NullPointerException"); return null; } if (fiType != null) { if (fiType == ByteFieldInfo.class) { if (fi.isByteField()) { return ei; } } else if (fiType == ShortFieldInfo.class) { if (fi.isShortField()) { return ei; } } else if (fiType == IntegerFieldInfo.class) { if (fi.isIntField()) { return ei; } } else if (fiType == LongFieldInfo.class) { if (fi.isLongField()) { return ei; } } else if (fiType == FloatFieldInfo.class) { if (fi.isFloatField()) { return ei; } } if (!fiType.isInstance(fi)) { env.throwException(ctx, "java.lang.IllegalArgumentException", "field type " + fi.getType() + " incompatible with " + type); return null; } } return ei; } @MJI public boolean getBoolean__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, BooleanFieldInfo.class, "boolean", false, ctx); if (ei != null){ return ei.getBooleanField(fi).getValue(); } return false; } @MJI public byte getByte__Ljava_lang_Object_2__B (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, ByteFieldInfo.class, "byte", false, ctx); if (ei != null){ return ei.getByteField(fi).getValue(); } return 0; } @MJI public char getChar__Ljava_lang_Object_2__C (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, CharFieldInfo.class, "char", false, ctx); if (ei != null){ return ei.getCharField(fi).getValue(); } return 0; } @MJI public short getShort__Ljava_lang_Object_2__S (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, ShortFieldInfo.class, "short", false, ctx); if (ei != null){ return ei.getShortField(fi).getValue(); } return 0; } @MJI public int getInt__Ljava_lang_Object_2__I (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, IntegerFieldInfo.class, "int", false, ctx); if (ei != null){ return ei.getIntField(fi).simplify(ctx).getValue(); } return 0; } @MJI public long getLong__Ljava_lang_Object_2__J (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, LongFieldInfo.class, "long", false, ctx); if (ei != null){ return ei.getLongField(fi).getValue(); } return 0; } @MJI public float getFloat__Ljava_lang_Object_2__F (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, FloatFieldInfo.class, "float", false, ctx); if (ei != null){ return ei.getFloatField(fi).getValue(); } return 0; } @MJI public double getDouble__Ljava_lang_Object_2__D (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, DoubleFieldInfo.class, "double", false, ctx); if (ei != null){ return ei.getDoubleField(fi).getValue(); } return 0; } @MJI public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef, int annotationClsRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx,env, objRef); ClassInfo aci = env.getReferredClassInfo(ctx, annotationClsRef); AnnotationInfo ai = fi.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; } } return MJIEnv.NULL; } @MJI public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef, FeatureExpr ctx){ FieldInfo fi = getFieldInfo(ctx,env, objRef); AnnotationInfo[] ai = fi.getAnnotations(); try { return env.newAnnotationProxies(ctx, ai); } catch (ClinitRequired x){ env.handleClinitRequest(ctx, x.getRequiredClassInfo()); return MJIEnv.NULL; } } @MJI public void setBoolean__Ljava_lang_Object_2Z__V (MJIEnv env, int objRef, int fobjRef, boolean val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, BooleanFieldInfo.class, "boolean", true, ctx); if (ei != null){ ei.setBooleanField(ctx,fi, new One<>(val)); } } @MJI public void setByte__Ljava_lang_Object_2B__V(MJIEnv env, int objRef, int fobjRef, byte val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)) { return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, ByteFieldInfo.class, "byte", true, ctx); if (ei != null) { ei.setByteField(ctx, fi, new One<>(val)); } } @MJI public void setChar__Ljava_lang_Object_2C__V (MJIEnv env, int objRef, int fobjRef, char val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, CharFieldInfo.class, "char", true, ctx); if (ei != null){ ei.setCharField(ctx,fi, new One<>(val)); } } @MJI public void setShort__Ljava_lang_Object_2S__V (MJIEnv env, int objRef, int fobjRef, short val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, ShortFieldInfo.class, "short", true, ctx); if (ei != null){ ei.setShortField(ctx,fi, new One<>(val)); } } @MJI public void setInt__Ljava_lang_Object_2I__V (MJIEnv env, int objRef, int fobjRef, int val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, IntegerFieldInfo.class, "int", true, ctx); if (ei != null){ ei.setIntField(ctx,fi, One.valueOf(val)); } } @MJI public void setLong__Ljava_lang_Object_2J__V (MJIEnv env, int objRef, int fobjRef, long val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, LongFieldInfo.class, "long", true, ctx); if (ei != null){ ei.setLongField(ctx,fi, new One<>(val)); } } @MJI public void setFloat__Ljava_lang_Object_2F__V (MJIEnv env, int objRef, int fobjRef, float val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, FloatFieldInfo.class, "float", true, ctx); if (ei != null){ ei.setFloatField(ctx,fi, new One<>(val)); } } @MJI public void setDouble__Ljava_lang_Object_2D__V (MJIEnv env, int objRef, int fobjRef, double val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } ElementInfo ei = getCheckedElementInfo(env, objRef, fi, fobjRef, DoubleFieldInfo.class, "double", true, ctx); if (ei != null){ ei.setDoubleField(ctx,fi, new One<>(val)); } } @MJI public int get__Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); ElementInfo ei = getCheckedElementInfo( env, objRef, fi, fobjRef, null, null, false, ctx); // no type check here if (ei == null){ return 0; } if (!(fi instanceof ReferenceFieldInfo)) { // primitive type, we need to box it if (fi instanceof DoubleFieldInfo){ Conditional<Double> d = ei.getDoubleField(fi); return env.newDouble(ctx, d); } else if (fi instanceof FloatFieldInfo){ Conditional<Float> f = ei.getFloatField(fi); return env.newFloat(ctx, f); } else if (fi instanceof LongFieldInfo){ Conditional<Long> l = ei.getLongField(fi); return env.newLong(ctx, l); } else if (fi instanceof IntegerFieldInfo){ // this might actually represent a plethora of types Conditional<Integer> i = ei.getIntField(fi); return env.newInteger(ctx, i); } else if (fi instanceof BooleanFieldInfo){ Conditional<Boolean> b = ei.getBooleanField(fi); return env.newBoolean(b.getValue()); } else if (fi instanceof ByteFieldInfo){ Conditional<Byte> z = ei.getByteField(fi); return env.newByte(ctx, z); } else if (fi instanceof CharFieldInfo){ Conditional<Character> c = ei.getCharField(fi); return env.newCharacter(ctx, c); } else if (fi instanceof ShortFieldInfo){ Conditional<Short> s = ei.getShortField(fi); return env.newShort(ctx, s); } } else { // it's a reference int ref = ei.getReferenceField(fi).simplify(ctx).getValue(); // we internally store it as int return ref; } env.throwException(ctx, "java.lang.IllegalArgumentException", "unknown field type"); return MJIEnv.NULL; } @MJI public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objref, FeatureExpr ctx){ FieldInfo fi = getFieldInfo(ctx, env, objref); ClassInfo ci = fi.getClassInfo(); return ci.getClassObjectRef(); } @MJI public boolean isSynthetic____Z (MJIEnv env, int objref, FeatureExpr ctx){ FieldInfo fi = getFieldInfo(ctx, env, objref); String fn = fi.getName(); return (fn.startsWith("this$") || fn.startsWith("val$")); } @MJI public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); int nameRef = env.getReferenceField( ctx, objRef, "name").getValue(); if (nameRef == MJIEnv.NULL) { nameRef = env.newString(ctx, fi.getName()); env.setReferenceField(ctx, objRef, "name", nameRef); } return nameRef; } static FieldInfo getFieldInfo (FeatureExpr ctx, MJIEnv env, int objRef) { int fidx = env.getIntField( objRef, "regIdx").simplify(ctx).getValue().intValue(); assert ((fidx >= 0) || (fidx < nRegistered)) : "illegal FieldInfo request: " + fidx + ", " + nRegistered; return registered[fidx]; } static boolean isAvailable (MJIEnv env, FieldInfo fi, int fobjRef, FeatureExpr ctx){ if (fi.isStatic()){ ClassInfo fci = fi.getClassInfo(); if (fci.pushRequiredClinits(ctx, env.getThreadInfo())){ env.repeatInvocation(); return false; } } else { if (fobjRef == MJIEnv.NULL){ env.throwException(ctx, "java.lang.NullPointerException"); return false; } // class had obviously been initialized, otherwise we won't have an instance of it } return true; } /** * Peer method for the <code>java.lang.reflect.Field.set</code> method. * * @author Mirko Stojmenovic (mirko.stojmenovic@gmail.com) * @author Igor Andjelkovic (igor.andjelkovic@gmail.com) * @author Milos Gligoric (milos.gligoric@gmail.com) * */ @MJI public void set__Ljava_lang_Object_2Ljava_lang_Object_2__V (MJIEnv env, int objRef, int fobjRef, int val, FeatureExpr ctx) { FieldInfo fi = getFieldInfo(ctx, env, objRef); if (!isAvailable(env, fi, fobjRef, ctx)){ return; } if (fi.isFinal() && fi.isStatic()) { env.throwException(ctx, IllegalAccessException.class.getName(), "Can not set static final " + fi.getType() + " field " + fi.getFullName()); return; } if (!env.getBooleanField(objRef, "isAccessible").getValue() && fi.isFinal()) { env.throwException(ctx, IllegalAccessException.class.getName(), "Can not set final " + fi.getType() + " field " + fi.getFullName()); return; } ClassInfo ci = fi.getClassInfo(); ClassInfo cio = env.getClassInfo(fobjRef); if (!fi.isStatic() && !cio.isInstanceOf(ci)) { env.throwException(ctx, IllegalAccessException.class.getName(), fi.getType() + "field " + fi.getName() + " does not belong to this object"); return; } if (!env.getBooleanField(objRef, "isAccessible").getValue()) { if (!fi.isStatic() && cio.isInstanceOf(ci)) { if (!fi.isPublic()) { env.throwException(ctx, IllegalAccessException.class.getName(), fi.getType() + " field " + fi.getName()); return; } } else { if (!fi.isPublic()) { env.throwException(ctx, IllegalAccessException.class.getName(), fi.getType() + " field " + fi.getName()); return; } } } Object[] attrs = env.getArgAttributes(); Object attr = (attrs==null)? null: attrs[2]; if (!setValue(ctx, env, fi, fobjRef, val, attr)) { env.throwException(ctx, IllegalAccessException.class.getName(), "Can not set " + fi.getType() + " field " + fi.getFullName() + " to " + ((MJIEnv.NULL != val) ? env.getClassInfo(val).getName() + " object " : "null")); } } private static boolean setValue(FeatureExpr ctx, MJIEnv env, FieldInfo fi, int obj, int value, Object attr) { ClassInfo fieldClassInfo = fi.getClassInfo(); String className = fieldClassInfo.getName(); String fieldType = fi.getType(); ClassInfo tci = fi.getTypeClassInfo(); ElementInfo ei = null; if (fi.isStatic()) { ei = fi.getClassInfo().getModifiableStaticElementInfo(); } else { ei = env.getModifiableElementInfo(obj); } if (tci.isPrimitive()) { if (value == MJIEnv.NULL) { return false; } // checks whether unboxing can be done by accessing the field "value" final String fieldName = "value"; FieldInfo finfo = env.getElementInfo(value).getFieldInfo(fieldName); if (finfo == null) { return false; } ei.setFieldAttr(fi, attr); if ("boolean".equals(fieldType)){ Conditional<Boolean> val = env.getBooleanField(value, fieldName); ei.setBooleanField(ctx, fi, val); return true; } else if ("byte".equals(fieldType)){ Conditional<Byte> val = env.getByteField(value, fieldName); ei.setByteField(ctx, fi, val); return true; } else if ("char".equals(fieldType)){ Conditional<Character> val = env.getCharField(value, fieldName); ei.setCharField(ctx, fi, val); return true; } else if ("short".equals(fieldType)){ Conditional<Short> val = env.getShortField(value, fieldName); ei.setShortField(ctx, fi, val); return true; } else if ("int".equals(fieldType)){ Conditional<Integer> val = env.getIntField(value, fieldName); ei.setIntField(ctx, fi, val); return true; } else if ("long".equals(fieldType)){ Conditional<Long> val = env.getLongField(value, fieldName); ei.setLongField(ctx, fi, val); return true; } else if ("float".equals(fieldType)){ Conditional<Float> val = env.getFloatField(value, fieldName); ei.setFloatField(ctx, fi, val); return true; } else if ("double".equals(fieldType)){ Conditional<Double> val = env.getDoubleField(value, fieldName); ei.setDoubleField(ctx, fi, val); return true; } else { return false; } } else { // it's a reference if (value != MJIEnv.NULL) { ClassInfo ciValue = env.getClassInfo(value); if (!ciValue.isInstanceOf(tci)) { return false; } } ei.setFieldAttr(fi, attr); if (fi.isStatic()) { env.setStaticReferenceField(ctx, className, fi.getName(), value); } else { env.setReferenceField(ctx, obj, fi.getName(), value); } return true; } } @MJI public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int fobjRef, FeatureExpr ctx){ int fidx = env.getIntField(fobjRef, "regIdx").getValue().intValue(); if (fidx >= 0 && fidx < nRegistered){ FieldInfo fi1 = getFieldInfo(ctx, env, objRef); FieldInfo fi2 = getFieldInfo(ctx, env, fobjRef); return ((fi1.getClassInfo() == fi2.getClassInfo()) && fi1.getName().equals(fi2.getName()) && fi1.getType().equals(fi2.getType())); } return false; } @MJI public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef, FeatureExpr ctx){ StringBuilder sb = new StringBuilder(); FieldInfo fi = getFieldInfo(ctx, env, objRef); sb.append(Modifier.toString(fi.getModifiers())); sb.append(' '); sb.append(fi.getType()); sb.append(' '); sb.append(fi.getFullName()); int sref = env.newString(ctx, sb.toString()); return sref; } @MJI public int hashCode____I (MJIEnv env, int objRef, FeatureExpr ctx){ FieldInfo fi = getFieldInfo(ctx, env, objRef); return fi.getClassInfo().getName().hashCode() ^ fi.getName().hashCode(); } @MJI public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef, FeatureExpr ctx){ return getAnnotations_____3Ljava_lang_annotation_Annotation_2(env, objRef, ctx); } }