< prev index next >

src/hotspot/share/interpreter/linkResolver.cpp

Print this page
rev 50604 : imported patch jep181-rev1
rev 50605 : imported patch jep181-rev2
rev 50606 : imported patch jep181-rev3

@@ -385,16 +385,17 @@
 
 // returns first instance method
 // Looks up method in classes, then looks up local default methods
 methodHandle LinkResolver::lookup_instance_method_in_klasses(Klass* klass,
                                                              Symbol* name,
-                                                             Symbol* signature, TRAPS) {
-  Method* result = klass->uncached_lookup_method(name, signature, Klass::find_overpass);
+                                                             Symbol* signature,
+                                                             Klass::PrivateLookupMode private_mode, TRAPS) {
+  Method* result = klass->uncached_lookup_method(name, signature, Klass::find_overpass, private_mode);
 
   while (result != NULL && result->is_static() && result->method_holder()->super() != NULL) {
     Klass* super_klass = result->method_holder()->super();
-    result = super_klass->uncached_lookup_method(name, signature, Klass::find_overpass);
+    result = super_klass->uncached_lookup_method(name, signature, Klass::find_overpass, private_mode);
   }
 
   if (klass->is_array_klass()) {
     // Only consider klass and super klass for arrays
     return methodHandle(THREAD, result);

@@ -580,15 +581,18 @@
     new_flags = new_flags | JVM_ACC_PUBLIC;
     flags.set_flags(new_flags);
   }
 //  assert(extra_arg_result_or_null != NULL, "must be able to return extra argument");
 
-  if (!Reflection::verify_field_access(ref_klass,
+  bool can_access = Reflection::verify_member_access(ref_klass,
                                        resolved_klass,
                                        sel_klass,
                                        flags,
-                                       true)) {
+                                                     true, false, CHECK);
+  // Any existing exceptions that may have been thrown, for example LinkageErrors
+  // from nest-host resolution, have been allowed to propagate.
+  if (!can_access) {
     ResourceMark rm(THREAD);
     Exceptions::fthrow(
       THREAD_AND_LOCATION,
       vmSymbols::java_lang_IllegalAccessError(),
       "tried to access method %s.%s%s from class %s",

@@ -755,11 +759,11 @@
                                                      link_info.name(),
                                                      link_info.signature()),
                     nested_exception, NULL);
   }
 
-  // 5. access checks, access checking may be turned off when calling from within the VM.
+  // 6. access checks, access checking may be turned off when calling from within the VM.
   Klass* current_klass = link_info.current_klass();
   if (link_info.check_access()) {
     assert(current_klass != NULL , "current_klass should not be null");
 
     // check if method can be accessed by the referring class

@@ -771,10 +775,28 @@
 
     // check loader constraints
     check_method_loader_constraints(link_info, resolved_method, "method", CHECK_NULL);
   }
 
+  // For private method invocation we should only find the method in the resolved class.
+  // If that is not the case then we have a found a supertype method that we have nestmate
+  // access to.
+  if (resolved_method->is_private() && resolved_method->method_holder() != resolved_klass) {
+    ResourceMark rm(THREAD);
+    DEBUG_ONLY(bool is_nestmate = InstanceKlass::cast(link_info.current_klass())->has_nestmate_access_to(InstanceKlass::cast(resolved_klass), THREAD);)
+    assert(is_nestmate, "was only expecting nestmates to get here!");
+    Exceptions::fthrow(
+      THREAD_AND_LOCATION,
+      vmSymbols::java_lang_NoSuchMethodError(),
+      "%s: method %s%s not found",
+      resolved_klass->external_name(),
+      resolved_method->name()->as_C_string(),
+      resolved_method->signature()->as_C_string()
+    );
+    return NULL;
+  }
+
   return resolved_method;
 }
 
 static void trace_method_resolution(const char* prefix,
                                     Klass* klass,

@@ -872,23 +894,10 @@
                  Method::name_and_sig_as_C_string(resolved_klass,
                  resolved_method->name(), resolved_method->signature()));
     THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
   }
 
-  if (code == Bytecodes::_invokeinterface && resolved_method->is_private()) {
-    ResourceMark rm(THREAD);
-    char buf[200];
-
-    Klass* current_klass = link_info.current_klass();
-    jio_snprintf(buf, sizeof(buf), "private interface method requires invokespecial, not invokeinterface: method %s, caller-class:%s",
-                 Method::name_and_sig_as_C_string(resolved_klass,
-                                                  resolved_method->name(),
-                                                  resolved_method->signature()),
-                                                  (current_klass == NULL ? "<NULL>" : current_klass->internal_name()));
-     THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
-  }
-
   if (log_develop_is_enabled(Trace, itables)) {
     char buf[200];
     jio_snprintf(buf, sizeof(buf), "%s resolved interface method: caller-class:",
                  Bytecodes::name(code));
     trace_method_resolution(buf, link_info.current_klass(), resolved_klass,

@@ -904,15 +913,18 @@
 void LinkResolver::check_field_accessability(Klass* ref_klass,
                                              Klass* resolved_klass,
                                              Klass* sel_klass,
                                              const fieldDescriptor& fd,
                                              TRAPS) {
-  if (!Reflection::verify_field_access(ref_klass,
+  bool can_access = Reflection::verify_member_access(ref_klass,
                                        resolved_klass,
                                        sel_klass,
                                        fd.access_flags(),
-                                       true)) {
+                                                     true, false, CHECK);
+  // Any existing exceptions that may have been thrown, for example LinkageErrors
+  // from nest-host resolution, have been allowed to propagate.
+  if (!can_access) {
     ResourceMark rm(THREAD);
     Exceptions::fthrow(
       THREAD_AND_LOCATION,
       vmSymbols::java_lang_IllegalAccessError(),
       "tried to access field %s.%s from class %s",

@@ -1126,11 +1138,12 @@
       resolved_method->signature()->as_C_string()
     );
     return NULL;
   }
 
-  // check if invokespecial's interface method reference is in an indirect superinterface
+  // ensure that invokespecial's interface method reference is in
+  // a direct superinterface, not an indirect superinterface
   Klass* current_klass = link_info.current_klass();
   if (current_klass != NULL && resolved_klass->is_interface()) {
     InstanceKlass* ck = InstanceKlass::cast(current_klass);
     InstanceKlass *klass_to_check = !ck->is_anonymous() ?
                                     ck :

@@ -1198,16 +1211,18 @@
         // b) check if the class of the resolved_klass is a superclass
         // (not supertype in order to exclude interface classes) of the current class.
         // This check is not performed for super.invoke for interface methods
         // in super interfaces.
         current_klass->is_subclass_of(resolved_klass) &&
-        current_klass != resolved_klass) {
+        current_klass != resolved_klass
+        ) {
       // Lookup super method
       Klass* super_klass = current_klass->super();
       sel_method = lookup_instance_method_in_klasses(super_klass,
                            resolved_method->name(),
-                           resolved_method->signature(), CHECK);
+                                                     resolved_method->signature(),
+                                                     Klass::find_private, CHECK);
       // check if found
       if (sel_method.is_null()) {
         ResourceMark rm(THREAD);
         THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
                   Method::name_and_sig_as_C_string(resolved_klass,

@@ -1354,15 +1369,16 @@
   } else {
     // at this point we are sure that resolved_method is virtual and not
     // a default or miranda method; therefore, it must have a valid vtable index.
     assert(!resolved_method->has_itable_index(), "");
     vtable_index = resolved_method->vtable_index();
-    // We could get a negative vtable_index for final methods,
-    // because as an optimization they are never put in the vtable,
-    // unless they override an existing method.
-    // If we do get a negative, it means the resolved method is the the selected
-    // method, and it can never be changed by an override.
+    // We could get a negative vtable_index of nonvirtual_vtable_index for private
+    // methods, or for final methods. Private methods never appear in the vtable
+    // and never override other methods. As an optimization, final methods are
+    // never put in the vtable, unless they override an existing method.
+    // So if we do get nonvirtual_vtable_index, it means the selected method is the
+    // resolved method, and it can never be changed by an override.
     if (vtable_index == Method::nonvirtual_vtable_index) {
       assert(resolved_method->can_be_statically_bound(), "cannot override this method");
       selected_method = resolved_method;
     } else {
       selected_method = methodHandle(THREAD, recv_klass->method_at_vtable(vtable_index));

@@ -1413,10 +1429,11 @@
                                                     const methodHandle& resolved_method,
                                                     Klass* resolved_klass,
                                                     Handle recv,
                                                     Klass* recv_klass,
                                                     bool check_null_and_abstract, TRAPS) {
+
   // check if receiver exists
   if (check_null_and_abstract && recv.is_null()) {
     THROW(vmSymbols::java_lang_NullPointerException());
   }
 

@@ -1428,16 +1445,23 @@
                  recv_klass->external_name(),
                  resolved_klass->external_name());
     THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
   }
 
+  methodHandle selected_method = resolved_method;
+
+  // resolve the method in the receiver class, unless it is private
+  if (!resolved_method()->is_private()) {
   // do lookup based on receiver klass
   // This search must match the linktime preparation search for itable initialization
-  // to correctly enforce loader constraints for interface method inheritance
-  methodHandle selected_method = lookup_instance_method_in_klasses(recv_klass,
+    // to correctly enforce loader constraints for interface method inheritance.
+    // Private methods are skipped as the resolved method was not private.
+    selected_method = lookup_instance_method_in_klasses(recv_klass,
                                                   resolved_method->name(),
-                                                  resolved_method->signature(), CHECK);
+                                                        resolved_method->signature(),
+                                                        Klass::skip_private, CHECK);
+
   if (selected_method.is_null() && !check_null_and_abstract) {
     // In theory this is a harmless placeholder value, but
     // in practice leaving in null affects the nsk default method tests.
     // This needs further study.
     selected_method = resolved_method;

@@ -1458,23 +1482,36 @@
   }
   // check if abstract
   if (check_null_and_abstract && selected_method->is_abstract()) {
     throw_abstract_method_error(resolved_method, selected_method, recv_klass, CHECK);
   }
+  }
 
   if (log_develop_is_enabled(Trace, itables)) {
     trace_method_resolution("invokeinterface selected method: receiver-class:",
                             recv_klass, resolved_klass, selected_method, true);
   }
   // setup result
-  if (!resolved_method->has_itable_index()) {
+  if (resolved_method->has_vtable_index()) {
     int vtable_index = resolved_method->vtable_index();
+    log_develop_trace(itables)("  -- vtable index: %d", vtable_index);
     assert(vtable_index == selected_method->vtable_index(), "sanity check");
     result.set_virtual(resolved_klass, recv_klass, resolved_method, selected_method, vtable_index, CHECK);
-  } else {
+  } else if (resolved_method->has_itable_index()) {
     int itable_index = resolved_method()->itable_index();
+    log_develop_trace(itables)("  -- itable index: %d", itable_index);
     result.set_interface(resolved_klass, recv_klass, resolved_method, selected_method, itable_index, CHECK);
+  } else {
+    int index = resolved_method->vtable_index();
+    log_develop_trace(itables)("  -- non itable/vtable index: %d", index);
+    assert(index == Method::nonvirtual_vtable_index, "Oops hit another case!");
+    assert(resolved_method()->is_private() ||
+           (resolved_method()->is_final() && resolved_method->method_holder() == SystemDictionary::Object_klass()),
+           "Should only have non-virtual invokeinterface for private or final-Object methods!");
+    assert(resolved_method()->can_be_statically_bound(), "Should only have non-virtual invokeinterface for statically bound methods!");
+    // This sets up the nonvirtual form of "virtual" call (as needed for final and private methods)
+    result.set_virtual(resolved_klass, resolved_klass, resolved_method, resolved_method, index, CHECK);
   }
 }
 
 
 methodHandle LinkResolver::linktime_resolve_interface_method_or_null(
< prev index next >