Ok so been toying around with some things. I'm curious if this following makes sense or if I need to think about this stuff a bit more first. Let's say we have component A:
(component
(import (locked "foo:base@1.0.0" integrity "as;ldfkj")(func))
)
And the package foo:base
in the registry has the following definition:
(component
(component
(core module $numbers
(func (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add
)
)
(core instance $firstInstance (instantiate $numbers))
(alias core export 0 "add" (core func))
(type (func (param "left" u32) (param "right" u32) (result u32)))
(func (type 0) (canon lift (core func 0)))
(export "adder" (func 0))
)
)
Is it incredibly naive to just fetch all component defs from the registry and then insert those definitions in the top level of Component A and finally replace import statements with aliases to the top level definitions?
essentially the operation on fetched imports would be more of a "bundling" than a composition or a linking
imports are resolved when the component is instantiated, which would happen in a new top level "linking" component
So say we've got a chain of n components. I guess they'd all be dropped into the top level "linking" component, and then instantiated and fed in from the bottom component nodes in the dep tree up to the component doing the top level imports
Yeah so if you have package deps A->B->C, then the new root component would do something like (pseudo-code):
c = new C()
b = new B(c: c)
a = new A(b: b)
<export some stuff from a, probably>
@Brian has been working on a "linker script" that looks a bit like :point_up:
(or did, the last I saw)
Ah makes sense. Think I see the path forward for now
Alright so i wrote a script that imports wasm-compose
and based on import statements, composes components in the warg cache. The above example generated the following wat file
(component
(component (;0;)
(type (;0;) (func (param "left" u32) (param "right" u32) (result u32)))
(import (unlocked "foo:add/bar" range "^1.0.0") (func (;0;) (type 0)))
(export (;1;) "adder" (func 0))
)
(component (;1;)
(core module $numbers (;0;)
(type (;0;) (func (param i32 i32) (result i32)))
(func (;0;) (type 0) (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add
)
(export "add" (func 0))
)
(core instance $firstInstance (;0;) (instantiate $numbers))
(alias core export $firstInstance "add" (core func (;0;)))
(type (;0;) (func (param "left" u32) (param "right" u32) (result u32)))
(func (;0;) (type 0) (canon lift (core func 0)))
(export (;1;) "numby" (func 0))
)
(instance (;0;) (instantiate 1))
(alias export 0 "numby" (func (;0;)))
(instance (;1;) (instantiate 0
(with "foo:add/bar" (func 0))
)
)
(export (;1;) "numby" (func 0))
)```
hopefully will be easy to get jco to use my branch of wasm-tools to allow the impl import syntax and maybe we can execute the result of doing a registry install
and it wasn't awful to get jco to execute it... just had to link a local version of wasmtime and wit-bindgen each of which used my branch of wasm-tools. so anyways, i think we can make transitive dep trees now and do an install which composes all of the deps into one binary. just need to generalize a few hardcoded things so that it's dynamic and works outside of the example i'm creating right now.
Considering how to further iterate on this... I'm thinking perhaps implementation imports should be added in the component index so that that way they can be instantiated and utilized in the component that is doing the importing.
(component
(import (locked "foo:base@1.0.0" integrity "as;ldfkj")(func))
(instantiate 0)
(do things with the package exports)
)
Is the component index here the registries index of components? or do you mean the index space within components?
The latter
And just to be clear, by implementation imports do you mean component or instance imports?
By implementation import i mean imports using the syntax proposed here https://github.com/WebAssembly/component-model/compare/main...add-impl-imports. In my head I've been thinking of these exclusively as component imports, rather than instance imports
I think most of the times components import each other, they'll want to import some instance and not a specific component that they decide internally how to instantiate. This is what enables us to de-duplicate imports by providing the same instance to instance imports whereas if two components did their own instantiation (which is reasonable sometimes) we can't do that.
Last updated: Dec 23 2024 at 12:05 UTC