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

[Nest mates] Moves nestmates host loading to visibility check #1224

Merged

Conversation

taliamccormick
Copy link
Contributor

Earlier nest mates spec draft loaded a class's nest host during class loading. More recent nest mates spec puts off host loading until required for an access check. This commit moves the nest host loading and verification to the visibility check.

The spec draft for checking visibility states:

To determine whether a class or interface C belongs to the same nest as
D (that is, whether C and D are nestmates), the following steps are
performed:

  1. If C and D are the same class or interface, they belong to the same nest.
  2. Otherwise, the nest host of D, H, is determined. If an exception is thrown, the nest membership test fails for the same reason.
  3. Otherwise, the nest host of C, H', is determined. If an exception is thrown, the nest membership test fails for the same reason.
  4. Otherwise, C and D belong to the same nest if H and H' are the same class or interface.

To determine the nest host of a class M, the following steps are performed:

  1. If M lacks a NestHost attribute (4.7.28), M is its own nest host.
  2. Otherwise, where i is the host_class_index item of M's NestHost attribute, the symbolic reference at index i of M's run-time constant pool is resolved to a class or interface H (5.4.3.1). Any of the exceptions pertaining to class or interface resolution can be thrown.
  3. If resolution of H succeeds, but H is not declared in the same
    run-time package as M, an IncompatibleClassChangeError is thrown.
  4. Otherwise, if H lacks a NestMembers attribute (4.7.29), or if, where M has name N, there exists no entry in the classes array of the NestMembers attribute of H that refers to a class or interface with name N, an IncompatibleClassChangeError is thrown.
  5. Otherwise, H is the nest host of M.

As taken from here: http://cr.openjdk.java.net/~dlsmith/nestmates.html

Signed-off-by: Talia McCormick Talia.McCormick@ibm.com

@taliamccormick
Copy link
Contributor Author

@tajila

}

/* If any problem occurred in nest host verification, mask 0th bit */
if (J9_VISIBILITY_ALLOWED != result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do you still need this if you are returning the J9_VISIBILITY_NEST* error code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I need some way of identifying which class - destClass or sourceClass - causes the nest verification error. I thought that the best way to do so was flag the 0 bit. A quick overview of the other options that I saw and my reasoning is below. Am open to other ideas/suggestions - having a non-functional non-null pointer may not be a good idea


Options:

  1. Separate error codes for nest host errors in destClass & sourceClass
  2. Changing function headers to pass a boolean/flag/other parameter to indicate nest host errors in either destClass or sourceClass
  3. Flagging the 0th bit on the address
  4. Putting a non-address value on the nest host (eg. -1)
  5. Making error messages non-dependent on which classes have issues

Issues:

  1. On a conceptual level, nest loading errors in either class would raise the same type of error, but nothing explicitly wrong with it (other than longer if-statements).
  2. Creates excessive changes to existing code.
  3. Equivalent to this method, but would require searching for a class if later changes to the code want to access the nest host.
  4. Risks that later changes to the code try to use the address to access nest host without masking the 0 bit; may cause confusion later.
  5. Need to know which class has nest loading issue, in order to raise accurate errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed yesterday, new/current approach is:

  • Create two sets of visibility error codes; one set for nest visibility/verification issues in the source class and one set for nest visibility/verification issues in the destination class
  • Shift the value of these visibility error codes such that they can be separated from the non-nest related error codes with a single comparison and that the source class codes and destination class codes can be separated from eachother via a single bitwise comparison.

eg.

Visibility check result Value Hex value Last eight bits
J9_VISIBILITY_ALLOWED 1 1 0000 0001
J9_VISIBILITY_NON_MODULE_ACCESS_ERROR 0 0 0
J9_VISIBILITY_MODULE_READ_ACCESS_ERROR -1 FFFF FFFF 1111 1111
J9_VISIBILITY_MODULE_PACKAGE_EXPORT_ERROR -2 FFFF FFFE 1111 1110
J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS -17 FFFF FFFF 1110 1111
J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_TARGET_CLASS -18 FFFF FFEF 1110 1110
J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_SOURCE_CLASS -19 FFFF FFED 1110 1101
J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_TARGET_CLASS -20 FFFF FFEC 1110 1100
J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_SOURCE_CLASS -21 FFFF FFEB 1110 1011
J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_TARGET_CLASS -22 FFFF FFEA 1110 1010

Then we can detect a nest-related error via checking the range or a single bitwise comparison on the 5th- 8th last bits

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS >= errorCode)
    && (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_TARGET_CLASS <= errorCode)) { ... }
#define J9_VISIBILITY_NEST_CODE 240 // 0000 ... 0000 1111 0000
#define J9_VISIBILITY_NEST_MASK 224 // 0000 ... 0000 1110 0000
if (J9_VISIBILITY_NEST_CODE == (J9_VISIBILITY_NEST_MASK & errorCode)) { ... }

And we can also use the parity bit to detect if the source class or destination class is causing the error/exception

if (0 == errorCode % 2) { ... /* target class */ }
if (1 == errorCode % 2) { ... /* destination class */ }

# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD=Class %2$.*1$s must be able to load its nest host %4$.*3$s.
Copy link
Contributor

Choose a reason for hiding this comment

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

do these messages matchup with the ones OpenJDK is using?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of them have different messages. The ones in which I add/directly use existing NLS messages, I will update to correspond with the ones OpenJDK is using.

What should I do for the cases in which existing error messages are different? By which I mean, the cases I did not add/change, eg. NoClassDefFound for testing the class loader case


I ran my updated set of test cases (currently found here: #1201) against both. For each test that was supposed to raise an error/exception, I printed: test case name + error/exception + cause of error/exception.

Relevant test cases for this PR are:

  • testDifferentClassLoadersWithAccess
  • testDifferentPackagesWithAccess
  • testNestMemberNotVerifiedWithAccess
  • testNesthostNotClaimedWithAccess

Ours:

testDifferentClassLoadersWithAccess java.lang.NoClassDefFoundError: FieldAccess$Inner java.lang.ClassNotFoundException

testDifferentPackagesWithAccess java.lang.IllegalAccessError: Class FieldAccess illegally accessing "package private" member of class pkg/FieldAccess$Inner null

testNestMemberNotVerifiedWithAccess java.lang.VerifyError: Class FieldAccess$Inner must be claimed by its nest host . null

testNesthostNotClaimedWithAccess java.lang.IllegalAccessError: Class FieldAccess illegally accessing "private" member of class FieldAccess$Inner null

OpenJDK:

testDifferentClassLoadersWithAccess java.lang.NoClassDefFoundError: FieldAccess$Inner java.lang.ClassNotFoundException: FieldAccess$Inner

testDifferentPackagesWithAccess java.lang.IllegalAccessError: failed to access class pkg.FieldAccess$Inner from class FieldAccess null

testNestMemberNotVerifiedWithAccess java.lang.IllegalAccessError: Type FieldAccess$Inner is not a nest member of FieldAccess: current type is not listed as a nest member null

testNesthostNotClaimedWithAccess java.lang.IllegalAccessError: tried to access field FieldAccess$Inner.f from class FieldAccess null


On a side note, how close do/should the parser errors be? I've copied the same output for those cases below and they are similar but not exactly the same.

This:

testMultipleNestMembersAttributes java.lang.ClassFormatError: JVMCFRE143 Multiple nest mates attributes; class=MultipleNestMembersAttributes, offset=166 null

testMultipleNestHostAttributes java.lang.ClassFormatError: JVMCFRE143 Multiple nest mates attributes; class=MultipleNestHostAttributes, offset=282 null

testMultipleNestAttributes java.lang.ClassFormatError: JVMCFRE143 Multiple nest mates attributes; class=MultipleNestAttributes, offset=188 null

OpenJDK:

testMultipleNestMembersAttributes java.lang.ClassFormatError: Multiple NestMembers attributes in class file MultipleNestMembersAttributes null

testMultipleNestHostAttributes java.lang.ClassFormatError: Multiple NestHost attributes in class file MultipleNestHostAttributes null

testMultipleNestAttributes java.lang.ClassFormatError: Conflicting NestHost and NestMembers attributes in class file MultipleNestAttributes null

Copy link
Contributor

Choose a reason for hiding this comment

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

We probably want to have a more specific error msg for these. Having more detail in the error message will make it easier to debug, but that doesn't have to be done in this PR

Copy link
Member

Choose a reason for hiding this comment

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

We don't want to copy the error messages. They should contain the same kind of information but shouldn't be copied from anywhere.

@pshipton pshipton added comp:vm project:valhalla Used to track Project Valhalla related work labels Feb 20, 2018
@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch 2 times, most recently from 1fdcb78 to 4487047 Compare February 21, 2018 16:31
# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD=Class %2$.*1$s must be able to load its nest host %4$.*3$s.
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably want to have a more specific error msg for these. Having more detail in the error message will make it easier to debug, but that doesn't have to be done in this PR

J9UTF8 *nestHostNameUTF = J9ROMCLASS_NESTHOSTNAME(romClass);

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS == errorType)
|| (J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_DEST_CLASS == errorType)){
Copy link
Contributor

Choose a reason for hiding this comment

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

needs space ){

Copy link
Contributor

Choose a reason for hiding this comment

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

also it is more clear like this:

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS == errorType)
    || (J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_DEST_CLASS == errorType)
) {

same for the rest.

*/
if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS <= errorType)
&& (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_DEST_CLASS >= errorType)) {
if (0 == errorType % 2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

a bit check might be less expensive then a modulo

&& (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_DEST_CLASS >= errorType)) {
if (0 == errorType % 2) {
unverifiedNestMemberClass = targetClass;
} else if (1 == ((UDATA)targetClass->nestHost & 1)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do you still need to check this? if errorType is an odd value is the error always sender?

UDATA classLoadingFlags = J9_FINDCLASS_FLAG_THROW_ON_FAIL;
nestHost = vmThread->javaVM->internalVMFunctions->internalFindClassUTF8(vmThread, J9UTF8_DATA(nestHostName), J9UTF8_LENGTH(nestHostName), clazz->classLoader, classLoadingFlags);

/* Nest host must successfully loaded by the same classloader in the same package & verify the nest member */
Copy link
Contributor

Choose a reason for hiding this comment

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

Nest host must be successfully loaded by

/* If any problem occurred in nest host verification, error code was set
* assuming that the nest host is being loaded for the source class; if
* not, add a -1 offset to change to dest class error code */
if (TRUE != isSourceClass) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this decrement result even if it is J9_VISIBILITY_ALLOWED

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed that - thanks

@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch 2 times, most recently from 98e86cc to eec3940 Compare February 22, 2018 15:29
/* If any problem occurred in nest host verification, error code was set
* assuming that the nest host is being loaded for the source class; if
* not, add a -1 offset to change to dest class error code */
if ((J9_VISIBILITY_ALLOWED == result) && (TRUE != isSourceClass)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be J9_VISIBILITY_ALLOWED != result

J9UTF8 *nestHostNameUTF = J9ROMCLASS_NESTHOSTNAME(romClass);

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS == errorType)
|| (J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_DEST_CLASS == errorType)
Copy link
Contributor

Choose a reason for hiding this comment

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

it looks like you have an extra tab here.

same below

@@ -678,8 +678,8 @@ resolveStaticFieldRefInto(J9VMThread *vmStruct, J9Method *method, J9ConstantPool
} else {
errorMsg = illegalAccessMessage(vmStruct, -1, classFromCP, targetClass, checkResult);
}
setCurrentExceptionUTF(vmStruct, J9VMCONSTANTPOOL_JAVALANGILLEGALACCESSERROR, errorMsg);
j9mem_free_memory(errorMsg);
setCurrentExceptionUTF(vmStruct, J9VMCONSTANTPOOL_JAVALANGILLEGALACCESSERROR, errorMsg);
Copy link
Contributor

Choose a reason for hiding this comment

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

this should also be tabbed

@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch 3 times, most recently from f9c19ff to 5c0df86 Compare February 26, 2018 15:46
/* Values of error codes for nestmates visibility checks on source class is
* odd and on destination/target class is even
*/
if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS <= errorType)
Copy link
Contributor

Choose a reason for hiding this comment

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

should be:

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS <= errorType)
    && (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_DEST_CLASS >= errorType)
) {

Copy link
Member

Choose a reason for hiding this comment

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

This approach feels very hacky. It will be easy for someone to add a new error message that doesn't follow this pattern without realizing it.

A switch statement will be more explicit and less fragile to future changes:

switch(errorType) {
case J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS:
case J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_SOURCE_CLASS:
case J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_SOURCE_CLASS:
    unverifiedNestMemberClass = targetClass;
    break;

case J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_DEST_CLASS
case J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_DEST_CLASS
case J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_DEST_CLASS
    unverifiedNestMemberClass = senderClass;
    break;
default:
   // Assert unreachable here is good when possible.  In this case, would still need to handle other error cases.

@@ -1729,7 +1729,7 @@ J9NLS_VM_NEST_HOST_HAS_DIFFERENT_PACKAGE.user_response=Load class and nest host
# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST=Class %2$.*1$s must be claimed by its nest host %4$.*1$s.
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST=Type %2$.*1$s is not a nest member of %4$.*3$s: current type is not listed as a nest member
Copy link
Member

Choose a reason for hiding this comment

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

Note the warning at the top of the file:

# New messages MUST be added at the end of this file.
# DO NOT delete messages from this file, as it will change their indices.
# If you wish to remove a message, delete its text, but leave the key in place

Typically we don't remove messages or add/remove format specifiers. In this case, it should be OK as the NestMates haven't been translated or shipped yet.

# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD=Class %2$.*1$s must be able to load its nest host %4$.*3$s.
Copy link
Member

Choose a reason for hiding this comment

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

We don't want to copy the error messages. They should contain the same kind of information but shouldn't be copied from anywhere.

/* Values of error codes for nestmates visibility checks on source class is
* odd and on destination/target class is even
*/
if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS <= errorType)
Copy link
Member

Choose a reason for hiding this comment

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

This approach feels very hacky. It will be easy for someone to add a new error message that doesn't follow this pattern without realizing it.

A switch statement will be more explicit and less fragile to future changes:

switch(errorType) {
case J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS:
case J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_SOURCE_CLASS:
case J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_SOURCE_CLASS:
    unverifiedNestMemberClass = targetClass;
    break;

case J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_DEST_CLASS
case J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR_DEST_CLASS
case J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR_DEST_CLASS
    unverifiedNestMemberClass = senderClass;
    break;
default:
   // Assert unreachable here is good when possible.  In this case, would still need to handle other error cases.

J9UTF8 *nestMemberNameUTF = J9ROMCLASS_CLASSNAME(romClass);
J9UTF8 *nestHostNameUTF = J9ROMCLASS_NESTHOSTNAME(romClass);

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR_SOURCE_CLASS == errorType)
Copy link
Member

Choose a reason for hiding this comment

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

This is another opportunity to use a switch. It may or may not be cleaner here.

@@ -29,6 +29,10 @@
#include "util_api.h"
#include "vm_internal.h"

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static VMINLINE loadAndVerifyNestHost(J9VMThread *vmThread, J9Class *clazz, BOOLEAN isSourceClass);
Copy link
Member

Choose a reason for hiding this comment

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

What's the return type for this function?

@@ -142,3 +155,55 @@ checkVisibility(J9VMThread *currentThread, J9Class* sourceClass, J9Class* destCl

return result;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static VMINLINE UDATA loadAndVerifyNestHost(J9VMThread *vmThread, J9Class *clazz, BOOLEAN isSourceClass) {
Copy link
Member

Choose a reason for hiding this comment

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

This should only be called once per class in non-failing cases as the nestHost will either be itself or filled in by the first call. I don't think it needs to be VMINLINEd.

For classes without a nest attribute, we can set the nestHost to themselves in createramclass.cpp and never call this function. (TODO: confirm we do that already)

nestHost = clazz;
} else {
UDATA classLoadingFlags = J9_FINDCLASS_FLAG_THROW_ON_FAIL;
nestHost = vmThread->javaVM->internalVMFunctions->internalFindClassUTF8(vmThread, J9UTF8_DATA(nestHostName), J9UTF8_LENGTH(nestHostName), clazz->classLoader, classLoadingFlags);
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need to call through the internalVMFunctions table as we're in the VM. It can call internalFindClassUTF8 directly

/* If any problem occurred in nest host verification, error code was set
* assuming that the nest host is being loaded for the source class; if
* not, add a -1 offset to change to dest class error code */
if ((J9_VISIBILITY_ALLOWED != result) && (TRUE != isSourceClass)) {
Copy link
Member

Choose a reason for hiding this comment

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

Not a fan of this approach. It assumes no one will ever renumber the constants and if they do, it will result in subtly wrong error messages.

I would rather see a new out parameter to indicate source / dest or a more obvious way of mapping between the two. A table maybe?

@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch 3 times, most recently from 0fa1703 to a88c8e2 Compare March 2, 2018 20:41
@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch 2 times, most recently from 014e853 to 6a882d3 Compare March 6, 2018 22:43
@@ -193,6 +193,7 @@ processMethod(J9VMThread * currentThread, UDATA lookupOptions, J9Method * method
} else {
*exception = J9VMCONSTANTPOOL_JAVALANGILLEGALACCESSERROR;
*exceptionClass = methodClass;
*errorType = J9_VISIBILITY_NON_MODULE_ACCESS_ERROR;
Copy link
Member

Choose a reason for hiding this comment

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

This is a really good find. Since this is a bug fix, can it be split into a separate PR?

That PR can address the other exit points from this method and ensure they set errorType appropriately as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a separate PR here: #1383
Other exit points look like they handle it correctly; this was the odd one out.

*/
if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR == errorType)) {
Copy link
Member

Choose a reason for hiding this comment

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

formatting nitpick: the closing ) { should be on its own line at the same indent level as the if. Makes it much easier to visually track the start / end of the if block.

J9UTF8 *nestMemberNameUTF = NULL;
J9UTF8 *nestHostNameUTF = NULL;

if (NULL == senderClass->nestHost) {
Copy link
Member

Choose a reason for hiding this comment

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

This needs a comment to describe why we can check in this order and know if the issue was with the sender or target.


if (bufLen > 0) {
buf = j9mem_allocate_memory(bufLen, OMRMEM_CATEGORY_VM);
if (NULL != buf) {
Copy link
Member

Choose a reason for hiding this comment

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

Since it uses a goto anyway, code can be written as:

if (NULL == buf) { 
    goto allocationFailures;
}
j9str_printf(PORTLIB, buf, bufLen, errorMsg,
	J9UTF8_LENGTH(nestMemberNameUTF),
	J9UTF8_DATA(nestMemberNameUTF),
	J9UTF8_LENGTH(nestHostNameUTF),
	J9UTF8_DATA(nestHostNameUTF));

Copy link
Member

Choose a reason for hiding this comment

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

This isn't something that needs to be changed. Just another way to write this code.

&& (sourceClass->nestHost != destClass->nestHost)
/* loadAndVerifyNestHost returns an error if setting nest host field fails */
if (NULL == destClass->nestHost) {
result = loadAndVerifyNestHost(currentThread, destClass);
Copy link
Member

Choose a reason for hiding this comment

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

if we add a goto, we can avoid the repeated (J9_VISIBILITY_ALLOWED == result) check in the common path.

Something like:

if (NULL == destClass->nestHost) {
	result = loadAndVerifyNestHost(currentThread, destClass);
	if ((J9_VISIBILITY_ALLOWED != result) {
		goto _exit;
	}
}
if (NULL == sourceClass->nestHost) {
....
}

if (NULL == nestHostName) {
nestHost = clazz;
} else {
UDATA classLoadingFlags = J9_FINDCLASS_FLAG_THROW_ON_FAIL;
Copy link
Member

Choose a reason for hiding this comment

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

TODO: check the code to see if we need to worry about this at JIT compile time. If it's a compile time resolve, we hopefully aren't doing access already.

Copy link
Member

Choose a reason for hiding this comment

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

This is called by checkVisibility, which is called by resolveClassRef during jit compile time resolves.

This code needs to check if this is a compile time resolve and if it is, replace J9_FINDCLASS_FLAG_THROW_ON_FAIL with J9_FINDCLASS_FLAG_EXISTING_ONLY.

Copy link
Member

Choose a reason for hiding this comment

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

checkVisiblity <- call by resolvefield.cpp code. Needs to also respect J9_RESOLVE_FLAG_NO_THROW_ON_FAIL to indicate not to throw.

Copy link
Member

Choose a reason for hiding this comment

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

if ((resolveFlags & J9_RESOLVE_FLAG_NO_THROW_ON_FAIL)
|| (resolveFlags & COMPILE_TIME)
|| .... AOT....
|| ... J9_FINDCLASS_FLAG_EXISTING_ONLY ...
) {
classLoadingFlags = J9_FINDCLASS_FLAG_EXISTING_ONLY;
}

Or make the callers pass in BOOLEAN canRunJavaCode and use that to pick the EXISTING_ONLY or not flags. Some callers may already know this.

}
}
if (!verified) {
result = J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR;
Copy link
Member

Choose a reason for hiding this comment

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

This can avoid the verified boolean if result = J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR; is set before the loop and then set to J9_VISIBILITY_ALLOWED if we find the nest member.

if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR == errorType)
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

needs to be shifted to the left by one tab

if (NULL == sourceClass->nestHost) {
result = loadAndVerifyNestHost(currentThread, sourceClass, lookupOptions);
}
if (J9_VISIBILITY_ALLOWED != result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why isn't this in the same scope has the (NULL == sourceClass->nestHost) check above

@@ -183,3 +213,52 @@ checkVisibility(J9VMThread *currentThread, J9Class* sourceClass, J9Class* destCl

return result;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static UDATA loadAndVerifyNestHost(J9VMThread *vmThread, J9Class *clazz, UDATA options) {
Copy link
Contributor

Choose a reason for hiding this comment

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

the { at the end should be on a separate line

nestHost = clazz;
} else {
UDATA classLoadingFlags = 0;
if ((options & J9_LOOK_NO_THROW ) != J9_LOOK_NO_THROW) {
Copy link
Contributor

Choose a reason for hiding this comment

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

use J9_ARE_NO_BITS_SET(...) macro for clarity

@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch from 502e251 to 772bc68 Compare March 21, 2018 22:21
@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch from 772bc68 to e264d3c Compare March 21, 2018 22:24
@taliamccormick taliamccormick changed the title Moves nestmates host loading to visibility check [Nest mates] Moves nestmates host loading to visibility check Mar 26, 2018
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD=Class %2$.*1$s must be able to load its nest host %4$.*3$s.
# START NON-TRANSLATABLE
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD.explanation=Class and nest host class were loaded with different class loaders.
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD.system_action=The JVM will throw a java/lang/VerifyError
Copy link
Member

Choose a reason for hiding this comment

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

java/lang/VerifyError -> ????? This is an IllegalAccessError now isn't it? The check doesn't occur during class load anymore...

result = loadAndVerifyNestHost(currentThread, sourceClass, lookupOptions);
if (J9_VISIBILITY_ALLOWED != result) {
goto _exit;
}
Copy link
Member

Choose a reason for hiding this comment

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

We need to update sourceClass to be the current class here again. Can you add:

sourceClass = J9_CURRENT_CLASS(sourceClass);

here?

In FSD mode, we may have created a new J9Class for sourceClass if a redefinition occurred and it may have a stale J9Class pointer.

result = loadAndVerifyNestHost(currentThread, destClass, lookupOptions);
if (J9_VISIBILITY_ALLOWED != result) {
goto _exit;
}
Copy link
Member

Choose a reason for hiding this comment

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

Same for destClass here. It also needs to be reloaded.

@@ -158,12 +158,16 @@ checkVisibility(J9VMThread *currentThread, J9Class* sourceClass, J9Class* destCl
*/
if (NULL == sourceClass->nestHost) {
result = loadAndVerifyNestHost(currentThread, sourceClass, lookupOptions);
sourceClass = J9_CURRENT_CLASS(sourceClass);
Copy link
Member

Choose a reason for hiding this comment

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

Small nitpick - these only need to be reloaded if the visibility succeeds so this could move after the if

if (J9_VISIBILITY_ALLOWED != result) {
goto _exit;
sourceClass = J9_CURRENT_CLASS(sourceClass);
destClass = J9_CURRENT_CLASS(destClass);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't these be after the if statement? Otherwise they're dead code following the goto

@DanHeidinga
Copy link
Member

The code in checkVisibility is missing some tracepoints for the module and nestmate failing cases. A separate PR can be used to add the tracepoints.

@DanHeidinga
Copy link
Member

Jenkins test sanity plinux jdk9

@DanHeidinga DanHeidinga self-assigned this Apr 5, 2018
@DanHeidinga
Copy link
Member

Recording that the build passed: https://ci.eclipse.org/openj9/job/PullRequest-Sanity-JDK9-linux_ppc-64_cmprssptrs_le-OpenJ9/202/

@taliamccormick Can you squash into logical sets of commits and we can merge this?

Talia McCormick added 2 commits April 6, 2018 14:52
Earlier nest mates spec draft loaded a class's nest host during class
loading. More recent nest mates spec puts off host loading until
required for an access check. This commit moves the nest host loading
and verification to the visibility check.

The spec draft for checking visibility states:

To determine whether a class or interface C belongs to the same nest as
D (that is, whether C and D are nestmates), the following steps are
performed:
1. If C and D are the same class or interface, they belong to the same
nest.
2. Otherwise, the nest host of D, H, is determined. If an exception is
thrown, the nest membership test fails for the same reason.
3. Otherwise, the nest host of C, H', is determined. If an exception is
thrown, the nest membership test fails for the same reason.
4. Otherwise, C and D belong to the same nest if H and H' are the same
class or interface.
To determine the nest host of a class M, the following steps are
performed:
1. If M lacks a NestHost attribute (4.7.28), M is its own nest host.
2. Otherwise, where i is the host_class_index item of M's NestHost
attribute, the symbolic reference at index i of M's run-time constant
pool is resolved to a class or interface H (5.4.3.1). Any of the
exceptions pertaining to class or interface resolution can be thrown.
3. If resolution of H succeeds, but H is not declared in the same
run-time package as M, an IncompatibleClassChangeError is thrown.
4. Otherwise, if H lacks a NestMembers attribute (4.7.29), or if, where
M has name N, there exists no entry in the classes array of the
NestMembers attribute of H that refers to a class or interface with name
N, an IncompatibleClassChangeError is thrown.
5. Otherwise, H is the nest host of M.

As taken from here: http://cr.openjdk.java.net/~dlsmith/nestmates.html

Signed-off-by: Talia McCormick <Talia.McCormick@ibm.com>
Earlier nest mates spec draft loaded a class's nest host during class
loading. More recent nest mates spec puts off host loading until
required for an access check. This commit removes the nest host loading
and verification from class loading and class verification respectively.

Signed-off-by: Talia McCormick <Talia.McCormick@ibm.com>
@taliamccormick taliamccormick force-pushed the UpdateNestmatesLinking branch from 859a2df to 6d6bc09 Compare April 6, 2018 18:57
@taliamccormick
Copy link
Contributor Author

@DanHeidinga Squashed

@DanHeidinga DanHeidinga merged commit 5727d76 into eclipse-openj9:master Apr 9, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:vm project:valhalla Used to track Project Valhalla related work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants