or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

c-interop.mdcollections.mdconfig.mdindex.mdjni-utils.mdnative-bridge.mdnative-image.mdpolyglot.mdsubstitutions.mdword.md

substitutions.mddocs/

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

```