• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.bcel.generic;
19 
20 import static com.sun.jna.platform.win32.WinReg.HKEY_LOCAL_MACHINE;
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.fail;
23 
24 import java.io.File;
25 import java.io.FileFilter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.nio.file.FileSystems;
29 import java.nio.file.FileVisitResult;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.PathMatcher;
33 import java.nio.file.SimpleFileVisitor;
34 import java.nio.file.attribute.BasicFileAttributes;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Enumeration;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.jar.JarEntry;
42 import java.util.jar.JarFile;
43 
44 import org.apache.bcel.classfile.ClassParser;
45 import org.apache.bcel.classfile.Code;
46 import org.apache.bcel.classfile.JavaClass;
47 import org.apache.bcel.classfile.Method;
48 import org.apache.bcel.util.ModularRuntimeImage;
49 import org.apache.commons.lang3.JavaVersion;
50 import org.apache.commons.lang3.StringUtils;
51 import org.apache.commons.lang3.SystemUtils;
52 import org.junit.Assert;
53 import org.junit.Assume;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 import org.junit.runners.Parameterized;
57 import org.junit.runners.Parameterized.Parameters;
58 
59 import com.sun.jna.platform.win32.Advapi32Util;
60 
61 /**
62  * Test that the generic dump() methods work on the JDK classes Reads each class into an instruction list and then dumps
63  * the instructions. The output bytes should be the same as the input.
64  */
65 @RunWith(Parameterized.class)
66 public class JdkGenericDumpTestCase {
67 
68     private static class ClassParserFilesVisitor extends SimpleFileVisitor<Path> {
69 
70         private final PathMatcher matcher;
71 
ClassParserFilesVisitor(final String pattern)72         ClassParserFilesVisitor(final String pattern) {
73             matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
74         }
75 
find(final Path path)76         private void find(final Path path) throws IOException {
77             final Path name = path.getFileName();
78             if (name != null && matcher.matches(name)) {
79                 try (final InputStream inputStream = Files.newInputStream(path)) {
80                     final ClassParser parser = new ClassParser(inputStream, name.toAbsolutePath().toString());
81                     final JavaClass jc = parser.parse();
82                     Assert.assertNotNull(jc);
83                 }
84 
85             }
86         }
87 
88         @Override
preVisitDirectory(final Path dir, final BasicFileAttributes attrs)89         public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
90             find(dir);
91             return FileVisitResult.CONTINUE;
92         }
93 
94         @Override
visitFile(final Path file, final BasicFileAttributes attrs)95         public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
96             find(file);
97             return FileVisitResult.CONTINUE;
98         }
99 
100         @Override
visitFileFailed(final Path file, final IOException e)101         public FileVisitResult visitFileFailed(final Path file, final IOException e) {
102             System.err.println(e);
103             return FileVisitResult.CONTINUE;
104         }
105     }
106 
107     private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
108 
109     private static final String KEY_JDK = "SOFTWARE\\JavaSoft\\Java Development Kit";
110 
111     private static final String KEY_JDK_9 = "SOFTWARE\\JavaSoft\\JDK";
112 
113     private static final String KEY_JRE = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
114 
115     private static final String KEY_JRE_9 = "SOFTWARE\\JavaSoft\\JRE";
116 
addAllJavaHomesOnWindows(final String keyJre, final Set<String> javaHomes)117     private static void addAllJavaHomesOnWindows(final String keyJre, final Set<String> javaHomes) {
118         if (Advapi32Util.registryKeyExists(HKEY_LOCAL_MACHINE, keyJre)) {
119             javaHomes.addAll(findJavaHomesOnWindows(keyJre, Advapi32Util.registryGetKeys(HKEY_LOCAL_MACHINE, keyJre)));
120         }
121     }
122 
bytesToHex(final byte[] bytes)123     private static String bytesToHex(final byte[] bytes) {
124         final char[] hexChars = new char[bytes.length * 3];
125         int i = 0;
126         for (final byte b : bytes) {
127             final int v = b & 0xFF;
128             hexChars[i++] = hexArray[v >>> 4];
129             hexChars[i++] = hexArray[v & 0x0F];
130             hexChars[i++] = ' ';
131         }
132         return new String(hexChars);
133     }
134 
135     @Parameters(name = "{0}")
data()136     public static Collection<String> data() {
137         return findJavaHomes();
138     }
139 
findJavaHomes()140     private static Set<String> findJavaHomes() {
141         if (SystemUtils.IS_OS_WINDOWS) {
142             return findJavaHomesOnWindows();
143         }
144         final Set<String> javaHomes = new HashSet<>(1);
145         javaHomes.add(SystemUtils.JAVA_HOME);
146         return javaHomes;
147     }
148 
findJavaHomesOnWindows()149     private static Set<String> findJavaHomesOnWindows() {
150         final Set<String> javaHomes = new HashSet<>();
151         addAllJavaHomesOnWindows(KEY_JRE, javaHomes);
152         addAllJavaHomesOnWindows(KEY_JRE_9, javaHomes);
153         addAllJavaHomesOnWindows(KEY_JDK, javaHomes);
154         addAllJavaHomesOnWindows(KEY_JDK_9, javaHomes);
155         addAllJavaHomes("ExtraJavaHomes", javaHomes);
156         return javaHomes;
157     }
158 
addAllJavaHomes(String extraJavaHomesProp, Set<String> javaHomes)159     private static void addAllJavaHomes(String extraJavaHomesProp, Set<String> javaHomes) {
160 		String path = System.getProperty(extraJavaHomesProp);
161 		if (StringUtils.isEmpty(path)) {
162 			return;
163 		}
164 		String[] paths = path.split(File.pathSeparator);
165 		javaHomes.addAll(Arrays.asList(paths));
166 
167 	}
168 
findJavaHomesOnWindows(final String keyJavaHome, final String[] keys)169 	private static Set<String> findJavaHomesOnWindows(final String keyJavaHome, final String[] keys) {
170         final Set<String> javaHomes = new HashSet<>(keys.length);
171         for (final String key : keys) {
172             if (Advapi32Util.registryKeyExists(HKEY_LOCAL_MACHINE, keyJavaHome + "\\" + key)) {
173                 final String javaHome = Advapi32Util.registryGetStringValue(HKEY_LOCAL_MACHINE,
174                         keyJavaHome + "\\" + key, "JavaHome");
175                 if (StringUtils.isNoneBlank(javaHome)) {
176                     if (new File(javaHome).exists()) {
177                         javaHomes.add(javaHome);
178                     }
179                 }
180             }
181         }
182         return javaHomes;
183     }
184 
185     private final String javaHome;
186 
JdkGenericDumpTestCase(final String javaHome)187     public JdkGenericDumpTestCase(final String javaHome) {
188         this.javaHome = javaHome;
189     }
190 
compare(final String name, final Method m)191     private void compare(final String name, final Method m) {
192         // System.out.println("Method: " + m);
193         final Code c = m.getCode();
194         if (c == null) {
195             return; // e.g. abstract method
196         }
197         final byte[] src = c.getCode();
198         final InstructionList il = new InstructionList(src);
199         final byte[] out = il.getByteCode();
200         if (src.length == out.length) {
201             assertArrayEquals(name + ": " + m.toString(), src, out);
202         } else {
203             System.out.println(name + ": " + m.toString() + " " + src.length + " " + out.length);
204             System.out.println(bytesToHex(src));
205             System.out.println(bytesToHex(out));
206             for (final InstructionHandle ih : il) {
207                 System.out.println(ih.toString(false));
208             }
209             fail("Array comparison failure");
210         }
211     }
212 
listJdkJars()213     private File[] listJdkJars() throws Exception {
214         final File javaLib = new File(javaHome, "lib");
215         return javaLib.listFiles(new FileFilter() {
216             @Override
217             public boolean accept(final File file) {
218                 return file.getName().endsWith(".jar");
219             }
220         });
221     }
222 
223     private File[] listJdkModules() throws Exception {
224         final File javaLib = new File(javaHome, "jmods");
225         return javaLib.listFiles(new FileFilter() {
226             @Override
227             public boolean accept(final File file) {
228                 return file.getName().endsWith(".jmod");
229             }
230         });
231     }
232 
233     private void testJar(final File file) throws Exception {
234         System.out.println(file);
235         try (JarFile jar = new JarFile(file)) {
236             final Enumeration<JarEntry> en = jar.entries();
237             while (en.hasMoreElements()) {
238                 final JarEntry e = en.nextElement();
239                 final String name = e.getName();
240                 if (name.endsWith(".class")) {
241                     // System.out.println("- " + name);
242                     try (InputStream in = jar.getInputStream(e)) {
243                         final ClassParser parser = new ClassParser(in, name);
244                         final JavaClass jc = parser.parse();
245                         for (final Method m : jc.getMethods()) {
246                             compare(name, m);
247                         }
248                     }
249                 }
250             }
251         }
252     }
253 
254     @Test
255     public void testJdkJars() throws Exception {
256         final File[] jars = listJdkJars();
257         if (jars != null) {
258             for (final File file : jars) {
259                 testJar(file);
260             }
261         }
262     }
263 
264     @Test
265     public void testJdkModules() throws Exception {
266         final File[] jmods = listJdkModules();
267         if (jmods != null) {
268             for (final File file : jmods) {
269                 testJar(file);
270             }
271         }
272     }
273 
274     @Test
275     public void testJreModules() throws Exception {
276         Assume.assumeTrue(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9));
277         try (final ModularRuntimeImage mri = new ModularRuntimeImage(javaHome)) {
278             final List<Path> modules = mri.modules();
279             Assert.assertFalse(modules.isEmpty());
280             for (final Path path : modules) {
281                 Files.walkFileTree(path, new ClassParserFilesVisitor("*.class"));
282             }
283         }
284     }
285 
286 }
287