view src/peers/gov/nasa/jpf/vm/JPF_java_util_Random.java @ 0:61d41facf527

initial v8 import (history reset)
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Fri, 23 Jan 2015 10:14:01 -0800
parents
children
line wrap: on
line source

/*
 * Copyright (C) 2014, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The Java Pathfinder core (jpf-core) platform is licensed under the
 * Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0. 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package gov.nasa.jpf.vm;

import gov.nasa.jpf.Config;
import gov.nasa.jpf.ConfigChangeListener;
import gov.nasa.jpf.JPFException;
import gov.nasa.jpf.annotation.MJI;
import gov.nasa.jpf.vm.JPF_gov_nasa_jpf_vm_Verify;
import gov.nasa.jpf.vm.MJIEnv;
import gov.nasa.jpf.vm.NativePeer;

import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;

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
 */
@SuppressWarnings("sunapi")
public class JPF_java_util_Random extends NativePeer {

  static class Delegatee extends Random {
    @Override
	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){
    env.setLongField(objRef, "seed", seed);
  }

  static long getSeed (MJIEnv env, int objRef){
    return env.getLongField(objRef, "seed");
  }
  
  static void restoreRandomState (MJIEnv env, int objRef, Random rand){
    long seed = getSeed( env, objRef);
    setNativeSeed( rand, seed);
  }
  
  static void storeRandomState (MJIEnv env, int objRef, Random rand){
    long seed = getNativeSeed( rand);
    storeSeed( env, objRef, seed);
  }
  
  
  //--- the publics

  @MJI
  public void $init____V (MJIEnv env, int objRef){
    long seed = computeDefaultSeed();
    storeSeed( env, objRef, seed);
  }
  
  @MJI
  public void $init__J__V (MJIEnv env, int objRef, long seedStarter){
    // 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);    
  }
  
  @MJI
  public void setSeed__J__V (MJIEnv env, int objRef, long seedStarter){
    // my, what an effort to change a long.
    restoreRandomState( env, objRef, delegatee);
    delegatee.setSeed(seedStarter); // compute the new internal value
    storeRandomState(env, objRef, delegatee);    
  }
  
  @MJI
  public boolean nextBoolean____Z (MJIEnv env, int objRef){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getBoolean____Z(env,-1);

    } else {
      restoreRandomState(env, objRef, delegatee);
      boolean ret = delegatee.nextBoolean();
      storeRandomState(env, objRef, delegatee);
      return ret;
    }
  }
  
  @MJI
  public int nextInt__I__I (MJIEnv env, int objRef, int n){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getInt__II__I(env,-1,0,n-1);
      
    } else {
      restoreRandomState(env, objRef, delegatee);
      int ret = delegatee.nextInt(n);
      storeRandomState(env, objRef, delegatee);
      return ret;
    }
  }
  
  @MJI
  public int nextInt____I (MJIEnv env, int objRef){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getIntFromList(env, defaultIntSet);
      
    } else {
      restoreRandomState(env, objRef, delegatee);
      int ret = delegatee.nextInt();
      storeRandomState(env, objRef, delegatee);
      return ret;
    }
  }
  
  @MJI
  public int next__I__I (MJIEnv env, int objRef, int nBits){
    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(env, objRef, delegatee);
      int ret = delegatee.next( nBits);
      storeRandomState(env, objRef, delegatee);
      return ret;
    }
  }
  
  @MJI
  public void nextBytes___3B__V (MJIEnv env, int objRef, int dataRef){
    // <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(dataRef);
    byte[] data = new byte[n];

    restoreRandomState(env, objRef, delegatee);
    delegatee.nextBytes(data);
    storeRandomState(env, objRef, delegatee);

    for (int i = 0; i < n; i++) {
      env.setByteArrayElement(dataRef, i, data[i]);
    }
  }
  
  @MJI
  public long nextLong____J (MJIEnv env, int objRef){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getLongFromList(env, defaultLongSet);
      
    } else {
      restoreRandomState(env, objRef, delegatee);
      long ret = delegatee.nextLong();
      storeRandomState(env, objRef, delegatee);
      return ret;
    }    
  }

  @MJI
  public float nextFloat____F (MJIEnv env, int objRef){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getFloatFromList(env, defaultFloatSet);
      
    } else {
      restoreRandomState(env, objRef, delegatee);
      float ret = delegatee.nextFloat();
      storeRandomState(env, objRef, delegatee);
      return ret;
    }    
  }

  @MJI
  public double nextDouble____D (MJIEnv env, int objRef){
    if (enumerateRandom){
      return JPF_gov_nasa_jpf_vm_Verify.getDoubleFromList(env, defaultDoubleSet);
      
    } else {
      restoreRandomState(env, objRef, delegatee);
      double ret = delegatee.nextDouble();
      storeRandomState(env, objRef, delegatee);
      return ret;
    }    
  }

  @MJI
  public double nextGaussian____D (MJIEnv env, int objRef){
    // <2do> we don't support this yet, neither for enumerateRandom nor
    // delegation (which would require an additional 'haveNextGaussian' state)
    restoreRandomState(env, objRef, delegatee);
    double ret = delegatee.nextGaussian();
    storeRandomState(env, objRef, delegatee);
    return ret;
  }
}