• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Code Intelligence GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.code_intelligence.jazzer.utils;
18 
19 import java.lang.invoke.MethodHandles;
20 import java.lang.invoke.MethodHandles.Lookup;
21 import java.lang.reflect.Array;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.util.Arrays;
26 import java.util.Optional;
27 import org.objectweb.asm.ClassWriter;
28 import org.objectweb.asm.Opcodes;
29 
30 public final class UnsafeUtils {
31   /**
32    * Dynamically creates a concrete class implementing the given abstract class.
33    *
34    * <p>The returned class will not be functional and should only be used to construct instances
35    * via {@link sun.misc.Unsafe#allocateInstance(Class)}.
36    */
defineAnonymousConcreteSubclass(Class<T> abstractClass)37   public static <T> Class<? extends T> defineAnonymousConcreteSubclass(Class<T> abstractClass) {
38     if (!Modifier.isAbstract(abstractClass.getModifiers())) {
39       throw new IllegalArgumentException(abstractClass + " is not abstract");
40     }
41 
42     ClassWriter cw = new ClassWriter(0);
43     String superClassName = abstractClass.getName().replace('.', '/');
44     // Only the package of the class name matters, the actual name is generated. defineHiddenClass
45     // requires the package of the new class to match the one of the lookup.
46     String className = UnsafeUtils.class.getPackage().getName().replace('.', '/') + "/Anonymous";
47     cw.visit(Opcodes.V1_8, 0, className, null, superClassName, null);
48     cw.visitEnd();
49 
50     try {
51       Optional<Method> defineHiddenClass =
52           Arrays.stream(Lookup.class.getMethods())
53               .filter(method -> method.getName().equals("defineHiddenClass"))
54               .findFirst();
55       Optional<Class<?>> classOption =
56           Arrays.stream(Lookup.class.getClasses())
57               .filter(clazz -> clazz.getSimpleName().equals("ClassOption"))
58               .findFirst();
59       // MethodHandles.Lookup#defineHiddenClass is available as of Java 15.
60       // Unsafe#defineAnonymousClass has been removed in Java 17.
61       if (defineHiddenClass.isPresent() && classOption.isPresent()) {
62         return ((MethodHandles.Lookup) defineHiddenClass.get().invoke(MethodHandles.lookup(),
63                     cw.toByteArray(), true, Array.newInstance(classOption.get(), 0)))
64             .lookupClass()
65             .asSubclass(abstractClass);
66       } else {
67         return (Class<? extends T>) UnsafeProvider.getUnsafe()
68             .getClass()
69             .getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class)
70             .invoke(UnsafeProvider.getUnsafe(), UnsafeUtils.class, cw.toByteArray(), null);
71       }
72     } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
73       throw new IllegalStateException(e);
74     }
75   }
76 
UnsafeUtils()77   private UnsafeUtils() {}
78 }
79