|
22 | 22 | */ |
23 | 23 | package com.oracle.truffle.espresso; |
24 | 24 |
|
| 25 | +import java.lang.invoke.MethodHandle; |
| 26 | +import java.lang.invoke.MethodHandles; |
25 | 27 | import java.lang.ref.PublicFinalReference; |
26 | 28 | import java.lang.reflect.AccessibleObject; |
27 | 29 | import java.lang.reflect.Field; |
@@ -93,42 +95,57 @@ public final class FinalizationSupport { |
93 | 95 | Class<?> publicFinalReference = injectClassInBootClassLoader("java/lang/ref/PublicFinalReference", PUBLIC_FINAL_REFERENCE_BYTES); |
94 | 96 | EspressoError.guarantee("java.lang.ref.FinalReference".equals(publicFinalReference.getSuperclass().getName()), |
95 | 97 | "Injected class does not subclass FinalReference"); |
96 | | - } catch (Exception e) { |
97 | | - throw EspressoError.shouldNotReachHere("Error injecting PublicFinalReference in the host (version " + HostJavaVersionUtil.JAVA_SPEC + ")", e); |
| 98 | + } catch (Throwable t) { |
| 99 | + throw EspressoError.shouldNotReachHere("Error injecting PublicFinalReference in the host (version " + HostJavaVersionUtil.JAVA_SPEC + ")", t); |
98 | 100 | } |
99 | 101 | } |
100 | 102 |
|
101 | 103 | public static void ensureInitialized() { |
102 | 104 | /* nop */ |
103 | 105 | } |
104 | 106 |
|
105 | | - private static Class<?> injectClassInBootClassLoader(String className, byte[] classBytes) throws Exception { |
| 107 | + private static Class<?> injectClassInBootClassLoader(String className, byte[] classBytes) throws Throwable { |
106 | 108 | if (HostJavaVersionUtil.JAVA_SPEC == 8) { |
107 | 109 | // Inject class via sun.misc.Unsafe#defineClass. |
108 | 110 | // The use of reflection here is deliberate, so the code compiles with both Java 8/11. |
109 | 111 | Method defineClass = Unsafe.class.getDeclaredMethod("defineClass", |
110 | 112 | String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); |
111 | 113 | defineClass.setAccessible(true); |
112 | 114 | return (Class<?>) defineClass.invoke(UnsafeAccess.get(), className, classBytes, 0, classBytes.length, null, null); |
113 | | - } else if (HostJavaVersionUtil.JAVA_SPEC == 11) { |
114 | | - // Inject class via j.l.ClassLoader#defineClass1. |
| 115 | + } else if (HostJavaVersionUtil.JAVA_SPEC >= 11) { |
| 116 | + // Inject class via j.l.ClassLoader#defineClass1 (private native method). |
115 | 117 | Method defineClass1 = ClassLoader.class.getDeclaredMethod("defineClass1", |
116 | 118 | ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class, String.class); |
117 | 119 |
|
118 | 120 | if (UnsafeOverride) { |
119 | 121 | /* |
120 | | - * Overwrites the AccessibleObject.override field via Unsafe to force-enable |
121 | | - * reflection access and get rid of illegal access warnings. |
| 122 | + * Overwrite the AccessibleObject.override field to enable reflection access and get |
| 123 | + * rid of illegal access warnings. |
122 | 124 | */ |
123 | | - Object unsafeInstance = UnsafeAccess.get(); |
124 | | - Method putBoolean = unsafeInstance.getClass().getMethod("putBoolean", Object.class, long.class, boolean.class); |
125 | | - Method objectFieldOffset = unsafeInstance.getClass().getMethod("objectFieldOffset", Field.class); |
126 | | - |
127 | | - Field overrideField = AccessibleObject.class.getDeclaredField("override"); |
128 | | - long overrideFieldOffset = (long) objectFieldOffset.invoke(unsafeInstance, overrideField); |
129 | | - |
130 | | - // Force-enable access to j.l.ClassLoader#defineClass1. |
131 | | - putBoolean.invoke(unsafeInstance, defineClass1, overrideFieldOffset, true); |
| 125 | + if (HostJavaVersionUtil.JAVA_SPEC == 11) { |
| 126 | + Object unsafeInstance = UnsafeAccess.get(); |
| 127 | + Method putBoolean = unsafeInstance.getClass().getMethod("putBoolean", Object.class, long.class, boolean.class); |
| 128 | + Method objectFieldOffset = unsafeInstance.getClass().getMethod("objectFieldOffset", Field.class); |
| 129 | + Field overrideField = AccessibleObject.class.getDeclaredField("override"); |
| 130 | + long overrideFieldOffset = (long) objectFieldOffset.invoke(unsafeInstance, overrideField); |
| 131 | + |
| 132 | + // Force-enable access to j.l.ClassLoader#defineClass1. |
| 133 | + putBoolean.invoke(unsafeInstance, defineClass1, overrideFieldOffset, true); |
| 134 | + } else if (HostJavaVersionUtil.JAVA_SPEC >= 12) { |
| 135 | + // Tested on Java 16. |
| 136 | + // In Java 12+, AccessibleObject.override was added to the reflection blocklist. |
| 137 | + // Illegal access checks can be circumvented by getting the implementation |
| 138 | + // lookup from MethodHandles with Unsafe. |
| 139 | + Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); |
| 140 | + Unsafe unsafe = UnsafeAccess.get(); |
| 141 | + long implLookupFieldOffset = unsafe.staticFieldOffset(implLookupField); |
| 142 | + Object lookupStaticFieldBase = unsafe.staticFieldBase(implLookupField); |
| 143 | + MethodHandles.Lookup implLookup = (MethodHandles.Lookup) unsafe.getObject(lookupStaticFieldBase, implLookupFieldOffset); |
| 144 | + final MethodHandle overrideSetter = implLookup.findSetter(AccessibleObject.class, "override", boolean.class); |
| 145 | + |
| 146 | + // Force-enable access to j.l.ClassLoader#defineClass1. |
| 147 | + overrideSetter.invokeWithArguments(defineClass1, true); |
| 148 | + } |
132 | 149 | } |
133 | 150 |
|
134 | 151 | return (Class<?>) defineClass1.invoke(null, null, className, classBytes, 0, classBytes.length, null, null); |
|
0 commit comments