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.nio.ByteBuffer;
24
25 import static java.lang.invoke.MethodType.methodType;
26
27
28
29
30
31 final class CleanerJava25 implements Cleaner {
32 private static final InternalLogger logger;
33
34 private static final MethodHandle INVOKE_ALLOCATOR;
35
36 static {
37 boolean suitableJavaVersion;
38 if (System.getProperty("org.graalvm.nativeimage.imagecode") != null) {
39
40
41 String v = System.getProperty("java.specification.version");
42 try {
43 suitableJavaVersion = Integer.parseInt(v) >= 25;
44 } catch (NumberFormatException e) {
45 suitableJavaVersion = false;
46 }
47
48 logger = null;
49 } else {
50
51
52
53 suitableJavaVersion = PlatformDependent0.javaVersion() >= 25;
54 logger = InternalLoggerFactory.getInstance(CleanerJava25.class);
55 }
56
57 MethodHandle method;
58 Throwable error;
59 if (suitableJavaVersion) {
60 try {
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 Class<?> arenaCls = Class.forName("java.lang.foreign.Arena");
79 Class<?> memsegCls = Class.forName("java.lang.foreign.MemorySegment");
80 Class<CleanableDirectBufferImpl> bufCls = CleanableDirectBufferImpl.class;
81
82
83 MethodHandles.Lookup lookup = MethodHandles.lookup();
84
85
86 MethodHandle ofShared = lookup.findStatic(arenaCls, "ofShared", methodType(arenaCls));
87
88 MethodHandle allocate = lookup.findVirtual(arenaCls, "allocate", methodType(memsegCls, long.class));
89
90 MethodHandle asByteBuffer = lookup.findVirtual(memsegCls, "asByteBuffer", methodType(ByteBuffer.class));
91
92 MethodHandle address = lookup.findVirtual(memsegCls, "address", methodType(long.class));
93
94 MethodHandle bufClsCtor = lookup.findConstructor(bufCls,
95 methodType(void.class, AutoCloseable.class, ByteBuffer.class, long.class));
96
97
98
99 MethodHandle allocateInt = MethodHandles.explicitCastArguments(allocate,
100 methodType(memsegCls, arenaCls, int.class));
101
102
103
104 MethodHandle ctorArenaMemsegMemseg = MethodHandles.explicitCastArguments(
105 MethodHandles.filterArguments(bufClsCtor, 1, asByteBuffer, address),
106 methodType(bufCls, arenaCls, memsegCls, memsegCls));
107
108
109
110
111
112
113 MethodHandle ctorArenaMemsegNull = MethodHandles.permuteArguments(ctorArenaMemsegMemseg,
114 methodType(bufCls, arenaCls, memsegCls, memsegCls), 0, 1, 1);
115
116
117 MethodHandle ctorArenaMemseg = MethodHandles.insertArguments(
118 ctorArenaMemsegNull, 2, new Object[]{null});
119
120
121
122 MethodHandle ctorArenaArenaInt = MethodHandles.collectArguments(ctorArenaMemseg, 1, allocateInt);
123
124
125
126
127
128 MethodHandle ctorArenaNullInt = MethodHandles.permuteArguments(ctorArenaArenaInt,
129 methodType(bufCls, arenaCls, arenaCls, int.class), 0, 0, 2);
130
131
132
133 MethodHandle ctorArenaInt = MethodHandles.insertArguments(ctorArenaNullInt, 1, new Object[]{null});
134
135
136
137
138
139 method = MethodHandles.foldArguments(ctorArenaInt, ofShared);
140 error = null;
141 } catch (Throwable throwable) {
142 method = null;
143 error = throwable;
144 }
145 } else {
146 method = null;
147 error = new UnsupportedOperationException("java.lang.foreign.MemorySegment unavailable");
148 }
149 if (logger != null) {
150 if (error == null) {
151 logger.debug("java.nio.ByteBuffer.cleaner(): available");
152 } else {
153 logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
154 }
155 }
156 INVOKE_ALLOCATOR = method;
157 }
158
159 static boolean isSupported() {
160 return INVOKE_ALLOCATOR != null;
161 }
162
163 @SuppressWarnings("OverlyStrongTypeCast")
164 @Override
165 public CleanableDirectBuffer allocate(int capacity) {
166 try {
167 return (CleanableDirectBufferImpl) INVOKE_ALLOCATOR.invokeExact(capacity);
168 } catch (RuntimeException e) {
169 throw e;
170 } catch (Throwable e) {
171 throw new IllegalStateException("Unexpected allocation exception", e);
172 }
173 }
174
175 @Override
176 public void freeDirectBuffer(ByteBuffer buffer) {
177 throw new UnsupportedOperationException("Cannot clean arbitrary ByteBuffer instances");
178 }
179
180 private static final class CleanableDirectBufferImpl implements CleanableDirectBuffer {
181 private final AutoCloseable closeable;
182 private final ByteBuffer buffer;
183 private final long memoryAddress;
184
185
186 CleanableDirectBufferImpl(AutoCloseable closeable, ByteBuffer buffer, long memoryAddress) {
187 this.closeable = closeable;
188 this.buffer = buffer;
189 this.memoryAddress = memoryAddress;
190 }
191
192 @Override
193 public ByteBuffer buffer() {
194 return buffer;
195 }
196
197 @Override
198 public void clean() {
199 try {
200 closeable.close();
201 } catch (RuntimeException e) {
202 throw e;
203 } catch (Exception e) {
204 throw new IllegalStateException("Unexpected close exception", e);
205 }
206 }
207
208 @Override
209 public boolean hasMemoryAddress() {
210 return true;
211 }
212
213 @Override
214 public long memoryAddress() {
215 return memoryAddress;
216 }
217 }
218 }