Skip to content

Commit

Permalink
Merge pull request #4955 from MarkQingGuo/moni
Browse files Browse the repository at this point in the history
Add ValueType support to the MonitorEnter and MonitorExit  bytecodes
  • Loading branch information
gacholio authored Mar 23, 2019
2 parents 0e845c1 + 946d963 commit eabc8e1
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 26 deletions.
11 changes: 10 additions & 1 deletion runtime/nls/j9vm/j9vm.nls
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2000, 2018 IBM Corp. and others
# Copyright (c) 2000, 2019 IBM Corp. and others
#
# This program and the accompanying materials are made available under
# the terms of the Eclipse Public License 2.0 which accompanies this
Expand Down Expand Up @@ -1871,3 +1871,12 @@ J9NLS_VM_ERROR_BYTECODE_OBJECTREF_MUST_BE_VALUE_TYPE.explanation=The class has s
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_MUST_BE_VALUE_TYPE.system_action=The JVM will throw a IncompatibleClassChangeError.
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_MUST_BE_VALUE_TYPE.user_response=Contact the provider of the classfile for a corrected version.
# END NON-TRANSLATABLE

J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE=bad object type %2$.*1$s: object that is synchronized is a value type
# START NON-TRANSLATABLE
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE.sample_input_1=3
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE.sample_input_2=Foo
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE.explanation=The class has specified the bytecode monitorenter or monitorexit operation which cannot be performed on a value type but the object on stack is a value type.
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE.system_action=The JVM will throw an IllegalMonitorStateException.
J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE.user_response=Contact the provider of the classfile for a corrected version.
# END NON-TRANSLATABLE
74 changes: 51 additions & 23 deletions runtime/vm/BytecodeInterpreter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7605,28 +7605,42 @@ done:;
if (NULL == obj) {
rc = THROW_NPE;
} else {
IDATA monitorRC = enterObjectMonitor(REGISTER_ARGS, obj);
/* Monitor enter can only fail in the nonblocking case, which does not
* release VM access, so the immediate async and failed enter cases are
* mutually exclusive.
*/
if (0 == monitorRC) {
rc = THROW_MONITOR_ALLOC_FAIL;
} else {
if (J9_UNEXPECTED(!VM_ObjectMonitor::recordBytecodeMonitorEnter(_currentThread, (j9object_t)monitorRC, _arg0EA))) {
objectMonitorExit(_currentThread, obj);
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
J9Class * objClass = J9OBJECT_CLAZZ(_currentThread, obj);
if (J9_IS_J9CLASS_VALUETYPE(objClass)) {
J9UTF8 *badClassName = J9ROMCLASS_CLASSNAME(objClass->romClass);
buildInternalNativeStackFrame(REGISTER_ARGS);
updateVMStruct(REGISTER_ARGS);
prepareForExceptionThrow(_currentThread);
setCurrentExceptionNLSWithArgs(_currentThread, J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE, J9VMCONSTANTPOOL_JAVALANGILLEGALMONITORSTATEEXCEPTION, J9UTF8_LENGTH(badClassName), J9UTF8_DATA(badClassName));
VMStructHasBeenUpdated(REGISTER_ARGS);
rc = GOTO_THROW_CURRENT_EXCEPTION;
} else
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
{
IDATA monitorRC = enterObjectMonitor(REGISTER_ARGS, obj);
/* Monitor enter can only fail in the nonblocking case, which does not
* release VM access, so the immediate async and failed enter cases are
* mutually exclusive.
*/
if (0 == monitorRC) {
rc = THROW_MONITOR_ALLOC_FAIL;
} else {
if (J9_UNEXPECTED(!VM_ObjectMonitor::recordBytecodeMonitorEnter(_currentThread, (j9object_t)monitorRC, _arg0EA))) {
objectMonitorExit(_currentThread, obj);
rc = THROW_MONITOR_ALLOC_FAIL;
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
}
goto done;
}
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
}
goto done;
}
if (immediateAsyncPending()) {
rc = GOTO_ASYNC_CHECK;
goto done;
_pc += 1;
_sp += 1;
}
_pc += 1;
_sp += 1;
}
}
done:
Expand All @@ -7643,12 +7657,26 @@ done:;
if (NULL == obj) {
rc = THROW_NPE;
} else {
IDATA monitorRC = exitObjectMonitor(REGISTER_ARGS, obj);
if (0 != monitorRC) {
rc = THROW_ILLEGAL_MONITOR_STATE;
} else {
VM_ObjectMonitor::recordBytecodeMonitorExit(_currentThread, obj);
_pc += 1;
#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)
J9Class * objClass = J9OBJECT_CLAZZ(_currentThread, obj);
if (J9_IS_J9CLASS_VALUETYPE(objClass)) {
J9UTF8 *badClassName = J9ROMCLASS_CLASSNAME(objClass->romClass);
buildInternalNativeStackFrame(REGISTER_ARGS);
updateVMStruct(REGISTER_ARGS);
prepareForExceptionThrow(_currentThread);
setCurrentExceptionNLSWithArgs(_currentThread, J9NLS_VM_ERROR_BYTECODE_OBJECTREF_CANNOT_BE_VALUE_TYPE, J9VMCONSTANTPOOL_JAVALANGILLEGALMONITORSTATEEXCEPTION, J9UTF8_LENGTH(badClassName), J9UTF8_DATA(badClassName));
VMStructHasBeenUpdated(REGISTER_ARGS);
rc = GOTO_THROW_CURRENT_EXCEPTION;
} else
#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */
{
IDATA monitorRC = exitObjectMonitor(REGISTER_ARGS, obj);
if (0 != monitorRC) {
rc = THROW_ILLEGAL_MONITOR_STATE;
} else {
VM_ObjectMonitor::recordBytecodeMonitorExit(_currentThread, obj);
_pc += 1;
}
}
}
return rc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ private static byte[] generateClass(String className, String[] fields, boolean i
testWithFieldOnNonValueType(cw, className, fields);
testWithFieldOnNull(cw, className, fields);
testWithFieldOnNonExistentClass(cw, className, fields);
testMonitorEnterOnObject(cw, className, fields);
testMonitorExitOnObject(cw, className, fields);
testMonitorEnterAndExitWithRefType(cw, className, fields);
} else {
makeValue(cw, className, makeValueSig, fields, makeMaxLocal);
if (!isVerifiable) {
Expand Down Expand Up @@ -174,7 +177,53 @@ private static void testWithFieldOnNonExistentClass(ClassWriter cw, String class
mv.visitMaxs(1, 2);
mv.visitEnd();
}


/*
* This function should only be called in the
* TestMonitorEnterOnValueType test and
* TestMonitorEnterWithRefType test
*/
private static void testMonitorEnterOnObject(ClassWriter cw, String className, String[] fields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testMonitorEnterOnObject", "(Ljava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(MONITORENTER);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}

/*
* This function should only be called in the
* TestMonitorExitOnValueType test and
* TestMonitorExitWithRefType test
*/
private static void testMonitorExitOnObject(ClassWriter cw, String className, String[] fields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testMonitorExitOnObject", "(Ljava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(MONITOREXIT);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}

/*
* This function should only be called in the
* TestMonitorEnterAndExitWithRefType test
*/
private static void testMonitorEnterAndExitWithRefType(ClassWriter cw, String className, String[] fields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testMonitorEnterAndExitWithRefType", "(Ljava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(DUP);
mv.visitInsn(MONITORENTER);
mv.visitInsn(MONITOREXIT);
mv.visitInsn(RETURN);
mv.visitMaxs(2,1);
mv.visitEnd();
}

private static void makeValue(ClassWriter cw, String valueName, String makeValueSig, String[] fields, int makeMaxLocal) {
boolean doubleDetected = false;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "makeValue", "(" + makeValueSig + ")L" + valueName + ";", null, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,116 @@ static public void testWithFieldOnNonExistentClass() throws Throwable {
*
* Assert.assertFalse((refType != refType), "An identity (!=) comparison on the same refType should always return false");
* }
*/
*/

/*
* Test monitorEnter on valueType
*
* class TestMonitorEnterOnValueType {
* long longField
* }
*/
@Test(priority=2)
static public void testMonitorEnterOnValueType() throws Throwable {
int x = 0;
int y = 0;
Object valueType = makePoint2D.invoke(x, y);

String fields[] = {"longField:J"};
Class<?> testMonitorEnterOnValueType = ValueTypeGenerator.generateRefClass("TestMonitorEnterOnValueType", fields);
MethodHandle monitorEnterOnValueType = lookup.findStatic(testMonitorEnterOnValueType, "testMonitorEnterOnObject", MethodType.methodType(void.class, Object.class));
try {
monitorEnterOnValueType.invoke(valueType);
Assert.fail("should throw exception. MonitorEnter cannot be used with ValueType");
} catch (IllegalMonitorStateException e) {}
}

/*
* Test monitorExit on valueType
*
* class TestMonitorExitOnValueType {
* long longField
* }
*/
@Test(priority=2)
static public void testMonitorExitOnValueType() throws Throwable {
int x = 1;
int y = 1;
Object valueType = makePoint2D.invoke(x, y);

String fields[] = {"longField:J"};
Class<?> testMonitorExitOnValueType = ValueTypeGenerator.generateRefClass("TestMonitorExitOnValueType", fields);
MethodHandle monitorExitOnValueType = lookup.findStatic(testMonitorExitOnValueType, "testMonitorExitOnObject", MethodType.methodType(void.class, Object.class));
try {
monitorExitOnValueType.invoke(valueType);
Assert.fail("should throw exception. MonitorExit cannot be used with ValueType");
} catch (IllegalMonitorStateException e) {}
}

/*
* Test monitorEnter with refType
*
* class TestMonitorEnterWithRefType {
* long longField
* }
*/
@Test(priority=1)
static public void testMonitorEnterWithRefType() throws Throwable {
int x = 0;
Object refType = (Object) x;

String fields[] = {"longField:J"};
Class<?> testMonitorEnterWithRefType = ValueTypeGenerator.generateRefClass("TestMonitorEnterWithRefType", fields);
MethodHandle monitorEnterWithRefType = lookup.findStatic(testMonitorEnterWithRefType, "testMonitorEnterOnObject", MethodType.methodType(void.class, Object.class));
try {
monitorEnterWithRefType.invoke(refType);
} catch (IllegalMonitorStateException e) {
Assert.fail("shouldn't throw exception. MonitorEnter should be used with refType");
}
}

/*
* Test monitorExit with refType
*
* class TestMonitorExitWithRefType {
* long longField
* }
*/
@Test(priority=1)
static public void testMonitorExitWithRefType() throws Throwable {
int x = 1;
Object refType = (Object) x;

String fields[] = {"longField:J"};
Class<?> testMonitorExitWithRefType = ValueTypeGenerator.generateRefClass("TestMonitorExitWithRefType", fields);
MethodHandle monitorExitWithRefType = lookup.findStatic(testMonitorExitWithRefType, "testMonitorExitOnObject", MethodType.methodType(void.class, Object.class));
try {
monitorExitWithRefType.invoke(refType);
Assert.fail("should throw exception. MonitorExit doesn't have a matching MonitorEnter");
} catch (IllegalMonitorStateException e) {}
}

/*
* Test monitorEnterAndExit with refType
*
* class TestMonitorEnterAndExitWithRefType {
* long longField
* }
*/
@Test(priority=1)
static public void testMonitorEnterAndExitWithRefType() throws Throwable {
int x = 2;
Object refType = (Object) x;

String fields[] = {"longField:J"};
Class<?> testMonitorEnterAndExitWithRefType = ValueTypeGenerator.generateRefClass("TestMonitorEnterAndExitWithRefType", fields);
MethodHandle monitorEnterAndExitWithRefType = lookup.findStatic(testMonitorEnterAndExitWithRefType, "testMonitorEnterAndExitWithRefType", MethodType.methodType(void.class, Object.class));
try {
monitorEnterAndExitWithRefType.invoke(refType);
} catch (IllegalMonitorStateException e) {
Assert.fail("shouldn't throw exception. MonitorEnter and MonitorExit should be used with refType");
}
}

static MethodHandle generateGetter(Class<?> clazz, String fieldName, Class<?> fieldType) {
try {
Expand Down

0 comments on commit eabc8e1

Please sign in to comment.