Wed, 22 Jan 2014 12:37:28 -0800
Added tag jdk8u5-b05 for changeset b90de55aca30
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. |
aoqi@0 | 8 | * |
aoqi@0 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 13 | * accompanied this code). |
aoqi@0 | 14 | * |
aoqi@0 | 15 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 18 | * |
aoqi@0 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 20 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 21 | * questions. |
aoqi@0 | 22 | */ |
aoqi@0 | 23 | |
aoqi@0 | 24 | import java.io.DataInputStream; |
aoqi@0 | 25 | import java.io.File; |
aoqi@0 | 26 | import java.io.FileInputStream; |
aoqi@0 | 27 | import java.io.FileWriter; |
aoqi@0 | 28 | import java.io.IOException; |
aoqi@0 | 29 | import java.io.PrintWriter; |
aoqi@0 | 30 | import javax.tools.JavaCompiler; |
aoqi@0 | 31 | import javax.tools.ToolProvider; |
aoqi@0 | 32 | |
aoqi@0 | 33 | /** |
aoqi@0 | 34 | * A class loader that generates new classes. |
aoqi@0 | 35 | * The generated classes are made by first emitting java sources with nested |
aoqi@0 | 36 | * static classes, these are then compiled and the class files are read back. |
aoqi@0 | 37 | * Some efforts are made to make the class instances unique and of not insignificant |
aoqi@0 | 38 | * size. |
aoqi@0 | 39 | */ |
aoqi@0 | 40 | public class GeneratedClassLoader extends ClassLoader { |
aoqi@0 | 41 | /** |
aoqi@0 | 42 | * Holds a pair of class bytecodes and class name (for use with defineClass). |
aoqi@0 | 43 | */ |
aoqi@0 | 44 | private static class GeneratedClass { |
aoqi@0 | 45 | public byte[] bytes; |
aoqi@0 | 46 | public String name; |
aoqi@0 | 47 | public GeneratedClass(byte[] bytes, String name) { |
aoqi@0 | 48 | this.bytes = bytes; this.name = name; |
aoqi@0 | 49 | } |
aoqi@0 | 50 | } |
aoqi@0 | 51 | |
aoqi@0 | 52 | /** |
aoqi@0 | 53 | * Used to uniquely name every class generated. |
aoqi@0 | 54 | */ |
aoqi@0 | 55 | private static int count = 0; |
aoqi@0 | 56 | /** |
aoqi@0 | 57 | * Used to enable/disable keeping the class files and java sources for |
aoqi@0 | 58 | * the generated classes. |
aoqi@0 | 59 | */ |
aoqi@0 | 60 | private static boolean deleteFiles = Boolean.parseBoolean( |
aoqi@0 | 61 | System.getProperty("GeneratedClassLoader.deleteFiles", "true")); |
aoqi@0 | 62 | |
aoqi@0 | 63 | private static String bigstr = |
aoqi@0 | 64 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " |
aoqi@0 | 65 | + "In facilisis scelerisque vehicula. Donec congue nisi a " |
aoqi@0 | 66 | + "leo posuere placerat lobortis felis ultrices. Pellentesque " |
aoqi@0 | 67 | + "habitant morbi tristique senectus et netus et malesuada " |
aoqi@0 | 68 | + "fames ac turpis egestas. Nam tristique velit at felis " |
aoqi@0 | 69 | + "iaculis at tempor sem vestibulum. Sed adipiscing lectus " |
aoqi@0 | 70 | + "non mi molestie sagittis. Morbi eu purus urna. Nam tempor " |
aoqi@0 | 71 | + "tristique massa eget semper. Mauris cursus, nulla et ornare " |
aoqi@0 | 72 | + "vehicula, leo dolor scelerisque metus, sit amet rutrum erat " |
aoqi@0 | 73 | + "sapien quis dui. Nullam eleifend risus et velit accumsan sed " |
aoqi@0 | 74 | + "suscipit felis pulvinar. Nullam faucibus suscipit gravida. " |
aoqi@0 | 75 | + "Pellentesque habitant morbi tristique senectus et netus et " |
aoqi@0 | 76 | + "malesuada fames ac turpis egestas. Nullam ut massa augue, " |
aoqi@0 | 77 | + "nec viverra mauris."; |
aoqi@0 | 78 | |
aoqi@0 | 79 | private static int getNextCount() { |
aoqi@0 | 80 | return count++; |
aoqi@0 | 81 | } |
aoqi@0 | 82 | |
aoqi@0 | 83 | ////// end statics |
aoqi@0 | 84 | |
aoqi@0 | 85 | private JavaCompiler javac; |
aoqi@0 | 86 | private String nameBase; |
aoqi@0 | 87 | |
aoqi@0 | 88 | public GeneratedClassLoader() { |
aoqi@0 | 89 | javac = ToolProvider.getSystemJavaCompiler(); |
aoqi@0 | 90 | nameBase = "TestSimpleClass"; |
aoqi@0 | 91 | } |
aoqi@0 | 92 | |
aoqi@0 | 93 | private long getBigValue(int which) { |
aoqi@0 | 94 | // > 65536 is too large to encode in the bytecode |
aoqi@0 | 95 | // so this will force us to emit a constant pool entry for this int |
aoqi@0 | 96 | return (long)which + 65537; |
aoqi@0 | 97 | } |
aoqi@0 | 98 | |
aoqi@0 | 99 | private String getBigString(int which) { |
aoqi@0 | 100 | return bigstr + which; |
aoqi@0 | 101 | } |
aoqi@0 | 102 | |
aoqi@0 | 103 | private String getClassName(int count) { |
aoqi@0 | 104 | return nameBase + count; |
aoqi@0 | 105 | } |
aoqi@0 | 106 | |
aoqi@0 | 107 | private String generateSource(int count, int sizeFactor, int numClasses) { |
aoqi@0 | 108 | StringBuilder sb = new StringBuilder(); |
aoqi@0 | 109 | sb.append("public class ").append(getClassName(count)).append("{\n"); |
aoqi@0 | 110 | for (int j = 0; j < numClasses; ++j) { |
aoqi@0 | 111 | sb.append("public static class ") |
aoqi@0 | 112 | .append("Class") |
aoqi@0 | 113 | .append(j) |
aoqi@0 | 114 | .append("{\n"); |
aoqi@0 | 115 | for (int i = 0; i < sizeFactor; ++i) { |
aoqi@0 | 116 | int value = i; |
aoqi@0 | 117 | sb.append("private long field") |
aoqi@0 | 118 | .append(i).append(" = ") |
aoqi@0 | 119 | .append(getBigValue(value++)) |
aoqi@0 | 120 | .append(";\n"); |
aoqi@0 | 121 | sb.append("public long method") |
aoqi@0 | 122 | .append(i) |
aoqi@0 | 123 | .append("() {\n"); |
aoqi@0 | 124 | sb.append("return ") |
aoqi@0 | 125 | .append(getBigValue(value++)) |
aoqi@0 | 126 | .append(";"); |
aoqi@0 | 127 | sb.append("}\n"); |
aoqi@0 | 128 | sb.append("private String str").append(i) |
aoqi@0 | 129 | .append(" = \"") |
aoqi@0 | 130 | .append(getBigString(i)) |
aoqi@0 | 131 | .append("\";"); |
aoqi@0 | 132 | } |
aoqi@0 | 133 | sb.append("\n}"); |
aoqi@0 | 134 | } |
aoqi@0 | 135 | sb.append("\n}"); |
aoqi@0 | 136 | return sb.toString(); |
aoqi@0 | 137 | } |
aoqi@0 | 138 | |
aoqi@0 | 139 | private GeneratedClass[] getGeneratedClass(int sizeFactor, int numClasses) throws IOException { |
aoqi@0 | 140 | int uniqueCount = getNextCount(); |
aoqi@0 | 141 | String src = generateSource(uniqueCount, sizeFactor, numClasses); |
aoqi@0 | 142 | String className = getClassName(uniqueCount); |
aoqi@0 | 143 | File file = new File(className + ".java"); |
aoqi@0 | 144 | try (PrintWriter pw = new PrintWriter(new FileWriter(file))) { |
aoqi@0 | 145 | pw.append(src); |
aoqi@0 | 146 | pw.flush(); |
aoqi@0 | 147 | } |
aoqi@0 | 148 | int exitcode = javac.run(null, null, null, file.getCanonicalPath()); |
aoqi@0 | 149 | if (exitcode != 0) { |
aoqi@0 | 150 | throw new RuntimeException("javac failure when compiling: " + |
aoqi@0 | 151 | file.getCanonicalPath()); |
aoqi@0 | 152 | } else { |
aoqi@0 | 153 | if (deleteFiles) { |
aoqi@0 | 154 | file.delete(); |
aoqi@0 | 155 | } |
aoqi@0 | 156 | } |
aoqi@0 | 157 | GeneratedClass[] gc = new GeneratedClass[numClasses]; |
aoqi@0 | 158 | for (int i = 0; i < numClasses; ++i) { |
aoqi@0 | 159 | String name = className + "$" + "Class" + i; |
aoqi@0 | 160 | File classFile = new File(name + ".class"); |
aoqi@0 | 161 | byte[] bytes; |
aoqi@0 | 162 | try (DataInputStream dis = new DataInputStream(new FileInputStream(classFile))) { |
aoqi@0 | 163 | bytes = new byte[dis.available()]; |
aoqi@0 | 164 | dis.readFully(bytes); |
aoqi@0 | 165 | } |
aoqi@0 | 166 | if (deleteFiles) { |
aoqi@0 | 167 | classFile.delete(); |
aoqi@0 | 168 | } |
aoqi@0 | 169 | gc[i] = new GeneratedClass(bytes, name); |
aoqi@0 | 170 | } |
aoqi@0 | 171 | if (deleteFiles) { |
aoqi@0 | 172 | new File(className + ".class").delete(); |
aoqi@0 | 173 | } |
aoqi@0 | 174 | return gc; |
aoqi@0 | 175 | } |
aoqi@0 | 176 | |
aoqi@0 | 177 | /** |
aoqi@0 | 178 | * Generate a single class, compile it and load it. |
aoqi@0 | 179 | * @param sizeFactor Fuzzy measure of how large the class should be. |
aoqi@0 | 180 | * @return the Class instance. |
aoqi@0 | 181 | * @throws IOException |
aoqi@0 | 182 | */ |
aoqi@0 | 183 | public Class<?> generateClass(int sizeFactor) throws IOException { |
aoqi@0 | 184 | return getGeneratedClasses(sizeFactor, 1)[0]; |
aoqi@0 | 185 | } |
aoqi@0 | 186 | |
aoqi@0 | 187 | /** |
aoqi@0 | 188 | * Generate several classes, compile and load them. |
aoqi@0 | 189 | * @param sizeFactor Fuzzy measure of how large each class should be. |
aoqi@0 | 190 | * @param numClasses The number of classes to create |
aoqi@0 | 191 | * @return an array of the Class instances. |
aoqi@0 | 192 | * @throws IOException |
aoqi@0 | 193 | */ |
aoqi@0 | 194 | public Class<?>[] getGeneratedClasses(int sizeFactor, int numClasses) throws IOException { |
aoqi@0 | 195 | GeneratedClass[] gc = getGeneratedClass(sizeFactor, numClasses); |
aoqi@0 | 196 | Class<?>[] classes = new Class[numClasses]; |
aoqi@0 | 197 | for (int i = 0; i < numClasses; ++i) { |
aoqi@0 | 198 | classes[i] = defineClass(gc[i].name, gc[i].bytes, 0 , gc[i].bytes.length); |
aoqi@0 | 199 | } |
aoqi@0 | 200 | return classes; |
aoqi@0 | 201 | } |
aoqi@0 | 202 | } |