diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a9bf7199a..e711f73a1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -621,3 +621,67 @@ jobs: - name: Run all script for (${{ matrix.project.name }}) run: | pnpm --filter '${{ matrix.project.workspace }}' run all + + test-extended: + runs-on: ${{ matrix.os }} + needs: + - build + permissions: + contents: read + strategy: + matrix: + os: + - ubuntu-latest + node: + - latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: ${{ matrix.node }} + + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + with: + version: 11 + + - uses: taiki-e/cache-cargo-install-action@417450f3c33ee20393705369577571770643d4c7 # v3.0.7 + with: + tool: wasm-tools + - uses: taiki-e/cache-cargo-install-action@417450f3c33ee20393705369577571770643d4c7 # v3.0.7 + with: + tool: wit-bindgen-cli + - uses: taiki-e/cache-cargo-install-action@417450f3c33ee20393705369577571770643d4c7 # v3.0.7 + with: + tool: wac-cli + - uses: taiki-e/cache-cargo-install-action@417450f3c33ee20393705369577571770643d4c7 # v3.0.7 + with: + tool: just + - uses: taiki-e/cache-cargo-install-action@417450f3c33ee20393705369577571770643d4c7 # v3.0.7 + with: + tool: wkg + + - id: wasi-sdk + uses: bytecodealliance/setup-wasi-sdk-action@b2de090b44eb70013ee96b393727d473b35e1728 + + - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + - run: pnpm install + + - name: Restore jco build output + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: build + path: packages/jco/obj + + - name: Build extended component tree + working-directory: test/components + env: + WASI_SDK: ${{ steps.wasi-sdk.outputs.wasi-sdk-path }} + run: just build + + - name: Run extended component tests + run: | + pnpm --filter '@bytecodealliance/jco' run test:extended diff --git a/packages/jco/package.json b/packages/jco/package.json index c42002a09..dfa2472dc 100644 --- a/packages/jco/package.json +++ b/packages/jco/package.json @@ -71,6 +71,7 @@ "lint:fix": "oxlint --fix", "test": "vitest run -c test/vitest.ts", "test:lts": "vitest run -c test/vitest.lts.ts", + "test:extended": "vitest run -c test/vitest.extended.ts", "prepack": "cargo xtask build release" }, "dependencies": { diff --git a/packages/jco/test/common.js b/packages/jco/test/common.js index fce356d54..3e44d948f 100644 --- a/packages/jco/test/common.js +++ b/packages/jco/test/common.js @@ -29,6 +29,9 @@ export const P3_COMPONENT_FIXTURES_DIR = join(COMPONENT_FIXTURES_DIR, "p3"); /** Path to built custom rust components (i.e. output of `cargo xtask build-test-components`) */ export const LOCAL_TEST_COMPONENTS_DIR = join(COMPONENT_FIXTURES_DIR, "../../output/rust-test-components"); +/** Path to built extended test components (i.e. output of `just build` run in `jco/test/components`) */ +export const EXTENDED_TEST_COMPONENTS_DIR = fileURLToPath(new URL("../../../test/components/output", import.meta.url)); + /** * Retrieve a list of all component fixtures * diff --git a/packages/jco/test/extended/jco-issue-1380.js b/packages/jco/test/extended/jco-issue-1380.js new file mode 100644 index 000000000..ea31aacf6 --- /dev/null +++ b/packages/jco/test/extended/jco-issue-1380.js @@ -0,0 +1,18 @@ +import { join } from "node:path"; + +import { suite, test, assert } from "vitest"; + +import { exec, jcoPath, fileExists } from "../helpers.js"; +import { EXTENDED_TEST_COMPONENTS_DIR } from "../common.js"; + +suite("jco-issue-1380", () => { + test("composed component runs", async () => { + const combinedComponentPath = join(EXTENDED_TEST_COMPONENTS_DIR, "jco-issue-1380/composed.wasm"); + assert(await fileExists(combinedComponentPath), "built composed component must be in place"); + + const { stdout, stderr } = await exec(jcoPath, "run", combinedComponentPath); + + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr, ""); + }); +}); diff --git a/packages/jco/test/helpers.js b/packages/jco/test/helpers.js index f91ce7136..2d998319f 100644 --- a/packages/jco/test/helpers.js +++ b/packages/jco/test/helpers.js @@ -662,3 +662,9 @@ export async function getCurrentWitComponentVersion() { CURRENT_WIT_COMPONENT_VERSION = version; return CURRENT_WIT_COMPONENT_VERSION; } + +export async function fileExists(p) { + return stat(p) + .then((f) => f.isFile()) + .catch(() => false); +} diff --git a/packages/jco/test/vitest.extended.ts b/packages/jco/test/vitest.extended.ts new file mode 100644 index 000000000..26f52955c --- /dev/null +++ b/packages/jco/test/vitest.extended.ts @@ -0,0 +1,30 @@ +import { availableParallelism } from "node:os"; + +import { defineConfig } from "vitest/config"; + +const DEFAULT_TIMEOUT_MS = 1000 * 60 * 1; // 60s +const CI_DEFAULT_TIMEOUT_MS = 1000 * 60 * 3; // 1m + +const REPORTERS = process.env.GITHUB_ACTIONS ? ["verbose", "github-actions"] : ["verbose"]; +const JSPI_EXEC_ARGV = "Suspending" in WebAssembly ? [] : ["--experimental-wasm-jspi"]; + +export default defineConfig({ + test: { + reporters: REPORTERS, + maxConcurrency: Math.max(availableParallelism() / 2, 5), + disableConsoleIntercept: true, + printConsoleTrace: true, + passWithNoTests: false, + setupFiles: ["test/meta-resolve-stub.ts"], + include: ["test/extended/**/*.js"], + testTimeout: process.env.CI ? CI_DEFAULT_TIMEOUT_MS : DEFAULT_TIMEOUT_MS, + hookTimeout: process.env.CI ? CI_DEFAULT_TIMEOUT_MS : DEFAULT_TIMEOUT_MS, + teardownTimeout: process.env.CI ? CI_DEFAULT_TIMEOUT_MS : DEFAULT_TIMEOUT_MS, + pool: "forks", + poolOptions: { + forks: { + execArgv: [...JSPI_EXEC_ARGV, "--stack-trace-limit=100"], + }, + }, + }, +}); diff --git a/packages/jco/test/vitest.lts.ts b/packages/jco/test/vitest.lts.ts index 3b9cd678b..bbfb49501 100644 --- a/packages/jco/test/vitest.lts.ts +++ b/packages/jco/test/vitest.lts.ts @@ -24,6 +24,7 @@ export default defineConfig({ setupFiles: ["test/meta-resolve-stub.ts"], include: ["test/*.js"], exclude: [ + "test/extended/*", "test/output/*", "test/fixtures/*", "test/common.js", diff --git a/packages/jco/test/vitest.ts b/packages/jco/test/vitest.ts index 53d6cab0d..21cc95aad 100644 --- a/packages/jco/test/vitest.ts +++ b/packages/jco/test/vitest.ts @@ -18,6 +18,7 @@ export default defineConfig({ setupFiles: ["test/meta-resolve-stub.ts"], include: ["test/**/*.js"], exclude: [ + "test/extended/*", "test/output/*", "test/fixtures/*", "test/common.js", diff --git a/test/components/.gitignore b/test/components/.gitignore new file mode 100644 index 000000000..4ba694e76 --- /dev/null +++ b/test/components/.gitignore @@ -0,0 +1,5 @@ +# while the output folder is present, we should ignore all it's contents +output/* + +# wasm files should not be checked in, generally +*.wasm \ No newline at end of file diff --git a/test/components/README.md b/test/components/README.md new file mode 100644 index 000000000..234739d6f --- /dev/null +++ b/test/components/README.md @@ -0,0 +1,60 @@ +# Test Components + +This folder contains components used for tests, written in various programming languages. + +While these tests are not required for local Jco builds, they are part of an extended +regression suite (mostly built from submitted bug reports) to ensure coverage for relatively +niche or rare cases. + +To run the tests that use these files, you will need to run the "extended" test suite: + +```console +pnpm --filter '@bytetcodealliance/jco' test:extended +``` + +# Building individual components + +## General dependencies + +Most components are built via the `justfile`s included in their code directories. Generally you +may need the following software to build any individual component: + +| Software | Description | +|------------------------------|---------------------------------------------------------------------| +| [`just`][just] | General task runner | +| [`wkg`][wkg] | Package manager for WebAssembly components | +| [`wit-bindgen`][wit-bindgen] | Bindings generator for WebAssembly that supports multiple languages | +| [`wac`][wac] | Component composition tool | + +You may list the `just` targets available at the top level (the `justfile` in this folder) via the command below: + +```console +just +``` + +Most components are buildable from the top level `justfile`. For example, the below command +will build all components related to the `jco-issue-1380` subproject: + +``` +just build jco-issue-1380 +``` + +[just]: https://github.com/casey/just +[wkg]: https://github.com/bytecodealliance/wasm-pkg-tools +[wit-bindgen]: https://github.com/bytecodealliance/wit-bindgen +[wac]: https://github.com/bytecodealliance/wac + +## Per-ecosystem dependencies + +Each langauge ecosystem may require separate build systems and local dependencies. A +useful but likely incomplete list is below: + +| Language | Dependency | Description | +|----------------------|--------------------------------------|------------------------------------------------------------------------------| +| [C++ (`cpp`)](./cpp) | [`wasi-sdk`][wasi-sdk] | Bytecode Alliance maintained toolin for building C/C++ components | +| [Python](./python) | [`uv`][uv] | Python package manager | +| | [`componentize-py`][componentize-py] | Bytecode Alliance maintained tooling for building Python projects components | + +[wasi-sdk]: https://github.com/WebAssembly/wasi-sdk +[uv]: https://github.com/astral-sh/uv +[componentize-py]: https://github.com/bytecodealliance/componentize-py/ diff --git a/test/components/cpp/jco-issue-1380/caller/.gitignore b/test/components/cpp/jco-issue-1380/caller/.gitignore new file mode 100644 index 000000000..c772fcc30 --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/.gitignore @@ -0,0 +1,2 @@ +bindings +component.wasm \ No newline at end of file diff --git a/test/components/cpp/jco-issue-1380/caller/component.cpp b/test/components/cpp/jco-issue-1380/caller/component.cpp new file mode 100644 index 000000000..cad61aa84 --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/component.cpp @@ -0,0 +1,10 @@ +#include "bindings/consumer_cpp.h" + +#include +#include + +std::expected exports::wasi::cli::run::Run() { + auto h = ::test::jco_bug::iface::OpenTemp(); + h.Append("|x|"); + return {}; +} diff --git a/test/components/cpp/jco-issue-1380/caller/justfile b/test/components/cpp/jco-issue-1380/caller/justfile new file mode 100644 index 000000000..a22572dfd --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/justfile @@ -0,0 +1,35 @@ +wkg := env_var_or_default("WKG", "wkg") +just := env_var_or_default("JUST", just_executable()) +wit_bindgen := env_var_or_default("WIT_BINDGEN", "wit-bindgen") + +# Required base dir to installation of wasi-sdk +# see: https://github.com/WebAssembly/wasi-sdk +wasi_sdk_base_dir := env_var_or_default("WASI_SDK", "/opt/wasi-sdk") + +@_default: + {{just}} --list + +# Fetch required WIT (i.e. wasi) +wit-fetch: + {{wkg}} wit fetch + +# NOTE: you will need a recent version of wit-bindgen +# +# Generate WIT bindings +gen-bindings: wit-fetch + {{wit_bindgen}} cpp wit --world consumer --out-dir bindings + +# Build python wasm component +build: gen-bindings + @if [ ! -d "{{wasi_sdk_base_dir}}" ]; then \ + echo '[error] failed to find wasi-sdk dir [{{wasi_sdk_base_dir}}]'; \ + echo ' is wasi-sdk installed? (at /opt/wasi-sdk, or specified via WASI_SDK env var)'; \ + exit -1; \ + fi + {{wasi_sdk_base_dir}}/bin/wasm32-wasip2-clang++ \ + -std=c++23 \ + -mexec-model=reactor \ + -o component.wasm \ + component.cpp \ + bindings/consumer.cpp \ + bindings/consumer_component_type.o diff --git a/test/components/cpp/jco-issue-1380/caller/wit/component.wit b/test/components/cpp/jco-issue-1380/caller/wit/component.wit new file mode 100644 index 000000000..bc284154a --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/component.wit @@ -0,0 +1,16 @@ +package test:jco-bug; + +interface iface { + resource handle { + constructor(path: string); + append: func(data: string); + } + + open-temp: func() -> own; +} + +world consumer { + include wasi:cli/imports@0.2.0; + import iface; + export wasi:cli/run@0.2.0; +} diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-cli-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-cli-0.2.0/package.wit new file mode 100644 index 000000000..0a2737b7c --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-cli-0.2.0/package.wit @@ -0,0 +1,159 @@ +package wasi:cli@0.2.0; + +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + initial-cwd: func() -> option; +} + +interface exit { + /// Exit the current instance and any linked instances. + exit: func(status: result); +} + +interface run { + /// Run the program. + run: func() -> result; +} + +interface stdin { + use wasi:io/streams@0.2.0.{input-stream}; + + get-stdin: func() -> input-stream; +} + +interface stdout { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stdout: func() -> output-stream; +} + +interface stderr { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +interface terminal-input { + /// The input side of a terminal. + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +interface terminal-output { + /// The output side of a terminal. + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +interface terminal-stdin { + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +interface terminal-stdout { + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +interface terminal-stderr { + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stderr: func() -> option; +} + +world imports { + import environment; + import exit; + import wasi:io/error@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; +} +world command { + import environment; + import exit; + import wasi:io/error@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; + + export run; +} diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-clocks-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-clocks-0.2.0/package.wit new file mode 100644 index 000000000..9e0ba3dca --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-clocks-0.2.0/package.wit @@ -0,0 +1,29 @@ +package wasi:clocks@0.2.0; + +interface monotonic-clock { + use wasi:io/poll@0.2.0.{pollable}; + + type instant = u64; + + type duration = u64; + + now: func() -> instant; + + resolution: func() -> duration; + + subscribe-instant: func(when: instant) -> pollable; + + subscribe-duration: func(when: duration) -> pollable; +} + +interface wall-clock { + record datetime { + seconds: u64, + nanoseconds: u32, + } + + now: func() -> datetime; + + resolution: func() -> datetime; +} + diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-filesystem-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-filesystem-0.2.0/package.wit new file mode 100644 index 000000000..cb6a2beb8 --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-filesystem-0.2.0/package.wit @@ -0,0 +1,158 @@ +package wasi:filesystem@0.2.0; + +interface types { + use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0.{datetime}; + + type filesize = u64; + + enum descriptor-type { + unknown, + block-device, + character-device, + directory, + fifo, + symbolic-link, + regular-file, + socket, + } + + flags descriptor-flags { + read, + write, + file-integrity-sync, + data-integrity-sync, + requested-write-sync, + mutate-directory, + } + + flags path-flags { + symlink-follow, + } + + flags open-flags { + create, + directory, + exclusive, + truncate, + } + + type link-count = u64; + + record descriptor-stat { + %type: descriptor-type, + link-count: link-count, + size: filesize, + data-access-timestamp: option, + data-modification-timestamp: option, + status-change-timestamp: option, + } + + variant new-timestamp { + no-change, + now, + timestamp(datetime), + } + + record directory-entry { + %type: descriptor-type, + name: string, + } + + enum error-code { + 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, + } + + enum advice { + normal, + sequential, + random, + will-need, + dont-need, + no-reuse, + } + + record metadata-hash-value { + lower: u64, + upper: u64, + } + + resource descriptor { + read-via-stream: func(offset: filesize) -> result; + write-via-stream: func(offset: filesize) -> result; + append-via-stream: func() -> result; + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + sync-data: func() -> result<_, error-code>; + get-flags: func() -> result; + get-type: func() -> result; + set-size: func(size: filesize) -> result<_, error-code>; + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + write: func(buffer: list, offset: filesize) -> result; + read-directory: func() -> result; + sync: func() -> result<_, error-code>; + create-directory-at: func(path: string) -> result<_, error-code>; + stat: func() -> result; + stat-at: func(path-flags: path-flags, path: string) -> result; + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + readlink-at: func(path: string) -> result; + remove-directory-at: func(path: string) -> result<_, error-code>; + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + unlink-file-at: func(path: string) -> result<_, error-code>; + is-same-object: func(other: borrow) -> bool; + metadata-hash: func() -> result; + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + resource directory-entry-stream { + read-directory-entry: func() -> result, error-code>; + } + + filesystem-error-code: func(err: borrow) -> option; +} + +interface preopens { + use types.{descriptor}; + + get-directories: func() -> list>; +} + diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-io-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-io-0.2.0/package.wit new file mode 100644 index 000000000..184002998 --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-io-0.2.0/package.wit @@ -0,0 +1,48 @@ +package wasi:io@0.2.0; + +interface error { + resource error { + to-debug-string: func() -> string; + } +} + +interface poll { + resource pollable { + ready: func() -> bool; + block: func(); + } + + poll: func(in: list>) -> list; +} + +interface streams { + use error.{error}; + use poll.{pollable}; + + variant stream-error { + last-operation-failed(error), + closed, + } + + resource input-stream { + read: func(len: u64) -> result, stream-error>; + blocking-read: func(len: u64) -> result, stream-error>; + skip: func(len: u64) -> result; + blocking-skip: func(len: u64) -> result; + subscribe: func() -> pollable; + } + + resource output-stream { + check-write: func() -> result; + write: func(contents: list) -> result<_, stream-error>; + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + flush: func() -> result<_, stream-error>; + blocking-flush: func() -> result<_, stream-error>; + subscribe: func() -> pollable; + write-zeroes: func(len: u64) -> result<_, stream-error>; + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + splice: func(src: borrow, len: u64) -> result; + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-random-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-random-0.2.0/package.wit new file mode 100644 index 000000000..58c179e1b --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-random-0.2.0/package.wit @@ -0,0 +1,18 @@ +package wasi:random@0.2.0; + +interface random { + get-random-bytes: func(len: u64) -> list; + + get-random-u64: func() -> u64; +} + +interface insecure { + get-insecure-random-bytes: func(len: u64) -> list; + + get-insecure-random-u64: func() -> u64; +} + +interface insecure-seed { + insecure-seed: func() -> tuple; +} + diff --git a/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-sockets-0.2.0/package.wit b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-sockets-0.2.0/package.wit new file mode 100644 index 000000000..0602b855d --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wit/deps/wasi-sockets-0.2.0/package.wit @@ -0,0 +1,179 @@ +package wasi:sockets@0.2.0; + +interface network { + resource network; + + enum error-code { + unknown, + access-denied, + not-supported, + invalid-argument, + out-of-memory, + timeout, + concurrency-conflict, + not-in-progress, + would-block, + invalid-state, + new-socket-limit, + address-not-bindable, + address-in-use, + remote-unreachable, + connection-refused, + connection-reset, + connection-aborted, + datagram-too-large, + name-unresolvable, + temporary-resolver-failure, + permanent-resolver-failure, + } + + enum ip-address-family { + ipv4, + ipv6, + } + + type ipv4-address = tuple; + + type ipv6-address = tuple; + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + record ipv4-socket-address { + port: u16, + address: ipv4-address, + } + + record ipv6-socket-address { + port: u16, + flow-info: u32, + address: ipv6-address, + scope-id: u32, + } + + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } +} + +interface instance-network { + use network.{network}; + + instance-network: func() -> network; +} + +interface udp { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + record incoming-datagram { + data: list, + remote-address: ip-socket-address, + } + + record outgoing-datagram { + data: list, + remote-address: option, + } + + resource udp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + %stream: func(remote-address: option) -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + address-family: func() -> ip-address-family; + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + } + + resource incoming-datagram-stream { + receive: func(max-results: u64) -> result, error-code>; + subscribe: func() -> pollable; + } + + resource outgoing-datagram-stream { + check-send: func() -> result; + send: func(datagrams: list) -> result; + subscribe: func() -> pollable; + } +} + +interface udp-create-socket { + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; + + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +interface tcp { + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/poll@0.2.0.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + enum shutdown-type { + receive, + send, + both, + } + + resource tcp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; + accept: func() -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + is-listening: func() -> bool; + address-family: func() -> ip-address-family; + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + keep-alive-enabled: func() -> result; + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + keep-alive-idle-time: func() -> result; + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + keep-alive-interval: func() -> result; + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + keep-alive-count: func() -> result; + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + hop-limit: func() -> result; + set-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +interface tcp-create-socket { + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; + + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +interface ip-name-lookup { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-address}; + + resource resolve-address-stream { + resolve-next-address: func() -> result, error-code>; + subscribe: func() -> pollable; + } + + resolve-addresses: func(network: borrow, name: string) -> result; +} + diff --git a/test/components/cpp/jco-issue-1380/caller/wkg.lock b/test/components/cpp/jco-issue-1380/caller/wkg.lock new file mode 100644 index 000000000..1604270bc --- /dev/null +++ b/test/components/cpp/jco-issue-1380/caller/wkg.lock @@ -0,0 +1,12 @@ +# This file is automatically generated. +# It is not intended for manual editing. +version = 1 + +[[packages]] +name = "wasi:cli" +registry = "wasi.dev" + +[[packages.versions]] +requirement = "=0.2.0" +version = "0.2.0" +digest = "sha256:e7e85458e11caf76554b724ebf4f113259decf0f3b1ee2e2930de096f72114a7" diff --git a/test/components/justfile b/test/components/justfile new file mode 100644 index 000000000..98a40b31b --- /dev/null +++ b/test/components/justfile @@ -0,0 +1,26 @@ +just := env_var_or_default("JUST", just_executable()) +wac := env_var_or_default("WAC", "wac") + +@_default: + {{just}} --list + +# Build all components for test +[group('build')] +build: + {{just}} build-jco-issue-1380 + +# Build the components required to test jco issue #1380 +[group('build')] +@build-jco-issue-1380: + echo "==> building jco-issue-1380 caller (cpp)..." + mkdir -p output/jco-issue-1380 + {{just}} -f cpp/jco-issue-1380/caller/justfile build + echo "==> building jco-issue-1380 callee (python)..." + mv cpp/jco-issue-1380/caller/component.wasm output/jco-issue-1380/caller.wasm + {{just}} -f python/jco-issue-1380/callee/justfile build + echo "==> composing components..." + mv python/jco-issue-1380/callee/component.wasm output/jco-issue-1380/callee.wasm + {{wac}} plug \ + output/jco-issue-1380/caller.wasm \ + --plug output/jco-issue-1380/callee.wasm \ + -o output/jco-issue-1380/composed.wasm diff --git a/test/components/python/jco-issue-1380/callee/.gitignore b/test/components/python/jco-issue-1380/callee/.gitignore new file mode 100644 index 000000000..5b101c161 --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/.gitignore @@ -0,0 +1,3 @@ +bindings +*.wasm +__pycache__ \ No newline at end of file diff --git a/test/components/python/jco-issue-1380/callee/component.py b/test/components/python/jco-issue-1380/callee/component.py new file mode 100644 index 000000000..37538b3db --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/component.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import sys + +from bindings.wit_world import exports +import bindings.wit_world.exports.iface as iface + +# componentize-py may resolve this module by top-level name. +sys.modules.setdefault("iface", iface) + +class Handle(iface.Handle): + _storage: dict[str, str] = {} + _seq: int = 0 + + def __init__(self, path: str) -> None: + self._path = path + Handle._storage.setdefault(self._path, "") + + def append(self, data: str) -> None: + Handle._storage[self._path] = Handle._storage.get(self._path, "") + data + + +class Iface(exports.Iface): + def open_temp(self) -> Handle: + Handle._seq += 1 + h = Handle(f"mem://h-{Handle._seq}") + return h + + +class Run(exports.Run): + def run(self) -> None: + api = Handle() + h = api.open_temp() + h.append("|py-run|") + return None diff --git a/test/components/python/jco-issue-1380/callee/justfile b/test/components/python/jco-issue-1380/callee/justfile new file mode 100644 index 000000000..db57aa68a --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/justfile @@ -0,0 +1,29 @@ +uv := env_var_or_default("UV", "uv") +wkg := env_var_or_default("WKG", "wkg") +just := env_var_or_default("JUST", just_executable()) + +bindings_dir := "bindings" + +@_default: + {{just}} --list + +# Set up the project +setup: + {{uv}} sync + +# Fetch required WIT (i.e. wasi) +wit-fetch: + {{wkg}} wit fetch + +# Generate WIT bindings +gen-bindings: wit-fetch + rm -rf {{bindings_dir}} + {{uv}} run componentize-py -d wit bindings {{bindings_dir}} + +# Build python wasm component +build: gen-bindings + {{uv}} run componentize-py \ + -d wit \ + componentize \ + -o component.wasm \ + component diff --git a/test/components/python/jco-issue-1380/callee/pyproject.toml b/test/components/python/jco-issue-1380/callee/pyproject.toml new file mode 100644 index 000000000..4793dc02f --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "python" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.14" +dependencies = [ + "componentize-py>=0.23.0", +] diff --git a/test/components/python/jco-issue-1380/callee/uv.lock b/test/components/python/jco-issue-1380/callee/uv.lock new file mode 100644 index 000000000..8af385c1b --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/uv.lock @@ -0,0 +1,27 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[[package]] +name = "componentize-py" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/d6/862d195136a671671ed06363ffcb45dc38682b3b5bb4a8eaa9500464de84/componentize_py-0.23.0.tar.gz", hash = "sha256:8d5ea3dbdf6642aca4a9329be30861189c1066e1651d2ab73bb3b0f1d9871407", size = 203590, upload-time = "2026-04-15T17:25:48.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/a9/f5a3975d05cec7f3a95a6e931d73204aa3592280113effd7a0ac1f741864/componentize_py-0.23.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bcd956598441713d1920396af9896ab19753bcb568299ff2db85f43bb65286ed", size = 16108001, upload-time = "2026-04-15T17:25:14.424Z" }, + { url = "https://files.pythonhosted.org/packages/75/9f/e4b08061e9f0c0abe61806b28f99ac094e2a19bdf312108c9b38be499d74/componentize_py-0.23.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d4b9b52e548f962c9ba36f15ce9793689bd29beb18a52b3480d317fc773e4506", size = 15269777, upload-time = "2026-04-15T17:25:21.826Z" }, + { url = "https://files.pythonhosted.org/packages/f8/df/b8161bcc0b4158a7cc75d558bd9b16b67bbd27ae7511ef2d06db4bea88c3/componentize_py-0.23.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:939acedff58eba1f4864ebe9327c742e5cf52bf6e08eddfb699e82761c5ede9f", size = 16237698, upload-time = "2026-04-15T17:25:29.46Z" }, + { url = "https://files.pythonhosted.org/packages/77/8d/426f07b84efed1b98076b4193f8d29e1a5e76a2f63fbc8770b2a6f3b589a/componentize_py-0.23.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:99eaa2cc8b5d737d2b23237618332a21838f892eec7f0359b44c464659a15ecc", size = 16808229, upload-time = "2026-04-15T17:25:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9c/1c725d88d33fdc99c008fde287098b037f71c18197d54477e4ed0db101b2/componentize_py-0.23.0-cp39-abi3-win_amd64.whl", hash = "sha256:1d65d73c97ea751d2dc958fba085c501e68a7c68cf4611df910b99e18fb2057e", size = 14884887, upload-time = "2026-04-15T17:25:44.983Z" }, +] + +[[package]] +name = "python" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "componentize-py" }, +] + +[package.metadata] +requires-dist = [{ name = "componentize-py", specifier = ">=0.23.0" }] diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-cli-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-cli-0.2.0/package.wit new file mode 100644 index 000000000..0a2737b7c --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-cli-0.2.0/package.wit @@ -0,0 +1,159 @@ +package wasi:cli@0.2.0; + +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + initial-cwd: func() -> option; +} + +interface exit { + /// Exit the current instance and any linked instances. + exit: func(status: result); +} + +interface run { + /// Run the program. + run: func() -> result; +} + +interface stdin { + use wasi:io/streams@0.2.0.{input-stream}; + + get-stdin: func() -> input-stream; +} + +interface stdout { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stdout: func() -> output-stream; +} + +interface stderr { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +interface terminal-input { + /// The input side of a terminal. + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +interface terminal-output { + /// The output side of a terminal. + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +interface terminal-stdin { + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +interface terminal-stdout { + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +interface terminal-stderr { + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + get-terminal-stderr: func() -> option; +} + +world imports { + import environment; + import exit; + import wasi:io/error@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; +} +world command { + import environment; + import exit; + import wasi:io/error@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import stdin; + import stdout; + import stderr; + import terminal-input; + import terminal-output; + import terminal-stdin; + import terminal-stdout; + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; + + export run; +} diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-clocks-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-clocks-0.2.0/package.wit new file mode 100644 index 000000000..9e0ba3dca --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-clocks-0.2.0/package.wit @@ -0,0 +1,29 @@ +package wasi:clocks@0.2.0; + +interface monotonic-clock { + use wasi:io/poll@0.2.0.{pollable}; + + type instant = u64; + + type duration = u64; + + now: func() -> instant; + + resolution: func() -> duration; + + subscribe-instant: func(when: instant) -> pollable; + + subscribe-duration: func(when: duration) -> pollable; +} + +interface wall-clock { + record datetime { + seconds: u64, + nanoseconds: u32, + } + + now: func() -> datetime; + + resolution: func() -> datetime; +} + diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-filesystem-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-filesystem-0.2.0/package.wit new file mode 100644 index 000000000..cb6a2beb8 --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-filesystem-0.2.0/package.wit @@ -0,0 +1,158 @@ +package wasi:filesystem@0.2.0; + +interface types { + use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0.{datetime}; + + type filesize = u64; + + enum descriptor-type { + unknown, + block-device, + character-device, + directory, + fifo, + symbolic-link, + regular-file, + socket, + } + + flags descriptor-flags { + read, + write, + file-integrity-sync, + data-integrity-sync, + requested-write-sync, + mutate-directory, + } + + flags path-flags { + symlink-follow, + } + + flags open-flags { + create, + directory, + exclusive, + truncate, + } + + type link-count = u64; + + record descriptor-stat { + %type: descriptor-type, + link-count: link-count, + size: filesize, + data-access-timestamp: option, + data-modification-timestamp: option, + status-change-timestamp: option, + } + + variant new-timestamp { + no-change, + now, + timestamp(datetime), + } + + record directory-entry { + %type: descriptor-type, + name: string, + } + + enum error-code { + 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, + } + + enum advice { + normal, + sequential, + random, + will-need, + dont-need, + no-reuse, + } + + record metadata-hash-value { + lower: u64, + upper: u64, + } + + resource descriptor { + read-via-stream: func(offset: filesize) -> result; + write-via-stream: func(offset: filesize) -> result; + append-via-stream: func() -> result; + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + sync-data: func() -> result<_, error-code>; + get-flags: func() -> result; + get-type: func() -> result; + set-size: func(size: filesize) -> result<_, error-code>; + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + write: func(buffer: list, offset: filesize) -> result; + read-directory: func() -> result; + sync: func() -> result<_, error-code>; + create-directory-at: func(path: string) -> result<_, error-code>; + stat: func() -> result; + stat-at: func(path-flags: path-flags, path: string) -> result; + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + readlink-at: func(path: string) -> result; + remove-directory-at: func(path: string) -> result<_, error-code>; + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + unlink-file-at: func(path: string) -> result<_, error-code>; + is-same-object: func(other: borrow) -> bool; + metadata-hash: func() -> result; + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + resource directory-entry-stream { + read-directory-entry: func() -> result, error-code>; + } + + filesystem-error-code: func(err: borrow) -> option; +} + +interface preopens { + use types.{descriptor}; + + get-directories: func() -> list>; +} + diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-io-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-io-0.2.0/package.wit new file mode 100644 index 000000000..184002998 --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-io-0.2.0/package.wit @@ -0,0 +1,48 @@ +package wasi:io@0.2.0; + +interface error { + resource error { + to-debug-string: func() -> string; + } +} + +interface poll { + resource pollable { + ready: func() -> bool; + block: func(); + } + + poll: func(in: list>) -> list; +} + +interface streams { + use error.{error}; + use poll.{pollable}; + + variant stream-error { + last-operation-failed(error), + closed, + } + + resource input-stream { + read: func(len: u64) -> result, stream-error>; + blocking-read: func(len: u64) -> result, stream-error>; + skip: func(len: u64) -> result; + blocking-skip: func(len: u64) -> result; + subscribe: func() -> pollable; + } + + resource output-stream { + check-write: func() -> result; + write: func(contents: list) -> result<_, stream-error>; + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + flush: func() -> result<_, stream-error>; + blocking-flush: func() -> result<_, stream-error>; + subscribe: func() -> pollable; + write-zeroes: func(len: u64) -> result<_, stream-error>; + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + splice: func(src: borrow, len: u64) -> result; + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-random-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-random-0.2.0/package.wit new file mode 100644 index 000000000..58c179e1b --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-random-0.2.0/package.wit @@ -0,0 +1,18 @@ +package wasi:random@0.2.0; + +interface random { + get-random-bytes: func(len: u64) -> list; + + get-random-u64: func() -> u64; +} + +interface insecure { + get-insecure-random-bytes: func(len: u64) -> list; + + get-insecure-random-u64: func() -> u64; +} + +interface insecure-seed { + insecure-seed: func() -> tuple; +} + diff --git a/test/components/python/jco-issue-1380/callee/wit/deps/wasi-sockets-0.2.0/package.wit b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-sockets-0.2.0/package.wit new file mode 100644 index 000000000..0602b855d --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/deps/wasi-sockets-0.2.0/package.wit @@ -0,0 +1,179 @@ +package wasi:sockets@0.2.0; + +interface network { + resource network; + + enum error-code { + unknown, + access-denied, + not-supported, + invalid-argument, + out-of-memory, + timeout, + concurrency-conflict, + not-in-progress, + would-block, + invalid-state, + new-socket-limit, + address-not-bindable, + address-in-use, + remote-unreachable, + connection-refused, + connection-reset, + connection-aborted, + datagram-too-large, + name-unresolvable, + temporary-resolver-failure, + permanent-resolver-failure, + } + + enum ip-address-family { + ipv4, + ipv6, + } + + type ipv4-address = tuple; + + type ipv6-address = tuple; + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + record ipv4-socket-address { + port: u16, + address: ipv4-address, + } + + record ipv6-socket-address { + port: u16, + flow-info: u32, + address: ipv6-address, + scope-id: u32, + } + + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } +} + +interface instance-network { + use network.{network}; + + instance-network: func() -> network; +} + +interface udp { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + record incoming-datagram { + data: list, + remote-address: ip-socket-address, + } + + record outgoing-datagram { + data: list, + remote-address: option, + } + + resource udp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + %stream: func(remote-address: option) -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + address-family: func() -> ip-address-family; + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + } + + resource incoming-datagram-stream { + receive: func(max-results: u64) -> result, error-code>; + subscribe: func() -> pollable; + } + + resource outgoing-datagram-stream { + check-send: func() -> result; + send: func(datagrams: list) -> result; + subscribe: func() -> pollable; + } +} + +interface udp-create-socket { + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; + + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +interface tcp { + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/poll@0.2.0.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + enum shutdown-type { + receive, + send, + both, + } + + resource tcp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; + accept: func() -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + is-listening: func() -> bool; + address-family: func() -> ip-address-family; + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + keep-alive-enabled: func() -> result; + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + keep-alive-idle-time: func() -> result; + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + keep-alive-interval: func() -> result; + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + keep-alive-count: func() -> result; + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + hop-limit: func() -> result; + set-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +interface tcp-create-socket { + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; + + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +interface ip-name-lookup { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-address}; + + resource resolve-address-stream { + resolve-next-address: func() -> result, error-code>; + subscribe: func() -> pollable; + } + + resolve-addresses: func(network: borrow, name: string) -> result; +} + diff --git a/test/components/python/jco-issue-1380/callee/wit/world.wit b/test/components/python/jco-issue-1380/callee/wit/world.wit new file mode 100644 index 000000000..2487cd667 --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wit/world.wit @@ -0,0 +1,18 @@ +package test:jco-bug; + +interface iface { + resource handle { + constructor(path: string); + append: func(data: string); + } + + open-temp: func() -> own; +} + +world component { + include wasi:cli/imports@0.2.0; + + export iface; + + export wasi:cli/run@0.2.0; +} \ No newline at end of file diff --git a/test/components/python/jco-issue-1380/callee/wkg.lock b/test/components/python/jco-issue-1380/callee/wkg.lock new file mode 100644 index 000000000..1604270bc --- /dev/null +++ b/test/components/python/jco-issue-1380/callee/wkg.lock @@ -0,0 +1,12 @@ +# This file is automatically generated. +# It is not intended for manual editing. +version = 1 + +[[packages]] +name = "wasi:cli" +registry = "wasi.dev" + +[[packages.versions]] +requirement = "=0.2.0" +version = "0.2.0" +digest = "sha256:e7e85458e11caf76554b724ebf4f113259decf0f3b1ee2e2930de096f72114a7"