changeset 13576:e583bb68cacb nestmates

8187221: [Nestmates] Virtual invocation for private interface methods Reviewed-by: Maurizio
author dholmes
date Thu, 07 Sep 2017 01:09:20 -0400
parents f2e4f1de7ace
children 8ee20a905c42
files src/share/vm/classfile/verifier.cpp src/share/vm/interpreter/linkResolver.cpp
diffstat 2 files changed, 88 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/classfile/verifier.cpp	Wed Aug 30 02:37:37 2017 -0400
+++ b/src/share/vm/classfile/verifier.cpp	Thu Sep 07 01:09:20 2017 -0400
@@ -2788,7 +2788,7 @@
           "Illegal call to internal method");
       return;
     }
-  } else if (opcode == Bytecodes::_invokespecial) {
+  } else if (UseNewCode && opcode == Bytecodes::_invokespecial) {
     bool same_or_direct_interface = is_same_or_direct_interface(current_class(), current_type(), ref_class_type);
     bool is_super_class = ref_class_type.equals(VerificationType::reference_type(current_class()->super()->name()));
 
@@ -2851,6 +2851,35 @@
         }
       }
     }
+  } else if (opcode == Bytecodes::_invokespecial
+             && !is_same_or_direct_interface(current_class(), current_type(), ref_class_type)
+             && !ref_class_type.equals(VerificationType::reference_type(current_class()->super()->name()))) {
+     bool subtype = false;
+     bool have_imr_indirect = cp->tag_at(index).value() == JVM_CONSTANT_InterfaceMethodref;
+     if (!current_class()->is_anonymous()) {
+       subtype = ref_class_type.is_assignable_from(current_type(), this, false, CHECK_VERIFY(this));
+     } else {
+       VerificationType host_klass_type =
+           VerificationType::reference_type(current_class()->host_klass()->name());
+       subtype = ref_class_type.is_assignable_from(host_klass_type, this, false, CHECK_VERIFY(this));
+
+       // If invokespecial of IMR, need to recheck for same or
+       // direct interface relative to the host class
+       have_imr_indirect = (have_imr_indirect &&
+                            !is_same_or_direct_interface(current_class()->host_klass(),
+                                                         host_klass_type, ref_class_type));
+     }
+     if (!subtype) {
+       verify_error(ErrorContext::bad_code(bci),
+                    "Bad invokespecial instruction: "
+                    "current class isn't assignable to reference class.");
+        return;
+     } else if (have_imr_indirect) {
+       verify_error(ErrorContext::bad_code(bci),
+                    "Bad invokespecial instruction: "
+                    "interface method reference is in an indirect superinterface.");
+       return;
+     }
   }
 
   // Match method descriptor with operand stack
@@ -2869,7 +2898,7 @@
       // Ensures that target class is assignable to current class (4.9.2),
       // unless performing a nestmate access
       if (opcode == Bytecodes::_invokespecial) {
-        if (nestmate_access) {
+        if (UseNewCode && nestmate_access) {
           VerificationType top = current_frame->pop_stack(CHECK_VERIFY(this));
         }
         else if (!current_class()->is_anonymous()) {
--- a/src/share/vm/interpreter/linkResolver.cpp	Wed Aug 30 02:37:37 2017 -0400
+++ b/src/share/vm/interpreter/linkResolver.cpp	Thu Sep 07 01:09:20 2017 -0400
@@ -334,7 +334,7 @@
 
   InstanceKlass* ik = InstanceKlass::cast(klass);
 
-  // JDK 8, JVMS 5.4.3.4: Interface method resolution should
+   // JDK 8, JVMS 5.4.3.4: Interface method resolution should
   // ignore static and non-public methods of java.lang.Object,
   // like clone, finalize, registerNatives.
   if (in_imethod_resolve &&
@@ -813,6 +813,9 @@
 #endif // PRODUCT
 }
 
+// FIXME: update to correct version
+#define VIRTUAL_PRIVATE_ACCESS_VERSION 53
+
 // Do linktime resolution of a method in the interface within the context of the specied bytecode.
 methodHandle LinkResolver::resolve_interface_method(const LinkInfo& link_info, Bytecodes::Code code, TRAPS) {
 
@@ -878,16 +881,18 @@
   }
 
   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);
+    assert(current_klass != NULL, "current_klass should not be null for invokeinterface");
+    if (InstanceKlass::cast(current_klass)->major_version() < VIRTUAL_PRIVATE_ACCESS_VERSION) {
+      ResourceMark rm(THREAD);
+      char buf[200];
+      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->internal_name());
+      THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
+    }
   }
 
   if (log_develop_is_enabled(Trace, itables)) {
@@ -1473,42 +1478,48 @@
     THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
   }
 
-  // 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 sel_method = lookup_instance_method_in_klasses(recv_klass,
-                                                  resolved_method->name(),
-                                                  resolved_method->signature(), CHECK);
-  if (sel_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.
-    sel_method = resolved_method;
-  }
-  // check if method exists
-  if (sel_method.is_null()) {
-    ResourceMark rm(THREAD);
-    THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
-                   Method::name_and_sig_as_C_string(recv_klass,
-                                                    resolved_method->name(),
-                                                    resolved_method->signature()));
-  }
-  // check access
-  // Throw Illegal Access Error if sel_method is not public.
-  if (!sel_method->is_public()) {
-    ResourceMark rm(THREAD);
-    THROW_MSG(vmSymbols::java_lang_IllegalAccessError(),
-              Method::name_and_sig_as_C_string(recv_klass,
-                                               sel_method->name(),
-                                               sel_method->signature()));
-  }
-  // check if abstract
-  if (check_null_and_abstract && sel_method->is_abstract()) {
-    ResourceMark rm(THREAD);
-    THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
-              Method::name_and_sig_as_C_string(recv_klass,
-                                               sel_method->name(),
-                                               sel_method->signature()));
+  methodHandle sel_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
+    sel_method = lookup_instance_method_in_klasses(recv_klass,
+                                                   resolved_method->name(),
+                                                   resolved_method->signature(), CHECK);
+
+    if (sel_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.
+      sel_method = resolved_method;
+    }
+    // check if method exists
+    if (sel_method.is_null()) {
+      ResourceMark rm(THREAD);
+      THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
+                Method::name_and_sig_as_C_string(recv_klass,
+                                                 resolved_method->name(),
+                                                 resolved_method->signature()));
+    }
+    // check access
+    // Throw Illegal Access Error if sel_method is not public.
+    if (!sel_method->is_public()) {
+      ResourceMark rm(THREAD);
+      THROW_MSG(vmSymbols::java_lang_IllegalAccessError(),
+                Method::name_and_sig_as_C_string(recv_klass,
+                                                 sel_method->name(),
+                                                 sel_method->signature()));
+    }
+    // check if abstract
+    if (check_null_and_abstract && sel_method->is_abstract()) {
+      ResourceMark rm(THREAD);
+      THROW_MSG(vmSymbols::java_lang_AbstractMethodError(),
+                Method::name_and_sig_as_C_string(recv_klass,
+                                                 sel_method->name(),
+                                                 sel_method->signature()));
+    }
   }
 
   if (log_develop_is_enabled(Trace, itables)) {