|
| 1 | +# Universal Dynamic Language Tooling |
| 2 | + |
| 3 | +## Purpose |
| 4 | + |
| 5 | +`extism-js` is a hyper focused and not very flexible tool. |
| 6 | +It's simplicity has served us well, but my hope was that adding a new dynamic language |
| 7 | +would give us an opportunity to build a universal tool for this |
| 8 | +and establish some cross language conventions to make it easy for |
| 9 | +extism users (or us) to add new (or customize extisting) runtimes. |
| 10 | +Now that we're working on a python PDK, now might be a good time to do this. |
| 11 | + |
| 12 | +e.g: Imagine if someone wants to add a ruby pdk. We should be able |
| 13 | +to do that without creating new tooling to create and run these modules. |
| 14 | +Or another example which has come up, imagine if a user wants to customize our js runtime |
| 15 | +with their own rust or c code, they should be able to fork our runtime |
| 16 | +and re-use all our tooling and just publish their own runtime.wasm (essentially). |
| 17 | +This should work with our compiler and with extising SDKs with a simple config change |
| 18 | +(pointing to their custom runtime). |
| 19 | + |
| 20 | +This has a couple of additional benefits: |
| 21 | + |
| 22 | +1. The dynamic plugins will be orders of magnitude smaller as it will only be your code changes. You'll be able to cache the runtime in memory for the host app. |
| 23 | +2. We could make some development mode tooling that will make feedback loops in development much faster and a better experience. |
| 24 | + |
| 25 | +## Solution |
| 26 | + |
| 27 | +There are still some details here I'm unsure of, but how i'd like for it to work is something like this. |
| 28 | +What we're now calling `core.wasm` will effectively be a "runtime" (TODO: name pending as this is an overloaded term). |
| 29 | +We could publish and version official runtimes. e.g. to start-off with we might have `quickjs` and `cpython`. |
| 30 | +This should be publishable as plain wasm modules which both the tool, and the SDKs can fetch via url. |
| 31 | +We might publish variants of these with different packages or purposes. e.g. we might |
| 32 | +have a `runtime.development.wasm` with mode debug or development tools. |
| 33 | +Or we could also publish variants like, a cpython that has numpy built in, etc. |
| 34 | + |
| 35 | + |
| 36 | +The technical challenge mainly lies in defining the different modules we need and |
| 37 | +what their interfaces are and doing that in a language agnostic way. I suspect |
| 38 | +we can borrow from what we're already doing in our compiler toolchain with |
| 39 | +wasm-merge, but saving the binding til runtime. See my [Draft PR](https://github.com/extism/python-pdk/pull/8) |
| 40 | +in the python-pdk for a more concrete example. |
| 41 | + |
| 42 | +### Separated Modules |
| 43 | + |
| 44 | +Let me take a stab at what I think it would look like. First, what we are calling `core.wasm` |
| 45 | +will instead be say `runtime.wasm`. This will contain a couple things: |
| 46 | + |
| 47 | +1. The generic language runtime (e.g. quickjs, no application code) |
| 48 | +2. The extism language bindings (e.g. in quickjs we include the rust-pdk and some binding code) |
| 49 | +3. Common exports to invoke the runtime |
| 50 | + |
| 51 | +> note: we may also have imports here for host functions? or we may need an additional import shim? more investigation needed. |
| 52 | +
|
| 53 | +We'll also have a `main.wasm`. This will be all the application code and will be the |
| 54 | +stuff that actually changes from plugin to plugin. This should be small |
| 55 | + |
| 56 | +### Module Interfaces |
| 57 | + |
| 58 | +These need a consistent interface to be interchangeable and for common tooling |
| 59 | +to be able to work with them (like wizen them, shim them, etc). The interface might look like this: |
| 60 | + |
| 61 | +`runtime.wasm` will have many imports (probably extism, probably wasi, etc), |
| 62 | +but will most importantly export 1 function: `__invoke`. This will do the invoke |
| 63 | +trick established in the [js compiler](https://github.com/extism/proposals/blob/main/EIP-009-js-pdk-interface-definition.md). |
| 64 | + |
| 65 | +I thinkt the above should work, though, we should consider if this is still necessary. What would be ideal is to have two exports like this: |
| 66 | + |
| 67 | +* `__eval` |
| 68 | +* `__evalByteCode` |
| 69 | + |
| 70 | +Each of these could take a pointer to memory where the code is. Many runtimes support |
| 71 | +evaluating both raw source as well as compiled bytecode specific to the vm. |
| 72 | + |
| 73 | +`main.wasm` (final name of the file doesn't matter) would work as expected. It would import the interface from the runtime: |
| 74 | + |
| 75 | +``` |
| 76 | +$ wasm-objdump main.wasm --section=Import -x |
| 77 | +
|
| 78 | +main.wasm: file format wasm 0x1 |
| 79 | +
|
| 80 | +Section Details: |
| 81 | +
|
| 82 | +Import[1]: |
| 83 | + - func[0] sig=0 <__invoke> <- runtime.__invoke |
| 84 | +``` |
| 85 | + |
| 86 | +It would finally export the extism func: |
| 87 | + |
| 88 | +``` |
| 89 | +wasm-objdump main.wasm --section=Export -x |
| 90 | +
|
| 91 | +main.wasm: file format wasm 0x1 |
| 92 | +
|
| 93 | +Section Details: |
| 94 | +
|
| 95 | +Export[1]: |
| 96 | + - func[1] <count_vowels> -> "count_vowels" |
| 97 | +``` |
| 98 | + |
| 99 | +And we should be able to run by linking them up dynamically: |
| 100 | + |
| 101 | +``` |
| 102 | +$ extism call main.wasm count_vowels --input="Hello World" --link core=./core.wasm --wasi |
| 103 | +{"count": 3} |
| 104 | +``` |
| 105 | + |
| 106 | +### Tooling |
| 107 | + |
| 108 | +From here we just need a way to manage all this in a single place. Perhaps |
| 109 | +we can do it in the extism-cli. I don't think this can be written in go, but |
| 110 | +we might be able to manage it as a "tool" to the go program. so it could be |
| 111 | +a separate or linked program that's written in rust. But to the user, it should |
| 112 | +ideally look like the extism cli. |
| 113 | + |
| 114 | +I'll think a little bit more about what the experience should be. Ideally |
| 115 | +a plug-in author should just be able to do `extism compile` |
| 116 | +and not worry much about the underlying details. |
| 117 | + |
| 118 | +## Considerations |
| 119 | + |
| 120 | +### Wizer |
| 121 | + |
| 122 | +I'm a little confused as to how this will work with wizer. I suspect the way my current python experiment is working, |
| 123 | +it's wizening the core which is not what we want. [Javy](https://github.com/bytecodealliance/javy) seems to be able to do this |
| 124 | +so there are probably some answers there. Furthermore, perhaps supporting an `evalByteCode` path might lessen the need for wizening? |
| 125 | + |
| 126 | +### VMWare Workers Server |
| 127 | + |
| 128 | +There is also some prior art to be studied with [vmware workers server](https://github.com/vmware-labs/wasm-workers-server). |
| 129 | +They do something similar with the indepdent publishing of runtimes and dynamic linking. We may |
| 130 | +be able to borrow some tricks or some of the user experience from there. |
| 131 | + |
0 commit comments