//
// 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.util.HashMap;
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.io.RandomAccessFile library abstraction
*
* @author Owen O'Malley
*/
public class JPF_java_io_RandomAccessFile extends NativePeer {
// need to see whether the file is already in use
// if so, then we'll update the file data and length in the original file
// we do update the length in the local object, but not the data
static HashMap<Integer, Integer> File2DataMap;
public static boolean init (Config conf) {
File2DataMap = new HashMap<>();
return (File2DataMap != null);
}
// get the mapped object if one exists
private static int getMapping(MJIEnv env, int this_ptr, FeatureExpr ctx) {
int fn_ptr = env.getReferenceField(ctx,this_ptr, "filename").getValue();
Object o = File2DataMap.get(new Integer(fn_ptr));
if (o == null)
return this_ptr;
return ((Integer)o).intValue();
}
// set the mapping during the constructor call
@MJI
public void setDataMap____V (MJIEnv env, int this_ptr, FeatureExpr ctx) {
int fn_ptr = env.getReferenceField(ctx,this_ptr, "filename").getValue();
if (!File2DataMap.containsKey(new Integer(fn_ptr)))
File2DataMap.put(new Integer(fn_ptr),new Integer(this_ptr));
}
static ClassInfo getDataRepresentationClassInfo (MJIEnv env, FeatureExpr ctx) {
ThreadInfo ti = env.getThreadInfo();
// Instruction insn = ti.getPC().getValue();
ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(DataRepresentation);
if (ci.pushRequiredClinits(ctx, ti)){
env.repeatInvocation();
return null;
}
return ci;
}
@MJI
public void writeByte__I__V (MJIEnv env, int this_ptr, int data, FeatureExpr ctx) {
long current_posn = env.getLongField(this_ptr, current_position).getValue();
long current_len = env.getLongField(this_ptr, current_length).getValue();
int chunk_size = env.getStaticIntField(RandomAccessFile, CHUNK_SIZE);
int chunk = findDataChunk(env, this_ptr, current_posn,
chunk_size, ctx);
setDataValue(env, chunk, current_posn, (byte) data, chunk_size, ctx);
current_posn += 1;
env.setLongField(ctx, this_ptr, current_position, new One<>(current_posn));
if (current_posn >= current_len) {
env.setLongField(ctx, this_ptr, current_length, new One<>(current_posn + 1));
// update length in the mapped object if it exists
env.setLongField(ctx, getMapping(env,this_ptr, ctx), current_length, new One<>(current_posn + 1));
}
}
/**
* This is a bit lame doing it this way, but it is easy.
*/
@SuppressWarnings("deprecation")
@MJI
public void write___3BII__V (MJIEnv env, int this_ptr, int data_array,
int start, int len, FeatureExpr ctx) {
byte[] data_values = env.getByteArrayObjectDeprecated(ctx, data_array);
for(int i=start; i < len; ++i) {
writeByte__I__V(env, this_ptr, data_values[i], ctx);
}
}
@MJI
public void setLength__J__V(MJIEnv env, int this_ptr, long len, FeatureExpr ctx) {
long current_posn = env.getLongField(this_ptr, current_position).getValue();
long current_len = env.getLongField(this_ptr, current_length).getValue();
if (current_posn >= len && len < current_len) {
env.setLongField(ctx, this_ptr, current_position, new One<>(len));
}
env.setLongField(ctx, this_ptr, current_length, new One<>(len));
// update length in the mapped object if it exists
env.setLongField(ctx, getMapping(env,this_ptr, ctx), current_length, new One<>(current_posn + 1));
}
@MJI
public int read___3BII__I (MJIEnv env, int this_ptr, int data_array,
int start, int len, FeatureExpr ctx) {
int i = 0;
long current_posn = env.getLongField(this_ptr, current_position).getValue();
long current_len = env.getLongField(this_ptr, current_length).getValue();
while (i < len && current_posn < current_len) {
env.setByteArrayElement(ctx, data_array, start + i, new One<>(readByte____B(env, this_ptr, ctx)));
i += 1;
current_posn += 1;
}
if (i == 0) {
return -1;
}
return i;
}
@MJI
public byte readByte____B (MJIEnv env, int this_ptr, FeatureExpr ctx) {
long current_posn = env.getLongField(this_ptr, current_position).getValue();
long current_len = env.getLongField(this_ptr, current_length).getValue();
int chunk_size = env.getStaticIntField(RandomAccessFile, CHUNK_SIZE);
if (current_posn >= current_len) {
env.throwException(ctx, EOFException);
}
int chunk = findDataChunk(env, this_ptr, current_posn,
chunk_size, ctx);
byte result = getDataValue(env, chunk, current_posn, chunk_size, ctx);
env.setLongField(ctx, this_ptr, current_position, new One<>(current_posn + 1));
return result;
}
private static final int INT_SIZE = 4;
private static final String data_root = "data_root";
private static final String current_position = "currentPosition";
private static final String current_length = "currentLength";
private static final String CHUNK_SIZE = "CHUNK_SIZE";
private static final String chunk_index = "chunk_index";
private static final String next = "next";
private static final String data = "data";
private static final String EOFException = "java.io.EOFException";
private static final String RandomAccessFile = "java.io.RandomAccessFile";
private static final String DataRepresentation =
RandomAccessFile + "$DataRepresentation";
private static int findDataChunk(MJIEnv env, int this_ptr, long position,
int chunk_size, FeatureExpr ctx) {
ClassInfo dataRep = getDataRepresentationClassInfo(env, ctx);
if (dataRep == null) {
// will be reexecuted
return 0;
}
//check if the file data is mapped, use mapped this_ptr if it exists
this_ptr = getMapping(env,this_ptr, ctx);
int prev_obj = MJIEnv.NULL;
int cur_obj = env.getReferenceField(ctx, this_ptr, data_root).getValue();
long chunk_idx = position/chunk_size;
while (cur_obj != MJIEnv.NULL &&
env.getLongField(cur_obj, chunk_index).getValue() < chunk_idx) {
prev_obj = cur_obj;
cur_obj = env.getReferenceField(ctx, cur_obj, next).getValue();
}
if (cur_obj != MJIEnv.NULL &&
env.getLongField(cur_obj, chunk_index).getValue() == chunk_idx) {
return cur_obj;
}
int result = env.newObject(ctx, dataRep);
int int_array = env.newIntArray(chunk_size/INT_SIZE);
env.setReferenceField(ctx, result, data, int_array);
env.setLongField(ctx, result, chunk_index, new One<>(chunk_idx));
env.setReferenceField(ctx, result, next, cur_obj);
if (prev_obj == MJIEnv.NULL) {
env.setReferenceField(ctx, this_ptr, data_root, result);
} else {
env.setReferenceField(ctx, prev_obj, next, result);
}
return result;
}
private static void setDataValue(MJIEnv env, int chunk_obj, long position,
byte data_value, int chunk_size, FeatureExpr ctx) {
int offset = (int) (position % chunk_size);
int index = offset / INT_SIZE;
int bit_shift = 8 * (offset % INT_SIZE);
int int_array = env.getReferenceField(ctx, chunk_obj, data).getValue();
int old_value = env.getIntArrayElement(int_array, index).getValue();
env.setIntArrayElement(ctx, int_array,
index, new One<>((old_value & ~(0xff << bit_shift)) |
data_value << bit_shift));
}
private static byte getDataValue(MJIEnv env, int chunk_obj, long position,
int chunk_size, FeatureExpr ctx) {
int offset = (int) (position % chunk_size);
int index = offset / INT_SIZE;
int bit_shift = 8 * (offset % INT_SIZE);
int int_array = env.getReferenceField(ctx, chunk_obj, data).getValue();
return (byte) (env.getIntArrayElement(int_array, index).getValue() >> bit_shift);
}
}