| /* |
| * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| import java.io.DataInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import javax.tools.JavaCompiler; |
| import javax.tools.ToolProvider; |
| |
| /** |
| * A class loader that generates new classes. |
| * The generated classes are made by first emitting java sources with nested |
| * static classes, these are then compiled and the class files are read back. |
| * Some efforts are made to make the class instances unique and of not insignificant |
| * size. |
| */ |
| public class GeneratedClassLoader extends ClassLoader { |
| /** |
| * Holds a pair of class bytecodes and class name (for use with defineClass). |
| */ |
| private static class GeneratedClass { |
| public byte[] bytes; |
| public String name; |
| public GeneratedClass(byte[] bytes, String name) { |
| this.bytes = bytes; this.name = name; |
| } |
| } |
| |
| /** |
| * Used to uniquely name every class generated. |
| */ |
| private static int count = 0; |
| /** |
| * Used to enable/disable keeping the class files and java sources for |
| * the generated classes. |
| */ |
| private static boolean deleteFiles = Boolean.parseBoolean( |
| System.getProperty("GeneratedClassLoader.deleteFiles", "true")); |
| |
| private static String bigstr = |
| "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " |
| + "In facilisis scelerisque vehicula. Donec congue nisi a " |
| + "leo posuere placerat lobortis felis ultrices. Pellentesque " |
| + "habitant morbi tristique senectus et netus et malesuada " |
| + "fames ac turpis egestas. Nam tristique velit at felis " |
| + "iaculis at tempor sem vestibulum. Sed adipiscing lectus " |
| + "non mi molestie sagittis. Morbi eu purus urna. Nam tempor " |
| + "tristique massa eget semper. Mauris cursus, nulla et ornare " |
| + "vehicula, leo dolor scelerisque metus, sit amet rutrum erat " |
| + "sapien quis dui. Nullam eleifend risus et velit accumsan sed " |
| + "suscipit felis pulvinar. Nullam faucibus suscipit gravida. " |
| + "Pellentesque habitant morbi tristique senectus et netus et " |
| + "malesuada fames ac turpis egestas. Nullam ut massa augue, " |
| + "nec viverra mauris."; |
| |
| private static int getNextCount() { |
| return count++; |
| } |
| |
| ////// end statics |
| |
| private JavaCompiler javac; |
| private String nameBase; |
| |
| public GeneratedClassLoader() { |
| javac = ToolProvider.getSystemJavaCompiler(); |
| nameBase = "TestSimpleClass"; |
| } |
| |
| private long getBigValue(int which) { |
| // > 65536 is too large to encode in the bytecode |
| // so this will force us to emit a constant pool entry for this int |
| return (long)which + 65537; |
| } |
| |
| private String getBigString(int which) { |
| return bigstr + which; |
| } |
| |
| private String getClassName(int count) { |
| return nameBase + count; |
| } |
| |
| private String generateSource(int count, int sizeFactor, int numClasses) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("public class ").append(getClassName(count)).append("{\n"); |
| for (int j = 0; j < numClasses; ++j) { |
| sb.append("public static class ") |
| .append("Class") |
| .append(j) |
| .append("{\n"); |
| for (int i = 0; i < sizeFactor; ++i) { |
| int value = i; |
| sb.append("private long field") |
| .append(i).append(" = ") |
| .append(getBigValue(value++)) |
| .append(";\n"); |
| sb.append("public long method") |
| .append(i) |
| .append("() {\n"); |
| sb.append("return ") |
| .append(getBigValue(value++)) |
| .append(";"); |
| sb.append("}\n"); |
| sb.append("private String str").append(i) |
| .append(" = \"") |
| .append(getBigString(i)) |
| .append("\";"); |
| } |
| sb.append("\n}"); |
| } |
| sb.append("\n}"); |
| return sb.toString(); |
| } |
| |
| private GeneratedClass[] getGeneratedClass(int sizeFactor, int numClasses) throws IOException { |
| int uniqueCount = getNextCount(); |
| String src = generateSource(uniqueCount, sizeFactor, numClasses); |
| String className = getClassName(uniqueCount); |
| File file = new File(className + ".java"); |
| try (PrintWriter pw = new PrintWriter(new FileWriter(file))) { |
| pw.append(src); |
| pw.flush(); |
| } |
| int exitcode = javac.run(null, null, null, file.getCanonicalPath()); |
| if (exitcode != 0) { |
| throw new RuntimeException("javac failure when compiling: " + |
| file.getCanonicalPath()); |
| } else { |
| if (deleteFiles) { |
| file.delete(); |
| } |
| } |
| GeneratedClass[] gc = new GeneratedClass[numClasses]; |
| for (int i = 0; i < numClasses; ++i) { |
| String name = className + "$" + "Class" + i; |
| File classFile = new File(name + ".class"); |
| byte[] bytes; |
| try (DataInputStream dis = new DataInputStream(new FileInputStream(classFile))) { |
| bytes = new byte[dis.available()]; |
| dis.readFully(bytes); |
| } |
| if (deleteFiles) { |
| classFile.delete(); |
| } |
| gc[i] = new GeneratedClass(bytes, name); |
| } |
| if (deleteFiles) { |
| new File(className + ".class").delete(); |
| } |
| return gc; |
| } |
| |
| /** |
| * Generate a single class, compile it and load it. |
| * @param sizeFactor Fuzzy measure of how large the class should be. |
| * @return the Class instance. |
| * @throws IOException |
| */ |
| public Class<?> generateClass(int sizeFactor) throws IOException { |
| return getGeneratedClasses(sizeFactor, 1)[0]; |
| } |
| |
| /** |
| * Generate several classes, compile and load them. |
| * @param sizeFactor Fuzzy measure of how large each class should be. |
| * @param numClasses The number of classes to create |
| * @return an array of the Class instances. |
| * @throws IOException |
| */ |
| public Class<?>[] getGeneratedClasses(int sizeFactor, int numClasses) throws IOException { |
| GeneratedClass[] gc = getGeneratedClass(sizeFactor, numClasses); |
| Class<?>[] classes = new Class[numClasses]; |
| for (int i = 0; i < numClasses; ++i) { |
| classes[i] = defineClass(gc[i].name, gc[i].bytes, 0 , gc[i].bytes.length); |
| } |
| return classes; |
| } |
| } |