When running the Java 8 distribution's code of jOOR on Java 9,
CACHED_LOOKUP_CONSTRUCTOR is null because we're not allowed to access
that from JDK 9+. This means we can no longer use it in reflection
logic, e.g. when implementing in-memory compilation. We have to use the
Java 9+ logic via meta-reflection.
This commit is contained in:
Lukas Eder 2019-12-10 11:29:45 +01:00
parent d8b63da46c
commit 3bfb0c9756
2 changed files with 106 additions and 31 deletions

View File

@ -28,7 +28,10 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
@ -60,29 +63,32 @@ class Compile {
try {
ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
List<CharSequenceJavaFileObject> files = new ArrayList<CharSequenceJavaFileObject>();
List<CharSequenceJavaFileObject> files = new ArrayList<>();
files.add(new CharSequenceJavaFileObject(className, content));
StringWriter out = new StringWriter();
List<String> options = new ArrayList<String>();
StringBuilder classpath = new StringBuilder();
String separator = System.getProperty("path.separator");
String prop = System.getProperty("java.class.path");
List<String> options = new ArrayList<>(compileOptions.options);
if (!options.contains("-classpath")) {
StringBuilder classpath = new StringBuilder();
String separator = System.getProperty("path.separator");
String prop = System.getProperty("java.class.path");
if (prop != null && !"".equals(prop))
classpath.append(prop);
if (prop != null && !"".equals(prop))
classpath.append(prop);
if (cl instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) cl).getURLs()) {
if (classpath.length() > 0)
classpath.append(separator);
if (cl instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) cl).getURLs()) {
if (classpath.length() > 0)
classpath.append(separator);
if ("file".equals(url.getProtocol()))
classpath.append(new File(url.getFile()));
if ("file".equals(url.getProtocol()))
classpath.append(new File(url.toURI()));
}
}
options.addAll(Arrays.asList("-classpath", classpath.toString()));
}
options.addAll(Arrays.asList("-classpath", classpath.toString()));
CompilationTask task = compiler.getTask(out, fileManager, null, options, null, files);
if (!compileOptions.processors.isEmpty())
@ -90,16 +96,21 @@ class Compile {
task.call();
if (fileManager.o == null)
if (fileManager.isEmpty())
throw new ReflectException("Compilation error: " + out);
Class<?> result = null;
// This works if we have private-access to the interfaces in the class hierarchy
if (Reflect.CACHED_LOOKUP_CONSTRUCTOR != null) {
byte[] b = fileManager.o.getBytes();
result = Reflect.on(cl).call("defineClass", className, b, 0, b.length).get();
}
result = fileManager.loadAndReturnMainClass(className,
(name, bytes) -> Reflect.on(cl).call("defineClass", name, bytes, 0, bytes.length).get());
@ -150,6 +161,22 @@ class Compile {
}
}
static final class JavaFileObject extends SimpleJavaFileObject {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
@ -168,10 +195,13 @@ class Compile {
}
static final class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
JavaFileObject o;
private final Map<String, JavaFileObject> fileObjectMap;
private Map<String, byte[]> classes;
ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
fileObjectMap = new HashMap<>();
}
@Override
@ -181,8 +211,42 @@ class Compile {
JavaFileObject.Kind kind,
FileObject sibling
) {
return o = new JavaFileObject(className, kind);
JavaFileObject result = new JavaFileObject(className, kind);
fileObjectMap.put(className, result);
return result;
}
boolean isEmpty() {
return fileObjectMap.isEmpty();
}
Map<String, byte[]> classes() {
if (classes == null) {
classes = new HashMap<>();
for (Entry<String, JavaFileObject> entry : fileObjectMap.entrySet())
classes.put(entry.getKey(), entry.getValue().getBytes());
}
return classes;
}
Class<?> loadAndReturnMainClass(String mainClassName, ThrowingBiFunction<String, byte[], Class<?>> definer) throws Exception {
Class<?> result = null;
for (Entry<String, byte[]> entry : classes().entrySet()) {
Class<?> c = definer.apply(entry.getKey(), entry.getValue());
if (mainClassName.equals(entry.getKey()))
result = c;
}
return result;
}
}
@FunctionalInterface
interface ThrowingBiFunction<T, U, R> {
R apply(T t, U u) throws Exception;
}
static final class CharSequenceJavaFileObject extends SimpleJavaFileObject {

View File

@ -13,10 +13,9 @@
*/
package org.jooq.tools.reflect;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -28,25 +27,37 @@ import javax.annotation.processing.Processor;
public final class CompileOptions {
final List<? extends Processor> processors;
final List<String> options;
public CompileOptions() {
this(
Collections.emptyList(),
Collections.emptyList()
);
}
private CompileOptions(List<? extends Processor> processors) {
private CompileOptions(
List<? extends Processor> processors,
List<String> options
) {
this.processors = processors;
this.options = options;
}
@SuppressWarnings("hiding")
public CompileOptions processors(Processor... processors) {
return processors(Arrays.asList(processors));
public final CompileOptions processors(Processor... newProcessors) {
return processors(Arrays.asList(newProcessors));
}
@SuppressWarnings("hiding")
public CompileOptions processors(List<? extends Processor> processors) {
return new CompileOptions(processors);
public final CompileOptions processors(List<? extends Processor> newProcessors) {
return new CompileOptions(newProcessors, options);
}
public final CompileOptions options(String... newOptions) {
return options(Arrays.asList(newOptions));
}
public final CompileOptions options(List<String> newOptions) {
return new CompileOptions(processors, newOptions);
}
}