// // 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.Field; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.Config; import gov.nasa.jpf.ConfigChangeListener; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.annotation.MJI; import sun.misc.Unsafe; /** * MJI NativePeer class for java.util.Random library abstraction * * model - peer delegation is done via restoring a singleton, which unfortunately * has to resort to the rather nasty Unsafe mechanism because the required * random internals are private. We could use per-instance native objects * stored as object attributes, but that would only partly solve the problem * because we still would have to backtrack the internal state of such objects */ public class JPF_java_util_Random extends NativePeer { static class Delegatee extends Random { public int next (int nBits){ return super.next(nBits); } } // we need this because cg.enumerate_random might be set on demand static class ConfigListener implements ConfigChangeListener { JPF_java_util_Random nativeRandom; public ConfigListener(JPF_java_util_Random nativeRandom) { this.nativeRandom = nativeRandom; } @Override public void propertyChanged(Config config, String key, String oldValue, String newValue) { if ("cg.enumerate_random".equals(key)) { nativeRandom.setEnumerateRandom(config); } } @Override public void jpfRunTerminated(Config config){ config.removeChangeListener(this); } } boolean enumerateRandom; // those are only used if enumerateRandom is not set, i.e. we delegate to the host VM boolean reproducibleRandom; long constantSeed; int[] defaultIntSet; // in case we have an nextInt(), i.e. an unspecified upper boundary long[] defaultLongSet; double[] defaultDoubleSet; float[] defaultFloatSet; // since peer methods are atomic, we just keep one delegator instead of per-object, // which would have to rely on attributes and still require storing/restoring // the seed state with nasty Unsafe static Delegatee delegatee = new Delegatee(); // this is bad stuff we need to set/retrieve the Random.seed value. We only have // a choice between a rock and a hard place here - either we depend on this // field and sun.misc.Unsafe, or we duplicate the algorithms. The hard place // seems worse than the rock private static Unsafe unsafe; private static long seedFieldOffset; static { try { // Unsafe.getUnsafe() can only be called from a SystemClassLoaderContext Field singletonField = Unsafe.class.getDeclaredField("theUnsafe"); singletonField.setAccessible(true); unsafe = (Unsafe)singletonField.get(null); seedFieldOffset = unsafe.objectFieldOffset(Random.class.getDeclaredField("seed")); } catch (Exception ex) { throw new JPFException("cannot access java.util.Random internals: " + ex); } } private static void setNativeSeed (Random rand, long seed) { AtomicLong al = (AtomicLong) unsafe.getObject(rand, seedFieldOffset); al.set(seed); } private static long getNativeSeed (Random rand){ AtomicLong al = (AtomicLong) unsafe.getObject(rand, seedFieldOffset); return al.longValue(); } public JPF_java_util_Random (Config conf) { setEnumerateRandom(conf); conf.addChangeListener(new ConfigListener(this)); reproducibleRandom = conf.getBoolean("vm.reproducible_random", true); constantSeed = conf.getLong("vm.random_seed", 42); defaultIntSet = conf.getIntArray("vm.random_ints", Integer.MIN_VALUE, 0, Integer.MAX_VALUE); defaultDoubleSet = conf.getDoubleArray("vm.random_doubles", Double.MIN_VALUE, 0, Double.MAX_VALUE); defaultLongSet = conf.getLongArray("vm.random_longs", Long.MIN_VALUE, 0, Long.MAX_VALUE); defaultFloatSet = conf.getFloatArray("vm.random_floats", Float.MIN_VALUE, 0, Float.MAX_VALUE); } void setEnumerateRandom (Config conf) { enumerateRandom = conf.getBoolean("cg.enumerate_random", false); if (enumerateRandom){ JPF_gov_nasa_jpf_vm_Verify.init(conf); } } long computeDefaultSeed(){ Random rand = (reproducibleRandom) ? new Random(constantSeed) : new Random(); return getNativeSeed( rand); } static void storeSeed (MJIEnv env, int objRef, long seed, FeatureExpr ctx){ env.setLongField(ctx, objRef, "seed", new One<>(seed)); } static long getSeed (FeatureExpr ctx, MJIEnv env, int objRef){ return env.getLongField(objRef, "seed").simplify(ctx).getValue(); } static void restoreRandomState (FeatureExpr ctx, MJIEnv env, int objRef, Random rand){ long seed = getSeed( ctx, env, objRef); setNativeSeed( rand, seed); } static void storeRandomState (MJIEnv env, int objRef, Random rand, FeatureExpr ctx){ long seed = getNativeSeed( rand); storeSeed( env, objRef, seed, ctx); } //--- the publics @MJI public void $init____V (MJIEnv env, int objRef, FeatureExpr ctx){ long seed = computeDefaultSeed(); storeSeed( env, objRef, seed, ctx); } @MJI public void $init__J__V (MJIEnv env, int objRef, long seedStarter, FeatureExpr ctx){ // note - the provided seedStarter is modified by java.util.Random, it is // NOT the internal value that is consecutively used Random rand = new Random(seedStarter); storeRandomState(env, objRef, rand, ctx); } @MJI public void setSeed__J__V (MJIEnv env, int objRef, long seedStarter, FeatureExpr ctx){ // my, what an effort to change a long. restoreRandomState( ctx, env, objRef, delegatee); delegatee.setSeed(seedStarter); // compute the new internal value storeRandomState(env, objRef, delegatee, ctx); } @MJI public boolean nextBoolean____Z (MJIEnv env, int objRef, FeatureExpr ctx){ if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getBoolean____Z(env,-1, ctx); } else { restoreRandomState(ctx, env, objRef, delegatee); boolean ret = delegatee.nextBoolean(); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public int nextInt__I__I (MJIEnv env, int objRef, int n, FeatureExpr ctx){ try { if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getInt__II__I(env,-1,One.valueOf(0), One.valueOf(n-1), ctx); } else { restoreRandomState(ctx, env, objRef, delegatee); int ret = delegatee.nextInt(n); storeRandomState(env, objRef, delegatee, ctx); return ret; } } catch (Exception e) { System.out.println(e.getMessage()); for (StackTraceElement stack: e.getStackTrace()) { System.out.println(stack); } throw e; } } @MJI public int nextInt____I (MJIEnv env, int objRef, FeatureExpr ctx){ if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getIntFromList(env, defaultIntSet); } else { restoreRandomState(ctx, env, objRef, delegatee); int ret = delegatee.nextInt(); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public int next__I__I (MJIEnv env, int objRef, int nBits, FeatureExpr ctx){ if (enumerateRandom){ // <2do> we can't do this with an interval since it most likely would explode our state space return JPF_gov_nasa_jpf_vm_Verify.getIntFromList(env, defaultIntSet); } else { restoreRandomState(ctx, env, objRef, delegatee); int ret = delegatee.next( nBits); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public void nextBytes___3B__V (MJIEnv env, int objRef, int dataRef, FeatureExpr ctx){ // <2do> this one is an even worse state exploder. We could use cascaded CGs, // but chances are this really kills us, so we just ignore 'enumerateRandom' for now int n = env.getArrayLength(ctx, dataRef); byte[] data = new byte[n]; restoreRandomState(ctx, env, objRef, delegatee); delegatee.nextBytes(data); storeRandomState(env, objRef, delegatee, ctx); for (int i = 0; i < n; i++) { env.setByteArrayElement(ctx, dataRef, i, new One<>(data[i])); } } @MJI public long nextLong____J (MJIEnv env, int objRef, FeatureExpr ctx){ if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getLongFromList(env, defaultLongSet); } else { restoreRandomState(ctx, env, objRef, delegatee); long ret = delegatee.nextLong(); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public float nextFloat____F (MJIEnv env, int objRef, FeatureExpr ctx){ if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getFloatFromList(env, defaultFloatSet, ctx); } else { restoreRandomState(ctx, env, objRef, delegatee); float ret = delegatee.nextFloat(); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public double nextDouble____D (MJIEnv env, int objRef, FeatureExpr ctx){ if (enumerateRandom){ return JPF_gov_nasa_jpf_vm_Verify.getDoubleFromList(env, defaultDoubleSet, ctx); } else { restoreRandomState(ctx, env, objRef, delegatee); double ret = delegatee.nextDouble(); storeRandomState(env, objRef, delegatee, ctx); return ret; } } @MJI public double nextGaussian____D (MJIEnv env, int objRef, FeatureExpr ctx){ // <2do> we don't support this yet, neither for enumerateRandom nor // delegation (which would require an additional 'haveNextGaussian' state) restoreRandomState(ctx, env, objRef, delegatee); double ret = delegatee.nextGaussian(); storeRandomState(env, objRef, delegatee, ctx); return ret; } }