/* * This file is part of the OpenJML project. * Author: David R. Cok */ package org.jmlspecs.utils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigInteger; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.annotation.Nullable; import org.jmlspecs.lang.Real; import org.jmlspecs.utils.JmlAssertionError.Precondition; /** * This class contains utility methods used in internal translations for both * ESC and RAC. In RAC, these functions are executed to provide the built-in * functionality; in ESC, the specifications written here are used to provide * background predicate logic for built-in functionality. * @author David Cok */ public class Utils { /** Reports a JML assertion without a specific label indicating the kind of assertion failure */ public static void assertionFailure(String message) { assertionFailureL(message,null); } /** Reports a JML assertion failure with optional additional information * @param message the message to report * @param optMessage possibly null additional information */ public static void assertionFailure2(String message, /*@ nullable*/ String optMessage) { if (optMessage != null) message = message + " (" + optMessage + ")"; assertionFailure(message); } /** Reports a JML assertion (any JML precondition, postcondition, etc.) * failure with the given message. * @param message The message to report */ // This one is declared first to minimize changes to its location public static final String ASSERTION_FAILURE = "assertionFailureL"; // Must match the method name public static void assertionFailureL(String message, /*@ nullable */String label) { if (useExceptions) { throw createException(message,label); } else if (useJavaAssert) { assert false: message; } else { System.out.println(message); System.out.flush(); if (showStack) { Error e = createException(message,label); e.printStackTrace(System.out); // Keep the new expressions on line 47 or some test results will change } } } /** This version of runtime assertion reporting reports only using exceptions */ public static final String ASSERTION_FAILURE_EX = "assertionFailureE"; // Must match the method name public static void assertionFailureE(String message, /*@ nullable */String label) { throw createException(message,label); } /** Helper method to create the appropriate class of JmlAssertion */ static private Error createException(String message, /*@ nullable */String label) { String exname = System.getProperty("org.openjml.exception."+label); if (exname == null) { exname = "org.jmlspecs.utils.JmlAssertionError" + "$" + label; } Class<?> c; try { c = Class.forName(exname); } catch (ClassNotFoundException e) { c = null; } if (c != null) { try { Constructor<? extends Error> cc = ((Class<? extends Error>)c).getConstructor(String.class,String.class); if (cc != null) { Error e = cc.newInstance(message,label); e.fillInStackTrace(); return e; } } catch (ClassCastException e) { return new JmlAssertionError("User-defined JML assertion is not a subtype of java.lang.Error: " + exname + System.getProperty("line.separator") + " " + message, label); } catch (NoSuchMethodException e) { return new JmlAssertionError(message,label); } catch (InstantiationException e) { return new JmlAssertionError(message,label); } catch (InvocationTargetException e) { return new JmlAssertionError(message,label); } catch (IllegalAccessException e) { return new JmlAssertionError(message,label); } } return new JmlAssertionError(message,label); } static public void convertPrecondition(Precondition ex) { throw new Precondition(ex); } /** Used to create empty lists for RAC handling of loops */ //@ public normal_behavior //@ ensures \result.size() == 0; static public /*@non_null pure */ <E> org.jmlspecs.lang.JMLList<E> defaultEmpty() { return null; } /** Determines whether to report assertion failures as exceptions (true) * or error messages (false). */ public static boolean useExceptions = System.getProperty("org.jmlspecs.openjml.racexceptions") != null; /** Determines whether to report assertion failures as java assertions (true) * or error messages (false). */ public static boolean useJavaAssert = System.getProperty("org.jmlspecs.openjml.racjavaassert") != null; /** If true, then error messages reporting assertion failures are * accompanied with a stack trace to log.errorWriter. */ public static boolean showStack = System.getProperty("org.jmlspecs.openjml.racshowstack") != null; // FIXME - what are these for - are they used? static final public String invariantMethodString = "_JML$$$checkInvariant"; static final public String staticinvariantMethodString = "_JML$$$checkStaticInvariant"; /** Returns true if the given class, found on the current classpath, if compiled with RAC */ public static boolean isRACCompiled(Class<?> clazz) { // The class named here must match that used in JmlCompiler.java return null != clazz.getAnnotation(org.jmlspecs.annotation.RACCompiled.class); } /** Reports a JML assertion failure with the given message if the second argument is null * @param message the message to report if the second argument is null * @param v value to be tested * @return the object which is the last argument */ //@ ensures \result == v; @Nullable public static <T> T nonNullCheck(@Nullable String message, @Nullable T v) { if (v == null) assertionFailure(message); return v; } /** Reports a JML assertion failure with the given message if the second argument is false * @param message the message to report if the second argument is false * @param b value to be tested * @param v value to be returned * @return the object which is the last argument */ public static <T> T trueCheck(String message, boolean b, T v) { if (!b) assertionFailure(message); return v; } /** Reports a JML assertion failure with the given message if the second argument * is not equal to the third (this is a test for object non-equality) * @param message the message to report if the second argument and third arguments are not equal * @param o value to be tested * @param v value to be returned * @return the object which is the last argument */ public static <T> T eqCheck(String message, Object o, T v) { if (o != v) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is * not within the given range (inclusive), otherwise returns the last argument. */ public static int intRangeCheck(String message, int low, int high, int v) { if (!(low <= v && v <= high)) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static int zeroIntCheck(String message, int v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static long zeroLongCheck(String message, long v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static double zeroDoubleCheck(String message, double v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static float zeroFloatCheck(String message, float v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static short zeroShortCheck(String message, short v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static byte zeroByteCheck(String message, byte v) { if (v == 0) assertionFailure(message); return v; } /** Reports a JML assertion with the given message if the given value is 0, * otherwise returns the value. */ public static char zeroCharCheck(String message, char v) { if (v == 0) assertionFailure(message); return v; } /** Returns the name of the class of the argument */ public static String typeName(Throwable t) { return t.getClass().toString(); } /** Tests that an array and all the elements of the array are non-null * @param array the array to test * @return true if all elements are not null, false if at least one is */ public static boolean nonnullElementCheck(Object[] array) { if (array == null) return false; for (Object o: array) { if (o == null) return false; } return true; } // The report... methods provide a mechanism for reporting // values encountered during execution. /** Just prints out a message */ public static void report(String str) { System.out.println(str); } public static void reportNoSuchField(NoSuchFieldError t, @Nullable String location) { t.printStackTrace(); String msg = t.getMessage(); int k = msg.indexOf('('); if (k >= 0) msg = msg.substring(0,k); msg = "Skipping a specification clause because it contains an uncompiled ghost or model field: " + msg; if (location != null) msg = msg + " (" + location + ")"; report(msg); } public static void reportNoSuchMethod(NoSuchMethodError t, @Nullable String location) { String msg = t.getMessage(); int k = msg.indexOf('('); if (k >= 0) msg = msg.substring(0,k); msg = "Skipping a specification clause because it contains an uncompiled model method: " + msg; if (location != null) msg = msg + " (" + location + ")"; report(msg); } public static final String REPORT_EXCEPTION = "reportException"; // must match method name /** Prints out a message, the exception message, and the exception stack */ public static void reportException(String str, RuntimeException e) { System.out.println(str); e.printStackTrace(System.out); } /** Reports a byte value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static byte reportByte(String key, byte v) { report("LABEL " + key + " = " + v); return v; } /** Reports a short value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static short reportShort(String key, short v) { report("LABEL " + key + " = " + v); return v; } /** Reports a char value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static char reportChar(String key, char v) { report("LABEL " + key + " = " + v); return v; } /** Reports a long value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static long reportLong(String key, long v) { report("LABEL " + key + " = " + v); return v; } /** Reports a float value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static float reportFloat(String key, float v) { report("LABEL " + key + " = " + v); return v; } /** Reports a double value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static double reportDouble(String key, double v) { report("LABEL " + key + " = " + v); return v; } /** Reports a boolean value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static boolean reportBoolean(String key, boolean v) { report("LABEL " + key + " = " + v);System.out.flush(); return v; } /** Reports a int value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static int reportInt(String key, int v) { report("LABEL " + key + " = " + v); return v; } /** Reports a Object value under the given key * @param key the identifier for the value * @param v the value to report * @return the value of v */ public static Object reportObject(String key, Object v) { report("LABEL " + key + " = " + v); return v; } static public void callClassInvariant(Object o, String fqClassName) { String ms = "???"; try { Class<?> clazz = Class.forName(fqClassName); ms = invariantMethodString + "$$" + fqClassName.replace(".","$"); Method m = clazz.getMethod(ms); m.invoke(o); } catch (Exception e) { // If no class or method found, we ignore //System.out.println("FAILED TO CALL INVARIANT FOR " + ms + " " + e.getMessage()); //e.printStackTrace(System.out); } } static public void callStaticClassInvariant(String fqClassName) { try { Class<?> clazz = Class.forName(fqClassName); Method m = clazz.getMethod(staticinvariantMethodString); m.invoke(null); } catch (Exception e) { //System.out.println("FAILED TO CALL STATIC INVARIANT FOR " + fqClassName); //e.printStackTrace(System.out); // If no class or method found, we ignore } } public static boolean equalTYPE(IJMLTYPE t1, IJMLTYPE t2) { if (t1 == t2) return true; if (t1 == null || t2 == null) return false; return t1.equals(t2); } // TODO - document - this and following public static @NonNull IJMLTYPE makeTYPE(@NonNull Class<?> base, @NonNull IJMLTYPE... args) { return JmlTypeRac.make(base,args); } public static IJMLTYPE makeTYPEN(Class<?> base) { return JmlTypeRac.make(base,null); } public static IJMLTYPE makeTYPEQ() { return JmlTypeRac.make(null,null); } public static final IJMLTYPE[] emptyArgs = {}; public static @NonNull IJMLTYPE makeTYPE0(@NonNull Class<?> base) { if (base == null) return null; return JmlTypeRac.make(base,emptyArgs); } public static @NonNull IJMLTYPE makeTYPE1(@NonNull Class<?> base, @NonNull IJMLTYPE a0) { if (base == null) return null; return JmlTypeRac.make(base,new IJMLTYPE[]{a0}); } public static @NonNull IJMLTYPE makeTYPE2(@NonNull Class<?> base, @NonNull IJMLTYPE a0, @NonNull IJMLTYPE a1) { if (base == null) return null; return JmlTypeRac.make(base,new IJMLTYPE[]{a0,a1}); } public static Class<?> erasure(IJMLTYPE t) { return t.erasure(); } public static IJMLTYPE[] typeargs(IJMLTYPE t) { return t.typeargs(); } public static boolean isArray(IJMLTYPE t) { return t.erasure().isArray(); } public static IJMLTYPE getComponentType(IJMLTYPE t) { return makeTYPE0(t.erasure().getComponentType()); } public static String getClassName(Object o) { return o.getClass().getName(); } public static String concat(String s1, String s2) { return s1 + s2; } public static boolean isSubTypeOf(IJMLTYPE t, IJMLTYPE tt) { try { return tt.erasure().isAssignableFrom(t.erasure()); } catch (java.lang.IncompatibleClassChangeError e) { System.err.println("ISTO: " + t.erasure() + " " + tt.erasure()); return false; } } public static boolean isEqualTo(IJMLTYPE t, IJMLTYPE tt) { if (t == tt) return true; if (t == null || tt == null) return false; return tt.erasure() == t.erasure(); } public static <T> Iterator<T> iterator(Iterable<T> iterable) { return iterable.iterator(); } public static <T> T next(Iterator<T> iterable) { return iterable.next(); } public static boolean hasNext(Iterator<?> iterable) { return iterable.hasNext(); } // TODO - document this and following private static class JmlTypeRac implements IJMLTYPE { final private Class<?> base; final private IJMLTYPE[] args; final private static Map<IJMLTYPE,IJMLTYPE> internSet = new HashMap<IJMLTYPE,IJMLTYPE>(); public static IJMLTYPE make(Class<?> base, IJMLTYPE[] args) { JmlTypeRac t = new JmlTypeRac(base,args); return t.intern(); } public String toString() { if (base == null) return "?"; // FIXME - really this is just unknown, not a wildcard String s = base.toString(); if (args != null && args.length > 0) { s = s + "<"; boolean first = true; for (IJMLTYPE t: args) { if (first) first = false; else s = s + ","; s = s + t.toString(); } s = s + ">"; } return s; } private IJMLTYPE intern() { IJMLTYPE tt = internSet.get(this); if (tt == null) { tt = this; internSet.put(this,this); } return tt; } private JmlTypeRac(Class<?> base, IJMLTYPE... args) { this.base = base; this.args = args; } @Override public IJMLTYPE[] typeargs() { return args; } @Override public boolean equals(IJMLTYPE t) { return isEqualTo(this,t); } //JAVA16 @Override public int hashCode() { if (base == null) return 0; int i = base.hashCode(); int k = 0; for (IJMLTYPE t: args) i = i + (t.hashCode()<< (++k)); return i; } @Override public Class<?> erasure() { return base; } @Override public boolean isArray() { return base.isArray(); } @Override public boolean isSubtypeOf(IJMLTYPE t) { return t.erasure().isAssignableFrom(this.base); } } public static interface ValueBool { public boolean value(final Object[] args); } public static interface ValueInt { public int value(final Object[] args); } public static interface ValueShort { public short value(final Object[] args); } public static interface ValueChar { public char value(final Object[] args); } public static interface ValueLong { public long value(final Object[] args); } public static interface ValueByte { public byte value(final Object[] args); } public static interface ValueFloat { public float value(final Object[] args); } public static interface ValueDouble { public double value(final Object[] args); } public static interface Value<T> { public T value(final Object[] args); } public static BigInteger bigint_add(BigInteger a, BigInteger b) { return a.add(b); } public static BigInteger bigint_sub(BigInteger a, BigInteger b) { return a.subtract(b); } public static BigInteger bigint_sub1(BigInteger a) { return a.subtract(BigInteger.ONE); } public static BigInteger bigint_mul(BigInteger a, BigInteger b) { return a.multiply(b); } public static BigInteger bigint_div(BigInteger a, BigInteger b) { return a.divide(b); } public static BigInteger bigint_mod(BigInteger a, BigInteger b) { return a.mod(b); } public static BigInteger bigint_neg(BigInteger a) { return a.negate(); } public static boolean bigint_lt(BigInteger a, BigInteger b) { return a.compareTo(b) < 0; } public static boolean bigint_le(BigInteger a, BigInteger b) { return a.compareTo(b) <= 0; } public static boolean bigint_gt(BigInteger a, BigInteger b) { return a.compareTo(b) > 0; } public static boolean bigint_ge(BigInteger a, BigInteger b) { return a.compareTo(b) >= 0; } public static boolean bigint_eq(BigInteger a, BigInteger b) { return a.compareTo(b) == 0; } public static boolean bigint_ne(BigInteger a, BigInteger b) { return a.compareTo(b) != 0; } public static boolean bigint_nonzero(BigInteger a) { return a.compareTo(BigInteger.ZERO) != 0; } public static float bigint_tofloat(BigInteger a) { return a.floatValue(); } public static double bigint_todouble(BigInteger a) { return a.doubleValue(); } public static long bigint_tolong(BigInteger a) { return a.longValue(); } public static int bigint_toint(BigInteger a) { return a.intValue(); } public static short bigint_toshort(BigInteger a) { return a.shortValue(); } public static byte bigint_tobyte(BigInteger a) { return a.byteValue(); } public static Real bigint_toreal(BigInteger a) { return new Real(a.doubleValue()); } public static BigInteger bigint_valueOf(long i) { return new BigInteger(Long.toString(i)); } public static Real real_add(Real a, Real b) { return a.add(b); } public static Real real_sub(Real a, Real b) { return a.subtract(b); } public static Real real_mul(Real a, Real b) { return a.multiply(b); } public static Real real_div(Real a, Real b) { return a.divide(b); } public static Real real_mod(Real a, Real b) { return a.mod(b); } public static Real real_neg(Real a) { return a.neg(); } public static boolean real_lt(Real a, Real b) { return a.compareTo(b) < 0; } public static boolean real_le(Real a, Real b) { return a.compareTo(b) <= 0; } public static boolean real_gt(Real a, Real b) { return a.compareTo(b) > 0; } public static boolean real_ge(Real a, Real b) { return a.compareTo(b) >= 0; } public static boolean real_eq(Real a, Real b) { return a.compareTo(b) == 0; } public static boolean real_ne(Real a, Real b) { return a.compareTo(b) != 0; } public static boolean real_nonzero(Real a) { return a.compareTo(Real.ZERO) != 0; } public static Real real_valueOf(double i) { return Real.valueOf(i); } public static double real_todouble(Real a) { return a.doubleValue(); } public static float real_tofloat(Real a) { return (float)a.doubleValue(); } /** Makes a new copy of the argument */ public static <T> T[] copyArray(T[] o) { T[] n = Arrays.copyOf(o,o.length); return n; } /** Makes a new copy of the argument */ public static byte[] copyByteArray(byte[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static int[] copyIntArray(int[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static short[] copyShortArray(short[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static char[] copyCharArray(char[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static boolean[] copyBooleanArray(boolean[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static double[] copyDoubleArray(double[] o) { return Arrays.copyOf(o,o.length); } /** Makes a new copy of the argument */ public static float[] copyFloatArray(float[] o) { return Arrays.copyOf(o,o.length); } public static String toStringObject(Object o) { return o == null ? "null" : o.toString(); } public static String toStringBoolean(boolean b) { return Boolean.toString(b); } public static String toStringInt(int b) { return Integer.toString(b); } public static String toStringLong(long b) { return Long.toString(b); } public static String toStringShort(short b) { return Short.toString(b); } public static String toStringByte(byte b) { return Byte.toString(b); } public static String toStringFloat(float b) { return Float.toString(b); } public static String toStringDouble(double b) { return Double.toString(b); } public static String toStringChar(char b) { return Character.toString(b); } /** Used just for debugging - a breakpoint is kept set within the method */ public boolean print(String msg) { System.out.println(msg); return true; } }