Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw NoClassDefFoundError instead of TypeNotPresentException #610

Merged
merged 2 commits into from
Nov 24, 2017

Conversation

babsingh
Copy link
Contributor

@babsingh babsingh commented Nov 16, 2017

Change 1:

When the VM is resolving a MethodType, NoClassDefFoundError should be thrown instead of TypeNotPresentException.

Change 2:

Renamed test_indyn_ldc_invalid_type to test_indyn_ldc_non_existent_class_methodtype. Updated test to check for NoClassDefFoundError instead of TypeNotPresentException.

Signed-off-by: Babneet Singh [email protected]

@babsingh babsingh force-pushed the fix_noclassdeferror branch from 2ebd50e to c9379d7 Compare November 16, 2017 21:20
@babsingh
Copy link
Contributor Author

babsingh commented Nov 16, 2017

@DanHeidinga
According to the below Javadoc for MethodType.fromMethodDescriptorString, TypeNotPresentException must be thrown for JCL usage. But, NoClassDefFoundError must be thrown by VM during the resolve stage.

Should I create two versions of MethodType.fromMethodDescriptorString? One for VM usage that throws NoClassDefFoundError, and one for JCL usage that throws TypeNotPresentException.

MethodType.fromMethodDescriptorString Javadoc: https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodType.html#fromMethodDescriptorString-java.lang.String-java.lang.ClassLoader-

@DanHeidinga
Copy link
Member

Duplicating code isn't a great answer. What about modifying the resolve MT path to convert the TNPE to a NCDFE?

@babsingh babsingh changed the title Throw NoClassDefFoundError instead of TypeNotPresentException WIP: Throw NoClassDefFoundError instead of TypeNotPresentException Nov 17, 2017
@babsingh babsingh force-pushed the fix_noclassdeferror branch 4 times, most recently from 0be2182 to f4aea60 Compare November 21, 2017 00:04
@babsingh babsingh changed the title WIP: Throw NoClassDefFoundError instead of TypeNotPresentException Throw NoClassDefFoundError instead of TypeNotPresentException Nov 21, 2017
@babsingh
Copy link
Contributor Author

Code has been updated - modified resolve MT path.
Peer Review - @JasonFengJ9

* instead of TypeNotPresentException during the VM resolve stage.
*
* @param methodDescriptor - the method descriptor string
* @param loader - the ClassLoader to be used or null for System ClassLoader
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loader passed in here is from VMAccess.getClassloader() --> clazz.getClassLoaderImpl() which returns the actual bootstrap classloader, not null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated description.

* @throws NoClassDefFoundError - if a named type cannot be found
*/
private static final MethodType vmResolveFromMethodDescriptorString(String methodDescriptor, ClassLoader loader) throws Throwable {
MethodType type = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initializing a variable to null here is not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

private static final MethodType vmResolveFromMethodDescriptorString(String methodDescriptor, ClassLoader loader) throws Throwable {
MethodType type = null;
try {
type = MethodType.fromMethodDescriptorString(methodDescriptor, loader);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably just return MethodType.fromMethodDescriptorString(methodDescriptor, loader);.

}
Assert.fail("NoClassDefFoundError not thrown when non-existent class is used to create MethodType via LDC");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Assert.fail can be moved after MethodType mtToString = com.ibm.j9.jsr292.indyn.GenIndyn.ldc_invalid_type();, and return within catch block can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.


boolean typeNotPresentExceptionThrown = false;

public void test_indyn_ldc_non_existent_class_methodtype() {
try {
MethodType mtToString = com.ibm.j9.jsr292.indyn.GenIndyn.ldc_invalid_type();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mtToString is not used, can it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should keep it since it tells what is being returned.

@babsingh babsingh force-pushed the fix_noclassdeferror branch from f4aea60 to 76cd3ff Compare November 21, 2017 19:05
@@ -883,6 +883,24 @@ sendFromMethodDescriptorString(J9VMThread *currentThread, J9UTF8 *descriptor, J9
currentThread->returnValue2 = (UDATA)J9VMJAVALANGINVOKEMETHODTYPE_FROMMETHODDESCRIPTORSTRINGAPPENDARG_METHOD(vm);
}
c_cInterpreter(currentThread);
if (VM_VMHelpers::exceptionPending(currentThread)) {
j9object_t currentException = currentThread->currentException;
J9Class *exceptionClass = J9VMJAVALANGTYPENOTPRESENTEXCEPTION(vm);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using name exceptionClassTNPE offers better clarity. In addition, to achieve putting a rvalue on the left when comparing for equality later, pls define it as J9Class * const exceptionClassTNPE.

Copy link
Contributor Author

@babsingh babsingh Nov 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done - changed to exceptionClassTNPE and added const.

*/
if (exceptionClass == J9OBJECT_CLAZZ(currentThread, currentException)) {
j9object_t cause = J9VMJAVALANGTHROWABLE_CAUSE(currentThread, currentException);
J9Class *causeClass = J9VMJAVALANGCLASSNOTFOUNDEXCEPTION(vm);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, using name causeClassThrowable offers better clarity. In addition, pls define it as J9Class * const causeClassThrowable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done - changed to causeClassCNFE and added const.

@babsingh babsingh force-pushed the fix_noclassdeferror branch 2 times, most recently from a730300 to 3f6b771 Compare November 21, 2017 21:45
@@ -883,6 +883,24 @@ sendFromMethodDescriptorString(J9VMThread *currentThread, J9UTF8 *descriptor, J9
currentThread->returnValue2 = (UDATA)J9VMJAVALANGINVOKEMETHODTYPE_FROMMETHODDESCRIPTORSTRINGAPPENDARG_METHOD(vm);
}
c_cInterpreter(currentThread);
if (VM_VMHelpers::exceptionPending(currentThread)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than duplicating this code natively, wouldn't it be cleaner to call vmResolveFromMethodDescriptorString() directly and let the java code convert the TNPE to a CNFE?

Copy link
Contributor Author

@babsingh babsingh Nov 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be better in terms of avoiding duplicate code. But, sendFromMethodDescriptorString calls MT.fromMethodDescriptorString and MT.fromMethodDescriptorStringAppendArg. So, the definition of MH.vmResolveFromMethodDescriptorString will change.

	private static final MethodType vmResolveFromMethodDescriptorString(String methodDescriptor, ClassLoader loader, Class<?> appendArgumentType) throws Throwable {
		try {
			if (null == appendArgumentType) {
				return MethodType.fromMethodDescriptorString(methodDescriptor, loader);
			} else {
				return MethodType.fromMethodDescriptorStringAppendArg(methodDescriptor, loader, appendArgumentType);
			}
		} catch (TypeNotPresentException e) {
			throw throwNoClassDefFoundError(e);
		}
	}

Currently, MT.fromMethodDescriptorStringAppendArg is a private method. It will change to a public method since it will be used in MethodHandle.java.

MH resolve code paths will become slightly longer due to this change. @DanHeidinga do you approve the above changes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot become a public method as that changes the public api for a class in the java.lang.invoke.* namespace.

It can become package or default access which will allow it to be called anywhere in the package without becoming API.

For the MH resolve, this shouldn't be an issue as the resolve occurs once per item.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

completed.

@babsingh babsingh force-pushed the fix_noclassdeferror branch from 3f6b771 to 8f427bd Compare November 23, 2017 08:45
Copy link
Member

@DanHeidinga DanHeidinga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change to the exception being thrown the expected behaviour in both Java 8 & 9?

try {
if (null == appendArgumentType) {
return MethodType.fromMethodDescriptorString(methodDescriptor, loader);
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: an if that terminates with a return or a throw doesn't need an else block after it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

@babsingh babsingh force-pushed the fix_noclassdeferror branch from 8f427bd to 6516407 Compare November 23, 2017 15:56
@babsingh
Copy link
Contributor Author

babsingh commented Nov 23, 2017

This is a JVM requirement for both Java 8 and Java 9 during resolution.

If the Java Virtual Machine ever attempts to load a class C during verification (§5.4.1) or resolution (§5.4.3) (but not initialization (§5.5)), and the class loader that is used to initiate loading of C throws an instance of ClassNotFoundException, then the Java Virtual Machine must throw an instance of NoClassDefFoundError whose cause is the instance of ClassNotFoundException.

}
}

/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix the formatting for the javadoc on this and the above method? The formatting is inconsistent between the two.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

When the VM is resolving a MethodType, NoClassDefFoundError should be
thrown instead of TypeNotPresentException.

Signed-off-by: Babneet Singh <[email protected]>
Renamed test_indyn_ldc_invalid_type to
test_indyn_ldc_non_existent_class_methodtype.

Updated test to check for NoClassDefFoundError instead of
TypeNotPresentException.

Signed-off-by: Babneet Singh <[email protected]>
@babsingh babsingh force-pushed the fix_noclassdeferror branch from 6516407 to 08d5f60 Compare November 24, 2017 13:08
@DanHeidinga
Copy link
Member

Jenkins test sanity plinux

@DanHeidinga DanHeidinga self-assigned this Nov 24, 2017
@DanHeidinga DanHeidinga merged commit 7ae77ca into eclipse-openj9:master Nov 24, 2017
@babsingh babsingh deleted the fix_noclassdeferror branch August 17, 2020 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants