1 /*
  2  * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /**
 25  * @test
 26  * @modules java.base/jdk.internal.misc
 27  * @library /test/lib
 28  * @compile p2/c2.java
 29  * @compile p4/c4.java
 30  * @build sun.hotspot.WhiteBox
 31  * @compile/module=java.base java/lang/ModuleHelper.java
 32  * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
 33  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI CCE_module_msg
 34  */
 35 
 36 import java.io.*;
 37 import java.net.URL;
 38 import java.net.URLClassLoader;
 39 import java.nio.file.Path;
 40 import java.nio.file.Paths;
 41 import static jdk.test.lib.Asserts.*;
 42 
 43 import jdk.test.lib.classloader.ClassUnloadCommon;
 44 
 45 // Test that the message in a runtime ClassCastException contains module info.
 46 public class CCE_module_msg {
 47     private static final Path CLASSES_DIR = Paths.get("classes");
 48 
 49     public static void main(String[] args) throws Throwable {
 50         // Should not display version
 51         invalidObjectToDerived();
 52         invalidOriginalInnerToDerived();
 53         invalidTimeToDerived();
 54         invalidHeadersToDerived();
 55         // Should display version
 56         invalidClassToString();
 57         // Should display customer class loader
 58         invalidClassToStringCustomLoader();
 59     }
 60 
 61     public static void invalidObjectToDerived() {
 62         java.lang.Object instance = java.util.Objects.newIdentity();
 63         int left = 23;
 64         int right = 42;
 65         try {
 66             for (int i = 0; i < 1; i += 1) {
 67                 left = ((Derived) instance).method(left, right);
 68             }
 69             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
 70         } catch (ClassCastException cce) {
 71             System.out.println(cce.toString());
 72             if (!cce.getMessage().contains("class java.util.Objects$1 cannot be cast to class Derived (java.util.Objects$1 is in module java.base of loader 'bootstrap'; Derived is in unnamed module of loader 'app')")) {
 73                 throw new RuntimeException("Wrong message: " + cce.getMessage());
 74             }
 75         }
 76     }
 77 
 78     public static void invalidOriginalInnerToDerived() {
 79         OriginalInner instance = new OriginalInner();
 80         int left = 23;
 81         int right = 42;
 82         try {
 83             for (int i = 0; i < 1; i += 1) {
 84                 left = ((Derived) (java.lang.Object)instance).method(left, right);
 85             }
 86             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
 87         } catch (ClassCastException cce) {
 88             System.out.println(cce.toString());
 89             if (!cce.getMessage().contains("class OriginalInner cannot be cast to class Derived (OriginalInner and Derived are in unnamed module of loader 'app')")) {
 90                 throw new RuntimeException("Wrong message: " + cce.getMessage());
 91             }
 92         }
 93     }
 94 
 95     // Test with a non-upgradeable 'java.' module other than java.base.
 96     public static void invalidTimeToDerived() {
 97         java.sql.Time instance = new java.sql.Time(10000);
 98         int left = 23;
 99         int right = 42;
100         try {
101             for (int i = 0; i < 1; i += 1) {
102                 left = ((Derived) (java.lang.Object)instance).method(left, right);
103             }
104             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
105         } catch (ClassCastException cce) {
106             System.out.println(cce.toString());
107             if (!cce.getMessage().contains("class java.sql.Time cannot be cast to class Derived (java.sql.Time is in module java.sql of loader 'platform'; Derived is in unnamed module of loader 'app')")) {
108                 throw new RuntimeException("Wrong message: " + cce.getMessage());
109             }
110         }
111     }
112 
113     // Test with a non-upgradeable 'jdk.' module.
114     public static void invalidHeadersToDerived() {
115         com.sun.net.httpserver.Headers instance = new com.sun.net.httpserver.Headers();
116         int left = 23;
117         int right = 42;
118         try {
119             for (int i = 0; i < 1; i += 1) {
120                 left = ((Derived) (java.lang.Object)instance).method(left, right);
121             }
122             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
123         } catch (ClassCastException cce) {
124             System.out.println(cce.toString());
125             if (!cce.getMessage().contains("class com.sun.net.httpserver.Headers cannot be cast to class Derived (com.sun.net.httpserver.Headers is in module jdk.httpserver of loader 'platform'; Derived is in unnamed module of loader 'app')")) {
126                 throw new RuntimeException("Wrong message: " + cce.getMessage());
127             }
128         }
129     }
130 
131     public static void invalidClassToString() throws Throwable {
132         // Get the java.lang.Module object for module java.base.
133         Class jlObject = Class.forName("java.lang.Object");
134         Object jlObject_jlM = jlObject.getModule();
135         assertNotNull(jlObject_jlM, "jlModule object of java.lang.Object should not be null");
136 
137         // Get the class loader for CCE_module_msg and assume it's also used to
138         // load classes p1.c1 and p2.c2.
139         ClassLoader this_cldr = CCE_module_msg.class.getClassLoader();
140 
141         // Define a module for p2.
142         Object m2x = ModuleHelper.ModuleObject("module_two", this_cldr, new String[] { "p2" });
143         assertNotNull(m2x, "Module should not be null");
144         ModuleHelper.DefineModule(m2x, false, "9.0", "m2x/there", new String[] { "p2" });
145         ModuleHelper.AddReadsModule(m2x, jlObject_jlM);
146 
147         try {
148             ModuleHelper.AddModuleExportsToAll(m2x, "p2");
149             Object p2Obj = new p2.c2();
150             System.out.println((String)p2Obj);
151             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
152         } catch (ClassCastException cce) {
153             String exception = cce.getMessage();
154             System.out.println(cce.toString());
155             if (!exception.contains("class p2.c2 cannot be cast to class java.lang.String (p2.c2 is in module module_two@") ||
156                 !exception.contains(" of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')")) {
157                 throw new RuntimeException("Wrong message: " + exception);
158             }
159         }
160     }
161 
162     public static void invalidClassToStringCustomLoader() throws Throwable {
163         // Get the java.lang.Module object for module java.base.
164         Class jlObject = Class.forName("java.lang.Object");
165         Object jlObject_jlM = jlObject.getModule();
166         assertNotNull(jlObject_jlM, "jlModule object of java.lang.Object should not be null");
167 
168         // Create a customer class loader to load class p4/c4.
169         URL[] urls = new URL[] { CLASSES_DIR.toUri().toURL() };
170         ClassLoader parent = ClassLoader.getSystemClassLoader();
171         MyURLClassLoader myCldr = new MyURLClassLoader("MyClassLoader", urls, parent);
172 
173         try {
174             // Class p4.c4 should be defined to the unnamed module of myCldr
175             Class p4_c4_class = myCldr.loadClass("p4.c4");
176             Object c4Obj = p4_c4_class.newInstance();
177             System.out.println((String)c4Obj);
178             throw new RuntimeException("ClassCastException wasn't thrown, test failed.");
179         } catch (ClassCastException cce) {
180             String exception = cce.getMessage();
181             System.out.println(cce.toString());
182             if (!exception.contains("class p4.c4 cannot be cast to class java.lang.String (p4.c4 is in unnamed module of loader 'MyClassLoader' @") ||
183                 !exception.contains("; java.lang.String is in module java.base of loader 'bootstrap')")) {
184                 throw new RuntimeException("Wrong message: " + exception);
185             }
186         }
187     }
188 }
189 
190 class OriginalInner extends java.lang.Object {
191     public int method(int left, int right) {
192         return right;
193     }
194 }
195 
196 class Derived extends java.lang.Object {
197     public int method(int left, int right) {
198         return right;
199     }
200 }
201 
202 class MyURLClassLoader extends URLClassLoader {
203     public MyURLClassLoader(String name,
204                           URL[] urls,
205                           ClassLoader parent) {
206         super(name, urls, parent);
207     }
208 
209     public Class loadClass(String name) throws ClassNotFoundException {
210         if (!name.equals("p4.c4")) {
211             return super.loadClass(name);
212         }
213         byte[] data = ClassUnloadCommon.getClassData(name);
214         return defineClass(name, data, 0, data.length);
215     }
216 }