[#7385] Upgrade jOOR dependency to 0.9.8

This commit is contained in:
lukaseder 2018-04-04 22:14:31 +03:00
parent df580a1fe6
commit 0031a344c6
2 changed files with 336 additions and 48 deletions

View File

@ -0,0 +1,164 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jooq.tools.reflect;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
// import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
/**
* A utility that simplifies in-memory compilation of new classes.
*
* @author Lukas Eder
*/
class Compile {
static Class<?> compile(String className, String content) {
Lookup lookup = MethodHandles.lookup();
try {
return lookup.lookupClass().getClassLoader().loadClass(className);
}
catch (ClassNotFoundException ignore) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
try {
ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
List<CharSequenceJavaFileObject> files = new ArrayList<CharSequenceJavaFileObject>();
files.add(new CharSequenceJavaFileObject(className, content));
compiler.getTask(null, fileManager, null, null, null, files).call();
Class<?> result = null;
// This works if we have private-access to the interfaces in the class hierarchy
// if (Reflect.CACHED_LOOKUP_CONSTRUCTOR != null) {
ClassLoader cl = lookup.lookupClass().getClassLoader();
byte[] b = fileManager.o.getBytes();
result = Reflect.on(cl).call("defineClass", className, b, 0, b.length).get();
// }
/* [java-9] */
// Lookup.defineClass() has only been introduced in Java 9. It is
// required to get private-access to interfaces in the class hierarchy
// else {
//
// // This method is called by client code from two levels up the current stack frame
// // We need a private-access lookup from the class in that stack frame in order to get
// // private-access to any local interfaces at that location.
// Class<?> caller = StackWalker
// .getInstance(RETAIN_CLASS_REFERENCE)
// .walk(s -> s
// .skip(2)
// .findFirst()
// .get()
// .getDeclaringClass());
//
// // If the compiled class is in the same package as the caller class, then
// // we can use the private-access Lookup of the caller class
// if (className.startsWith(caller.getPackageName() + ".")) {
// result = MethodHandles
// .privateLookupIn(caller, lookup)
// .defineClass(fileManager.o.getBytes());
// }
//
// // Otherwise, use an arbitrary class loader. This approach doesn't allow for
// // loading private-access interfaces in the compiled class's type hierarchy
// else {
// result = new ClassLoader() {
// @Override
// protected Class<?> findClass(String name) throws ClassNotFoundException {
// byte[] b = fileManager.o.getBytes();
// return defineClass(className, b, 0, b.length);
// }
// }.loadClass(className);
// }
// }
/* [/java-9] */
return result;
}
catch (Exception e) {
throw new ReflectException("Error while compiling " + className, e);
}
}
}
static final class JavaFileObject extends SimpleJavaFileObject {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
JavaFileObject(String name, JavaFileObject.Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
byte[] getBytes() {
return os.toByteArray();
}
@Override
public OutputStream openOutputStream() {
return os;
}
}
static final class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
JavaFileObject o;
ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
}
@Override
public JavaFileObject getJavaFileForOutput(
JavaFileManager.Location location,
String className,
JavaFileObject.Kind kind,
FileObject sibling
) {
return o = new JavaFileObject(className, kind);
}
}
static final class CharSequenceJavaFileObject extends SimpleJavaFileObject {
final CharSequence content;
public CharSequenceJavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
}

View File

@ -1,6 +1,4 @@
/*
* Copyright (c) 2011-2017, Data Geekery GmbH (http://www.datageekery.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@ -15,6 +13,7 @@
*/
package org.jooq.tools.reflect;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@ -45,6 +44,7 @@ import java.util.Map;
*
* @author Lukas Eder
* @author Irek Matysiewicz
* @author Thomas Darimont
*/
public class Reflect {
@ -52,6 +52,32 @@ public class Reflect {
// Static API used as entrance points to the fluent API
// ---------------------------------------------------------------------
/**
* Compile a class at runtime and reflect on it.
* <p>
* For example:
* <code><pre>
* Supplier&lt;String> supplier = Reflect.compile(
* "org.joor.Test",
* "package org.joor;\n" +
* "class Test implements java.util.function.Supplier&lt;String> {\n" +
* " public String get() {\n" +
* " return \"Hello World!\";\n" +
* " }\n" +
* "}\n").create().get();
* </pre></code>
*
* @param name The qualified class name
* @param content The source code for the class
* @return A wrapped {@link Class}
* @throws ReflectException if anything went wrong compiling the class.
*/
public static Reflect compile(String name, String content) throws ReflectException {
return on(Compile.compile(name, content));
}
/**
* Wrap a class name.
* <p>
@ -66,6 +92,23 @@ public class Reflect {
return on(forName(name));
}
/**
* Wrap a class name, loading it via a given class loader.
* <p>
* This is the same as calling
* <code>on(Class.forName(name, classLoader))</code>
*
* @param name A fully qualified class name.
* @param classLoader The class loader in whose context the class should be
* loaded.
* @return A wrapped class object, to be used for further reflection.
* @throws ReflectException If any reflection exception occurred.
* @see #on(Class)
*/
public static Reflect on(String name, ClassLoader classLoader) throws ReflectException {
return on(forName(name, classLoader));
}
/**
* Wrap a class.
* <p>
@ -90,7 +133,11 @@ public class Reflect {
* @return A wrapped object, to be used for further reflection.
*/
public static Reflect on(Object object) {
return new Reflect(object);
return new Reflect(object == null ? Object.class : object.getClass(), object);
}
private static Reflect on(Class<?> type, Object object) {
return new Reflect(type, object);
}
/**
@ -118,9 +165,8 @@ public class Reflect {
}
// [jOOQ #3392] The accessible flag is set to false by default, also for public members.
if (!accessible.isAccessible()) {
if (!accessible.isAccessible())
accessible.setAccessible(true);
}
return accessible;
}
@ -129,30 +175,54 @@ public class Reflect {
// Members
// ---------------------------------------------------------------------
/**
* The wrapped object
*/
private final Object object;
static final Constructor<MethodHandles.Lookup> CACHED_LOOKUP_CONSTRUCTOR;
static {
Constructor<MethodHandles.Lookup> result;
/* [java-9] */
// if (true)
// result = null;
// else
/* [/java-9] */
try {
result = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
if (!result.isAccessible())
result.setAccessible(true);
}
// Can no longer access the above in JDK 9
catch (Throwable ignore) {
result = null;
}
CACHED_LOOKUP_CONSTRUCTOR = result;
}
/**
* A flag indicating whether the wrapped object is a {@link Class} (for
* accessing static fields and methods), or any other type of {@link Object}
* (for accessing instance fields and methods).
* The type of the wrapped object.
*/
private final boolean isClass;
private final Class<?> type;
/**
* The wrapped object.
*/
private final Object object;
// ---------------------------------------------------------------------
// Constructors
// ---------------------------------------------------------------------
private Reflect(Class<?> type) {
this.object = type;
this.isClass = true;
this(type, type);
}
private Reflect(Object object) {
private Reflect(Class<?> type, Object object) {
this.type = type;
this.object = object;
this.isClass = false;
}
// ---------------------------------------------------------------------
@ -176,6 +246,20 @@ public class Reflect {
* wrapped object is a {@link Class}, then this will set a value to a static
* member field. If the wrapped object is any other {@link Object}, then
* this will set a value to an instance member field.
* <p>
* This method is also capable of setting the value of (static) final
* fields. This may be convenient in situations where no
* {@link SecurityManager} is expected to prevent this, but do note that
* (especially static) final fields may already have been inlined by the
* javac and/or JIT and relevant code deleted from the runtime verison of
* your program, so setting these fields might not have any effect on your
* execution.
* <p>
* For restrictions of usage regarding setting values on final fields check:
* <a href=
* "http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection">http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection</a>
* ... and <a href=
* "http://pveentjer.blogspot.co.at/2017/01/final-static-boolean-jit.html">http://pveentjer.blogspot.co.at/2017/01/final-static-boolean-jit.html</a>
*
* @param name The field name
* @param value The new field value
@ -185,6 +269,18 @@ public class Reflect {
public Reflect set(String name, Object value) throws ReflectException {
try {
Field field = field0(name);
if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
try {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
// [#48] E.g. Android doesn't have this field
catch (NoSuchFieldException ignore) {}
}
field.set(object, unwrap(value));
return this;
}
@ -228,7 +324,7 @@ public class Reflect {
public Reflect field(String name) throws ReflectException {
try {
Field field = field0(name);
return on(field.get(object));
return on(field.getType(), field.get(object));
}
catch (Exception e) {
throw new ReflectException(e);
@ -236,24 +332,24 @@ public class Reflect {
}
private Field field0(String name) throws ReflectException {
Class<?> type = type();
Class<?> t = type();
// Try getting a public field
try {
return type.getField(name);
return accessible(t.getField(name));
}
// Try again, getting a non-public field
catch (NoSuchFieldException e) {
do {
try {
return accessible(type.getDeclaredField(name));
return accessible(t.getDeclaredField(name));
}
catch (NoSuchFieldException ignore) {}
type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);
throw new ReflectException(e);
}
@ -276,11 +372,11 @@ public class Reflect {
*/
public Map<String, Reflect> fields() {
Map<String, Reflect> result = new LinkedHashMap<String, Reflect>();
Class<?> type = type();
Class<?> t = type();
do {
for (Field field : type.getDeclaredFields()) {
if (!isClass ^ Modifier.isStatic(field.getModifiers())) {
for (Field field : t.getDeclaredFields()) {
if (type != object ^ Modifier.isStatic(field.getModifiers())) {
String name = field.getName();
if (!result.containsKey(name))
@ -288,9 +384,9 @@ public class Reflect {
}
}
type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);
return result;
}
@ -379,24 +475,24 @@ public class Reflect {
* If no exact match could be found, we let the {@code NoSuchMethodException} pass through.
*/
private Method exactMethod(String name, Class<?>[] types) throws NoSuchMethodException {
Class<?> type = type();
Class<?> t = type();
// first priority: find a public method with exact signature match in class hierarchy
try {
return type.getMethod(name, types);
return t.getMethod(name, types);
}
// second priority: find a private method with exact signature match on declaring class
catch (NoSuchMethodException e) {
do {
try {
return type.getDeclaredMethod(name, types);
return t.getDeclaredMethod(name, types);
}
catch (NoSuchMethodException ignore) {}
type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);
throw new NoSuchMethodException();
}
@ -411,11 +507,11 @@ public class Reflect {
* returned, otherwise a {@code NoSuchMethodException} is thrown.
*/
private Method similarMethod(String name, Class<?>[] types) throws NoSuchMethodException {
Class<?> type = type();
Class<?> t = type();
// first priority: find a public method with a "similar" signature in class hierarchy
// similar interpreted in when primitive argument types are converted to their wrappers
for (Method method : type.getMethods()) {
for (Method method : t.getMethods()) {
if (isSimilarSignature(method, name, types)) {
return method;
}
@ -423,15 +519,15 @@ public class Reflect {
// second priority: find a non-public method with a "similar" signature on declaring class
do {
for (Method method : type.getDeclaredMethods()) {
for (Method method : t.getDeclaredMethods()) {
if (isSimilarSignature(method, name, types)) {
return method;
}
}
type = type.getSuperclass();
t = t.getSuperclass();
}
while (type != null);
while (t != null);
throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + ".");
}
@ -515,17 +611,17 @@ public class Reflect {
* @return A proxy for the wrapped object
*/
@SuppressWarnings("unchecked")
public <P> P as(Class<P> proxyType) {
public <P> P as(final Class<P> proxyType) {
final boolean isMap = (object instanceof Map);
final InvocationHandler handler = new InvocationHandler() {
@SuppressWarnings("null")
@Override
@SuppressWarnings("null")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
// Actual method name matches always come first
try {
return on(object).call(name, args).get();
return on(type, object).call(name, args).get();
}
// [#14] Emulate POJO behaviour on wrapped map objects
@ -546,6 +642,30 @@ public class Reflect {
}
}
if (method.isDefault()) {
/* [java-9] */
// Java 9 version
// if (CACHED_LOOKUP_CONSTRUCTOR == null) {
// return MethodHandles
// .privateLookupIn(proxyType, MethodHandles.lookup())
// .in(proxyType)
// .unreflectSpecial(method, proxyType)
// .bindTo(proxy)
// .invokeWithArguments(args);
// }
/* [/java-9] */
// Java 8 version
return CACHED_LOOKUP_CONSTRUCTOR
.newInstance(proxyType)
.unreflectSpecial(method, proxyType)
.bindTo(proxy)
.invokeWithArguments(args);
}
throw e;
}
}
@ -635,7 +755,7 @@ public class Reflect {
*/
private static Reflect on(Constructor<?> constructor, Object... args) throws ReflectException {
try {
return on(accessible(constructor).newInstance(args));
return on(constructor.getDeclaringClass(), accessible(constructor).newInstance(args));
}
catch (Exception e) {
throw new ReflectException(e);
@ -707,18 +827,22 @@ public class Reflect {
}
}
private static Class<?> forName(String name, ClassLoader classLoader) throws ReflectException {
try {
return Class.forName(name, true, classLoader);
}
catch (Exception e) {
throw new ReflectException(e);
}
}
/**
* Get the type of the wrapped object.
*
* @see Object#getClass()
*/
public Class<?> type() {
if (isClass) {
return (Class<?>) object;
}
else {
return object.getClass();
}
return type;
}
/**