1 /*
2  * Copyright (c) 2019, 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 import java.io.DataInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import javax.tools.JavaCompiler;
32 import javax.tools.ToolProvider;
33 
34 /**
35  * A factory that generates a class with many methods.
36  */
37 public class ClassWithManyMethodsClassLoader extends ClassLoader {
38     /**
39      * Used to enable/disable keeping the class files and java sources for
40      * the generated classes.
41      */
42     private static boolean deleteFiles = Boolean.parseBoolean(
43         System.getProperty("ClassWithManyMethodsClassLoader.deleteFiles", "true"));
44 
45     private JavaCompiler javac;
46 
ClassWithManyMethodsClassLoader()47     public ClassWithManyMethodsClassLoader() {
48         javac = ToolProvider.getSystemJavaCompiler();
49     }
50 
generateSource(String className, String methodPrefix, int methodCount)51     private String generateSource(String className, String methodPrefix, int methodCount) {
52         StringBuilder sb = new StringBuilder();
53         sb.append("public class ")
54             .append(className)
55             .append("{\n");
56 
57         for (int i = 0; i < methodCount; i++) {
58             sb.append("public void ")
59                 .append(methodPrefix)
60                 .append(i)
61                 .append("() {}\n");
62         }
63 
64         sb.append("\n}");
65 
66         return sb.toString();
67     }
68 
generateClassBytes(String className, String methodPrefix, int methodCount)69     private byte[] generateClassBytes(String className, String methodPrefix, int methodCount) throws IOException {
70         String src = generateSource(className, methodPrefix, methodCount);
71         File file = new File(className + ".java");
72         try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
73             pw.append(src);
74             pw.flush();
75         }
76         ByteArrayOutputStream err = new ByteArrayOutputStream();
77         int exitcode = javac.run(null, null, err, file.getCanonicalPath());
78         if (exitcode != 0) {
79             // Print Error
80             System.err.print(err);
81             if (err.toString().contains("java.lang.OutOfMemoryError: Java heap space")) {
82                 throw new OutOfMemoryError("javac failed with resources exhausted");
83             } else {
84               throw new RuntimeException("javac failure when compiling: " +
85                       file.getCanonicalPath());
86             }
87         } else {
88             if (deleteFiles) {
89                 file.delete();
90             }
91         }
92 
93         File classFile = new File(className + ".class");
94         byte[] bytes;
95         try (DataInputStream dis = new DataInputStream(new FileInputStream(classFile))) {
96             bytes = new byte[dis.available()];
97             dis.readFully(bytes);
98         }
99         if (deleteFiles) {
100             classFile.delete();
101         }
102 
103         return bytes;
104     }
105 
create(String className, String methodPrefix, int methodCount)106     public Class<?> create(String className, String methodPrefix, int methodCount) throws IOException {
107         byte[] bytes = generateClassBytes(className, methodPrefix, methodCount);
108         return defineClass(className, bytes, 0, bytes.length);
109     }
110 }
111