0
# Native Image Substitutions
1
2
Build-time class modification system for replacing methods, fields, and types during native image compilation. **Note: This API is provided for compatibility only and is not part of the supported public API.**
3
4
## Capabilities
5
6
### Class Targeting
7
8
Annotations for identifying which classes to modify during native image compilation.
9
10
```java { .api }
11
/**
12
* Identifies the target class for substitutions
13
*/
14
@Target(ElementType.TYPE)
15
@Retention(RetentionPolicy.RUNTIME)
16
public @interface TargetClass {
17
/** Target class to substitute */
18
Class<?> value() default void.class;
19
20
/** Target class name as string (for classes not on classpath) */
21
String className() default "";
22
23
/** Provider for computing target class name dynamically */
24
Class<? extends Function<TargetClass, String>> classNameProvider() default NoClassNameProvider.class;
25
26
/** Target class only on specific platforms */
27
Class<? extends BooleanSupplier>[] onlyWith() default {};
28
29
/** Exclude target class on specific platforms */
30
Class<? extends BooleanSupplier>[] innerClass() default {};
31
}
32
33
/**
34
* Fine-grained targeting of specific class elements
35
*/
36
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
37
@Retention(RetentionPolicy.RUNTIME)
38
public @interface TargetElement {
39
/** Target element name */
40
String name() default "";
41
42
/** Target method parameter types */
43
Class<?>[] parameters() default {};
44
45
/** Target element only on specific platforms */
46
Class<? extends BooleanSupplier>[] onlyWith() default {};
47
}
48
```
49
50
**Usage Examples:**
51
52
```java
53
import com.oracle.svm.core.annotate.*;
54
55
// Substitute methods in java.lang.System
56
@TargetClass(System.class)
57
final class SystemSubstitutions {
58
59
// Target specific method with parameters
60
@TargetElement(name = "getProperty", parameters = {String.class})
61
@Substitute
62
public static String getProperty(String key) {
63
// Custom implementation for native image
64
return NativeImageProperties.getProperty(key);
65
}
66
}
67
68
// Target class by name (when class not available at build time)
69
@TargetClass(className = "com.example.PlatformSpecific")
70
final class PlatformSpecificSubstitutions {
71
72
@Substitute
73
public static void platformMethod() {
74
// Native image compatible implementation
75
throw new UnsupportedOperationException("Not supported in native image");
76
}
77
}
78
79
// Conditional targeting based on platform
80
@TargetClass(value = WindowsSpecificClass.class,
81
onlyWith = WindowsPlatform.class)
82
final class WindowsSubstitutions {
83
84
@Substitute
85
public static void windowsOnlyMethod() {
86
// Windows-specific native implementation
87
}
88
}
89
```
90
91
### Method Substitution
92
93
Replace method implementations with native image compatible versions.
94
95
```java { .api }
96
/**
97
* Marks methods that replace original implementations
98
*/
99
@Target(ElementType.METHOD)
100
@Retention(RetentionPolicy.RUNTIME)
101
public @interface Substitute {
102
/** Handle methods with polymorphic signatures */
103
boolean polymorphicSignature() default false;
104
105
/** Override static modifier detection */
106
boolean isStatic() default false;
107
}
108
109
/**
110
* Keep original method alongside substitution
111
*/
112
@Target(ElementType.METHOD)
113
@Retention(RetentionPolicy.RUNTIME)
114
public @interface KeepOriginal {
115
}
116
117
/**
118
* Add annotations to original method without changing implementation
119
*/
120
@Target(ElementType.METHOD)
121
@Retention(RetentionPolicy.RUNTIME)
122
public @interface AnnotateOriginal {
123
}
124
```
125
126
**Usage Examples:**
127
128
```java
129
@TargetClass(java.util.concurrent.ThreadLocalRandom.class)
130
final class ThreadLocalRandomSubstitutions {
131
132
// Replace problematic method with native image compatible version
133
@Substitute
134
public static ThreadLocalRandom current() {
135
// Use global random instead of thread-local in native image
136
return GlobalRandom.getInstance();
137
}
138
139
// Keep original for specific use cases
140
@KeepOriginal
141
@Substitute
142
public int nextInt() {
143
// This preserves original method as backup
144
return original_nextInt();
145
}
146
147
// Add annotation without changing implementation
148
@AnnotateOriginal
149
@TruffleBoundary
150
public native double nextGaussian();
151
}
152
153
// Handle polymorphic signatures (like MethodHandle.invoke)
154
@TargetClass(java.lang.invoke.MethodHandle.class)
155
final class MethodHandleSubstitutions {
156
157
@Substitute
158
@MethodHandle.PolymorphicSignature
159
public native Object invoke(Object... args) throws Throwable;
160
161
@Substitute
162
@MethodHandle.PolymorphicSignature
163
public native Object invokeExact(Object... args) throws Throwable;
164
}
165
```
166
167
### Field and Element Management
168
169
Control field behavior and manage class elements during compilation.
170
171
```java { .api }
172
/**
173
* Create alias to access original field or method
174
*/
175
@Target({ElementType.FIELD, ElementType.METHOD})
176
@Retention(RetentionPolicy.RUNTIME)
177
public @interface Alias {
178
/** Accessor methods for private fields */
179
boolean accessors() default false;
180
}
181
182
/**
183
* Remove elements from target class
184
*/
185
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
186
@Retention(RetentionPolicy.RUNTIME)
187
public @interface Delete {
188
/** Override static modifier detection */
189
boolean isStatic() default false;
190
}
191
192
/**
193
* Inject new fields or methods into target class
194
*/
195
@Target({ElementType.FIELD, ElementType.METHOD})
196
@Retention(RetentionPolicy.RUNTIME)
197
public @interface Inject {
198
}
199
200
/**
201
* Generate accessor methods for fields
202
*/
203
@Target(ElementType.FIELD)
204
@Retention(RetentionPolicy.RUNTIME)
205
public @interface InjectAccessors {
206
/** Accessor class to generate */
207
Class<?> value();
208
}
209
```
210
211
**Usage Examples:**
212
213
```java
214
@TargetClass(java.lang.Thread.class)
215
final class ThreadSubstitutions {
216
217
// Create alias to access private field
218
@Alias
219
private String name;
220
221
// Create accessor methods for private field
222
@Alias
223
@InjectAccessors(ThreadAccessors.class)
224
private ThreadGroup group;
225
226
// Inject new field into target class
227
@Inject
228
private static volatile int nativeThreadCounter = 0;
229
230
// Remove problematic method
231
@Delete
232
public static native void dumpThreads(Thread[] threads);
233
234
// Inject new utility method
235
@Inject
236
public static int getNativeThreadCount() {
237
return nativeThreadCounter;
238
}
239
240
// Use alias in substitution
241
@Substitute
242
public String getName() {
243
return name != null ? name : "native-thread-" + nativeThreadCounter++;
244
}
245
}
246
247
// Accessor class for generated methods
248
final class ThreadAccessors {
249
public static ThreadGroup getGroup(Thread thread) {
250
// Generated by @InjectAccessors
251
return null; // Implementation provided by annotation processor
252
}
253
254
public static void setGroup(Thread thread, ThreadGroup group) {
255
// Generated by @InjectAccessors
256
}
257
}
258
```
259
260
### Field Value Recomputation
261
262
Transform static field values during image build for optimization and configuration.
263
264
```java { .api }
265
/**
266
* Recompute static field values at build time
267
*/
268
@Target(ElementType.FIELD)
269
@Retention(RetentionPolicy.RUNTIME)
270
public @interface RecomputeFieldValue {
271
/** Recomputation strategy */
272
Kind kind();
273
274
/** Declaring class for field reference */
275
Class<?> declClass() default void.class;
276
277
/** Field name for reference */
278
String name() default "";
279
280
/** Make field final after recomputation */
281
boolean isFinal() default true;
282
283
/** Transformation kinds */
284
enum Kind {
285
/** Reset to default value (0, null, false) */
286
Reset,
287
288
/** Copy value from another field */
289
FromAlias,
290
291
/** Compute value using custom transformer */
292
Custom,
293
294
/** Set to array length */
295
ArrayLength,
296
297
/** Set to current timestamp */
298
AtBuildTime
299
}
300
}
301
```
302
303
**Usage Examples:**
304
305
```java
306
@TargetClass(java.lang.System.class)
307
final class SystemFieldSubstitutions {
308
309
// Reset security manager to null in native image
310
@Alias
311
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)
312
private static SecurityManager security;
313
314
// Copy build-time property value
315
@Alias
316
@RecomputeFieldValue(
317
kind = RecomputeFieldValue.Kind.FromAlias,
318
name = "buildTimeJavaVersion"
319
)
320
private static String javaVersion;
321
322
@Inject
323
private static final String buildTimeJavaVersion = System.getProperty("java.version");
324
325
// Custom field transformation
326
@Alias
327
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom,
328
declClass = BuildTimeComputer.class,
329
name = "computeMemorySize")
330
private static long maxMemory;
331
332
// Set to build timestamp
333
@Inject
334
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.AtBuildTime)
335
private static final long buildTimestamp = System.currentTimeMillis();
336
}
337
338
// Custom field value computer
339
final class BuildTimeComputer {
340
public static long computeMemorySize() {
341
// Compute appropriate memory size for native image
342
return Runtime.getRuntime().maxMemory() / 2;
343
}
344
}
345
```
346
347
### Automatic Feature Registration
348
349
Automatically register feature classes for native image generation.
350
351
```java { .api }
352
/**
353
* Automatically register feature classes during image build
354
*/
355
@Target(ElementType.TYPE)
356
@Retention(RetentionPolicy.RUNTIME)
357
public @interface AutomaticFeature {
358
}
359
```
360
361
**Usage Examples:**
362
363
```java
364
import org.graalvm.nativeimage.hosted.Feature;
365
366
// Automatically registered feature
367
@AutomaticFeature
368
public class DatabaseConnectionFeature implements Feature {
369
370
@Override
371
public void beforeAnalysis(BeforeAnalysisAccess access) {
372
// Register database driver classes for reflection
373
access.registerClassForReflection(
374
com.mysql.cj.jdbc.Driver.class,
375
org.postgresql.Driver.class
376
);
377
}
378
379
@Override
380
public void afterRegistration(AfterRegistrationAccess access) {
381
// Initialize connection pool
382
DatabaseConnectionPool pool = new DatabaseConnectionPool();
383
ImageSingletons.add(DatabaseConnectionPool.class, pool);
384
}
385
}
386
387
// Feature for JSON serialization support
388
@AutomaticFeature
389
public class JsonSerializationFeature implements Feature {
390
391
@Override
392
public void beforeAnalysis(BeforeAnalysisAccess access) {
393
// Find all classes with JSON annotations
394
for (Class<?> clazz : findJsonAnnotatedClasses()) {
395
access.registerClassForReflection(clazz);
396
397
// Register all fields and methods for JSON processing
398
for (Field field : clazz.getDeclaredFields()) {
399
access.registerFieldForReflection(field);
400
}
401
402
for (Method method : clazz.getDeclaredMethods()) {
403
if (isJsonMethod(method)) {
404
access.registerMethodForReflection(method);
405
}
406
}
407
}
408
}
409
410
private List<Class<?>> findJsonAnnotatedClasses() {
411
// Implementation to find JSON-annotated classes
412
return Collections.emptyList();
413
}
414
415
private boolean isJsonMethod(Method method) {
416
// Check if method is relevant for JSON processing
417
return method.getName().startsWith("get") ||
418
method.getName().startsWith("set") ||
419
method.getName().startsWith("is");
420
}
421
}
422
```
423
424
### Platform Conditional Substitutions
425
426
Create platform-specific substitutions that only apply on certain operating systems or architectures.
427
428
```java { .api }
429
// Platform detection interfaces
430
public interface Platform extends BooleanSupplier {
431
interface HOSTED_ONLY extends Platform {}
432
interface LINUX extends Platform {}
433
interface DARWIN extends Platform {}
434
interface WINDOWS extends Platform {}
435
interface AMD64 extends Platform {}
436
interface AARCH64 extends Platform {}
437
}
438
```
439
440
**Usage Examples:**
441
442
```java
443
// Windows-specific substitutions
444
@TargetClass(value = File.class, onlyWith = Platform.WINDOWS.class)
445
final class WindowsFileSubstitutions {
446
447
@Substitute
448
public boolean setExecutable(boolean executable) {
449
// Windows-specific implementation
450
return WindowsFileSystem.setExecutable(this, executable);
451
}
452
}
453
454
// Linux-specific substitutions
455
@TargetClass(value = UnixFileSystem.class, onlyWith = Platform.LINUX.class)
456
final class LinuxFileSystemSubstitutions {
457
458
@Substitute
459
public long getSpace(File f, int t) {
460
// Linux-specific disk space implementation
461
return LinuxNativeFS.getSpace(f.getPath(), t);
462
}
463
}
464
465
// Architecture-specific optimizations
466
@TargetClass(value = CRC32.class, onlyWith = Platform.AMD64.class)
467
final class AMD64CRC32Substitutions {
468
469
@Substitute
470
public void update(byte[] b, int off, int len) {
471
// AMD64-optimized CRC32 implementation
472
updateAMD64(b, off, len);
473
}
474
475
private native void updateAMD64(byte[] b, int off, int len);
476
}
477
478
// Custom platform conditions
479
public final class CustomPlatform implements BooleanSupplier {
480
@Override
481
public boolean getAsBoolean() {
482
return System.getProperty("custom.platform.enabled", "false").equals("true");
483
}
484
}
485
486
@TargetClass(value = CustomClass.class, onlyWith = CustomPlatform.class)
487
final class ConditionalSubstitutions {
488
489
@Substitute
490
public void customMethod() {
491
// Only included when custom.platform.enabled=true
492
}
493
}
494
```
495
496
## Types
497
498
```java { .api }
499
// Exception types
500
public class SubstitutionException extends RuntimeException {
501
public SubstitutionException(String message);
502
public SubstitutionException(String message, Throwable cause);
503
}
504
505
// Utility interfaces
506
public interface BooleanSupplier {
507
boolean getAsBoolean();
508
}
509
510
public interface Function<T, R> {
511
R apply(T t);
512
}
513
514
// Internal marker class
515
final class NoClassNameProvider implements Function<TargetClass, String> {
516
@Override
517
public String apply(TargetClass targetClass) {
518
return "";
519
}
520
}
521
522
// Build-time transformer interface
523
public interface FieldValueTransformer {
524
Object transform(Object receiver, Object originalValue);
525
}
526
```