1
1
/*
2
- * Copyright (c) 2013, 2023 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2013, 2024 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
26
26
import java .io .ByteArrayOutputStream ;
27
27
import java .io .IOException ;
28
28
import java .io .OutputStream ;
29
+ import java .io .StringWriter ;
30
+ import java .io .Writer ;
29
31
30
32
import java .net .URI ;
31
33
import java .util .ArrayList ;
32
34
import java .util .Arrays ;
35
+ import java .util .Collection ;
36
+ import java .util .HashMap ;
37
+ import java .util .LinkedList ;
33
38
import java .util .List ;
39
+ import java .util .Map ;
40
+ import java .util .Map .Entry ;
34
41
35
42
import javax .tools .FileObject ;
36
43
import javax .tools .ForwardingJavaFileManager ;
76
83
* </pre>
77
84
*/
78
85
public class InMemoryJavaCompiler {
79
- private static class MemoryJavaFileObject extends SimpleJavaFileObject {
80
- private final String className ;
81
- private final CharSequence sourceCode ;
82
- private final ByteArrayOutputStream byteCode ;
83
-
84
- public MemoryJavaFileObject (String className , CharSequence sourceCode ) {
85
- super (URI .create ("string:///" + className .replace ('.' ,'/' ) + Kind .SOURCE .extension ), Kind .SOURCE );
86
- this .className = className ;
87
- this .sourceCode = sourceCode ;
88
- this .byteCode = new ByteArrayOutputStream ();
89
- }
90
-
91
- @ Override
92
- public CharSequence getCharContent (boolean ignoreEncodingErrors ) {
93
- return sourceCode ;
94
- }
95
-
96
- @ Override
97
- public OutputStream openOutputStream () throws IOException {
98
- return byteCode ;
99
- }
100
-
101
- public byte [] getByteCode () {
102
- return byteCode .toByteArray ();
103
- }
104
-
105
- public String getClassName () {
106
- return className ;
107
- }
108
- }
109
86
110
87
private static class FileManagerWrapper extends ForwardingJavaFileManager <JavaFileManager > {
111
88
private static final Location PATCH_LOCATION = new Location () {
@@ -119,29 +96,31 @@ public boolean isOutputLocation() {
119
96
return false ;
120
97
}
121
98
};
122
- private final MemoryJavaFileObject file ;
99
+ private final SourceFile srcFile ;
100
+ private ClassFile clsFile ;
123
101
private final String moduleOverride ;
124
102
125
- public FileManagerWrapper (MemoryJavaFileObject file , String moduleOverride ) {
103
+ public FileManagerWrapper (SourceFile file , String moduleOverride ) {
126
104
super (getCompiler ().getStandardFileManager (null , null , null ));
127
- this .file = file ;
105
+ this .srcFile = file ;
128
106
this .moduleOverride = moduleOverride ;
129
107
}
130
108
131
109
@ Override
132
110
public JavaFileObject getJavaFileForOutput (Location location , String className ,
133
111
Kind kind , FileObject sibling )
134
112
throws IOException {
135
- if (!file .getClassName ().equals (className )) {
136
- throw new IOException ("Expected class with name " + file .getClassName () +
113
+ if (!srcFile .getClassName ().equals (className )) {
114
+ throw new IOException ("Expected class with name " + srcFile .getClassName () +
137
115
", but got " + className );
138
116
}
139
- return file ;
117
+ clsFile = new ClassFile (className );
118
+ return clsFile ;
140
119
}
141
120
142
121
@ Override
143
122
public Location getLocationForModule (Location location , JavaFileObject fo ) throws IOException {
144
- if (fo == file && moduleOverride != null ) {
123
+ if (fo == srcFile && moduleOverride != null ) {
145
124
return PATCH_LOCATION ;
146
125
}
147
126
return super .getLocationForModule (location , fo );
@@ -160,6 +139,100 @@ public boolean hasLocation(Location location) {
160
139
return super .hasLocation (location ) || location == StandardLocation .PATCH_MODULE_PATH ;
161
140
}
162
141
142
+ public byte [] getByteCode () {
143
+ return clsFile .toByteArray ();
144
+ }
145
+
146
+ }
147
+
148
+ // Wraper for class file
149
+ static class ClassFile extends SimpleJavaFileObject {
150
+
151
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream ();
152
+
153
+ protected ClassFile (String name ) {
154
+ super (URI .create ("memo:///" + name .replace ('.' , '/' ) + Kind .CLASS .extension ), Kind .CLASS );
155
+ }
156
+
157
+ @ Override
158
+ public ByteArrayOutputStream openOutputStream () { return this .baos ; }
159
+
160
+ byte [] toByteArray () { return baos .toByteArray (); }
161
+ }
162
+
163
+ // File manager which spawns ClassFile instances by demand
164
+ static class FileManager extends ForwardingJavaFileManager <JavaFileManager > {
165
+
166
+ private Map <String , ClassFile > classesMap = new HashMap <String , ClassFile >();
167
+
168
+ protected FileManager (JavaFileManager fileManager ) {
169
+ super (fileManager );
170
+ }
171
+
172
+ @ Override
173
+ public ClassFile getJavaFileForOutput (Location location , String name , JavaFileObject .Kind kind , FileObject source ) {
174
+ ClassFile classFile = new ClassFile (name );
175
+ classesMap .put (name , classFile );
176
+ return classFile ;
177
+ }
178
+
179
+ public Map <String , byte []> getByteCode () {
180
+ Map <String , byte []> result = new HashMap <String , byte []>();
181
+ for (Entry <String , ClassFile > entry : classesMap .entrySet ()) {
182
+ result .put (entry .getKey (), entry .getValue ().toByteArray ());
183
+ }
184
+ return result ;
185
+ }
186
+ }
187
+
188
+ // Wrapper for source file
189
+ static class SourceFile extends SimpleJavaFileObject {
190
+
191
+ private CharSequence sourceCode ;
192
+ private String className ;
193
+
194
+ public SourceFile (String name , CharSequence sourceCode ) {
195
+ super (URI .create ("memo:///" + name .replace ('.' , '/' ) + Kind .SOURCE .extension ), Kind .SOURCE );
196
+ this .sourceCode = sourceCode ;
197
+ this .className = name ;
198
+ }
199
+
200
+ @ Override
201
+ public CharSequence getCharContent (boolean ignore ) {
202
+ return this .sourceCode ;
203
+ }
204
+
205
+ public String getClassName () {
206
+ return this .className ;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Compiles the list of classes with the given map of name and source code.
212
+ * This overloaded version of compile is useful for batch compile use cases.
213
+ *
214
+ * @param inputMap The map containing the name of the class and corresponding source code
215
+ * @throws RuntimeException if the compilation did not succeed
216
+ * @return The resulting byte code from the compilation
217
+ */
218
+ public static Map <String , byte []> compile (Map <String , ? extends CharSequence > inputMap ) {
219
+ Collection <JavaFileObject > sourceFiles = new LinkedList <JavaFileObject >();
220
+ for (Entry <String , ? extends CharSequence > entry : inputMap .entrySet ()) {
221
+ sourceFiles .add (new SourceFile (entry .getKey (), entry .getValue ()));
222
+ }
223
+
224
+ JavaCompiler compiler = ToolProvider .getSystemJavaCompiler ();
225
+ FileManager fileManager = new FileManager (compiler .getStandardFileManager (null , null , null ));
226
+
227
+ Writer writer = new StringWriter ();
228
+ Boolean exitCode = compiler .getTask (writer , fileManager , null , null , null , sourceFiles ).call ();
229
+ if (!exitCode ) {
230
+ System .out .println ("*********** javac output begin ***********" );
231
+ System .out .println (writer .toString ());
232
+ System .out .println ("*********** javac output end ***********" );
233
+ throw new RuntimeException ("Test bug: in memory compilation failed." );
234
+ }
235
+ return fileManager .getByteCode ();
163
236
}
164
237
165
238
/**
@@ -173,7 +246,7 @@ public boolean hasLocation(Location location) {
173
246
* @return The resulting byte code from the compilation
174
247
*/
175
248
public static byte [] compile (String className , CharSequence sourceCode , String ... options ) {
176
- MemoryJavaFileObject file = new MemoryJavaFileObject (className , sourceCode );
249
+ SourceFile file = new SourceFile (className , sourceCode );
177
250
List <String > opts = new ArrayList <>();
178
251
String moduleOverride = null ;
179
252
for (String opt : options ) {
@@ -183,13 +256,13 @@ public static byte[] compile(String className, CharSequence sourceCode, String..
183
256
opts .add (opt );
184
257
}
185
258
}
186
- try (JavaFileManager fileManager = new FileManagerWrapper (file , moduleOverride )) {
259
+ try (FileManagerWrapper fileManager = new FileManagerWrapper (file , moduleOverride )) {
187
260
CompilationTask task = getCompiler ().getTask (null , fileManager , null , opts , null , Arrays .asList (file ));
188
261
if (!task .call ()) {
189
262
throw new RuntimeException ("Could not compile " + className + " with source code " + sourceCode );
190
263
}
191
264
192
- return file .getByteCode ();
265
+ return fileManager .getByteCode ();
193
266
} catch (IOException ioe ) {
194
267
throw new RuntimeException (ioe );
195
268
}
0 commit comments