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.InputStream; 27 import java.util.Collection; 28 import java.util.Enumeration; 29 import java.util.HashSet; 30 import java.util.Set; 31 import java.util.jar.JarEntry; 32 import java.util.jar.JarFile; 33 34 import org.apache.bcel.classfile.ClassParser; 35 import org.apache.bcel.classfile.Code; 36 import org.apache.bcel.classfile.JavaClass; 37 import org.apache.bcel.classfile.Method; 38 import org.apache.commons.lang3.StringUtils; 39 import org.apache.commons.lang3.SystemUtils; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.Parameterized; 43 import org.junit.runners.Parameterized.Parameters; 44 45 import com.sun.jna.platform.win32.Advapi32Util; 46 47 /** 48 * Test that the generic dump() methods work on the JDK classes Reads each class into an instruction list and then dumps 49 * the instructions. The output bytes should be the same as the input. 50 */ 51 @RunWith(Parameterized.class) 52 public class JDKGenericDumpTestCase { 53 54 private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); 55 56 private static final String KEY_JDK = "SOFTWARE\\JavaSoft\\Java Development Kit"; 57 58 private static final String KEY_JDK_9 = "SOFTWARE\\JavaSoft\\JDK"; 59 60 private static final String KEY_JRE = "SOFTWARE\\JavaSoft\\Java Runtime Environment"; 61 62 private static final String KEY_JRE_9 = "SOFTWARE\\JavaSoft\\JRE"; 63 addAllJavaHomesOnWindows(final String keyJre, final Set<String> javaHomes)64 private static void addAllJavaHomesOnWindows(final String keyJre, final Set<String> javaHomes) { 65 javaHomes.addAll(findJavaHomesOnWindows(keyJre, Advapi32Util.registryGetKeys(HKEY_LOCAL_MACHINE, keyJre))); 66 } 67 bytesToHex(final byte[] bytes)68 private static String bytesToHex(final byte[] bytes) { 69 final char[] hexChars = new char[bytes.length * 3]; 70 int i = 0; 71 for (final byte b : bytes) { 72 final int v = b & 0xFF; 73 hexChars[i++] = hexArray[v >>> 4]; 74 hexChars[i++] = hexArray[v & 0x0F]; 75 hexChars[i++] = ' '; 76 } 77 return new String(hexChars); 78 } 79 80 @Parameters(name = "{0}") data()81 public static Collection<String> data() { 82 return findJavaHomes(); 83 } 84 findJavaHomes()85 private static Set<String> findJavaHomes() { 86 if (SystemUtils.IS_OS_WINDOWS) { 87 return findJavaHomesOnWindows(); 88 } 89 final Set<String> javaHomes = new HashSet<>(1); 90 javaHomes.add(SystemUtils.JAVA_HOME); 91 return javaHomes; 92 } 93 findJavaHomesOnWindows()94 private static Set<String> findJavaHomesOnWindows() { 95 final Set<String> javaHomes = new HashSet<>(); 96 addAllJavaHomesOnWindows(KEY_JRE, javaHomes); 97 addAllJavaHomesOnWindows(KEY_JRE_9, javaHomes); 98 addAllJavaHomesOnWindows(KEY_JDK, javaHomes); 99 addAllJavaHomesOnWindows(KEY_JDK_9, javaHomes); 100 return javaHomes; 101 } 102 findJavaHomesOnWindows(final String keyJavaHome, final String[] keys)103 private static Set<String> findJavaHomesOnWindows(final String keyJavaHome, final String[] keys) { 104 final Set<String> javaHomes = new HashSet<>(keys.length); 105 for (final String key : keys) { 106 if (Advapi32Util.registryKeyExists(HKEY_LOCAL_MACHINE, keyJavaHome + "\\" + key)) { 107 final String javaHome = Advapi32Util.registryGetStringValue(HKEY_LOCAL_MACHINE, 108 keyJavaHome + "\\" + key, "JavaHome"); 109 if (StringUtils.isNoneBlank(javaHome)) { 110 if (new File(javaHome).exists()) { 111 javaHomes.add(javaHome); 112 } 113 } 114 } 115 } 116 return javaHomes; 117 } 118 119 private final String javaHome; 120 JDKGenericDumpTestCase(final String javaHome)121 public JDKGenericDumpTestCase(final String javaHome) { 122 this.javaHome = javaHome; 123 } 124 compare(final String name, final Method m)125 private void compare(final String name, final Method m) { 126 // System.out.println("Method: " + m); 127 final Code c = m.getCode(); 128 if (c == null) { 129 return; // e.g. abstract method 130 } 131 final byte[] src = c.getCode(); 132 final InstructionList il = new InstructionList(src); 133 final byte[] out = il.getByteCode(); 134 if (src.length == out.length) { 135 assertArrayEquals(name + ": " + m.toString(), src, out); 136 } else { 137 System.out.println(name + ": " + m.toString() + " " + src.length + " " + out.length); 138 System.out.println(bytesToHex(src)); 139 System.out.println(bytesToHex(out)); 140 for (final InstructionHandle ih : il) { 141 System.out.println(ih.toString(false)); 142 } 143 fail("Array comparison failure"); 144 } 145 } 146 listJDKjars()147 private File[] listJDKjars() throws Exception { 148 final File javaLib = new File(javaHome, "lib"); 149 return javaLib.listFiles(new FileFilter() { 150 @Override 151 public boolean accept(final File file) { 152 return file.getName().endsWith(".jar"); 153 } 154 }); 155 } 156 157 private void testJar(final File file) throws Exception { 158 System.out.println(file); 159 try (JarFile jar = new JarFile(file)) { 160 final Enumeration<JarEntry> en = jar.entries(); 161 while (en.hasMoreElements()) { 162 final JarEntry e = en.nextElement(); 163 final String name = e.getName(); 164 if (name.endsWith(".class")) { 165 // System.out.println("- " + name); 166 try (InputStream in = jar.getInputStream(e)) { 167 final ClassParser parser = new ClassParser(in, name); 168 final JavaClass jc = parser.parse(); 169 for (final Method m : jc.getMethods()) { 170 compare(name, m); 171 } 172 } 173 } 174 } 175 } 176 } 177 178 @Test 179 public void testJDKjars() throws Exception { 180 final File[] jars = listJDKjars(); 181 if (jars != null) { 182 for (final File file : jars) { 183 testJar(file); 184 } 185 } 186 } 187 } 188