Skip to content

[Web Image] --tool:svm-wasm: AtomicReferenceFieldUpdater.newUpdater throws NullPointerException at run time #14194

Description

@amritk

Describe the Issue

An AtomicReferenceFieldUpdater created with newUpdater throws a NullPointerException when the image runs. Because the updater is almost always a static final field, this surfaces as an ExceptionInInitializerError from the declaring class, and the class is unusable. The image builds without a warning, so there is nothing at build time to act on.

This is not a reachability-metadata gap. Registering the target field for reflection makes no difference, with or without allowUnsafeAccess, and the minimal reproducer below has no metadata at all and no third-party code.

Web Image appears to have no substitution for the field updaters: grep -r AtomicReferenceFieldUpdater web-image/src/ finds nothing, and there is no objectFieldOffset support in the suite. SubstrateVM handles these through recomputed field offsets (RecomputedFields.java), which does not carry over to WasmGC, where objects are structs with typed fields rather than bytes at an offset. So this looks less like a bug in existing code than a piece that is not there yet. Please read the title as descriptive rather than a claim about the cause.

Impact is broad, because field updaters are common in concurrency-aware libraries. I hit it via IntelliJ's ExtensionPointImpl through kotlin-compiler-embeddable; kotlinx.coroutines, Netty and Reactor all use them too. Any of those makes a Web Image build unusable at run time even when it compiles cleanly.

AtomicIntegerFieldUpdater and AtomicLongFieldUpdater are presumably in the same position, though I only tested the reference one.

Using the latest version of GraalVM can resolve many issues.

GraalVM Version

java version "25.0.4" 2026-07-21 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 25.2.4+7.1 (build 25.0.4+7-LTS-jvmci-25.2-b20)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 25.2.4+7.1 (build 25.0.4+7-LTS-jvmci-25.2-b20, mixed mode, sharing)

Operating System and Version

Linux 6.18.5 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux

Troubleshooting Confirmation

Run Command

node --experimental-wasm-exnref afu.js

Expected Behavior

The updater resolves the field and the program prints:

get=initial
after set=updated
cas=true
final=cased

which is what the same class files print on a JVM.

Actual Behavior

The class initializer fails and nothing else runs:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
Caused by: java.lang.NullPointerException

Frames are wasm indices only. Resolved against the .wat name section, the failure is in AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.(Class, Class, String, Class), called from the declaring class's initializer.

Steps to Reproduce

  1. Save this as Afu.java. No dependencies, no reflection metadata, no @js.

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class Afu {
static class Holder {
volatile String value = "initial";
}

private static final AtomicReferenceFieldUpdater<Holder, String> UPDATER =
AtomicReferenceFieldUpdater.newUpdater(Holder.class, String.class, "value");

public static void main(String[] args) {
Holder h = new Holder();
System.out.println("get=" + UPDATER.get(h));
UPDATER.set(h, "updated");
System.out.println("after set=" + UPDATER.get(h));
System.out.println("cas=" + UPDATER.compareAndSet(h, "updated", "cased"));
System.out.println("final=" + h.value);
}
}

  1. Build it. The build succeeds in about 20 seconds with no diagnostics.

javac -d ac Afu.java
native-image --tool:svm

Additional Context

No response

Run-Time Log Output and Error Messages

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions