Skip to content

[Web Image] Fix unhandled UNSTRUCTURED_LOCKING and an order-dependent single-abstract-method failure - #14195

Open
amritk wants to merge 1 commit into
oracle:masterfrom
amritk:webimage-unstructured-locking-and-sam-fixes
Open

[Web Image] Fix unhandled UNSTRUCTURED_LOCKING and an order-dependent single-abstract-method failure#14195
amritk wants to merge 1 commit into
oracle:masterfrom
amritk:webimage-unstructured-locking-and-sam-fixes

Conversation

@amritk

@amritk amritk commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Two independent Web Image build failures, both found compiling ktfmt (the Kotlin
formatter, which embeds the Kotlin compiler frontend and IntelliJ's
intellij-core) with --tool:svm-wasm.

Unhandled UNSTRUCTURED_LOCKING.
WebImageImplicitExceptionsFeature.getSupportMethodName maps thirteen
BytecodeExceptionKinds to support methods in Web Image's ImplicitExceptions
and falls through to shouldNotReachHere for UNSTRUCTURED_LOCKING, so an image
whose reachable graph produces such a node cannot be built at all. This adds the
missing nullary creator, following the shape of the other kinds that carry a
fixed message (createNewNegativeLengthException,
createNewArgumentIsNotArrayException), and maps the kind to it.

A nullary creator rather than one taking the message: the regular SVM backend
maps this kind to CREATE_ILLEGAL_MONITOR_STATE_EXCEPTION, which takes a
String, and NonSnippetLowerings appends getExceptionMessage() as a constant
argument during lowering. Web Image's feature compares node.getArguments()
against the support method's parameter count directly with no such append, so a
one-parameter creator would fail the arity check.

java.lang.Object filter silently disabled by initialization order.
ReflectUtil.singleAbstractMethodForInterface skips abstract methods matching a
public java.lang.Object method, per JLS 9.8, so a @FunctionalInterface may
legally redeclare hashCode(). It built that filter set from
ResolvedJavaType.getDeclaredMethods(false), which returns an empty array for a
type that is not linked yet and is never allowed to force linking
(AnalysisType.getDeclaredMethods guarantees !forceLink). The result is cached
in a static field for the rest of the build, so an empty result silently disables
the filter for every later query.

The symptom is a failed guarantee ("Incorrect single abstract method logic")
naming an interface that is a legitimate functional interface — IntelliJ's
RefHashMap.Key declares get() and hashCode(). It is order dependent: two
identical builds of the same application named two different interfaces,
RefHashMap$Key and ConcurrentRefHashMap$KeyReference, because which query
happens to populate the cache first depends on the parallel analysis.

Building the set by reflecting over the host java.lang.Object removes the
linkage dependency entirely, and a guarantee makes an empty set loud rather than
silently wrong.

Related Issues

Testing

No automated test is included, and I would appreciate direction on where one
belongs.

I did write a unit test against singleAbstractMethodForInterface and then
removed it: called with host JVMCI types it returns empty for every interface,
because GuestAnnotationAccess cannot read @FunctionalInterface off them, so
it asserted nothing. It also would not have caught the original bug, which
depends on analysis ordering rather than on the shape of the interface. A
meaningful regression test looks like a real image build, and I would rather
follow your lead than guess at the harness.

Verified manually:

Scope note: the module builds and boots but cannot yet do useful work, because
AtomicReferenceFieldUpdater.newUpdater throws at run time under Web Image.
That is unrelated to either fix here, reproduces on a released 25.2.4 with a
15-line example, and is filed separately — nothing in this PR is intended to
address it.

Documentation

No documentation updates. Both changes are internal to the Web Image builder and
alter no documented behaviour, option, or API.

Contributor Checklist

  • I have read the contribution guide.
  • I have the right to contribute the submitted material under the project terms.
  • I have updated tests and documentation where appropriate.
  • If I used a coding assistant, I remain responsible for the entire contribution and have reviewed it accordingly.

Both were found compiling ktfmt (the Kotlin formatter, which embeds the
Kotlin compiler frontend and IntelliJ's intellij-core) with --tool:svm-wasm.

Unhandled UNSTRUCTURED_LOCKING (GR issue: unhandled BytecodeExceptionKind)

WebImageImplicitExceptionsFeature.getSupportMethodName maps thirteen
BytecodeExceptionKinds to support methods in Web Image's ImplicitExceptions and
falls through to shouldNotReachHere for UNSTRUCTURED_LOCKING, so an image whose
reachable graph produces such a node cannot be built at all. This adds the
missing nullary creator, following the shape of the other kinds that carry a
fixed message (createNewNegativeLengthException and
createNewArgumentIsNotArrayException), and maps the kind to it.

java.lang.Object filter silently disabled by initialization order

ReflectUtil.singleAbstractMethodForInterface skips abstract methods that match a
public java.lang.Object method, per JLS 9.8, so a @FunctionalInterface may
legally redeclare hashCode(). It built that filter set from
ResolvedJavaType.getDeclaredMethods(false), which returns an empty array for a
type that is not linked yet and is never allowed to force linking. The result is
cached in a static field for the rest of the build, so an empty result silently
disables the filter for every later query.

The symptom is a failed guarantee ("Incorrect single abstract method logic")
naming an interface that is a legitimate functional interface - IntelliJ's
RefHashMap.Key declares get() and hashCode(). It is order dependent: two
identical builds of the same application named two different interfaces,
RefHashMap$Key and ConcurrentRefHashMap$KeyReference, because which query
happens to populate the cache first depends on the parallel analysis.

Building the set by reflecting over the host java.lang.Object removes the
linkage dependency entirely, and a guarantee makes an empty set loud rather than
silently wrong.

Verified by compiling ktfmt 0.64 with --tool:svm-wasm: the build now completes
and produces a 33MB WasmGC module, where before it failed at analysis on the
second issue and at code generation on the first.
@oracle-contributor-agreement

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
The following contributors of this PR have not signed the OCA:

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application.

When signing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated.

If you are an Oracle employee, please make sure that you are a member of the main Oracle GitHub organization, and your membership in this organization is public.

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. label Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Required At least one contributor does not have an approved Oracle Contributor Agreement.

Projects

None yet

1 participant