123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.util.proxy;
-
- import java.lang.invoke.MethodHandle;
- import java.lang.invoke.MethodHandles;
- import java.lang.invoke.MethodHandles.Lookup;
- import java.lang.reflect.Method;
- import java.security.ProtectionDomain;
- import java.util.List;
-
- import javassist.CannotCompileException;
- import javassist.bytecode.ClassFile;
-
- /**
- * Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}.
- *
- * @since 3.22
- */
- public class DefineClassHelper {
-
- private static abstract class Helper {
- abstract Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError, CannotCompileException;
- }
-
- private static class Java11 extends JavaOther {
- Class<?> defineClass(String name, byte[] bcode, int off, int len, Class<?> neighbor,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError, CannotCompileException
- {
- if (neighbor != null)
- return toClass(neighbor, bcode);
- else {
- // Lookup#defineClass() is not available. So fallback to invoking defineClass on
- // ClassLoader, which causes a warning message.
- return super.defineClass(name, bcode, off, len, neighbor, loader, protectionDomain);
- }
- }
- }
-
- private static class Java9 extends Helper {
- final class ReferencedUnsafe {
- private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe;
- private final MethodHandle defineClass;
-
- ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth) {
- this.sunMiscUnsafeTheUnsafe = usf;
- this.defineClass = meth;
- }
-
- Class<?> defineClass(String name, byte[] b, int off, int len,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- try {
- if (getCallerClass.invoke(stack) != Java9.class)
- throw new IllegalAccessError("Access denied for caller.");
- } catch (Exception e) {
- throw new RuntimeException("cannot initialize", e);
- }
- try {
- return (Class<?>) defineClass.invokeWithArguments(
- sunMiscUnsafeTheUnsafe.theUnsafe,
- name, b, off, len, loader, protectionDomain);
- } catch (Throwable e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- if (e instanceof ClassFormatError) throw (ClassFormatError) e;
- throw new ClassFormatError(e.getMessage());
- }
- }
- }
-
- private final Object stack;
- private final Method getCallerClass;
- private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe();
-
- Java9 () {
- Class<?> stackWalkerClass = null;
- try {
- stackWalkerClass = Class.forName("java.lang.StackWalker");
- } catch (ClassNotFoundException e) {
- // Skip initialization when the class doesn't exist i.e. we are on JDK < 9
- }
- if (stackWalkerClass != null) {
- try {
- Class<?> optionClass = Class.forName("java.lang.StackWalker$Option");
- stack = stackWalkerClass.getMethod("getInstance", optionClass)
- // The first one is RETAIN_CLASS_REFERENCE
- .invoke(null, optionClass.getEnumConstants()[0]);
- getCallerClass = stackWalkerClass.getMethod("getCallerClass");
- } catch (Throwable e) {
- throw new RuntimeException("cannot initialize", e);
- }
- } else {
- stack = null;
- getCallerClass = null;
- }
- }
-
- private final ReferencedUnsafe getReferencedUnsafe() {
- try {
- if (privileged != null && getCallerClass.invoke(stack) != this.getClass())
- throw new IllegalAccessError("Access denied for caller.");
- } catch (Exception e) {
- throw new RuntimeException("cannot initialize", e);
- }
- try {
- SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously();
- List<Method> defineClassMethod = usf.methods.get("defineClass");
- // On Java 11+ the defineClass method does not exist anymore
- if (null == defineClassMethod)
- return null;
- MethodHandle meth = MethodHandles.lookup().unreflect(defineClassMethod.get(0));
- return new ReferencedUnsafe(usf, meth);
- } catch (Throwable e) {
- throw new RuntimeException("cannot initialize", e);
- }
- }
-
- @Override
- Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- try {
- if (getCallerClass.invoke(stack) != DefineClassHelper.class)
- throw new IllegalAccessError("Access denied for caller.");
- } catch (Exception e) {
- throw new RuntimeException("cannot initialize", e);
- }
- return sunMiscUnsafe.defineClass(name, b, off, len, loader,
- protectionDomain);
- }
- }
-
- private static class Java7 extends Helper {
- private final SecurityActions stack = SecurityActions.stack;
- private final MethodHandle defineClass = getDefineClassMethodHandle();
- private final MethodHandle getDefineClassMethodHandle() {
- if (privileged != null && stack.getCallerClass() != this.getClass())
- throw new IllegalAccessError("Access denied for caller.");
- try {
- return SecurityActions.getMethodHandle(ClassLoader.class, "defineClass",
- new Class[] {
- String.class, byte[].class, int.class, int.class,
- ProtectionDomain.class
- });
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("cannot initialize", e);
- }
- }
-
- @Override
- Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError
- {
- if (stack.getCallerClass() != DefineClassHelper.class)
- throw new IllegalAccessError("Access denied for caller.");
- try {
- return (Class<?>) defineClass.invokeWithArguments(
- loader, name, b, off, len, protectionDomain);
- } catch (Throwable e) {
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- if (e instanceof ClassFormatError) throw (ClassFormatError) e;
- throw new ClassFormatError(e.getMessage());
- }
- }
- }
-
- private static class JavaOther extends Helper {
- private final Method defineClass = getDefineClassMethod();
- private final SecurityActions stack = SecurityActions.stack;
-
- private final Method getDefineClassMethod() {
- if (privileged != null && stack.getCallerClass() != this.getClass())
- throw new IllegalAccessError("Access denied for caller.");
- try {
- return SecurityActions.getDeclaredMethod(ClassLoader.class, "defineClass",
- new Class[] {
- String.class, byte[].class, int.class, int.class, ProtectionDomain.class
- });
- } catch (NoSuchMethodException e) {
- throw new RuntimeException("cannot initialize", e);
- }
- }
-
- @Override
- Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
- ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError, CannotCompileException
- {
- Class<?> klass = stack.getCallerClass();
- if (klass != DefineClassHelper.class && klass != this.getClass())
- throw new IllegalAccessError("Access denied for caller.");
- try {
- SecurityActions.setAccessible(defineClass, true);
- return (Class<?>) defineClass.invoke(loader, new Object[] {
- name, b, off, len, protectionDomain
- });
- } catch (Throwable e) {
- if (e instanceof ClassFormatError) throw (ClassFormatError) e;
- if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new CannotCompileException(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 = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10
- ? new Java11()
- : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
- ? new Java9()
- : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther();
-
- /**
- * Loads a class file by a given class loader.
- *
- * <p>This first tries to use {@code java.lang.invoke.MethodHandle} to load a class.
- * Otherwise, or if {@code neighbor} is null,
- * this tries to use {@code sun.misc.Unsafe} to load a class.
- * Then it tries to use a {@code protected} method in {@code java.lang.ClassLoader}
- * via {@code PrivilegedAction}. Since the latter approach is not available
- * any longer by default in Java 9 or later, the JVM argument
- * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM.
- * If this JVM argument cannot be given, {@link #toPublicClass(String,byte[])}
- * should be used instead.
- * </p>
- *
- * @param className the name of the loaded class.
- * @param neighbor the class contained in the same package as the loaded class.
- * @param loader the class loader. It can be null if {@code neighbor} is not null
- * and the JVM is Java 11 or later.
- * @param domain if it is null, a default domain is used.
- * @param bcode the bytecode for the loaded class.
- * @since 3.22
- */
- public static Class<?> toClass(String className, Class<?> neighbor, ClassLoader loader,
- ProtectionDomain domain, byte[] bcode)
- throws CannotCompileException
- {
- try {
- return privileged.defineClass(className, bcode, 0, bcode.length,
- neighbor, loader, domain);
- }
- catch (RuntimeException e) {
- throw e;
- }
- catch (CannotCompileException e) {
- throw e;
- }
- catch (ClassFormatError e) {
- Throwable t = e.getCause();
- throw new CannotCompileException(t == null ? e : t);
- }
- catch (Exception e) {
- throw new CannotCompileException(e);
- }
- }
-
-
- /**
- * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
- * It is obtained by using {@code neighbor}.
- *
- * @param neighbor a class belonging to the same package that the loaded
- * class belogns to.
- * @param bcode the bytecode.
- * @since 3.24
- */
- public static Class<?> toClass(Class<?> neighbor, byte[] bcode)
- throws CannotCompileException
- {
- try {
- DefineClassHelper.class.getModule().addReads(neighbor.getModule());
- Lookup lookup = MethodHandles.lookup();
- Lookup prvlookup = MethodHandles.privateLookupIn(neighbor, lookup);
- return prvlookup.defineClass(bcode);
- } catch (IllegalAccessException | IllegalArgumentException e) {
- throw new CannotCompileException(e.getMessage() + ": " + neighbor.getName()
- + " has no permission to define the class");
- }
- }
-
- /**
- * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
- * It can be obtained by {@code MethodHandles.lookup()} called from
- * somewhere in the package that the loaded class belongs to.
- *
- * @param bcode the bytecode.
- * @since 3.24
- */
- public static Class<?> toClass(Lookup lookup, byte[] bcode)
- throws CannotCompileException
- {
- try {
- return lookup.defineClass(bcode);
- } catch (IllegalAccessException | IllegalArgumentException e) {
- throw new CannotCompileException(e.getMessage());
- }
- }
-
- /**
- * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
- *
- * @since 3.22
- */
- static Class<?> toPublicClass(String className, byte[] bcode)
- throws CannotCompileException
- {
- try {
- Lookup lookup = MethodHandles.lookup();
- lookup = lookup.dropLookupMode(java.lang.invoke.MethodHandles.Lookup.PRIVATE);
- return lookup.defineClass(bcode);
- }
- catch (Throwable t) {
- throw new CannotCompileException(t);
- }
- }
-
- private DefineClassHelper() {}
- }
|