hotspot/src/share/vm/runtime/reflection.cpp
changeset 36508 5f9eee6b383b
parent 35900 d64cf9290fc4
child 37179 4dbcb3a642d2
--- a/hotspot/src/share/vm/runtime/reflection.cpp	Tue Mar 15 13:48:21 2016 -0700
+++ b/hotspot/src/share/vm/runtime/reflection.cpp	Thu Mar 17 19:04:01 2016 +0000
@@ -24,6 +24,8 @@
 
 #include "precompiled.hpp"
 #include "classfile/javaClasses.hpp"
+#include "classfile/moduleEntry.hpp"
+#include "classfile/packageEntry.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/verifier.hpp"
@@ -452,25 +454,193 @@
   return false;
 }
 
-bool Reflection::verify_class_access(const Klass* current_class,
-                                     const Klass* new_class,
-                                     bool classloader_only) {
+/*
+    Type Accessibility check for public types: Callee Type T is accessible to Caller Type S if:
+
+                        Callee T in             Callee T in package PT,
+                        unnamed module          runtime module MT
+ ------------------------------------------------------------------------------------------------
+
+ Caller S in package     If MS is loose: YES      If same classloader/package (PS == PT): YES
+ PS, runtime module MS   If MS can read T's       If same runtime module: (MS == MT): YES
+                         unnamed module: YES
+                                                  Else if (MS can read MT (Establish readability) &&
+                                                    MT exports PT to MS or to all modules): YES
+
+ ------------------------------------------------------------------------------------------------
+ Caller S in unnamed         YES                  Readability exists because unnamed module
+ module UM                                            "reads" all modules
+                                                  if (MT exports PT to UM or to all modules): YES
+
+ ------------------------------------------------------------------------------------------------
+
+ Note: a loose module is a module that can read all current and future unnamed modules.
+*/
+Reflection::VerifyClassAccessResults Reflection::verify_class_access(
+  const Klass* current_class, const Klass* new_class, bool classloader_only) {
+
   // Verify that current_class can access new_class.  If the classloader_only
   // flag is set, we automatically allow any accesses in which current_class
   // doesn't have a classloader.
   if ((current_class == NULL) ||
       (current_class == new_class) ||
-      (new_class->is_public()) ||
       is_same_class_package(current_class, new_class)) {
-    return true;
+    return ACCESS_OK;
   }
   // Allow all accesses from sun/reflect/MagicAccessorImpl subclasses to
   // succeed trivially.
   if (current_class->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass())) {
-    return true;
+    return ACCESS_OK;
+  }
+
+  // module boundaries
+  if (new_class->is_public()) {
+    // Ignore modules for DumpSharedSpaces because we do not have any package
+    // or module information for modules other than java.base.
+    if (DumpSharedSpaces) {
+      return ACCESS_OK;
+    }
+
+    // Find the module entry for current_class, the accessor
+    ModuleEntry* module_from = InstanceKlass::cast(current_class)->module();
+    // Find the module entry for new_class, the accessee
+    if (new_class->is_objArray_klass()) {
+      new_class = ObjArrayKlass::cast(new_class)->bottom_klass();
+    }
+    if (!new_class->is_instance_klass()) {
+      // Everyone can read a typearray.
+      assert (new_class->is_typeArray_klass(), "Unexpected klass type");
+      return ACCESS_OK;
+    }
+    ModuleEntry* module_to = InstanceKlass::cast(new_class)->module();
+
+    // both in same (possibly unnamed) module
+    if (module_from == module_to) {
+      return ACCESS_OK;
+    }
+
+    // Acceptable access to a type in an unamed module.  Note that since
+    // unnamed modules can read all unnamed modules, this also handles the
+    // case where module_from is also unnamed but in a different class loader.
+    if (!module_to->is_named() &&
+        (module_from->can_read_all_unnamed() || module_from->can_read(module_to))) {
+      return ACCESS_OK;
+    }
+
+    // Establish readability, check if module_from is allowed to read module_to.
+    if (!module_from->can_read(module_to)) {
+      return MODULE_NOT_READABLE;
+    }
+
+    PackageEntry* package_to = InstanceKlass::cast(new_class)->package();
+    assert(package_to != NULL, "can not obtain new_class' package");
+
+    // Once readability is established, if module_to exports T unqualifiedly,
+    // (to all modules), than whether module_from is in the unnamed module
+    // or not does not matter, access is allowed.
+    if (package_to->is_unqual_exported()) {
+      return ACCESS_OK;
+    }
+
+    // Access is allowed if both 1 & 2 hold:
+    //   1. Readability, module_from can read module_to (established above).
+    //   2. Either module_to exports T to module_from qualifiedly.
+    //      or
+    //      module_to exports T to all unnamed modules and module_from is unnamed.
+    //      or
+    //      module_to exports T unqualifiedly to all modules (checked above).
+    if (!package_to->is_qexported_to(module_from)) {
+      return TYPE_NOT_EXPORTED;
+    }
+    return ACCESS_OK;
   }
 
-  return can_relax_access_check_for(current_class, new_class, classloader_only);
+  if (can_relax_access_check_for(current_class, new_class, classloader_only)) {
+    return ACCESS_OK;
+  }
+  return OTHER_PROBLEM;
+}
+
+// Return an error message specific to the specified Klass*'s and result.
+// This function must be called from within a block containing a ResourceMark.
+char* Reflection::verify_class_access_msg(const Klass* current_class,
+                                          const Klass* new_class,
+                                          VerifyClassAccessResults result) {
+  assert(result != ACCESS_OK, "must be failure result");
+  char * msg = NULL;
+  if (result != OTHER_PROBLEM && new_class != NULL && current_class != NULL) {
+    // Find the module entry for current_class, the accessor
+    ModuleEntry* module_from = InstanceKlass::cast(current_class)->module();
+    const char * module_from_name = module_from->is_named() ? module_from->name()->as_C_string() : UNNAMED_MODULE;
+    const char * current_class_name = current_class->external_name();
+
+    // Find the module entry for new_class, the accessee
+    ModuleEntry* module_to = NULL;
+    if (new_class->is_objArray_klass()) {
+      new_class = ObjArrayKlass::cast(new_class)->bottom_klass();
+    }
+    if (new_class->is_instance_klass()) {
+      module_to = InstanceKlass::cast(new_class)->module();
+    } else {
+      module_to = ModuleEntryTable::javabase_module();
+    }
+    const char * module_to_name = module_to->is_named() ? module_to->name()->as_C_string() : UNNAMED_MODULE;
+    const char * new_class_name = new_class->external_name();
+
+    if (result == MODULE_NOT_READABLE) {
+      assert(module_from->is_named(), "Unnamed modules can read all modules");
+      if (module_to->is_named()) {
+        size_t len = 100 + strlen(current_class_name) + 2*strlen(module_from_name) +
+          strlen(new_class_name) + 2*strlen(module_to_name);
+        msg = NEW_RESOURCE_ARRAY(char, len);
+        jio_snprintf(msg, len - 1,
+          "class %s (in module %s) cannot access class %s (in module %s) because module %s does not read module %s",
+          current_class_name, module_from_name, new_class_name,
+          module_to_name, module_from_name, module_to_name);
+      } else {
+        jobject jlrm = module_to->module();
+        assert(jlrm != NULL, "Null jlrm in module_to ModuleEntry");
+        intptr_t identity_hash = JNIHandles::resolve(jlrm)->identity_hash();
+        size_t len = 160 + strlen(current_class_name) + 2*strlen(module_from_name) +
+          strlen(new_class_name) + 2*sizeof(uintx);
+        msg = NEW_RESOURCE_ARRAY(char, len);
+        jio_snprintf(msg, len - 1,
+          "class %s (in module %s) cannot access class %s (in unnamed module @" SIZE_FORMAT_HEX ") because module %s does not read unnamed module @" SIZE_FORMAT_HEX,
+          current_class_name, module_from_name, new_class_name, uintx(identity_hash),
+          module_from_name, uintx(identity_hash));
+      }
+
+    } else if (result == TYPE_NOT_EXPORTED) {
+      assert(InstanceKlass::cast(new_class)->package() != NULL,
+             "Unnamed packages are always exported");
+      const char * package_name =
+        InstanceKlass::cast(new_class)->package()->name()->as_klass_external_name();
+      assert(module_to->is_named(), "Unnamed modules export all packages");
+      if (module_from->is_named()) {
+        size_t len = 118 + strlen(current_class_name) + 2*strlen(module_from_name) +
+          strlen(new_class_name) + 2*strlen(module_to_name) + strlen(package_name);
+        msg = NEW_RESOURCE_ARRAY(char, len);
+        jio_snprintf(msg, len - 1,
+          "class %s (in module %s) cannot access class %s (in module %s) because module %s does not export %s to module %s",
+          current_class_name, module_from_name, new_class_name,
+          module_to_name, module_to_name, package_name, module_from_name);
+      } else {
+        jobject jlrm = module_from->module();
+        assert(jlrm != NULL, "Null jlrm in module_from ModuleEntry");
+        intptr_t identity_hash = JNIHandles::resolve(jlrm)->identity_hash();
+        size_t len = 170 + strlen(current_class_name) + strlen(new_class_name) +
+          2*strlen(module_to_name) + strlen(package_name) + 2*sizeof(uintx);
+        msg = NEW_RESOURCE_ARRAY(char, len);
+        jio_snprintf(msg, len - 1,
+          "class %s (in unnamed module @" SIZE_FORMAT_HEX ") cannot access class %s (in module %s) because module %s does not export %s to unnamed module @" SIZE_FORMAT_HEX,
+          current_class_name, uintx(identity_hash), new_class_name, module_to_name,
+          module_to_name, package_name, uintx(identity_hash));
+      }
+    } else {
+        ShouldNotReachHere();
+    }
+  }  // result != OTHER_PROBLEM...
+  return msg;
 }
 
 bool Reflection::verify_field_access(const Klass* current_class,