Hugh.C
5 years ago
42 changed files with 571 additions and 500 deletions
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2006-2018 the original author or authors. |
||||||
|
* |
||||||
|
* 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 com.fr.third.org.objenesis.instantiator.util; |
||||||
|
|
||||||
|
import com.fr.third.org.objenesis.ObjenesisException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class for to play with classes. It contains everything needed to play with a class
|
||||||
|
* except the dodgy (Java 8) code you will find in {@link ClassDefinitionUtils}. |
||||||
|
* |
||||||
|
* @author Henri Tremblay |
||||||
|
*/ |
||||||
|
public final class ClassUtils { |
||||||
|
|
||||||
|
private ClassUtils() { } |
||||||
|
|
||||||
|
/** |
||||||
|
* Will convert a class name to its name in the class definition format (e.g {@code com.fr.third.org.objenesis.EmptyClass} |
||||||
|
* becomes {@code org/objenesis/EmptyClass}) |
||||||
|
* |
||||||
|
* @param className full class name including the package
|
||||||
|
* @return the internal name |
||||||
|
*/ |
||||||
|
public static String classNameToInternalClassName(String className) { |
||||||
|
return className.replace('.', '/'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will convert a class name to its class loader resource name (e.g {@code com.fr.third.org.objenesis.EmptyClass} |
||||||
|
* becomes {@code org/objenesis/EmptyClass.class}) |
||||||
|
* |
||||||
|
* @param className full class name including the package
|
||||||
|
* @return the resource name |
||||||
|
*/ |
||||||
|
public static String classNameToResource(String className) { |
||||||
|
return classNameToInternalClassName(className) + ".class"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if this class already exists in the class loader and return it if it does |
||||||
|
* |
||||||
|
* @param <T> type of the class returned |
||||||
|
* @param classLoader Class loader where to search the class
|
||||||
|
* @param className Class name with full path |
||||||
|
* @return the class if it already exists or null |
||||||
|
*/ |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public static <T> Class<T> getExistingClass(ClassLoader classLoader, String className) { |
||||||
|
try { |
||||||
|
return (Class<T>) Class.forName(className, true, classLoader); |
||||||
|
} |
||||||
|
catch (ClassNotFoundException e) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public static <T> T newInstance(Class<T> clazz) { |
||||||
|
try { |
||||||
|
return clazz.newInstance(); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2006-2018 the original author or authors. |
||||||
|
* |
||||||
|
* 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 com.fr.third.org.objenesis.instantiator.util; |
||||||
|
|
||||||
|
import sun.misc.Unsafe; |
||||||
|
import com.fr.third.org.objenesis.ObjenesisException; |
||||||
|
import com.fr.third.org.objenesis.strategy.PlatformDescription; |
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle; |
||||||
|
import java.lang.invoke.MethodHandles; |
||||||
|
import java.lang.invoke.MethodType; |
||||||
|
import java.security.ProtectionDomain; |
||||||
|
|
||||||
|
/** |
||||||
|
* Java 11+ removed sun.misc.Unsafe.defineClass. This class bridges the gap to work from Java 1.8 up to 11. |
||||||
|
* <p> |
||||||
|
* It was inspired from <a href="https://github.com/jboss-javassist/javassist/blob/master/src/main/javassist/util/proxy/DefineClassHelper.java">javassist</a>. |
||||||
|
* |
||||||
|
* @author Henri Tremblay |
||||||
|
*/ |
||||||
|
public final class DefineClassHelper { |
||||||
|
|
||||||
|
private static abstract class Helper { |
||||||
|
abstract Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, |
||||||
|
ClassLoader loader, ProtectionDomain protectionDomain); |
||||||
|
} |
||||||
|
|
||||||
|
private static class Java8 extends Helper { |
||||||
|
|
||||||
|
private final MethodHandle defineClass = defineClass(); |
||||||
|
|
||||||
|
private MethodHandle defineClass() { |
||||||
|
MethodType mt = MethodType.methodType(Class.class, String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); |
||||||
|
MethodHandle m; |
||||||
|
try { |
||||||
|
m = MethodHandles.publicLookup().findVirtual(Unsafe.class, "defineClass", mt); |
||||||
|
} catch(Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
Unsafe unsafe = UnsafeUtils.getUnsafe(); |
||||||
|
return m.bindTo(unsafe); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
Class<?> defineClass(String className, byte[] b, int off, int len, Class<?> neighbor, ClassLoader loader, ProtectionDomain protectionDomain) { |
||||||
|
try { |
||||||
|
return (Class<?>) defineClass.invokeExact(className, b, off, len, loader, protectionDomain); |
||||||
|
} catch (Throwable e) { |
||||||
|
if(e instanceof Error) { |
||||||
|
throw (Error) e; |
||||||
|
} |
||||||
|
if(e instanceof RuntimeException) { |
||||||
|
throw (RuntimeException) e; |
||||||
|
} |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class Java11 extends Helper { |
||||||
|
|
||||||
|
private final Class<?> module = module(); |
||||||
|
private final MethodHandles.Lookup lookup = MethodHandles.lookup(); |
||||||
|
private final MethodHandle getModule = getModule(); |
||||||
|
private final MethodHandle addReads = addReads(); |
||||||
|
private final MethodHandle privateLookupIn = privateLookupIn(); |
||||||
|
private final MethodHandle defineClass = defineClass(); |
||||||
|
|
||||||
|
private Class<?> module() { |
||||||
|
try { |
||||||
|
return Class.forName("java.lang.Module"); |
||||||
|
} catch (ClassNotFoundException e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private MethodHandle getModule() { |
||||||
|
try { |
||||||
|
return lookup.findVirtual(Class.class, "getModule", MethodType.methodType(module)); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private MethodHandle addReads() { |
||||||
|
try { |
||||||
|
return lookup.findVirtual(module, "addReads", MethodType.methodType(module, module)); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private MethodHandle privateLookupIn() { |
||||||
|
try { |
||||||
|
return lookup.findStatic(MethodHandles.class, "privateLookupIn", MethodType.methodType(MethodHandles.Lookup.class, Class.class, MethodHandles.Lookup.class)); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private MethodHandle defineClass() { |
||||||
|
try { |
||||||
|
return lookup.findVirtual(MethodHandles.Lookup.class, "defineClass", MethodType.methodType(Class.class, byte[].class)); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ObjenesisException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
Class<?> defineClass(String className, byte[] b, int off, int len, Class<?> neighbor, ClassLoader loader, ProtectionDomain protectionDomain) { |
||||||
|
try { |
||||||
|
Object module = getModule.invokeWithArguments(DefineClassHelper.class); |
||||||
|
Object neighborModule = getModule.invokeWithArguments(neighbor); |
||||||
|
addReads.invokeWithArguments(module, neighborModule); |
||||||
|
MethodHandles.Lookup prvlookup = (MethodHandles.Lookup) privateLookupIn.invokeExact(neighbor, lookup); |
||||||
|
return (Class<?>) defineClass.invokeExact(prvlookup, b); |
||||||
|
} catch (Throwable e) { |
||||||
|
throw new ObjenesisException(neighbor.getName() + " has no permission to define the class", e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Java 11+ removed sun.misc.Unsafe.defineClass, so we fallback to invoking defineClass on
|
||||||
|
// ClassLoader until we have an implementation that uses MethodHandles.Lookup.defineClass
|
||||||
|
private static final Helper privileged = PlatformDescription.isAfterJava11() ? |
||||||
|
new Java11() : new Java8(); |
||||||
|
|
||||||
|
public static Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, |
||||||
|
ClassLoader loader, ProtectionDomain protectionDomain) { |
||||||
|
return privileged.defineClass(name, b, off, len, neighbor, loader, protectionDomain); |
||||||
|
} |
||||||
|
|
||||||
|
private DefineClassHelper() {} |
||||||
|
} |
Loading…
Reference in new issue