view src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.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.JPFException;
import gov.nasa.jpf.annotation.MJI;
import gov.nasa.jpf.vm.ArrayFields;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.FieldInfo;
import gov.nasa.jpf.vm.MJIEnv;
import gov.nasa.jpf.vm.NativePeer;
import gov.nasa.jpf.vm.SystemState;
import gov.nasa.jpf.vm.ThreadInfo;


/**
 * we don't want this class! This is a hodgepodge of stuff that shouldn't be in Java, but
 * is handy for some hacks. The reason we have it here - very rudimentary - is that
 * java.util.concurrent makes use of the atomic compare&swap which is in it.
 * The choice was to duplicate a lot of relatively difficult code in the "right" class
 * (java.util.concurrent.locks.AbstractQueuedSynchronizer) or a small amount of straight forward
 * code in the "wrong" class (sun.misc.Unsafe). Knowing a bit about the "library chase" game,
 * we opt for the latter
 *
 * <2do> this might change with better modeling of high level java.util.concurrent constructs
 */
public class JPF_sun_misc_Unsafe extends NativePeer {

  @MJI
  public int getUnsafe____Lsun_misc_Unsafe_2 (MJIEnv env, int clsRef) {
    int objRef = env.getStaticReferenceField("sun.misc.Unsafe", "theUnsafe");
    return objRef;
  }

  @MJI
  public long objectFieldOffset__Ljava_lang_reflect_Field_2__J (MJIEnv env, int unsafeRef, int fieldRef) {
    return fieldOffset__Ljava_lang_reflect_Field_2__I(env, unsafeRef, fieldRef);
  }

  /**
   * we don't really return an offset here, since that would be useless. What we really want is
   * to identify the corresponding FieldInfo, and that's much easier done with the Field
   * registration id
   */
  @MJI
  public int fieldOffset__Ljava_lang_reflect_Field_2__I (MJIEnv env, int unsafeRef, int fieldRef) {
    //FieldInfo fi = JPF_java_lang_reflect_Field.getFieldInfo(env, fieldRef);
    //return fi.getStorageOffset();
    return env.getIntField(fieldRef, "regIdx");
  }

  @MJI
  public boolean compareAndSwapObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z (MJIEnv env, int unsafeRef,
                                                                                                             int objRef, long fieldOffset,
                                                                                                             int expectRef, int updateRef) {
    int actual = getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, unsafeRef, objRef, fieldOffset);
    if (actual == expectRef) {
      putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, updateRef);
      return true;
    }
    return false;
  }

  @MJI
  public boolean compareAndSwapInt__Ljava_lang_Object_2JII__Z (MJIEnv env, int unsafeRef,
                                                                      int objRef, long fieldOffset, int expect, int update) {
    int actual = getInt__Ljava_lang_Object_2J__I(env, unsafeRef, objRef, fieldOffset);
    if (actual == expect) {
      putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, update);
      return true;
    }
    return false;
  }

  @MJI
  public boolean compareAndSwapLong__Ljava_lang_Object_2JJJ__Z (MJIEnv env, int unsafeRef,
                                                                       int objRef, long fieldOffset, long expect, long update) {
    long actual = getLong__Ljava_lang_Object_2J__J(env, unsafeRef, objRef, fieldOffset);
    if (actual == expect) {
      putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, update);
      return true;
    }
    return false;
  }


  // this is a specialized, native wait() for the current thread that does not require a lock, and that can
  // be turned off by a preceding unpark() call (which is not accumulative)
  // park can be interrupted, but it doesn't throw an InterruptedException, and it doesn't clear the status
  // it can only be called from the current (parking) thread
  @MJI
  public void park__ZJ__V (MJIEnv env, int unsafeRef, boolean isAbsoluteTime, long timeout) {
    ThreadInfo ti = env.getThreadInfo();
    int objRef = ti.getThreadObjectRef();
    int permitRef = env.getReferenceField( objRef, "permit");
    ElementInfo ei = env.getModifiableElementInfo(permitRef);

    if (ti.isInterrupted(false)) {
      // there is no lock, so we go directly back to running and therefore
      // have to remove ourself from the contender list
      ei.setMonitorWithoutLocked(ti);

      // note that park() does not throw an InterruptedException
      return;
    }
    
    if (!ti.isFirstStepInsn()){
      if (ei.getBooleanField("blockPark")) { // we have to wait, but don't need a lock
        // running -> waiting | timeout_waiting
        ei.wait(ti, timeout, false);
        
      } else {
        ei.setBooleanField("blockPark", true); // re-arm for next park
        return;
      }
    }
    
    // scheduling point
    if (ti.getScheduler().setsParkCG( ti, isAbsoluteTime, timeout)) {
      env.repeatInvocation();
      return;
    }
    
    switch (ti.getState()) {
      case WAITING:
      case TIMEOUT_WAITING:
        throw new JPFException("blocking park() without transition break");   
      
      case NOTIFIED:
      case TIMEDOUT:
      case INTERRUPTED:
        ti.resetLockRef();
        ti.setRunning();
        break;
        
      default:
        // nothing
    } 
  }

  @MJI
  public void unpark__Ljava_lang_Object_2__V (MJIEnv env, int unsafeRef, int objRef) {
    ThreadInfo tiCurrent = env.getThreadInfo();
    ThreadInfo tiParked = env.getThreadInfoForObjRef(objRef);
      
    if (tiParked.isTerminated()){
      return; // nothing to do
    }
    
    if (!tiCurrent.isFirstStepInsn()){
      SystemState ss = env.getSystemState();
      int permitRef = env.getReferenceField( objRef, "permit");
      ElementInfo eiPermit = env.getModifiableElementInfo(permitRef);

      if (tiParked.getLockObject() == eiPermit){
        // note that 'permit' is only used in park/unpark, so there never is more than
        // one waiter, which immediately becomes runnable again because it doesn't hold a lock
        // (park is a lockfree wait). unpark() therefore has to be a right mover
        // and we have to register a ThreadCG here
        eiPermit.notifies(ss, tiCurrent, false);
        
      } else {
        eiPermit.setBooleanField("blockPark", false);
        return;
      }
    }
    
    if (tiCurrent.getScheduler().setsUnparkCG(tiCurrent, tiParked)){
      env.repeatInvocation();
      return;
    }
  }
  
  @MJI
  public void ensureClassInitialized__Ljava_lang_Class_2__V (MJIEnv env, int unsafeRef, int clsObjRef) {
    // <2do> not sure if we have to do anyting here - if we have a class object, the class should already
    // be initialized
  }

  @MJI
  public int getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2 (MJIEnv env, int unsafeRef,
                                                                          int objRef, long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getReferenceField(fi);
    } else {
      return ei.getReferenceElement((int)fieldOffset);
    }
  }
  
  @MJI
  public int getObjectVolatile__Ljava_lang_Object_2J__Ljava_lang_Object_2 (MJIEnv env, int unsafeRef,
      int objRef, long fieldOffset) {
    return getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2( env, unsafeRef, objRef, fieldOffset);
  }  

  @MJI
  public void putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V (MJIEnv env, int unsafeRef,
                                                                            int objRef, long fieldOffset, int valRef) {
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setReferenceField(fi, valRef);
    } else {
      ei.setReferenceElement((int)fieldOffset, valRef);
    }
  }
  
  @MJI
  public void putObjectVolatile__Ljava_lang_Object_2JLjava_lang_Object_2__V (MJIEnv env, int unsafeRef,
      int objRef, long fieldOffset, int valRef) {
    putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V( env, unsafeRef, objRef, fieldOffset, valRef);
  }

  @MJI
  public void putOrderedObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(
                                                                                  MJIEnv env,
                                                                                  int unsafeRef,
                                                                                  int objRef,
                                                                                  long fieldOffset,
                                                                                  int valRef) {
    putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, valRef);
  }
  
  @MJI
  public boolean getBoolean__Ljava_lang_Object_2J__Z(MJIEnv env,
                                                            int unsafeRef,
                                                            int objRef,
                                                            long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getBooleanField(fi);
    } else {
      return ei.getBooleanElement((int)fieldOffset);
    }
  }

  @MJI
  public boolean getBooleanVolatile__Ljava_lang_Object_2J__Z(MJIEnv env, int unsafeRef,int objRef,long fieldOffset) {
    return getBoolean__Ljava_lang_Object_2J__Z( env, unsafeRef, objRef, fieldOffset);
  }
  
  @MJI
  public void putBoolean__Ljava_lang_Object_2JZ__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, boolean val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setBooleanField(fi, val);
    } else {
      ei.setBooleanElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putBooleanVolatile__Ljava_lang_Object_2JZ__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, boolean val){
    putBoolean__Ljava_lang_Object_2JZ__V( env, unsafeRef, objRef, fieldOffset, val);
  }

  @MJI
  public byte getByte__Ljava_lang_Object_2J__B(MJIEnv env,
                                                      int unsafeRef,
                                                      int objRef,
                                                      long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getByteField(fi);
    } else {
      return ei.getByteElement((int)fieldOffset);
    }
  }

  @MJI
  public byte getByteVolatile__Ljava_lang_Object_2J__B(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
    return getByte__Ljava_lang_Object_2J__B(env, unsafeRef, objRef, fieldOffset);
  }

  @MJI
  public void putByte__Ljava_lang_Object_2JB__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, byte val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setByteField(fi, val);
    } else {
      ei.setByteElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putByteVolatile__Ljava_lang_Object_2JB__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, byte val){
    putByte__Ljava_lang_Object_2JB__V( env, unsafeRef, objRef, fieldOffset, val);
  }

  @MJI
  public char getChar__Ljava_lang_Object_2J__C(MJIEnv env,
                                                      int unsafeRef,
                                                      int objRef,
                                                      long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getCharField(fi);
    } else {
      return ei.getCharElement((int)fieldOffset);
    }
  }

  @MJI
  public char getCharVolatile__Ljava_lang_Object_2J__C(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
    return getChar__Ljava_lang_Object_2J__C( env, unsafeRef, objRef, fieldOffset);
  }
  
  @MJI
  public void putChar__Ljava_lang_Object_2JC__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, char val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setCharField(fi, val);
    } else {
      ei.setCharElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putCharVolatile__Ljava_lang_Object_2JC__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, char val){
    putChar__Ljava_lang_Object_2JC__V( env, unsafeRef, objRef, fieldOffset, val);
  }
  
  @MJI
  public short getShort__Ljava_lang_Object_2J__S(MJIEnv env,
                                                        int unsafeRef,
                                                        int objRef,
                                                        long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getShortField(fi);
    } else {
      return ei.getShortElement((int)fieldOffset);
    }
  }

  @MJI
  public short getShortVolatile__Ljava_lang_Object_2J__S(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
    return getShort__Ljava_lang_Object_2J__S( env, unsafeRef, objRef, fieldOffset);
  }
  
  @MJI
  public void putShort__Ljava_lang_Object_2JS__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, short val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setShortField(fi, val);
    } else {
      ei.setShortElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putShortVolatile__Ljava_lang_Object_2JS__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, short val){
    putShort__Ljava_lang_Object_2JS__V( env, unsafeRef, objRef, fieldOffset, val);
  }  

  @MJI
  public int getInt__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef,
                                                    int objRef, long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getIntField(fi);
    } else {
      return ei.getIntElement((int)fieldOffset);
    }
  }

  @MJI
  public int getIntVolatile__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef, int objRef, long fieldOffset) {
    return getInt__Ljava_lang_Object_2J__I( env, unsafeRef, objRef, fieldOffset);
  }
  
  @MJI
  public void putInt__Ljava_lang_Object_2JI__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, int val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setIntField(fi, val);
    } else {
      ei.setIntElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putIntVolatile__Ljava_lang_Object_2JI__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val){
    putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val);
  }  

  @MJI
  public void putOrderedInt__Ljava_lang_Object_2JI__V(MJIEnv env,
                                                             int unsafeRef,
                                                             int objRef,
                                                             long fieldOffset,
                                                             int val) {
    // volatile?
    putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val);
  }

  @MJI
  public float getFloat__Ljava_lang_Object_2J__F(MJIEnv env,
                                                        int unsafeRef,
                                                        int objRef,
                                                        long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getFloatField(fi);
    } else {
      return ei.getFloatElement((int)fieldOffset);
    }
  }

  @MJI
  public float getFloatVolatile__Ljava_lang_Object_2J__F(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
    return getFloat__Ljava_lang_Object_2J__F( env, unsafeRef, objRef, fieldOffset);
  }  

  @MJI
  public void putFloat__Ljava_lang_Object_2JF__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, float val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setFloatField(fi, val);
    } else {
      ei.setFloatElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putFloatVolatile__Ljava_lang_Object_2JF__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, float val){
    putFloat__Ljava_lang_Object_2JF__V( env, unsafeRef, objRef, fieldOffset, val);
  }  

  @MJI
  public long getLong__Ljava_lang_Object_2J__J(MJIEnv env,
                                                      int unsafeRef,
                                                      int objRef,
                                                      long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getLongField(fi);
    } else {
      return ei.getLongElement((int)fieldOffset);
    }
  }

  @MJI
  public long getLongVolatile__Ljava_lang_Object_2J__J(MJIEnv env, int unsafeRef, int objRef, long fieldOffset) {
    return getLong__Ljava_lang_Object_2J__J( env, unsafeRef, objRef, fieldOffset);
  }

  @MJI
  public void putLong__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, long val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setLongField(fi, val);
    } else {
      ei.setLongElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putLongVolatile__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val){
    putLong__Ljava_lang_Object_2JJ__V( env, unsafeRef, objRef, fieldOffset, val);
  }  

  @MJI
  public void putOrderedLong__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef,
                                                        int objRef, long fieldOffset, long val) {
    putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, val);
  }

  @MJI
  public double getDouble__Ljava_lang_Object_2J__D(MJIEnv env,
                                                         int unsafeRef,
                                                         int objRef,
                                                         long fieldOffset) {
    ElementInfo ei = env.getElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      return ei.getDoubleField(fi);
    } else {
      return ei.getDoubleElement((int)fieldOffset);
    }
  }

  @MJI
  public double getDoubleVolatile__Ljava_lang_Object_2J__D(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
    return getDouble__Ljava_lang_Object_2J__D( env, unsafeRef, objRef, fieldOffset);
  }
  
  @MJI
  public void putDouble__Ljava_lang_Object_2JD__V (MJIEnv env, int unsafeRef,
                                                       int objRef, long fieldOffset, double val){
    ElementInfo ei = env.getModifiableElementInfo(objRef);
    if (!ei.isArray()) {
      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
      ei.setDoubleField(fi, val);
    } else {
      ei.setDoubleElement((int)fieldOffset, val);
    }
  }

  @MJI
  public void putDoubleVolatile__Ljava_lang_Object_2JD__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, double val){
    putDouble__Ljava_lang_Object_2JD__V( env, unsafeRef, objRef, fieldOffset, val);
  }
  
  @MJI
  public int arrayBaseOffset__Ljava_lang_Class_2__I (MJIEnv env, int unsafeRef, int clazz) {
    return 0;
  }

  @MJI
  public int arrayIndexScale__Ljava_lang_Class_2__I (MJIEnv env, int unsafeRef, int clazz) {
    return 1;
  }

  private static FieldInfo getRegisteredFieldInfo(long fieldOffset) {
    return JPF_java_lang_reflect_Field.getRegisteredFieldInfo((int)fieldOffset);
  }

  
  //--- the explicit memory buffer allocation/free + access methods - evil pointer arithmetic

  /*
   * we shy away from maintaining our own address table by means of knowing that
   * the byte[] object stored in the ArrayFields will not be recycled, and hashCode() will
   * return its address, so the start/endAdr pairs we get from that have to be
   * non-overlapping. Of course that falls apart if  hashCode() would do something
   * different, which is the case for any address that exceeds 32bit
   */
  
  static class Alloc {
    int objRef;
    
    int startAdr;
    int endAdr;
    
    Alloc next;
    
    Alloc (MJIEnv env, int baRef, long length){
      this.objRef = baRef;

      ElementInfo ei = env.getElementInfo(baRef);
      ArrayFields afi = (ArrayFields) ei.getFields();
      byte[] mem = afi.asByteArray();

      startAdr = mem.hashCode();
      endAdr = startAdr + (int)length -1;
    }
    
    @Override
	public String toString(){
      return String.format("Alloc[objRef=%x,startAdr=%x,endAdr=%x]", objRef, startAdr, endAdr);
    }
  }
  
  Alloc firstAlloc;
  
  // for debugging purposes only
  private void dumpAllocs(){
    System.out.println("Unsafe allocated memory blocks:{");
    for (Alloc a = firstAlloc; a != null; a = a.next){
      System.out.print("  ");
      System.out.println(a);
    }
    System.out.println('}');
  }
  
  private void sortInAlloc(Alloc newAlloc){
    int startAdr = newAlloc.startAdr;
    
    if (firstAlloc == null){
      firstAlloc = newAlloc;
      
    } else {
      Alloc prev = null;
      for (Alloc a = firstAlloc; a != null; prev = a, a = a.next){
        if (startAdr < a.startAdr){
          newAlloc.next = a;
          if (prev == null){
            firstAlloc = newAlloc;
          } else {
            prev.next = newAlloc;
          }
        }
      }
    }
  }
  
  private Alloc getAlloc (int address){
    for (Alloc a = firstAlloc; a != null; a = a.next){
      if (address >= a.startAdr && address <= a.endAdr){
        return a;
      }
    }
    
    return null;
  }
  
  private Alloc removeAlloc (int startAddress){
    Alloc prev = null;
    for (Alloc a = firstAlloc; a != null; prev = a, a = a.next) {
      if (a.startAdr == startAddress){
        if (prev == null){
          firstAlloc = a.next;
        } else {
          prev.next = a.next;
        }
        
        return a;
      }
    }
    
    return null;
  }
  
  @MJI
  public long allocateMemory__J__J (MJIEnv env, int unsafeRef, long nBytes) {
    if (nBytes < 0 || nBytes > Integer.MAX_VALUE) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory block size: " + nBytes);
      return 0;
    }
    
    // <2do> we should probably also throw OutOfMemoryErrors on configured thresholds 
    
    int baRef = env.newByteArray((int) nBytes);
    // the corresponding objects have to be freed explicitly
    env.registerPinDown(baRef);
    
    Alloc alloc = new Alloc(env, baRef, nBytes);
    sortInAlloc(alloc);
    
    return alloc.startAdr;
  }
  
  @MJI
  public void freeMemory__J__V (MJIEnv env, int unsafeRef, long startAddress) {
    int addr = (int)startAddress;

    if (startAddress != MJIEnv.NULL){
      Alloc a = removeAlloc(addr);
      if (a == null){
        env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      } else {
        env.releasePinDown(a.objRef);
      }
    }
  }
  
  @MJI
  public byte getByte__J__B (MJIEnv env, int unsafeRef, long address) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return 0;
    }
    
    ElementInfo ei = env.getElementInfo(a.objRef);
    return ei.getByteElement(addr - a.startAdr);
  }

  @MJI
  public void putByte__JB__V (MJIEnv env, int unsafeRef, long address, byte val) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return;
    }
    
    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
    ei.setByteElement(addr - a.startAdr, val);
  }
  
  @MJI
  public char getChar__J__C (MJIEnv env, int unsafeRef, long address) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return 0;
    }
    
    ElementInfo ei = env.getElementInfo(a.objRef);
    byte[] ba = ei.asByteArray();
    
    byte b0 = ba[addr];
    byte b1 = ba[addr+1];
    
    char val;
    if (env.isBigEndianPlatform()){
      val = (char) ((b0 << 8) | b1);
    } else {
      val = (char) ((b1 << 8) | b0);      
    }
    
    return val;
  }

  @MJI
  public void putChar__JC__V (MJIEnv env, int unsafeRef, long address, char val) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return;
    }
        
    byte b1 = (byte)(0xff & val);
    byte b0 = (byte)(0xff & (val >>> 8));
    
    ElementInfo ei = env.getModifiableElementInfo(a.objRef);

    if (env.isBigEndianPlatform()){
      ei.setByteElement(addr,   b0);
      ei.setByteElement(addr+1, b1);
    } else {
      ei.setByteElement(addr,   b1);
      ei.setByteElement(addr+1, b0);      
    }
  }

  @MJI
  public int getInt__J__I (MJIEnv env, int unsafeRef, long address) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return 0;
    }
    
    ElementInfo ei = env.getElementInfo(a.objRef);
    byte[] ba = ei.asByteArray();
    
    byte b0 = ba[addr];
    byte b1 = ba[addr+1];
    byte b2 = ba[addr+2];
    byte b3 = ba[addr+3];
    
    int val;
    if (env.isBigEndianPlatform()){
      val = b0;
      val = (val << 8) | b1;
      val = (val << 8) | b2;
      val = (val << 8) | b3;

    } else {
      val = b3;
      val = (val << 8) | b2;
      val = (val << 8) | b1;
      val = (val << 8) | b0;
    }
    
    return val;
  }

  @MJI
  public void putInt__JI__V (MJIEnv env, int unsafeRef, long address, int val) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return;
    }
        
    byte b3 = (byte)(0xff & val);
    byte b2 = (byte)(0xff & (val >>> 8));
    byte b1 = (byte)(0xff & (val >>> 16));
    byte b0 = (byte)(0xff & (val >>> 24));    
    
    ElementInfo ei = env.getModifiableElementInfo(a.objRef);

    if (env.isBigEndianPlatform()){
      ei.setByteElement(addr,   b0);
      ei.setByteElement(addr+1, b1);
      ei.setByteElement(addr+2, b2);
      ei.setByteElement(addr+3, b3);
    } else {
      ei.setByteElement(addr,   b3);
      ei.setByteElement(addr+1, b2);
      ei.setByteElement(addr+2, b1);
      ei.setByteElement(addr+3, b0);
    }
  }

  @MJI
  public long getLong__J__J (MJIEnv env, int unsafeRef, long address) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return 0;
    }
    
    ElementInfo ei = env.getElementInfo(a.objRef);
    byte[] ba = ei.asByteArray();
    int offset = addr - a.startAdr;
    
    byte b0 = ba[offset];
    byte b1 = ba[offset+1];
    byte b2 = ba[offset+2];
    byte b3 = ba[offset+3];
    byte b4 = ba[offset+4];
    byte b5 = ba[offset+5];
    byte b6 = ba[offset+6];
    byte b7 = ba[offset+7];
    
    int val;
    if (env.isBigEndianPlatform()){
      val = b0;
      val = (val << 8) | b1;
      val = (val << 8) | b2;
      val = (val << 8) | b3;
      val = (val << 8) | b4;
      val = (val << 8) | b5;
      val = (val << 8) | b6;
      val = (val << 8) | b7;

    } else {
      val = b7;
      val = (val << 8) | b6;
      val = (val << 8) | b5;
      val = (val << 8) | b4;
      val = (val << 8) | b3;
      val = (val << 8) | b2;
      val = (val << 8) | b1;
      val = (val << 8) | b0;
    }
    
    return val;
  }

  @MJI
  public void putLong__JJ__V (MJIEnv env, int unsafeRef, long address, long val) {
    int addr = (int)address;
    Alloc a = getAlloc(addr);
    
    if (a == null) {
      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
      return;
    }
        
    byte b7 = (byte)(0xff & val);
    byte b6 = (byte)(0xff & (val >>> 8));
    byte b5 = (byte)(0xff & (val >>> 16));
    byte b4 = (byte)(0xff & (val >>> 24));    
    byte b3 = (byte)(0xff & (val >>> 32));    
    byte b2 = (byte)(0xff & (val >>> 40));    
    byte b1 = (byte)(0xff & (val >>> 48));    
    byte b0 = (byte)(0xff & (val >>> 56));    

    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
    int offset = addr - a.startAdr;
    
    if (env.isBigEndianPlatform()){
      ei.setByteElement(offset,   b0);
      ei.setByteElement(offset+1, b1);
      ei.setByteElement(offset+2, b2);
      ei.setByteElement(offset+3, b3);
      ei.setByteElement(offset+4, b4);
      ei.setByteElement(offset+5, b5);
      ei.setByteElement(offset+6, b6);
      ei.setByteElement(offset+7, b7);
      
    } else {
      ei.setByteElement(offset,   b7);
      ei.setByteElement(offset+1, b6);
      ei.setByteElement(offset+2, b5);
      ei.setByteElement(offset+3, b4);
      ei.setByteElement(offset+4, b3);
      ei.setByteElement(offset+5, b2);
      ei.setByteElement(offset+6, b1);
      ei.setByteElement(offset+7, b0);
    }
  }

}