// // 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.function.BiFunction; import cmu.conditional.Conditional; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.annotation.MJI; import sun.misc.Unsafe; /** * 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, FeatureExpr ctx) { 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, FeatureExpr ctx) { return fieldOffset__Ljava_lang_reflect_Field_2__I(env, unsafeRef, fieldRef, ctx); } /** * 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, FeatureExpr ctx) { // FieldInfo fi = JPF_java_lang_reflect_Field.getFieldInfo(env, // fieldRef); // return fi.getStorageOffset(); return env.getIntField(fieldRef, "regIdx").simplify(ctx).getValue().intValue(); } @MJI public Conditional<Boolean> compareAndSwapObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z (MJIEnv env, int unsafeRef, Conditional<Integer> objRef, Conditional<Long> fieldOffset, Conditional<Integer> expectRef, Conditional<Integer> updateRef, FeatureExpr ctx) { return objRef.mapf(ctx, (BiFunction<FeatureExpr, Integer, Conditional<Boolean>>) (ctx1, objRef1) -> fieldOffset.mapf(ctx1, (BiFunction<FeatureExpr, Long, Conditional<Boolean>>) (ctx2, fieldOffset1) -> expectRef.mapf(ctx2, (BiFunction<FeatureExpr, Integer, Conditional<Boolean>>) (ctx3, expectRef1) -> updateRef.mapf(ctx3, (BiFunction<FeatureExpr, Integer, Conditional<Boolean>>) (ctx4, updateRef1) -> compareAndSwapObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z(env, unsafeRef, objRef1, fieldOffset1, expectRef1, updateRef1, ctx4) )))).simplify(); } // original function private Conditional<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, FeatureExpr ctx) { final Conditional<Integer> actual = getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, unsafeRef, objRef, fieldOffset, ctx); return actual.mapf(ctx, (FeatureExpr ctx1, Integer actual1) -> { if (actual1 == expectRef) { putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, updateRef, ctx1); return One.TRUE; } return One.FALSE; }); } @MJI public boolean compareAndSwapInt__Ljava_lang_Object_2JII__Z(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int expect, int update, FeatureExpr ctx) { int actual = getInt__Ljava_lang_Object_2J__I(env, unsafeRef, objRef, fieldOffset, ctx); if (actual == expect) { putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, update, ctx); 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, FeatureExpr ctx) { long actual = getLong__Ljava_lang_Object_2J__J(env, unsafeRef, objRef, fieldOffset, ctx); if (actual == expect) { putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, update, ctx); return true; } return false; } // this is a specialized, native wait 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, FeatureExpr ctx) { ThreadInfo ti = env.getThreadInfo(); int objRef = ti.getThreadObjectRef(); int permitRef = env.getReferenceField(ctx, objRef, "permit").getValue(); ElementInfo ei = env.getModifiableElementInfo(permitRef); if (ti.isFirstStepInsn()) { // re-executed // assert ti.getLockObject() == null : "private 'permit' object // locked"; // notified | timedout | interrupted -> running switch (ti.getState()) { case NOTIFIED: case TIMEDOUT: case INTERRUPTED: ti.resetLockRef(); ti.setRunning(); break; default: // nothing } } else { // first time if (ti.isInterrupted(ctx, 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 (ei.getBooleanField("blockPark").getValue()) { // we have to // wait, but // don't need a // lock // running -> waiting | timeout_waiting ei.wait(ti, timeout, false); assert ti.isWaiting(); // note we pass in the timeout value, since this might determine // the type of CG that is created ChoiceGenerator<?> cg = env.getSchedulerFactory().createParkCG(ei, ti, isAbsoluteTime, timeout); env.setMandatoryNextChoiceGenerator(cg, "no CG on blocking park()"); env.repeatInvocation(); } else { ei.setBooleanField(ctx, "blockPark", One.TRUE); // next time } } } @MJI public void unpark__Ljava_lang_Object_2__V(MJIEnv env, int unsafeRef, int objRef, FeatureExpr ctx) { ThreadInfo ti = env.getThreadInfo(); if (!ti.isFirstStepInsn()) { ThreadInfo tiParked = env.getThreadInfoForObjRef(objRef); if (tiParked == null || tiParked.isTerminated()) { return; } SystemState ss = env.getSystemState(); int permitRef = env.getReferenceField(ctx, objRef, "permit").getValue(); 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, ti, false); ChoiceGenerator<?> cg = env.getSchedulerFactory().createUnparkCG(tiParked); if (cg != null) { ss.setNextChoiceGenerator(cg); env.repeatInvocation(); } } else { eiPermit.setBooleanField(ctx, "blockPark", One.FALSE); } } } @MJI public void ensureClassInitialized__Ljava_lang_Class_2__V(MJIEnv env, int unsafeRef, int clsObjRef, FeatureExpr ctx) { // <2do> not sure if we have to do anyting here - if we have a class // object, the class should already // be initialized } @MJI public Conditional<Integer> getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getReferenceField(fi).simplify(ctx); } else { return ei.getReferenceElement((int) fieldOffset).simplify(ctx); } } @MJI public Conditional<Integer> getObjectVolatile__Ljava_lang_Object_2J__Ljava_lang_Object_2(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int valRef, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setReferenceField(ctx, fi, new One<>(valRef)); } else { ei.setReferenceElement(ctx, (int) fieldOffset, new One<>(valRef)); } } @MJI public void putObjectVolatile__Ljava_lang_Object_2JLjava_lang_Object_2__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int valRef, FeatureExpr ctx) { putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, valRef, ctx); } @MJI public void putOrderedObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int valRef, FeatureExpr ctx) { putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, valRef, ctx); } @MJI public boolean getBoolean__Ljava_lang_Object_2J__Z(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getBooleanField(fi).getValue(); } else { return ei.getBooleanElement((int) fieldOffset).getValue(); } } @MJI public boolean getBooleanVolatile__Ljava_lang_Object_2J__Z(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getBoolean__Ljava_lang_Object_2J__Z(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putBoolean__Ljava_lang_Object_2JZ__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, boolean val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setBooleanField(ctx, fi, new One<>(val)); } else { ei.setBooleanElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putBooleanVolatile__Ljava_lang_Object_2JZ__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, boolean val, FeatureExpr ctx) { putBoolean__Ljava_lang_Object_2JZ__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public byte getByte__Ljava_lang_Object_2J__B(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getByteField(fi).getValue(); } else { return ei.getByteElement((int) fieldOffset).getValue(); } } @MJI public byte getByteVolatile__Ljava_lang_Object_2J__B(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getByte__Ljava_lang_Object_2J__B(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putByte__Ljava_lang_Object_2JB__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, byte val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setByteField(ctx, fi, new One<>(val)); } else { ei.setByteElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putByteVolatile__Ljava_lang_Object_2JB__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, byte val, FeatureExpr ctx) { putByte__Ljava_lang_Object_2JB__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public char getChar__Ljava_lang_Object_2J__C(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getCharField(fi).getValue(); } else { return ei.getCharElement((int) fieldOffset).getValue(); } } @MJI public char getCharVolatile__Ljava_lang_Object_2J__C(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getChar__Ljava_lang_Object_2J__C(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putChar__Ljava_lang_Object_2JC__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, char val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setCharField(ctx, fi, new One<>(val)); } else { ei.setCharElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putCharVolatile__Ljava_lang_Object_2JC__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, char val, FeatureExpr ctx) { putChar__Ljava_lang_Object_2JC__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public short getShort__Ljava_lang_Object_2J__S(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getShortField(fi).getValue(); } else { return ei.getShortElement((int) fieldOffset).getValue(); } } @MJI public short getShortVolatile__Ljava_lang_Object_2J__S(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getShort__Ljava_lang_Object_2J__S(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putShort__Ljava_lang_Object_2JS__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, short val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setShortField(ctx, fi, new One<>(val)); } else { ei.setShortElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putShortVolatile__Ljava_lang_Object_2JS__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, short val, FeatureExpr ctx) { putShort__Ljava_lang_Object_2JS__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public int getInt__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getIntField(fi).simplify(ctx).getValue(); } else { return ei.getIntElement((int) fieldOffset).simplify(ctx).getValue(); } } @MJI public int getIntVolatile__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getInt__Ljava_lang_Object_2J__I(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putInt__Ljava_lang_Object_2JI__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setIntField(ctx, fi, new One<>(val)); } else { ei.setIntElement(null, (int) fieldOffset, new One<>(val)); } } @MJI public void putIntVolatile__Ljava_lang_Object_2JI__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val, FeatureExpr ctx) { putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public void putOrderedInt__Ljava_lang_Object_2JI__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val, FeatureExpr ctx) { // volatile? putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public float getFloat__Ljava_lang_Object_2J__F(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getFloatField(fi).getValue(); } else { return ei.getFloatElement((int) fieldOffset).getValue(); } } @MJI public float getFloatVolatile__Ljava_lang_Object_2J__F(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getFloat__Ljava_lang_Object_2J__F(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putFloat__Ljava_lang_Object_2JF__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, float val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setFloatField(ctx, fi, new One<>(val)); } else { ei.setFloatElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putFloatVolatile__Ljava_lang_Object_2JF__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, float val, FeatureExpr ctx) { putFloat__Ljava_lang_Object_2JF__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public long getLong__Ljava_lang_Object_2J__J(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getLongField(fi).simplify(ctx).getValue(); } else { return ei.getLongElement((int) fieldOffset).getValue(); } } @MJI public long getLongVolatile__Ljava_lang_Object_2J__J(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getLong__Ljava_lang_Object_2J__J(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putLong__Ljava_lang_Object_2JJ__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setLongField(ctx, fi, new One<>(val)); } else { ei.setLongElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public void putLongVolatile__Ljava_lang_Object_2JJ__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val, FeatureExpr ctx) { putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public void putOrderedLong__Ljava_lang_Object_2JJ__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val, FeatureExpr ctx) { putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public double getDouble__Ljava_lang_Object_2J__D(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { ElementInfo ei = env.getElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); return ei.getDoubleField(fi).getValue(); } else { return ei.getDoubleElement((int) fieldOffset).getValue(); } } @MJI public double getDoubleVolatile__Ljava_lang_Object_2J__D(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, FeatureExpr ctx) { return getDouble__Ljava_lang_Object_2J__D(env, unsafeRef, objRef, fieldOffset, ctx); } @MJI public void putDouble__Ljava_lang_Object_2JD__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, double val, FeatureExpr ctx) { ElementInfo ei = env.getModifiableElementInfo(objRef); if (!ei.isArray()) { FieldInfo fi = getRegisteredFieldInfo(fieldOffset); ei.setDoubleField(ctx, fi, new One<>(val)); } else { ei.setDoubleElement(ctx, (int) fieldOffset, new One<>(val)); } } @MJI public int getAndAddInt__Ljava_lang_Object_2JI__I(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val, FeatureExpr ctx) { int actual = getInt__Ljava_lang_Object_2J__I(env, unsafeRef, objRef, fieldOffset, ctx); putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, actual + val, ctx); return actual + val; } @MJI public long getAndAddLong__Ljava_lang_Object_2JJ__J(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val, FeatureExpr ctx) { long actual = getLong__Ljava_lang_Object_2J__J(env, unsafeRef, objRef, fieldOffset, ctx); putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, actual + val, ctx); return actual + val; } @MJI public void putDoubleVolatile__Ljava_lang_Object_2JD__V(MJIEnv env, int unsafeRef, int objRef, long fieldOffset, double val, FeatureExpr ctx) { putDouble__Ljava_lang_Object_2JD__V(env, unsafeRef, objRef, fieldOffset, val, ctx); } @MJI public int arrayBaseOffset__Ljava_lang_Class_2__I(MJIEnv env, int unsafeRef, int clazz, FeatureExpr ctx) { return 0; } @MJI public int arrayIndexScale__Ljava_lang_Class_2__I(MJIEnv env, int unsafeRef, int clazz, FeatureExpr ctx) { 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(); Conditional<Byte>[] mem = afi.asByteArray(); startAdr = mem.hashCode(); endAdr = startAdr + (int) length - 1; } public String toString() { return String.format("Alloc[objRef=%x,startAdr=%x,endAdr=%x]", objRef, startAdr, endAdr); } } Alloc firstAlloc; // for debugging purposes only @SuppressWarnings("unused") 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, FeatureExpr ctx) { if (nBytes < 0 || nBytes > Integer.MAX_VALUE) { env.throwException(ctx, "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, FeatureExpr ctx) { int addr = (int) startAddress; if (startAddress != MJIEnv.NULL) { Alloc a = removeAlloc(addr); if (a == null) { env.throwException(ctx, "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, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return 0; } ElementInfo ei = env.getElementInfo(a.objRef); return ei.getByteElement(addr - a.startAdr).getValue(); } @MJI public void putByte__JB__V(MJIEnv env, int unsafeRef, long address, byte val, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return; } ElementInfo ei = env.getModifiableElementInfo(a.objRef); ei.setByteElement(ctx, addr - a.startAdr, new One<>(val)); } @MJI public char getChar__J__C(MJIEnv env, int unsafeRef, long address, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return 0; } ElementInfo ei = env.getElementInfo(a.objRef); Conditional<Byte>[] ba = ei.asByteArray(); byte b0 = ba[addr].getValue(); byte b1 = ba[addr + 1].getValue(); 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, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "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(ctx, addr, new One<>(b0)); ei.setByteElement(ctx, addr + 1, new One<>(b1)); } else { ei.setByteElement(ctx, addr, new One<>(b1)); ei.setByteElement(ctx, addr + 1, new One<>(b0)); } } @MJI public int getInt__J__I(MJIEnv env, int unsafeRef, long address, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return 0; } ElementInfo ei = env.getElementInfo(a.objRef); Conditional<Byte>[] ba = ei.asByteArray(); final int offset = addr - a.startAdr; byte b0 = ba[offset].getValue(); byte b1 = ba[offset + 1].getValue(); byte b2 = ba[offset + 2].getValue(); byte b3 = ba[offset + 3].getValue(); 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, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "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); int offset = addr - a.startAdr; if (env.isBigEndianPlatform()) { ei.setByteElement(ctx, offset, new One<>(b0)); ei.setByteElement(ctx, offset + 1, new One<>(b1)); ei.setByteElement(ctx, offset + 2, new One<>(b2)); ei.setByteElement(ctx, offset + 3, new One<>(b3)); } else { ei.setByteElement(ctx, offset, new One<>(b3)); ei.setByteElement(ctx, offset + 1, new One<>(b2)); ei.setByteElement(ctx, offset + 2, new One<>(b1)); ei.setByteElement(ctx, offset + 3, new One<>(b0)); } } @MJI public long getLong__J__J(MJIEnv env, int unsafeRef, long address, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return 0; } ElementInfo ei = env.getElementInfo(a.objRef); Conditional<Byte>[] ba = ei.asByteArray(); int offset = addr - a.startAdr; byte b0 = ba[offset].getValue(); byte b1 = ba[offset + 1].getValue(); byte b2 = ba[offset + 2].getValue(); byte b3 = ba[offset + 3].getValue(); byte b4 = ba[offset + 4].getValue(); byte b5 = ba[offset + 5].getValue(); byte b6 = ba[offset + 6].getValue(); byte b7 = ba[offset + 7].getValue(); 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, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "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(ctx, offset, new One<>(b0)); ei.setByteElement(ctx, offset + 1, new One<>(b1)); ei.setByteElement(ctx, offset + 2, new One<>(b2)); ei.setByteElement(ctx, offset + 3, new One<>(b3)); ei.setByteElement(ctx, offset + 4, new One<>(b4)); ei.setByteElement(ctx, offset + 5, new One<>(b5)); ei.setByteElement(ctx, offset + 6, new One<>(b6)); ei.setByteElement(ctx, offset + 7, new One<>(b7)); } else { ei.setByteElement(ctx, offset, new One<>(b7)); ei.setByteElement(ctx, offset + 1, new One<>(b6)); ei.setByteElement(ctx, offset + 2, new One<>(b5)); ei.setByteElement(ctx, offset + 3, new One<>(b4)); ei.setByteElement(ctx, offset + 4, new One<>(b3)); ei.setByteElement(ctx, offset + 5, new One<>(b2)); ei.setByteElement(ctx, offset + 6, new One<>(b1)); ei.setByteElement(ctx, offset + 7, new One<>(b0)); } } /** * Sets all bytes in a given block of memory to a fixed value (usually * zero). This provides a single-register addressing mode, as discussed in * #getInt(Object,long) . Equivalent to setMemory(null, address, bytes, * value). */ @MJI public void setMemory__JJB__V(MJIEnv env, int unsafeRef, long address, long bytes, byte value, FeatureExpr ctx) { int addr = (int) address; Alloc a = getAlloc(addr); if (a == null) { env.throwException(ctx, "java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr)); return; } ElementInfo ei = env.getModifiableElementInfo(a.objRef); int offset = addr - a.startAdr; for (long i = bytes; i > 0; i--) { ei.setByteElement(ctx, offset++, new One<>(value)); } } @MJI public int pageSize____I(MJIEnv env, int unsafeRef, FeatureExpr ctx) { Field f; try { f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe = (Unsafe)f.get(null); return unsafe.pageSize(); } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { } return 0; } }