bytecodealliance / wasm-tools

CLI and Rust libraries for low-level manipulation of WebAssembly modules
Apache License 2.0
1.36k stars 246 forks source link

conflicting import when composing components #1254

Open Falumpaset opened 1 year ago

Falumpaset commented 1 year ago

Hello!

I'd be thrilled if you could help me resolve my issue with wasm compose.

I want to build an application that is composed of two components. Both components are made with cargo component, and I use the wit deps out of the wasmtime 14 branch.

The application has one application component, the root component. This component imports the backend components interface.

When composing with

wasm-tools compose   -o composed.wasm  -c config.yml sample-application.wasm

I encountered this error:

cannot import instance with name `wasi:io/streams` for an instantiation argument of component `backend` because it conflicts with an imported instantiation argument of component `$input

My config:

instantiations:
  $input:
    arguments:
      falumpaset:backend/connector@0.1.0:
        instance: backend
  backend:
    dependency: backend

dependencies:
  backend: backend.wasm

I understand that both components import the wasi:io/streams interface. However, they seem to be conflicting.

I´ve looked into the composing logic, and the error seems to originate from the process of merging the two wasi:io interfaces. Hence, I´ve looked into the translated components and compared the two wasi:io/stream imports. It occurs to me that one instance is entirely different from the other.

Within the backend component, I notice that the wasi:io interface also exports elements from the wasi:http world. Is this intended?

If not, how can I resolve this issue?

The backend component

Its world after building:

package root:component

world root {
  import wasi:io/poll
  import wasi:io/streams
  import wasi:http/types
  import wasi:http/outgoing-handler
  import wasi:filesystem/types
  import wasi:filesystem/preopens
  import wasi:sockets/tcp
  import wasi:cli/environment
  import wasi:cli/exit
  import wasi:cli/stdin
  import wasi:cli/stdout
  import wasi:cli/stderr
  import wasi:cli/terminal-input
  import wasi:cli/terminal-output
  import wasi:cli/terminal-stdin
  import wasi:cli/terminal-stdout
  import wasi:cli/terminal-stderr

  export falumpaset:backend/connector@0.1.0
}
(import (interface "wasi:io/streams") (instance (;1;) (type 2)))
  (alias export 1 "input-stream" (type (;3;)))
  (alias export 0 "pollable" (type (;4;)))
  (type (;5;)
    (instance
      (export (;0;) "fields" (type (sub resource)))
      (type (;1;) (variant (case "get") (case "head") (case "post") (case "put") (case "delete") (case "connect") (case "options") (case "trace") (case "patch") (case "other" string)))
      (export (;2;) "method" (type (eq 1)))
      (type (;3;) (variant (case "HTTP") (case "HTTPS") (case "other" string)))
      (export (;4;) "scheme" (type (eq 3)))
      (export (;5;) "headers" (type (eq 0)))
      (export (;6;) "outgoing-request" (type (sub resource)))
      (export (;7;) "outgoing-body" (type (sub resource)))
      (export (;8;) "incoming-response" (type (sub resource)))
      (type (;9;) u16)
      (export (;10;) "status-code" (type (eq 9)))
      (export (;11;) "incoming-body" (type (sub resource)))
      (alias outer 1 3 (type (;12;)))
      (export (;13;) "input-stream" (type (eq 12)))
      (export (;14;) "trailers" (type (eq 0)))
      (export (;15;) "future-incoming-response" (type (sub resource)))
      (type (;16;) (variant (case "invalid-url" string) (case "timeout-error" string) (case "protocol-error" string) (case "unexpected-error" string)))
      (export (;17;) "error" (type (eq 16)))
      (alias outer 1 4 (type (;18;)))
      (export (;19;) "pollable" (type (eq 18)))
      (type (;20;) (option u32))
      (type (;21;) (record (field "connect-timeout-ms" 20) (field "first-byte-timeout-ms" 20) (field "between-bytes-timeout-ms" 20)))
      (export (;22;) "request-options" (type (eq 21)))
      (type (;23;) (list u8))
      (type (;24;) (tuple string 23))
      (type (;25;) (list 24))
      (type (;26;) (own 0))
      (type (;27;) (func (param "entries" 25) (result 26)))
      (export (;0;) "[constructor]fields" (func (type 27)))
      (type (;28;) (borrow 0))
      (type (;29;) (func (param "self" 28) (result 25)))
      (export (;1;) "[method]fields.entries" (func (type 29)))
      (type (;30;) (option string))
      (type (;31;) (option 4))
      (type (;32;) (borrow 5))
      (type (;33;) (own 6))
      (type (;34;) (func (param "method" 2) (param "path-with-query" 30) (param "scheme" 31) (param "authority" 30) (param "headers" 32) (result 33)))
      (export (;2;) "[constructor]outgoing-request" (func (type 34)))
      (type (;35;) (borrow 6))
      (type (;36;) (own 7))
      (type (;37;) (result 36))
      (type (;38;) (func (param "self" 35) (result 37)))
      (export (;3;) "[method]outgoing-request.write" (func (type 38)))
      (type (;39;) (borrow 8))
      (type (;40;) (func (param "self" 39) (result 10)))
      (export (;4;) "[method]incoming-response.status" (func (type 40)))
      (type (;41;) (own 5))
      (type (;42;) (func (param "self" 39) (result 41)))
      (export (;5;) "[method]incoming-response.headers" (func (type 42)))
      (type (;43;) (own 11))
      (type (;44;) (result 43))
      (type (;45;) (func (param "self" 39) (result 44)))
      (export (;6;) "[method]incoming-response.consume" (func (type 45)))
      (type (;46;) (borrow 11))
      (type (;47;) (own 13))
      (type (;48;) (result 47))
      (type (;49;) (func (param "self" 46) (result 48)))
      (export (;7;) "[method]incoming-body.stream" (func (type 49)))
      (type (;50;) (own 14))
      (type (;51;) (option 50))
      (type (;52;) (func (param "this" 36) (param "trailers" 51)))
      (export (;8;) "[static]outgoing-body.finish" (func (type 52)))
      (type (;53;) (borrow 15))
      (type (;54;) (own 8))
      (type (;55;) (result 54 (error 17)))
      (type (;56;) (result 55))
      (type (;57;) (option 56))
      (type (;58;) (func (param "self" 53) (result 57)))
      (export (;9;) "[method]future-incoming-response.get" (func (type 58)))
      (type (;59;) (own 19))
      (type (;60;) (func (param "self" 53) (result 59)))
      (export (;10;) "[method]future-incoming-response.subscribe" (func (type 60)))
    )

The application component

The world:

package root:component

world root {
  import falumpaset:backend/connector@0.1.0
  import wasi:io/streams
  import wasi:filesystem/types
  import wasi:filesystem/preopens
  import wasi:sockets/tcp
  import wasi:cli/environment
  import wasi:cli/exit
  import wasi:cli/stdin
  import wasi:cli/stdout
  import wasi:cli/stderr
  import wasi:cli/terminal-input
  import wasi:cli/terminal-output
  import wasi:cli/terminal-stdin
  import wasi:cli/terminal-stdout
  import wasi:cli/terminal-stderr

  export wasi:cli/run
}

The WAT:

(import (interface "wasi:io/streams") (instance (;1;) (type 1)))
  (alias export 1 "output-stream" (type (;2;)))
  (alias export 1 "error" (type (;3;)))
  (type (;4;)
    (instance
      (export (;0;) "descriptor" (type (sub resource)))
      (type (;1;) u64)
      (export (;2;) "filesize" (type (eq 1)))
      (alias outer 1 2 (type (;3;)))
      (export (;4;) "output-stream" (type (eq 3)))
      (type (;5;) (enum "access" "would-block" "already" "bad-descriptor" "busy" "deadlock" "quota" "exist" "file-too-large" "illegal-byte-sequence" "in-progress" "interrupted" "invalid" "io" "is-directory" "loop" "too-many-links" "message-size" "name-too-long" "no-device" "no-entry" "no-lock" "insufficient-memory" "insufficient-space" "not-directory" "not-empty" "not-recoverable" "unsupported" "no-tty" "no-such-device" "overflow" "not-permitted" "pipe" "read-only" "invalid-seek" "text-file-busy" "cross-device"))
      (export (;6;) "error-code" (type (eq 5)))
      (type (;7;) (enum "unknown" "block-device" "character-device" "directory" "fifo" "symbolic-link" "regular-file" "socket"))
      (export (;8;) "descriptor-type" (type (eq 7)))
      (type (;9;) (flags "symlink-follow"))
      (export (;10;) "path-flags" (type (eq 9)))
      (type (;11;) (flags "create" "directory" "exclusive" "truncate"))
      (export (;12;) "open-flags" (type (eq 11)))
      (type (;13;) (flags "read" "write" "file-integrity-sync" "data-integrity-sync" "requested-write-sync" "mutate-directory"))
      (export (;14;) "descriptor-flags" (type (eq 13)))
      (type (;15;) (flags "readable" "writable" "executable"))
      (export (;16;) "modes" (type (eq 15)))
      (alias outer 1 3 (type (;17;)))
      (export (;18;) "error" (type (eq 17)))
      (export (;19;) "directory-entry-stream" (type (sub resource)))
      (type (;20;) (borrow 0))
      (type (;21;) (own 4))
      (type (;22;) (result 21 (error 6)))
      (type (;23;) (func (param "self" 20) (param "offset" 2) (result 22)))
      (export (;0;) "[method]descriptor.write-via-stream" (func (type 23)))
      (type (;24;) (func (param "self" 20) (result 22)))
      (export (;1;) "[method]descriptor.append-via-stream" (func (type 24)))
      (type (;25;) (result 8 (error 6)))
      (type (;26;) (func (param "self" 20) (result 25)))
      (export (;2;) "[method]descriptor.get-type" (func (type 26)))
      (type (;27;) (own 0))
      (type (;28;) (result 27 (error 6)))
      (type (;29;) (func (param "self" 20) (param "path-flags" 10) (param "path" string) (param "open-flags" 12) (param "flags" 14) (param "modes" 16) (result 28)))
      (export (;3;) "[method]descriptor.open-at" (func (type 29)))
      (type (;30;) (borrow 18))
      (type (;31;) (option 6))
      (type (;32;) (func (param "err" 30) (result 31)))
      (export (;4;) "filesystem-error-code" (func (type 32)))
    )
  )

I hope this finds you well, and thanks in advance!

jeffparsons commented 1 year ago

This sounds like the same issue I ran into here: https://bytecodealliance.zulipchat.com/#narrow/stream/407292-cargo-component/topic/Component.20composition.20confusion/near/396244085

Falumpaset commented 1 year ago

Thanks for sharing that! I will try and go back to pre resource WITs then.

jeffparsons commented 1 year ago

If I understand correctly (big if 😛) then this may be a manifestation of https://github.com/bytecodealliance/wasm-tools/issues/1253, which would be resolved once https://github.com/bytecodealliance/wasm-tools/pull/1252 (impl of https://github.com/WebAssembly/component-model/pull/248) lands.

Someone with a better understanding and more sleep please feel free to correct me!