diff --git a/build.xml b/build.xml index 9be556d6..761898dd 100644 --- a/build.xml +++ b/build.xml @@ -161,6 +161,9 @@ includes="*,gov/nasa/jpf/**,classloader_specific_tests/**"> + + + diff --git a/src/classes/modules/java.base/jdk/internal/misc/Unsafe.java b/src/classes/modules/java.base/jdk/internal/misc/Unsafe.java index 90fac10f..6cf88ed5 100644 --- a/src/classes/modules/java.base/jdk/internal/misc/Unsafe.java +++ b/src/classes/modules/java.base/jdk/internal/misc/Unsafe.java @@ -41,6 +41,11 @@ public static Unsafe getUnsafe() { // a numeric id for the corresponding FieldInfo here public native int fieldOffset (Field f); public native long objectFieldOffset (Field f); + public native long objectFieldOffset(Class c, String name); + + public final native boolean compareAndSetInt(Object o, long offset, int expected, int x); + public final native boolean compareAndSetLong(Object o, long offset, long expected, long x); + public final native boolean compareAndSetObject(Object o, long offset, Object expected, Object x); // those do the usual CAS magic public native boolean compareAndSwapObject (Object oThis, long offset, Object expect, Object update); @@ -73,6 +78,10 @@ public void putInt(Object obj, int offset, int i) { public native Object getObject(Object obj, long l); public native Object getObjectVolatile(Object obj, long l); + public final Object getObjectAcquire(Object o, long offset) { + return getObjectVolatile(o, offset); + } + @Deprecated public Object getObject(Object obj, int offset) { return getObject(obj, (long) offset); @@ -208,8 +217,11 @@ public void putDouble(Object obj, int offset, double d) { public native int arrayBaseOffset(Class clazz); public native int arrayIndexScale(Class clazz); - - + + public final void putObjectRelease(Object o, long offset, Object x) { + putObjectVolatile(o, offset, x); + } + //--- java.nio finally breaks object boundaries - hello, evil pointer arithmetic /** diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java index 90a5dcfe..40132bce 100644 --- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java +++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java @@ -490,7 +490,7 @@ public int getConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_ } // this is only used for system classes such as java.lang.reflect.Method - ClassInfo getInitializedClassInfo (MJIEnv env, String clsName){ + static ClassInfo getInitializedClassInfo (MJIEnv env, String clsName){ ThreadInfo ti = env.getThreadInfo(); Instruction insn = ti.getPC(); ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo( clsName); diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java b/src/peers/gov/nasa/jpf/vm/JPF_jdk_internal_misc_Unsafe.java similarity index 91% rename from src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java rename to src/peers/gov/nasa/jpf/vm/JPF_jdk_internal_misc_Unsafe.java index bccad5ce..ab65914e 100644 --- a/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java +++ b/src/peers/gov/nasa/jpf/vm/JPF_jdk_internal_misc_Unsafe.java @@ -27,6 +27,7 @@ import gov.nasa.jpf.vm.SystemState; import gov.nasa.jpf.vm.ThreadInfo; +import static gov.nasa.jpf.vm.JPF_java_lang_Class.FIELD_CLASSNAME; /** * we don't want this class! This is a hodgepodge of stuff that shouldn't be in Java, but @@ -39,11 +40,11 @@ * * <2do> this might change with better modeling of high level java.util.concurrent constructs */ -public class JPF_sun_misc_Unsafe extends NativePeer { +public class JPF_jdk_internal_misc_Unsafe extends NativePeer { @MJI public int getUnsafe____Lsun_misc_Unsafe_2 (MJIEnv env, int clsRef) { - int objRef = env.getStaticReferenceField("sun.misc.Unsafe", "theUnsafe"); + int objRef = env.getStaticReferenceField("jdk.internal.misc.Unsafe", "theUnsafe"); return objRef; } @@ -52,6 +53,48 @@ public long objectFieldOffset__Ljava_lang_reflect_Field_2__J (MJIEnv env, int un return fieldOffset__Ljava_lang_reflect_Field_2__I(env, unsafeRef, fieldRef); } + /** + * NativePeer method for {@link jdk.internal.misc.Unsafe#objectFieldOffset(java.lang.Class, java.lang.String)} + */ + @MJI + public long objectFieldOffset__Ljava_lang_Class_2Ljava_lang_String_2__J (MJIEnv env, int unsafeRef, int clsRef, int nameRef) { + ClassInfo ci = env.getReferredClassInfo(clsRef); + String fname = env.getStringObject(nameRef); + FieldInfo fi = null; + + fi = ci.getInstanceField(fname); + if (fi == null) { + fi = ci.getStaticField(fname); + } + + if (fi == null) { + env.throwException("java.lang.NoSuchFieldException", fname); + return MJIEnv.NULL; + + } else { + ClassInfo fci = JPF_java_lang_Class.getInitializedClassInfo(env, FIELD_CLASSNAME); + if (fci == null) { + env.repeatInvocation(); + return MJIEnv.NULL; + } + int fieldRef = JPF_java_lang_Class.createFieldObject(env, fi, fci); + return env.getIntField(fieldRef, "regIdx"); + } + } + + /** + * NativePeer method for {@link jdk.internal.misc.Unsafe#compareAndSetObject(java.lang.Object, long, java.lang.Object, java.lang.Object)} + */ + @MJI + public boolean compareAndSetObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z(MJIEnv env, int unsafeRef, int oRef, long offset, int expectedRef, int xRef) { + int actual = getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, unsafeRef, oRef, offset); + if (actual == expectedRef) { + putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, oRef, offset, xRef); + return true; + } + return false; + } + /** * 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 @@ -64,6 +107,34 @@ public int fieldOffset__Ljava_lang_reflect_Field_2__I (MJIEnv env, int unsafeRef return env.getIntField(fieldRef, "regIdx"); } + /** + * NativePeer method for {@link jdk.internal.misc.Unsafe#compareAndSetInt(java.lang.Object, long, int, int)} + */ + @MJI + public boolean compareAndSetInt__Ljava_lang_Object_2JII__Z (MJIEnv env, int unsafeRef, + int oRef, long offset, int expected, int x) { + int actual = getInt__Ljava_lang_Object_2J__I(env, unsafeRef, oRef, offset); + if (actual == expected) { + putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, oRef, offset, x); + return true; + } + return false; + } + + /** + * NativePeer method for {@link jdk.internal.misc.Unsafe#compareAndSetLong(Object, long, long, long)} + */ + @MJI + public boolean compareAndSetLong__Ljava_lang_Object_2JJJ__Z (MJIEnv env, int unsafeRef, + int oRef, long offset, long expected, long x) { + long actual = getLong__Ljava_lang_Object_2J__J(env, unsafeRef, oRef, offset); + if (actual == expected) { + putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, oRef, offset, x); + return true; + } + return false; + } + @MJI public boolean compareAndSwapObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, diff --git a/src/tests/gov/nasa/jpf/test/java/misc/UnsafeTest.java b/src/tests/gov/nasa/jpf/test/java/misc/UnsafeTest.java new file mode 100644 index 00000000..ee052b80 --- /dev/null +++ b/src/tests/gov/nasa/jpf/test/java/misc/UnsafeTest.java @@ -0,0 +1,68 @@ +package gov.nasa.jpf.test.java.misc; + +import gov.nasa.jpf.util.test.TestJPF; +import jdk.internal.misc.Unsafe; +import org.junit.Test; + +public class UnsafeTest extends TestJPF { + + private class TestClass { + int intVar = 0; + } + + private final Unsafe unsafe = Unsafe.getUnsafe(); + + /** + * Test NativePeer method for {@link Unsafe#objectFieldOffset(java.lang.Class, java.lang.String)} + */ + @Test + public void objectFieldOffset() { + if (verifyNoPropertyViolation()) { + unsafe.objectFieldOffset(TestClass.class, "intVar"); + } + } + + /** + * Test NativePeer method for {@link Unsafe#getInt(Object, long)} + */ + @Test + public void getInt() { + if (verifyNoPropertyViolation()) { + TestClass o = new TestClass(); + long intFieldOffset = unsafe.objectFieldOffset(TestClass.class, "intVar"); + + assert unsafe.getInt(o, intFieldOffset) == 0; + } + } + + /** + * Test NativePeer method for {@link Unsafe#putInt(Object, long, int)} + */ + @Test + public void putInt() { + if (verifyNoPropertyViolation()) { + TestClass o = new TestClass(); + long intFieldOffset = unsafe.objectFieldOffset(TestClass.class, "intVar"); + int x = 77; + + unsafe.putInt(o, intFieldOffset, x); + assert o.intVar == x; + } + } + + /** + * Test NativePeer method for {@link Unsafe#compareAndSetInt(Object, long, int, int)} + */ + @Test + public void compareAndSetInt() { + if (verifyNoPropertyViolation()) { + TestClass o = new TestClass(); + long intFieldOffset = unsafe.objectFieldOffset(TestClass.class, "intVar"); + int expected = 0; + int x = 3; + + unsafe.compareAndSetInt(o, intFieldOffset, expected, x); + assert o.intVar == 3; + } + } +} \ No newline at end of file