Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/as-fs/src/main.wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import { log } from 'console';
import { readFile } from 'fs';

/**
* Reads the greeting file and logs its UTF-8 contents when available.
*/
export function _start(): void {
log('Opening file...');
let data = readFile('/mnt/data/greeting.txt');
Expand Down
7 changes: 6 additions & 1 deletion examples/cpp-saludo/src/main.wasm.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// C++ WASM example using high-level Console API
#include "console.h"

extern "C" int _start() {
extern "C" /**
* @brief Logs a greeting when the WebAssembly module starts.
*
* @return 0 to indicate successful completion.
*/
int _start() {
console::log("Hola desde C++ WASM!");
return 0;
}
7 changes: 6 additions & 1 deletion examples/mounts-demo/src/main.wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
#include "console.h"
#include "fs.h"

extern "C" int _start() {
extern "C" /**
* @brief Reads the mounted greeting file and writes its contents to standard output.
*
* @return int 0 after processing the greeting file.
*/
int _start() {
console::log("Opening file...");

char buf[512];
Expand Down
10 changes: 9 additions & 1 deletion examples/rust-fs/src/main.wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ use wasm_apps_bindings::{console, fs, wasi, wasm_setup};

wasm_setup!();

#[no_mangle]
/// Reads the greeting file from the mounted data directory and writes its contents to standard output.
///
/// Logs a fallback message when the mounted directory or file is unavailable.
///
/// # Examples
///
/// ```
/// _start();
/// ```”】【
pub extern "C" fn _start() {
console::log("Opening file...");
let data = fs::read_file(b"/mnt/data/greeting.txt");
Expand Down
7 changes: 7 additions & 0 deletions examples/rust-hello/src/main.wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ use wasm_apps_bindings::{console, wasm_setup};

wasm_setup!();

/// Logs a greeting message when the WebAssembly module starts.
///
/// # Examples
///
/// ```
/// _start();
/// ```
#[no_mangle]
pub extern "C" fn _start() {
console::log("Hola desde Rust WASM!");
Expand Down
20 changes: 20 additions & 0 deletions packages/compiler/src/bindings/console.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@

#include "wasi.h"

/**
* Writes a message followed by a newline to standard output.
* @param msg Message to write.
*/

/**
* Writes a warning followed by a newline to standard error.
* @param msg Warning message to write.
*/

/**
* Writes an error message followed by a newline to standard error.
* @param msg Error message to write.
*/

/**
* Verifies a condition and terminates execution with an assertion message if it is false.
* @param cond Condition to verify.
* @param msg Message to include when the assertion fails.
*/
namespace console {

inline void log(const char* msg) {
Expand Down
27 changes: 27 additions & 0 deletions packages/compiler/src/bindings/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,43 @@

use crate::wasi;

/// Writes a message followed by a newline to standard output.
///
/// # Examples
///
/// ```
/// log("Build completed");
/// ```
pub fn log(msg: &str) {
let _ = wasi::stdout_str(msg);
let _ = wasi::stdout_write(b"\n");
}

/// Writes a warning message followed by a newline to standard error.

///

/// # Examples

///

/// ```

/// warn("Configuration is missing");

/// ```
pub fn warn(msg: &str) {
let _ = wasi::stderr_write(msg.as_bytes());
let _ = wasi::stderr_write(b"\n");
}

/// Writes an error message followed by a newline to standard error.
///
/// # Examples
///
/// ```
/// error("Failed to load configuration");
/// ```
pub fn error(msg: &str) {
let _ = wasi::stderr_write(msg.as_bytes());
let _ = wasi::stderr_write(b"\n");
Expand Down
21 changes: 21 additions & 0 deletions packages/compiler/src/bindings/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,45 @@

import { stdoutWrite, stderrWrite } from "./wasi";

/**
* Writes a newline-terminated message to standard output.
*
* @param msg - The message to write
*/
export function log(msg: string): void {
let buf = String.UTF8.encode(msg + "\n");
let arr = Uint8Array.wrap(buf);
stdoutWrite(arr.dataStart as usize, arr.length);
}

/**
* Writes a warning message to standard error.
*
* @param msg - The warning message to write
*/
export function warn(msg: string): void {
let buf = String.UTF8.encode(msg + "\n");
let arr = Uint8Array.wrap(buf);
stderrWrite(arr.dataStart as usize, arr.length);
}

/**
* Writes a newline-terminated error message to standard error.
*
* @param msg - The error message to write
*/
export function error(msg: string): void {
let buf = String.UTF8.encode(msg + "\n");
let arr = Uint8Array.wrap(buf);
stderrWrite(arr.dataStart as usize, arr.length);
}

/**
* Verifies a condition and terminates execution when it is false.
*
* @param cond - The condition to verify
* @param msg - The message written when the condition is false
*/
export function assert(cond: bool, msg: string): void {
if (!cond) {
let full = "Assertion failed: " + msg + "\n";
Expand Down
8 changes: 8 additions & 0 deletions packages/compiler/src/bindings/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
#include <stddef.h>
#include <stdint.h>

/**
* Resolves a path against a preopened directory.
*
* @param path Path to resolve.
* @param out_dir_fd Receives the matching directory descriptor.
* @param out_rel_offset Receives the offset of the path relative to that directory.
* @return 0 on success, or -1 if no preopened directory matches the path.
*/
namespace fs {

// ── Internal: resolve path to (dirFd, relative) ─────
Expand Down
127 changes: 127 additions & 0 deletions packages/compiler/src/bindings/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ struct ResolvedPath {
relative: Vec<u8>,
}

/// Resolves a path against the available preopened directories.
///
/// # Examples
///
/// ```no_run
/// let resolved = resolve_path(b"/data/file.txt");
/// assert!(resolved.is_some());
/// ```
///
/// Returns the first matching preopened directory and the path relative to it.
///
/// # Returns
///
/// `Some(ResolvedPath)` for a matching preopened directory, or `None` if no directory matches.
fn resolve_path(path: &[u8]) -> Option<ResolvedPath> {
for fd in 3..=63i32 {
// Try to read prestat
Expand Down Expand Up @@ -49,6 +63,21 @@ fn resolve_path(path: &[u8]) -> Option<ResolvedPath> {

// ── Public API ────────────────────────────────────

/// Reads a file into a byte vector.
///
/// The path is interpreted relative to a preopened directory.
///
/// # Errors
///
/// Returns a WASI error code if the path cannot be resolved or the file cannot
/// be opened or read.
///
/// # Examples
///
/// ```no_run
/// let contents = read_file(b"example.txt").expect("failed to read file");
/// assert!(!contents.is_empty());
/// ```
pub fn read_file(path: &[u8]) -> Result<Vec<u8>, i32> {
let r = resolve_path(path).ok_or(-1)?;
let fd = wasi::path_open(r.dir_fd, &r.relative, false, false)?;
Expand All @@ -61,6 +90,27 @@ pub fn read_file(path: &[u8]) -> Result<Vec<u8>, i32> {
Ok(buf)
}

/// Reads a file into a caller-provided buffer.
///
/// The returned byte count indicates how much data was written to `buf`.
///
/// # Examples
///
/// ```
/// let mut buffer = [0u8; 1024];
/// let result = read_file_to_buf(b"file.txt", &mut buffer);
/// assert!(result.is_ok() || result.is_err());
/// ```
///
/// # Parameters
///
/// * `path` — The path of the file to read.
/// * `buf` — The buffer receiving the file contents.
///
/// # Returns
///
/// The number of bytes read, or a WASI error code.
pub fn read_file_to_buf(path: &[u8], buf: &mut [u8]) -> Result<usize, i32> {
pub fn read_file_to_buf(path: &[u8], buf: &mut [u8]) -> Result<usize, i32> {
let r = resolve_path(path).ok_or(-1)?;
let fd = wasi::path_open(r.dir_fd, &r.relative, false, false)?;
Expand All @@ -69,6 +119,37 @@ pub fn read_file_to_buf(path: &[u8], buf: &mut [u8]) -> Result<usize, i32> {
Ok(n)
}

/// Writes data to a file, creating it when necessary.

///

/// # Examples

///

/// ```

/// # fn example() -> Result<(), i32> {

/// let bytes_written = write_file(b"output.txt", b"hello")?;

/// assert_eq!(bytes_written, 5);

/// # Ok(())

/// # }

/// ```

///

/// # Returns

///

/// The number of bytes written, or the WASI error code if the path cannot be

/// resolved, the file cannot be opened, or the data cannot be written.
pub fn write_file(path: &[u8], data: &[u8]) -> Result<usize, i32> {
let r = resolve_path(path).ok_or(-1)?;
let fd = wasi::path_open(r.dir_fd, &r.relative, true, true)?;
Expand All @@ -77,6 +158,23 @@ pub fn write_file(path: &[u8], data: &[u8]) -> Result<usize, i32> {
Ok(n)
}

/// Checks whether a path refers to an existing file.

///

/// Returns `false` when the path cannot be resolved.

///

/// # Examples

///

/// ```

/// assert!(!exists(b"/definitely-missing"));

/// ```
pub fn exists(path: &[u8]) -> bool {
let r = match resolve_path(path) {
Some(v) => v,
Expand All @@ -85,12 +183,41 @@ pub fn exists(path: &[u8]) -> bool {
wasi::file_exists(r.dir_fd, &r.relative)
}

/// Removes a file at the specified path.
///
/// # Parameters
///
/// * `path` - The path of the file to remove.
///
/// # Returns
///
/// `Ok(())` if the file is removed, or an error code if path resolution or removal fails.
///
/// # Examples
///
/// ```
/// let result = unlink(b"temporary.txt");
/// assert!(result.is_ok() || result.is_err());
/// ```
pub fn unlink(path: &[u8]) -> Result<(), i32> {
let r = resolve_path(path).ok_or(-1)?;
wasi::path_unlink_file(r.dir_fd, &r.relative).map_err(|e| e.0)?;
Ok(())
}

/// Creates a directory at the specified path.
///
/// # Examples
///
/// ```
/// mkdir(b"cache").expect("failed to create directory");
/// ```
///
/// # Returns
///
/// `Ok(())` when the directory is created, or the WASI error code when creation fails.
///
/// `path` is interpreted relative to a preopened directory.
pub fn mkdir(path: &[u8]) -> Result<(), i32> {
let r = resolve_path(path).ok_or(-1)?;
wasi::path_create_directory(r.dir_fd, &r.relative).map_err(|e| e.0)?;
Expand Down
Loading