Skip to content

Bindgen Annotation Processor - #41

Merged
jeremyg484 merged 2 commits into
roastedroot:mainfrom
jeremyg484:bindgen-pr
Sep 3, 2026
Merged

Bindgen Annotation Processor#41
jeremyg484 merged 2 commits into
roastedroot:mainfrom
jeremyg484:bindgen-pr

Conversation

@jeremyg484

@jeremyg484 jeremyg484 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

An initial implementation of a @Bindgen annotation and corresponding
annotation processor is added. This closely mimics the functionality of
the Wasmtime bindgen! macro, looking for .wit files in a /wit directory,
allowing a typical Java project to keep them in src/main/resources. It
also allows specifying WIT definitions direction inline in the
annotation.

The annotation processor uses wasm-tools to convert the textual WIT
format into the corresponding binary .wasm format. The binary
representation conforms to the binary format of the Component Model,
thus we are then able to use ComponentParser to read the output into a
WasmComponent AST and traverse the structure to generate corresponding
Java bindings.

The annotation processor'a unit tests use the Google compile-testing
library to test the processor's output, comparing the generated bindings
for a given WIT definition to a set of golden files.

A separate bindgen-processor-tests module contains integration tests for
the processor. This module configures the annotation processor so that
the @Bindgen annotation may be used directly on test classes to fully
generate the bindings for the targeted WIT. Core wasm modules written in
WAT are used for testing the interaction between the host and guest and
validate that the bindings work as intended at runtime. These tests
replicate all of the non-async examples from Wasmtime's bindgen! macro.

Further work remains to be done to fully generate code for all of the
Component Model's types that may cross the host-guest boundary. The full
details of what has been implemented so far and what is still planned to
be implemented is captured in bindgen-processor/design.md

An initial implementation of a @Bindgen annotation and corresponding
annotation processor is added. This closely mimics the functionality of
the Wasmtime bindgen! macro, looking for .wit files in a /wit directory,
allowing a typical Java project to keep them in src/main/resources. It
also allows specifying WIT definitions direction inline in the
annotation.

The annotation processor uses wasm-tools to convert the textual WIT
format into the corresponding binary .wasm format. The binary
representation conforms to the binary format of the Component Model,
thus we are then able to use ComponentParser to read the output into a
WasmComponent AST and traverse the structure to generate corresponding
Java bindings.

The annotation processor'a unit tests use the Google compile-testing
library to test the processor's output, comparing the generated bindings
for a given WIT definition to a set of golden files.

A separate bindgen-processor-tests module contains integration tests for
the processor. This module configures the annotation processor so that
the @Bindgen annotation may be used directly on test classes to fully
generate the bindings for the targeted WIT. Core wasm modules written in
WAT are used for testing the interaction between the host and guest and
validate that the bindings work as intended at runtime. These tests
replicate all of the non-async examples from Wasmtime's bindgen! macro.

Further work remains to be done to fully generate code for all of the
Component Model's types that may cross the host-guest boundary. The full
details of what has been implemented so far and what is still planned to
be implemented is captured in bindgen-processor/design.md
@jeremyg484
jeremyg484 requested a review from andreaTP September 2, 2026 23:34
@jeremyg484

jeremyg484 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

@andreaTP A decent amount of work still remains on the processor, but I thought I should go ahead and clean up what I have so far and get it up here as a starting point. No worries if you don't have time to review this before your vacation, I'm feeling pretty good about the shape of this and am happy to continue work on it in further branches.

@andreaTP andreaTP left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too long to be reviewed properly in a short amount of time.

I agree with the final goal and this is surely as great starting point.
Do you have rights to merge the PR @jeremyg484 ? (please 🙏 do it so that I know you can work autonomously while I'm away 🙂 )

Comment thread pom.xml
</dependency>
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

for (WitInterface exported : world.exportedInterfaces()) {
String name = fieldName(exported.simpleName());
type.addMember(
StaticJavaParser.parseBodyDeclaration(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind using JavaParser is to avoid dumping code in plain strings that is ugly and harder to maintain in the longer run.
In the longer run we should avoid StaticJavaParser.parse

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I admittedly let the AI get a bit carried away with this class in particular. I'll make a point to clean it up in the next revision.

// A bare import carries its function type as a constant, whether or not any interface does.
Stream<WitFunction> everything = Stream.concat(world.imports().stream(), allImports(world));
List<WitFunction> all = everything.collect(Collectors.toList());
if (all.stream().anyMatch(WorldGenerator::hasParams)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be strict about having "just the right imports" in generated code, it's not a big concern for humans(especially in Java) and they complicate the code risking bugs without much in return.

private String importsInterface(WitTypes types) {
StringBuilder body = new StringBuilder();
body.append("/** The world's imports, which the embedder implements. */\n");
body.append("public interface Imports {\n");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use JavaParser AST for those things, it's really hard to read through.


private String constructor(WitTypes types, String className) {
StringBuilder body = new StringBuilder();
body.append("private ").append(className).append("(ComponentInstance instance) {\n");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my eyes are bleeding at this point 😅

Comment thread CLAUDE.md
Code should always use idiomatic Java style and follow the patterns that have already been established throughout
the codebase. Code should adhere to the style enforced by the project's CheckStyle and Spotless build plugins.

When writing comments, always prefer simple, clear, and concise sentences. Omit unnecessary words and avoid overly

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just switched to a different model and did the same 👍

@jeremyg484

Copy link
Copy Markdown
Collaborator Author

Too long to be reviewed properly in a short amount of time.

I agree with the final goal and this is surely as great starting point. Do you have rights to merge the PR @jeremyg484 ? (please 🙏 do it so that I know you can work autonomously while I'm away 🙂 )

Yes, it looks like I've got permissions, I'll do it now. Enjoy your time off!

@jeremyg484
jeremyg484 merged commit 31f8abc into roastedroot:main Sep 3, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants