Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasm module annotation (low level/C-like bindings) #690

Merged
merged 19 commits into from
Dec 20, 2024

Conversation

andreaTP
Copy link
Collaborator

@andreaTP andreaTP commented Dec 2, 2024

Opening to hear feedback before I stabilize the implementation with proper tests etc. etc.

Here, as discussed a few times, I'm generating low level(C-like) bindings to give type safe interfaces to the user.

The diff itself is not very interesting, but I encourage to test out the behavior locally, this is what I got running it on OPA (make sure to open the OpaWasm file, and, locally, the files generated by the annotations):
StyraInc/opa-java-wasm@main...andreaTP:try-wasm-module

This implementation is not very opinionated, and there is space for a more seamless integration(leaving to the user less wiring), but is a good "toolbox" approach to the problem imho.

The WasmModuleInterface annotation generates 3 things (examples from OPA):

  • _ModuleExports an interface that holds default implementation of the Wasm module export section, i.e.:
public interface OpaWasm_ModuleExports {

    Instance instance();

    default Memory memory() {
        return instance().memory();
    }

    default int opaEvalCtxNew() {
        long result = instance().export("opa_eval_ctx_new").apply()[0];
        return (int) result;
    }

    default int opaMalloc(int arg0) {
        long result = instance().export("opa_malloc").apply((long) arg0)[0];
        return (int) result;
    }

    default void opaEvalCtxSetInput(int arg0, int arg1) {
        instance().export("opa_eval_ctx_set_input").apply((long) arg0,
                                                          (long) arg1);
        return;
    }
   ...
  • _ModuleImports maps the low level module imports and group them by module name, e.g.:
public interface OpaWasm_ModuleImports {
    OpaWasm_Env env();
}
  • the actual ModuleImports interfaces, i.e.:
public interface OpaWasm_Env {
    int opaBuiltin0(int arg0, int arg1);
    int opaBuiltin1(int arg0, int arg1, int arg2);
    ...

I'm not sure the interaction with HostModule is ideal (but I might be overlooking it).
I'm pretty happy with the ModuleExports encoding and it saves a lot of upfront work when dealing with a new wasm file.
The ModuleImports section acts more as an implementation guideline and might benefit from being more opinionated.
Happy to hear feedback to finalize the PR!

@electrum
Copy link
Collaborator

electrum commented Dec 7, 2024

I like the overall direction here. I have some hesitation about the always-generated _ModuleExports, but I can that workflow being useful for some use cases.

This will be the public API for a WASM module, so it seems better if this could be hand-written, or maybe generated once and hand-maintained. This would allow better method names (if needed), parameter names, Javadoc, etc. The generator can then create the implementation of the interface.

The default methods that call export() have the overhead of doing a map string lookup for every invocation. With a generated class, the exports could be looked up once. For AOT, we'd like a more efficient implementation that directly calls the method without any boxing.

@andreaTP
Copy link
Collaborator Author

andreaTP commented Dec 9, 2024

Thanks @electrum for the feedback!

so it seems better if this could be hand-written

I thought about this, in my experience, the user would simply provide more convenient overload over the generated methods, or, alternatively, wrap the lower level API with an additional class.
If you feel strongly about it, I can add a boolean flag to the annotation to disable the generation, though, it's just an interface and the user can just avoid using it.

have the overhead of doing a map string lookup for every invocation

Agree, I just went for the most straightforward solution at first, I think that this can be a fix in a follow up PR, wdyt?

@andreaTP
Copy link
Collaborator Author

Finally found the time to finalize this effort.
Ready for review!

The updated diff of opa is available at the link: StyraInc/opa-java-wasm@main...andreaTP:try-wasm-module

and there are tests for most code paths.

@electrum to answer this:

it seems better if this could be hand-written

Here we are only generating interfaces, the user can use them or not, start from the generated code to roll his own implementation etc.
The advantage of the current approach is that everything is optional, so the risk is really low.

@andreaTP andreaTP marked this pull request as ready for review December 19, 2024 12:29
@andreaTP
Copy link
Collaborator Author

I'd say that this fixes #482

Copy link
Collaborator

@evacchi evacchi left a comment

Choose a reason for hiding this comment

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

considering this is still experimental, I'd say it's good to go, we can iterate further as we go :)

@andreaTP andreaTP merged commit 9a53caa into dylibso:main Dec 20, 2024
16 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.

3 participants