1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util.internal;
17
18 import io.netty.util.internal.logging.InternalLogger;
19 import io.netty.util.internal.logging.InternalLoggerFactory;
20
21 import java.lang.invoke.MethodHandle;
22 import java.lang.invoke.MethodHandles;
23 import java.lang.reflect.Array;
24 import java.nio.ByteBuffer;
25
26 import static java.lang.invoke.MethodType.methodType;
27
28 public class CleanerJava24Linker implements Cleaner {
29 private static final InternalLogger logger;
30
31 private static final MethodHandle INVOKE_MALLOC;
32 private static final MethodHandle INVOKE_CREATE_BYTEBUFFER;
33 private static final MethodHandle INVOKE_FREE;
34
35 static {
36 boolean suitableJavaVersion;
37 if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
38
39
40 String v = System.getProperty("java.specification.version");
41 try {
42 suitableJavaVersion = Integer.parseInt(v) >= 25;
43 } catch (NumberFormatException e) {
44 suitableJavaVersion = false;
45 }
46
47 logger = null;
48 } else {
49
50
51
52
53
54
55 suitableJavaVersion = PlatformDependent0.javaVersion() >= 24;
56 logger = InternalLoggerFactory.getInstance(CleanerJava24Linker.class);
57 }
58
59 MethodHandle mallocMethod;
60 MethodHandle wrapMethod;
61 MethodHandle freeMethod;
62 Throwable error;
63
64 if (suitableJavaVersion) {
65 try {
66
67 MethodHandles.Lookup lookup = MethodHandles.lookup();
68 Class<?> moduleCls = Class.forName("java.lang.Module");
69 MethodHandle getModule = lookup.findVirtual(
70 Class.class, "getModule", methodType(moduleCls));
71 MethodHandle isNativeAccessEnabledModule = lookup.findVirtual(
72 moduleCls, "isNativeAccessEnabled", methodType(boolean.class));
73 MethodHandle isNativeAccessEnabledForClass = MethodHandles.filterArguments(
74 isNativeAccessEnabledModule, 0, getModule);
75 boolean isNativeAccessEnabled =
76 (boolean) isNativeAccessEnabledForClass.invokeExact(CleanerJava24Linker.class);
77 if (!isNativeAccessEnabled) {
78 throw new UnsupportedOperationException(
79 "Native access (restricted methods) is not enabled for the io.netty.common module.");
80 }
81
82
83
84 Class<?> memoryLayoutCls = Class.forName("java.lang.foreign.MemoryLayout");
85 Class<?> memoryLayoutArrayCls = Class.forName("[Ljava.lang.foreign.MemoryLayout;");
86 Class<?> valueLayoutCls = Class.forName("java.lang.foreign.ValueLayout");
87 Class<?> valueLayoutAddressCls = Class.forName("java.lang.foreign.AddressLayout");
88 MethodHandle addressLayoutGetter = lookup.findStaticGetter(
89 valueLayoutCls, "ADDRESS", valueLayoutAddressCls);
90 MethodHandle byteSize = lookup.findVirtual(valueLayoutAddressCls, "byteSize", methodType(long.class));
91 MethodHandle byteSizeOfAddress = MethodHandles.foldArguments(byteSize, addressLayoutGetter);
92 long addressSize = (long) byteSizeOfAddress.invokeExact();
93 if (addressSize != Long.BYTES) {
94 throw new UnsupportedOperationException(
95 "Linking to malloc and free is only supported on 64-bit platforms.");
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109
110 Class<?> ofLongValueLayoutCls = Class.forName("java.lang.foreign.ValueLayout$OfLong");
111 Class<?> linkerCls = Class.forName("java.lang.foreign.Linker");
112 Class<?> linkerOptionCls = Class.forName("java.lang.foreign.Linker$Option");
113 Class<?> linkerOptionArrayCls = Class.forName("[Ljava.lang.foreign.Linker$Option;");
114 Class<?> symbolLookupCls = Class.forName("java.lang.foreign.SymbolLookup");
115 Class<?> memSegCls = Class.forName("java.lang.foreign.MemorySegment");
116 Class<?> funcDescCls = Class.forName("java.lang.foreign.FunctionDescriptor");
117
118 MethodHandle nativeLinker = lookup.findStatic(linkerCls, "nativeLinker", methodType(linkerCls));
119 MethodHandle defaultLookupStatic = MethodHandles.foldArguments(
120 lookup.findVirtual(linkerCls, "defaultLookup", methodType(symbolLookupCls)),
121 nativeLinker);
122 MethodHandle downcallHandleStatic = MethodHandles.foldArguments(
123 lookup.findVirtual(linkerCls, "downcallHandle",
124 methodType(MethodHandle.class, memSegCls, funcDescCls, linkerOptionArrayCls)),
125 nativeLinker);
126 MethodHandle findSymbol = MethodHandles.foldArguments(
127 lookup.findVirtual(symbolLookupCls, "findOrThrow", methodType(memSegCls, String.class)),
128 defaultLookupStatic);
129
130
131 Object longLayout = lookup.findStaticGetter(valueLayoutCls, "JAVA_LONG", ofLongValueLayoutCls).invoke();
132 Object layoutArray = Array.newInstance(memoryLayoutCls, 1);
133 Array.set(layoutArray, 0, longLayout);
134 MethodHandle mallocFuncDesc = MethodHandles.insertArguments(
135 lookup.findStatic(funcDescCls, "of",
136 methodType(funcDescCls, memoryLayoutCls, memoryLayoutArrayCls)),
137 0, longLayout, layoutArray);
138 MethodHandle mallocLinker = MethodHandles.foldArguments(
139 MethodHandles.foldArguments(downcallHandleStatic,
140 MethodHandles.foldArguments(findSymbol,
141 MethodHandles.constant(String.class, "malloc"))),
142 mallocFuncDesc);
143 mallocMethod = (MethodHandle) mallocLinker.invoke(Array.newInstance(linkerOptionCls, 0));
144
145
146 MethodHandle freeFuncDesc = MethodHandles.insertArguments(
147 lookup.findStatic(funcDescCls, "ofVoid",
148 methodType(funcDescCls, memoryLayoutArrayCls)),
149 0, layoutArray);
150 MethodHandle freeLinker = MethodHandles.foldArguments(
151 MethodHandles.foldArguments(downcallHandleStatic,
152 MethodHandles.foldArguments(findSymbol,
153 MethodHandles.constant(String.class, "free"))),
154 freeFuncDesc);
155 freeMethod = (MethodHandle) freeLinker.invoke(Array.newInstance(linkerOptionCls, 0));
156
157
158 MethodHandle ofAddress = lookup.findStatic(memSegCls, "ofAddress", methodType(memSegCls, long.class));
159 MethodHandle reinterpret = lookup.findVirtual(memSegCls, "reinterpret",
160 methodType(memSegCls, long.class));
161 MethodHandle asByteBuffer = lookup.findVirtual(memSegCls, "asByteBuffer", methodType(ByteBuffer.class));
162 wrapMethod = MethodHandles.filterReturnValue(
163 MethodHandles.filterArguments(reinterpret, 0, ofAddress),
164 asByteBuffer);
165
166 error = null;
167 } catch (Throwable throwable) {
168 mallocMethod = null;
169 wrapMethod = null;
170 freeMethod = null;
171 error = throwable;
172 }
173 } else {
174 mallocMethod = null;
175 wrapMethod = null;
176 freeMethod = null;
177 error = new UnsupportedOperationException("java.lang.foreign.MemorySegment unavailable");
178 }
179
180 if (logger != null) {
181 if (error == null) {
182 logger.debug("java.nio.ByteBuffer.cleaner(): available");
183 } else {
184 logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
185 }
186 }
187 INVOKE_MALLOC = mallocMethod;
188 INVOKE_CREATE_BYTEBUFFER = wrapMethod;
189 INVOKE_FREE = freeMethod;
190 }
191
192 static boolean isSupported() {
193 return INVOKE_MALLOC != null;
194 }
195
196 @Override
197 public CleanableDirectBuffer allocate(int capacity) {
198 return new CleanableDirectBufferImpl(capacity);
199 }
200
201 @Override
202 public void freeDirectBuffer(ByteBuffer buffer) {
203 throw new UnsupportedOperationException("Cannot clean arbitrary ByteBuffer instances");
204 }
205
206 static long malloc(int capacity) {
207 final long addr;
208 try {
209 addr = (long) INVOKE_MALLOC.invokeExact((long) capacity);
210 } catch (Throwable e) {
211 throw new Error(e);
212 }
213 if (addr == 0) {
214 throw new OutOfMemoryError("malloc(2) failed to allocate " + capacity + " bytes");
215 }
216 return addr;
217 }
218
219 static void free(long memoryAddress) {
220 try {
221 INVOKE_FREE.invokeExact(memoryAddress);
222 } catch (Throwable e) {
223 throw new Error(e);
224 }
225 }
226
227 private static final class CleanableDirectBufferImpl implements CleanableDirectBuffer {
228 private final ByteBuffer buffer;
229 private final long memoryAddress;
230
231 private CleanableDirectBufferImpl(int capacity) {
232 long addr = malloc(capacity);
233 try {
234 memoryAddress = addr;
235 buffer = (ByteBuffer) INVOKE_CREATE_BYTEBUFFER.invokeExact(addr, (long) capacity);
236 } catch (Throwable throwable) {
237 Error error = new Error(throwable);
238 try {
239 free(addr);
240 } catch (Throwable e) {
241 error.addSuppressed(e);
242 }
243 throw error;
244 }
245 }
246
247 @Override
248 public ByteBuffer buffer() {
249 return buffer;
250 }
251
252 @Override
253 public void clean() {
254 free(memoryAddress);
255 }
256
257 @Override
258 public boolean hasMemoryAddress() {
259 return true;
260 }
261
262 @Override
263 public long memoryAddress() {
264 return memoryAddress;
265 }
266 }
267 }