changeset 7750:401fcded71c4

Add module information to JVM generated ClassCastException messages
author hseigel
date Mon, 26 Jan 2015 17:03:46 -0500
parents 88ffaead0056
children 5c450268abbd
files src/share/vm/c1/c1_Runtime1.cpp src/share/vm/interpreter/bytecodeInterpreter.cpp src/share/vm/interpreter/interpreterRuntime.cpp src/share/vm/runtime/reflection.cpp src/share/vm/runtime/sharedRuntime.cpp src/share/vm/runtime/sharedRuntime.hpp test/runtime/modules/AccessCheckRead.java test/runtime/modules/AccessCheckSuper.java test/runtime/modules/CCE_module_msg.java
diffstat 9 files changed, 116 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/c1/c1_Runtime1.cpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/c1/c1_Runtime1.cpp	Mon Jan 26 17:03:46 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -638,7 +638,7 @@
   NOT_PRODUCT(_throw_class_cast_exception_count++;)
   ResourceMark rm(thread);
   char* message = SharedRuntime::generate_class_cast_message(
-    thread, object->klass()->external_name());
+    thread, object->klass());
   SharedRuntime::throw_and_post_jvmti_exception(
     thread, vmSymbols::java_lang_ClassCastException(), message);
 JRT_END
--- a/src/share/vm/interpreter/bytecodeInterpreter.cpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/interpreter/bytecodeInterpreter.cpp	Mon Jan 26 17:03:46 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -2259,10 +2259,8 @@
               // Decrement counter at checkcast.
               BI_PROFILE_SUBTYPECHECK_FAILED(objKlass);
               ResourceMark rm(THREAD);
-              const char* objName = objKlass->external_name();
-              const char* klassName = klassOf->external_name();
               char* message = SharedRuntime::generate_class_cast_message(
-                objName, klassName);
+                objKlass, klassOf);
               VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message, note_classCheck_trap);
             }
             // Profile checkcast with null_seen and receiver.
--- a/src/share/vm/interpreter/interpreterRuntime.cpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/interpreter/interpreterRuntime.cpp	Mon Jan 26 17:03:46 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -358,7 +358,7 @@
 
   ResourceMark rm(thread);
   char* message = SharedRuntime::generate_class_cast_message(
-    thread, obj->klass()->external_name());
+    thread, obj->klass());
 
   if (ProfileTraps) {
     note_trap(thread, Deoptimization::Reason_class_check, CHECK);
--- a/src/share/vm/runtime/reflection.cpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/runtime/reflection.cpp	Mon Jan 26 17:03:46 2015 -0500
@@ -552,7 +552,7 @@
             len = len + 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, %s can not read %s",
+              "class %s (in module: %s) cannot access class %s (in module: %s), %s cannot read %s",
               current_class_name, module_from_name, new_class_name,
               module_to_name, module_from_name, module_to_name);
 
--- a/src/share/vm/runtime/sharedRuntime.cpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/runtime/sharedRuntime.cpp	Mon Jan 26 17:03:46 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,8 @@
 #include "interpreter/interpreterRuntime.hpp"
 #include "memory/gcLocker.inline.hpp"
 #include "memory/universe.inline.hpp"
+#include "oops/klass.hpp"
+#include "oops/objArrayKlass.hpp"
 #include "oops/oop.inline.hpp"
 #include "prims/forte.hpp"
 #include "prims/jvmtiExport.hpp"
@@ -1760,28 +1762,57 @@
 }
 JRT_END
 
+// The caller of generate_class_cast_message() (or one of its callers)
+// must use a ResourceMark in order to correctly free the result.
 char* SharedRuntime::generate_class_cast_message(
-    JavaThread* thread, const char* objName) {
+    JavaThread* thread, Klass* caster_klass) {
 
   // Get target class name from the checkcast instruction
   vframeStream vfst(thread, true);
   assert(!vfst.at_end(), "Java frame must exist");
   Bytecode_checkcast cc(vfst.method(), vfst.method()->bcp_from(vfst.bci()));
-  Klass* targetKlass = vfst.method()->constants()->klass_at(
+  Klass* target_klass = vfst.method()->constants()->klass_at(
     cc.index(), thread);
-  return generate_class_cast_message(objName, targetKlass->external_name());
+  return generate_class_cast_message(caster_klass, target_klass);
 }
 
 char* SharedRuntime::generate_class_cast_message(
-    const char* objName, const char* targetKlassName, const char* desc) {
-  size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1;
+    Klass* caster_klass, Klass* target_klass) {
+
+  const char* caster_klass_name = caster_klass->external_name();
+  Klass* c_klass = caster_klass->oop_is_objArray() ?
+    ObjArrayKlass::cast(caster_klass)->bottom_klass() : caster_klass;
+  ModuleEntry* caster_module;
+  if (c_klass->oop_is_instance()) {
+    caster_module = InstanceKlass::cast(c_klass)->module();
+  } else {
+    caster_module = NULL;
+  }
+  const char* caster_module_name = caster_module == NULL ? "unnamed" :
+    caster_module->name()->as_C_string();
+
+  const char* target_klass_name = target_klass->external_name();
+  Klass* t_klass = target_klass->oop_is_objArray() ?
+    ObjArrayKlass::cast(target_klass)->bottom_klass() : target_klass;
+  ModuleEntry* target_module;
+  if (t_klass->oop_is_instance()) {
+    target_module = InstanceKlass::cast(t_klass)->module();
+  } else {
+    target_module = NULL;
+  }
+  const char* target_module_name = target_module == NULL ? "unnamed" :
+    target_module->name()->as_C_string();
+
+  size_t msglen = strlen(caster_klass_name) + strlen(caster_module_name) +
+     strlen(target_klass_name) + strlen(target_module_name) + 50;
 
   char* message = NEW_RESOURCE_ARRAY(char, msglen);
   if (NULL == message) {
     // Shouldn't happen, but don't cause even more problems if it does
-    message = const_cast<char*>(objName);
+    message = const_cast<char*>(caster_klass_name);
   } else {
-    jio_snprintf(message, msglen, "%s%s%s", objName, desc, targetKlassName);
+    jio_snprintf(message, msglen, "%s (in module: %s) cannot be cast to %s (in module: %s)",
+      caster_klass_name, caster_module_name, target_klass_name, target_module_name);
   }
   return message;
 }
--- a/src/share/vm/runtime/sharedRuntime.hpp	Mon Jan 26 12:05:45 2015 +0100
+++ b/src/share/vm/runtime/sharedRuntime.hpp	Mon Jan 26 17:03:46 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -274,22 +274,21 @@
   // Fill in the "X cannot be cast to a Y" message for ClassCastException
   //
   // @param thr the current thread
-  // @param name the name of the class of the object attempted to be cast
+  // @param caster_klass the class of the object we are casting
   // @return the dynamically allocated exception message (must be freed
   // by the caller using a resource mark)
   //
   // BCP must refer to the current 'checkcast' opcode for the frame
   // on top of the stack.
-  // The caller (or one of it's callers) must use a ResourceMark
+  // The caller (or one of its callers) must use a ResourceMark
   // in order to correctly free the result.
   //
-  static char* generate_class_cast_message(JavaThread* thr, const char* name);
+  static char* generate_class_cast_message(JavaThread* thr, Klass* caster_klass);
 
   // Fill in the "X cannot be cast to a Y" message for ClassCastException
   //
-  // @param name the name of the class of the object attempted to be cast
-  // @param klass the name of the target klass attempt
-  // @param gripe the specific kind of problem being reported
+  // @param caster_klass the class of the object we are casting
+  // @param target_klass the target klass attempt
   // @return the dynamically allocated exception message (must be freed
   // by the caller using a resource mark)
   //
@@ -298,8 +297,7 @@
   // The caller (or one of it's callers) must use a ResourceMark
   // in order to correctly free the result.
   //
-  static char* generate_class_cast_message(const char* name, const char* klass,
-                                           const char* gripe = " cannot be cast to ");
+  static char* generate_class_cast_message(Klass* caster_klass, Klass* target_klass);
 
   // Resolves a call site- may patch in the destination of the call into the
   // compiled code.
--- a/test/runtime/modules/AccessCheckRead.java	Mon Jan 26 12:05:45 2015 +0100
+++ b/test/runtime/modules/AccessCheckRead.java	Mon Jan 26 17:03:46 2015 -0500
@@ -39,8 +39,8 @@
 
 public class AccessCheckRead {
 
-    // Test that a class in a package in module1 can not access a class in
-    // a package n module2 if module1 can not read module2.
+    // Test that a class in a package in module1 cannot access a class in
+    // a package n module2 if module1 cannot read module2.
     public static void main(String args[]) throws Exception {
         WhiteBox wb = WhiteBox.getWhiteBox();
         Object m1, m2;
@@ -70,13 +70,13 @@
         Class p1_c1_class = Class.forName("p1.c1");
 
         // p1.c1's ctor tries to call a method in p2.c2, but p1's module
-        // can not read p2's module.  So should get IllegalAccessError.
+        // cannot read p2's module.  So should get IllegalAccessError.
         try {
             p1_c1_class.newInstance();
             throw new RuntimeException("Failed to get IAE (m1 can't read m2)");
         } catch (IllegalAccessError e) {
             System.out.println(e.getMessage());
-            if (!e.getMessage().contains("can not read")) {
+            if (!e.getMessage().contains("cannot read")) {
                 throw new RuntimeException("Wrong message: " + e.getMessage());
             }
         }
--- a/test/runtime/modules/AccessCheckSuper.java	Mon Jan 26 12:05:45 2015 +0100
+++ b/test/runtime/modules/AccessCheckSuper.java	Mon Jan 26 17:03:46 2015 -0500
@@ -58,7 +58,7 @@
             throw new RuntimeException("Failed to get IAE (can't read superclass)");
         } catch (IllegalAccessError e) {
             if (!e.getMessage().contains("superclass access check failed") ||
-                !e.getMessage().contains("can not read")) {
+                !e.getMessage().contains("cannot read")) {
                 throw new RuntimeException("Wrong message: " + e.getMessage());
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/modules/CCE_module_msg.java	Mon Jan 26 17:03:46 2015 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @run main/othervm CCE_module_msg
+ */
+
+// Test that the message in a runtime ClassCastException contains module info.
+public class CCE_module_msg {
+
+    public static void main(String[] args) {
+        invalidCastTest();
+    }
+
+    public static void invalidCastTest() {
+        java.lang.Object instance = new java.lang.Object();
+        int left = 23;
+        int right = 42;
+        try {
+            for (int i = 0; i < 1; i += 1) {
+                left = ((Derived) instance).method(left, right);
+            }
+            throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
+        } catch (ClassCastException cce) {
+            System.out.println(cce.getMessage());
+            if (!cce.getMessage().contains("java.lang.Object (in module: java.base) cannot be cast")) {
+                throw new RuntimeException("Wrong message: " + cce.getMessage());
+            }
+        }
+    }
+}
+
+class Derived extends java.lang.Object {
+    public int method(int left, int right) {
+        return right;
+    }
+}