1 /*
2  * Copyright (c) 2020, 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  * @summary Test that a hidden class has the same module as its lookup class.
27  * @library /test/lib
28  * @modules jdk.compiler
29  * @compile pkg/HasNamedModule.java
30  * @run main/othervm HiddenGetModule
31  */
32 
33 import java.io.ByteArrayOutputStream;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.lang.invoke.MethodType;
37 import java.lang.invoke.MethodHandles;
38 import java.lang.invoke.MethodHandles.Lookup;
39 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
40 import java.io.IOException;
41 import java.lang.ModuleLayer;
42 import java.lang.module.Configuration;
43 import java.lang.module.ModuleDescriptor;
44 import java.lang.module.ModuleFinder;
45 import java.lang.module.ModuleReader;
46 import java.lang.module.ModuleReference;
47 import java.lang.reflect.Method;
48 import java.net.URI;
49 import java.nio.ByteBuffer;
50 import java.nio.channels.FileChannel;
51 import java.util.HashMap;
52 import java.util.HashSet;
53 import java.util.Map;
54 import java.util.Objects;
55 import java.util.Optional;
56 import java.util.Set;
57 
58 import jdk.test.lib.compiler.InMemoryJavaCompiler;
59 
60 public class HiddenGetModule {
61 
62    static byte unnamedKlassbuf[] = InMemoryJavaCompiler.compile("TestClass",
63        "public class TestClass { " +
64        "    public static void concat(String one, String two) throws Throwable { " +
65        "        System.out.println(one + two);" +
66        " } } ");
67 
finderOf(ModuleDescriptor... descriptors)68     public static ModuleFinder finderOf(ModuleDescriptor... descriptors) {
69 
70         // Create a ModuleReference for each module
71         Map<String, ModuleReference> namesToReference = new HashMap<>();
72 
73         for (ModuleDescriptor descriptor : descriptors) {
74             String name = descriptor.name();
75 
76             URI uri = URI.create("module:/" + name);
77 
78             ModuleReference mref = new ModuleReference(descriptor, uri) {
79                 @Override
80                 public ModuleReader open() {
81                     throw new UnsupportedOperationException();
82                 }
83             };
84 
85             namesToReference.put(name, mref);
86         }
87 
88         return new ModuleFinder() {
89             @Override
90             public Optional<ModuleReference> find(String name) {
91                 Objects.requireNonNull(name);
92                 return Optional.ofNullable(namesToReference.get(name));
93             }
94             @Override
95             public Set<ModuleReference> findAll() {
96                 return new HashSet<>(namesToReference.values());
97             }
98         };
99     }
100 
101     public static void main(String[] args) throws Throwable {
102 
103         // Test unnamed module.
104         Lookup lookup = MethodHandles.lookup();
105         Class<?> cl = lookup.defineHiddenClass(unnamedKlassbuf, false, NESTMATE).lookupClass();
106         if (cl.getModule() != HiddenGetModule.class.getModule()) {
107             throw new RuntimeException("hidden class and lookup class have different unnamed modules");
108         }
109 
110         // Test named module.
111         MyClassLoader myClassLoader = new MyClassLoader();
112 
113         // Define a module named HiddenModule containing package pkg.
114         ModuleDescriptor descriptor = ModuleDescriptor.newModule("HiddenModule")
115                 .requires("java.base")
116                 .exports("pkg")
117                 .build();
118 
119         // Set up a ModuleFinder containing the module for this layer.
120         ModuleFinder finder = finderOf(descriptor);
121 
122         // Resolves "HiddenModule"
123         Configuration cf = ModuleLayer.boot()
124                 .configuration()
125                 .resolve(finder, ModuleFinder.of(), Set.of("HiddenModule"));
126 
127         // map module to class loader
128         Map<String, ClassLoader> map = new HashMap<>();
129         map.put("HiddenModule", myClassLoader);
130 
131         // Create layer that contains HiddenModule
132         ModuleLayer layer = ModuleLayer.boot().defineModules(cf, map::get);
133 
134         byte klassbuf[] = InMemoryJavaCompiler.compile("pkg.TestClass",
135             "package pkg; " +
136             "public class TestClass { " +
137             "    public static void concat(String one, String two) throws Throwable { " +
138             "        System.out.println(one + two);" +
139             " } } ");
140 
141         // Load the class and call the method that defines a hidden class and compares modules.
142         Class<?>c = Class.forName("pkg.HasNamedModule", true, myClassLoader);
143         if (c.getClassLoader() != myClassLoader) {
144             throw new RuntimeException("pkg.HasNamedModule defined by wrong classloader: " + c.getClassLoader());
145         }
146         Method m = c.getDeclaredMethod("compareModules", byte[].class);
147         m.invoke(null, klassbuf);
148     }
149 
150 
151     public static class MyClassLoader extends ClassLoader {
152 
153         public static final String CLASS_NAME = "HasNamedModule";
154 
155         static ByteBuffer readClassFile(String name) {
156             File f = new File(System.getProperty("test.classes", "."), name);
157             try (FileInputStream fin = new FileInputStream(f);
158                  FileChannel fc = fin.getChannel()) {
159                 return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
160             } catch (IOException e) {
161                 throw new RuntimeException("Can't open file: " + name + ", " + e.toString());
162             }
163         }
164 
165         protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
166             Class<?> c;
167             if (!name.contains(CLASS_NAME)) {
168                 c = super.loadClass(name, resolve);
169             } else {
170                 // should not delegate to the system class loader
171                 c = findClass(name);
172                 if (resolve) {
173                     resolveClass(c);
174                 }
175             }
176             return c;
177         }
178 
179         protected Class<?> findClass(String name) throws ClassNotFoundException {
180             if (!name.contains(CLASS_NAME)) {
181                 throw new ClassNotFoundException("Unexpected class: " + name);
182             }
183             return defineClass(name, readClassFile(name.replace(".", File.separator) + ".class"), null);
184         }
185     } /* MyClassLoader */
186 
187 }
188