mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-23 06:25:10 +00:00
moved platform-switching
This commit is contained in:
parent
dc205eab66
commit
3dad6aba7c
34 changed files with 25 additions and 25 deletions
|
@ -1,7 +0,0 @@
|
|||
rocLovesC
|
||||
rocLovesPlatforms
|
||||
rocLovesRust
|
||||
rocLovesSwift
|
||||
rocLovesWebAssembly
|
||||
rocLovesZig
|
||||
*.wasm
|
|
@ -1,20 +0,0 @@
|
|||
# Platform switching
|
||||
|
||||
To run, `cd` into this directory and run this in your terminal:
|
||||
|
||||
```bash
|
||||
roc run
|
||||
```
|
||||
|
||||
This will run `main.roc` because, unless you explicitly give it a filename, `roc run`
|
||||
defaults to running a file named `main.roc`. Other `roc` commands (like `roc build`, `roc test`, and so on) also default to `main.roc` unless you explicitly give them a filename.
|
||||
|
||||
## About this example
|
||||
|
||||
This uses a very simple platform which does nothing more than printing the string you give it.
|
||||
|
||||
The line `main = "Which platform am I running on now?\n"` sets this string to be `"Which platform am I running on now?"` with a newline at the end, and the lines `packages { pf: "c-platform/main.roc" }` and `provides [main] to pf` specify that the `c-platform/` directory contains this app's platform.
|
||||
|
||||
This platform is called `c-platform` because its lower-level code is written in C. There's also a `rust-platform`, `zig-platform`, and so on; if you like, you can try switching `pf: "c-platform/main.roc"` to `pf: "zig-platform/main.roc"` or `pf: "rust-platform/main.roc"` to try one of those platforms instead. They all do similar things, so the application won't look any different.
|
||||
|
||||
If you want to start building your own platforms, these are some very simple example platforms to use as starting points.
|
|
@ -1,89 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); }
|
||||
|
||||
void* roc_realloc(void* ptr, size_t new_size, size_t old_size,
|
||||
unsigned int alignment) {
|
||||
return realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); }
|
||||
|
||||
void roc_panic(void* ptr, unsigned int alignment) {
|
||||
char* msg = (char*)ptr;
|
||||
fprintf(stderr,
|
||||
"Application crashed with message\n\n %s\n\nShutting down\n", msg);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void* roc_memcpy(void* dest, const void* src, size_t n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); }
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; }
|
||||
|
||||
// Determine the length of the string, taking into
|
||||
// account the small string optimization
|
||||
size_t roc_str_len(struct RocStr str) {
|
||||
char* bytes = (char*)&str;
|
||||
char last_byte = bytes[sizeof(str) - 1];
|
||||
char last_byte_xored = last_byte ^ 0b10000000;
|
||||
size_t small_len = (size_t)(last_byte_xored);
|
||||
size_t big_len = str.len;
|
||||
|
||||
// Avoid branch misprediction costs by always
|
||||
// determining both small_len and big_len,
|
||||
// so this compiles to a cmov instruction.
|
||||
if (is_small_str(str)) {
|
||||
return small_len;
|
||||
} else {
|
||||
return big_len;
|
||||
}
|
||||
}
|
||||
|
||||
extern void roc__mainForHost_1_exposed_generic(struct RocStr *string);
|
||||
|
||||
int main() {
|
||||
|
||||
struct RocStr str;
|
||||
roc__mainForHost_1_exposed_generic(&str);
|
||||
|
||||
// Determine str_len and the str_bytes pointer,
|
||||
// taking into account the small string optimization.
|
||||
size_t str_len = roc_str_len(str);
|
||||
char* str_bytes;
|
||||
|
||||
if (is_small_str(str)) {
|
||||
str_bytes = (char*)&str;
|
||||
} else {
|
||||
str_bytes = str.bytes;
|
||||
}
|
||||
|
||||
// Write to stdout
|
||||
if (write(1, str_bytes, str_len) >= 0) {
|
||||
// Writing succeeded!
|
||||
|
||||
// NOTE: the string is a static string, read from in the binary
|
||||
// if you make it a heap-allocated string, it'll be leaked here
|
||||
return 0;
|
||||
} else {
|
||||
printf("Error writing to stdout: %s\n", strerror(errno));
|
||||
|
||||
// NOTE: the string is a static string, read from in the binary
|
||||
// if you make it a heap-allocated string, it'll be leaked here
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
platform "echo-in-c"
|
||||
requires {} { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,11 +0,0 @@
|
|||
app "rocLovesPlatforms"
|
||||
packages { pf: "c-platform/main.roc" }
|
||||
# To switch platforms, comment-out the line above and un-comment one below.
|
||||
# packages { pf: "rust-platform/main.roc" }
|
||||
# packages { pf: "swift-platform/main.roc" }
|
||||
# packages { pf: "web-assembly-platform/main.roc" } # See ./web-assembly-platform/README.md
|
||||
# packages { pf: "zig-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Which platform am I running on now?\n"
|
|
@ -1,6 +0,0 @@
|
|||
app "rocLovesC"
|
||||
packages { pf: "c-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Roc <3 C!\n"
|
|
@ -1,6 +0,0 @@
|
|||
app "rocLovesRust"
|
||||
packages { pf: "rust-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Roc <3 Rust!\n"
|
|
@ -1,6 +0,0 @@
|
|||
app "rocLovesSwift"
|
||||
packages { pf: "swift-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Roc <3 Swift!\n"
|
|
@ -1,6 +0,0 @@
|
|||
app "rocLovesWebAssembly"
|
||||
packages { pf: "web-assembly-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Roc <3 Web Assembly!\n"
|
|
@ -1,6 +0,0 @@
|
|||
app "rocLovesZig"
|
||||
packages { pf: "zig-platform/main.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main = "Roc <3 Zig!\n"
|
|
@ -1,37 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"roc_std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "host"
|
||||
version = "0.0.1"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2021"
|
||||
links = "app"
|
||||
|
||||
[lib]
|
||||
name = "host"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[[bin]]
|
||||
name = "host"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../../crates/roc_std" }
|
||||
libc = "0.2"
|
||||
|
||||
[workspace]
|
|
@ -1,4 +0,0 @@
|
|||
fn main() {
|
||||
println!("cargo:rustc-link-lib=dylib=app");
|
||||
println!("cargo:rustc-link-search=.");
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
extern int rust_main();
|
||||
|
||||
int main() { return rust_main(); }
|
|
@ -1,9 +0,0 @@
|
|||
platform "echo-in-rust"
|
||||
requires {} { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,78 +0,0 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use core::ffi::c_void;
|
||||
use roc_std::RocStr;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: &mut RocStr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
unsafe {
|
||||
// ManuallyDrop must be used here in order to prevent the RocStr from
|
||||
// getting dropped as soon as it's no longer referenced anywhere, which
|
||||
// happens earlier than the libc::write that receives a pointer to its data.
|
||||
let mut roc_str = ManuallyDrop::new(RocStr::default());
|
||||
roc_main(&mut roc_str);
|
||||
|
||||
let len = roc_str.len();
|
||||
let str_bytes = roc_str.as_bytes().as_ptr() as *const libc::c_void;
|
||||
|
||||
if libc::write(1, str_bytes, len) < 0 {
|
||||
panic!("Writing to stdout failed!");
|
||||
}
|
||||
|
||||
ManuallyDrop::drop(&mut roc_str)
|
||||
}
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
fn main() {
|
||||
std::process::exit(host::rust_main() as _);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data);
|
|
@ -1,62 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
@_cdecl("roc_alloc")
|
||||
func rocAlloc(size: Int, _alignment: UInt) -> UInt {
|
||||
guard let ptr = malloc(size) else {
|
||||
return 0
|
||||
}
|
||||
return UInt(bitPattern: ptr)
|
||||
}
|
||||
|
||||
@_cdecl("roc_dealloc")
|
||||
func rocDealloc(ptr: UInt, _alignment: UInt) {
|
||||
free(UnsafeMutableRawPointer(bitPattern: ptr))
|
||||
}
|
||||
|
||||
@_cdecl("roc_realloc")
|
||||
func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UInt {
|
||||
guard let ptr = realloc(UnsafeMutableRawPointer(bitPattern: ptr), newSize) else {
|
||||
return 0
|
||||
}
|
||||
return UInt(bitPattern: ptr)
|
||||
}
|
||||
|
||||
func isSmallString(rocStr: RocStr) -> Bool {
|
||||
return rocStr.capacity < 0
|
||||
}
|
||||
|
||||
func getStrLen(rocStr: RocStr) -> Int {
|
||||
if isSmallString(rocStr: rocStr) {
|
||||
// Small String length is last in the byte of capacity.
|
||||
var cap = rocStr.capacity
|
||||
let count = MemoryLayout.size(ofValue: cap)
|
||||
let bytes = Data(bytes: &cap, count: count)
|
||||
let lastByte = bytes[count - 1]
|
||||
return Int(lastByte ^ 0b1000_0000)
|
||||
} else {
|
||||
return rocStr.len
|
||||
}
|
||||
}
|
||||
|
||||
func getSwiftString(rocStr: RocStr) -> String {
|
||||
let length = getStrLen(rocStr: rocStr)
|
||||
|
||||
if isSmallString(rocStr: rocStr) {
|
||||
let data: Data = withUnsafePointer(to: rocStr) { ptr in
|
||||
Data(bytes: ptr, count: length)
|
||||
}
|
||||
return String(data: data, encoding: .utf8)!
|
||||
} else {
|
||||
let data = Data(bytes: rocStr.bytes, count: length)
|
||||
return String(data: data, encoding: .utf8)!
|
||||
}
|
||||
}
|
||||
|
||||
@_cdecl("main")
|
||||
func main() -> UInt8 {
|
||||
var rocStr = RocStr()
|
||||
roc__mainForHost_1_exposed_generic(&rocStr)
|
||||
|
||||
print(getSwiftString(rocStr: rocStr), terminator: "")
|
||||
return 0
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
platform "echo-in-swift"
|
||||
requires {} { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,55 +0,0 @@
|
|||
# Hello, World!
|
||||
|
||||
To run this website, first compile either of these identical apps:
|
||||
|
||||
```bash
|
||||
# Option A: Compile crates/cli_testing_examples/platform-switching/rocLovesWebAssembly.roc
|
||||
cargo run -- build --target=wasm32 crates/cli_testing_examples/platform-switching/rocLovesWebAssembly.roc
|
||||
|
||||
# Option B: Compile crates/cli_testing_examples/platform-switching/main.roc with `pf: "web-assembly-platform/main.roc"` and move the result
|
||||
cargo run -- build --target=wasm32 crates/cli_testing_examples/platform-switching/main.roc
|
||||
(cd crates/cli_testing_examples/platform-switching && mv rocLovesPlatforms.wasm web-assembly-platform/rocLovesWebAssembly.wasm)
|
||||
```
|
||||
|
||||
Then `cd` into the website directory
|
||||
and run any web server that can handle WebAssembly.
|
||||
For example, with `http-server`:
|
||||
|
||||
```bash
|
||||
cd crates/cli_testing_examples/platform-switching/web-assembly-platform
|
||||
npm install -g http-server
|
||||
http-server
|
||||
```
|
||||
|
||||
Now open your browser at <http://localhost:8080>
|
||||
|
||||
## Design Notes
|
||||
|
||||
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
||||
function (in this case, a thunk that always returns `"Hello, World!\n"`) and
|
||||
then the host calls that function. Fundamentally, that's the whole idea! The host
|
||||
might not even have a `main` - it could be a library, a plugin, anything.
|
||||
Everything else is built on this basic "hosts calling linked pure functions" design.
|
||||
|
||||
For example, things get more interesting when the compiled Roc function returns
|
||||
a `Task` - that is, a tagged union data structure containing function pointers
|
||||
to callback closures. This lets the Roc pure function describe arbitrary
|
||||
chainable effects, which the host can interpret to perform I/O as requested by
|
||||
the Roc program. (The tagged union `Task` would have a variant for each supported
|
||||
I/O operation.)
|
||||
|
||||
In this trivial example, it's very easy to line up the API between the host and
|
||||
the Roc program. In a more involved host, this would be much trickier - especially
|
||||
if the API were changing frequently during development.
|
||||
|
||||
The idea there is to have a first-class concept of "glue code" which host authors
|
||||
can write (it would be plain Roc code, but with some extra keywords that aren't
|
||||
available in normal modules - kinda like `port module` in Elm), and which
|
||||
describe both the Roc-host/C boundary as well as the Roc-host/Roc-app boundary.
|
||||
Roc application authors only care about the Roc-host/Roc-app portion, and the
|
||||
host author only cares about the Roc-host/C boundary when implementing the host.
|
||||
|
||||
Using this glue code, the Roc compiler can generate C header files describing the
|
||||
boundary. This not only gets us host compatibility with C compilers, but also
|
||||
Rust FFI for free, because [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen)
|
||||
generates correct Rust FFI bindings from C headers.
|
|
@ -1,58 +0,0 @@
|
|||
async function roc_web_platform_run(wasm_filename, callback) {
|
||||
const decoder = new TextDecoder();
|
||||
let memory_bytes;
|
||||
let exit_code;
|
||||
|
||||
function js_display_roc_string(str_bytes, str_len) {
|
||||
const utf8_bytes = memory_bytes.subarray(str_bytes, str_bytes + str_len);
|
||||
const js_string = decoder.decode(utf8_bytes);
|
||||
callback(js_string);
|
||||
}
|
||||
|
||||
const importObj = {
|
||||
wasi_snapshot_preview1: {
|
||||
proc_exit: (code) => {
|
||||
if (code !== 0) {
|
||||
console.error(`Exited with code ${code}`);
|
||||
}
|
||||
exit_code = code;
|
||||
},
|
||||
fd_write: (x) => { console.error(`fd_write not supported: ${x}`); }
|
||||
},
|
||||
env: {
|
||||
js_display_roc_string,
|
||||
roc_panic: (_pointer, _tag_id) => {
|
||||
throw "Roc panicked!";
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let wasm;
|
||||
|
||||
const response = await fetch(wasm_filename);
|
||||
|
||||
if (WebAssembly.instantiateStreaming) {
|
||||
// streaming API has better performance if available
|
||||
wasm = await WebAssembly.instantiateStreaming(response, importObj);
|
||||
} else {
|
||||
const module_bytes = await response.arrayBuffer();
|
||||
wasm = await WebAssembly.instantiate(module_bytes, importObj);
|
||||
}
|
||||
|
||||
memory_bytes = new Uint8Array(wasm.instance.exports.memory.buffer);
|
||||
|
||||
try {
|
||||
wasm.instance.exports._start();
|
||||
} catch (e) {
|
||||
const is_ok = e.message === "unreachable" && exit_code === 0;
|
||||
if (!is_ok) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = {
|
||||
roc_web_platform_run,
|
||||
};
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
* Node.js test file for helloWeb example
|
||||
* We are not running this in CI currently, and Node.js is not a Roc dependency.
|
||||
* But if you happen to have it, you can run this.
|
||||
*/
|
||||
|
||||
// Node doesn't have the fetch API
|
||||
const fs = require("fs/promises");
|
||||
global.fetch = (filename) =>
|
||||
fs.readFile(filename).then((buffer) => ({
|
||||
arrayBuffer() {
|
||||
return buffer;
|
||||
},
|
||||
}));
|
||||
|
||||
const { roc_web_platform_run } = require("./host");
|
||||
|
||||
roc_web_platform_run("./rocLovesWebAssembly.wasm", (string_from_roc) => {
|
||||
const expected = "Roc <3 Web Assembly!\n";
|
||||
if (string_from_roc !== expected) {
|
||||
console.error(`Expected "${expected}", but got "${string_from_roc}"`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("OK");
|
||||
});
|
|
@ -1,74 +0,0 @@
|
|||
const std = @import("std");
|
||||
const str = @import("str");
|
||||
const builtin = @import("builtin");
|
||||
const RocStr = str.RocStr;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const Align = extern struct { a: usize, b: usize };
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) anyopaque;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||
extern fn memcpy(dest: *anyopaque, src: *anyopaque, count: usize) *anyopaque;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
_ = alignment;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
|
||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||
_ = alignment;
|
||||
|
||||
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_memcpy(dest: *anyopaque, src: *anyopaque, count: usize) callconv(.C) void {
|
||||
_ = memcpy(dest, src, count);
|
||||
}
|
||||
|
||||
// NOTE roc_panic is provided in the JS file, so it can throw an exception
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed(*RocStr) void;
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
extern fn js_display_roc_string(str_bytes: ?[*]u8, str_len: usize) void;
|
||||
|
||||
pub fn main() u8 {
|
||||
// actually call roc to populate the callresult
|
||||
var callresult = RocStr.empty();
|
||||
roc__mainForHost_1_exposed(&callresult);
|
||||
|
||||
// display the result using JavaScript
|
||||
js_display_roc_string(callresult.asU8ptr(), callresult.len());
|
||||
|
||||
callresult.deinit();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<div id="output"></div>
|
||||
<script src="./host.js"></script>
|
||||
<script>
|
||||
const elem = document.getElementById("output");
|
||||
roc_web_platform_run("./rocLovesWebAssembly.wasm", (string_from_roc) => {
|
||||
elem.textContent = string_from_roc;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +0,0 @@
|
|||
platform "echo-in-web-assembly"
|
||||
requires {} { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,106 +0,0 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const str = @import("str");
|
||||
const RocStr = str.RocStr;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const Align = 2 * @alignOf(usize);
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
|
||||
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
|
||||
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
|
||||
|
||||
const DEBUG: bool = false;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
if (DEBUG) {
|
||||
var ptr = malloc(size);
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
|
||||
return ptr;
|
||||
} else {
|
||||
return malloc(size);
|
||||
}
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||
if (DEBUG) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
|
||||
}
|
||||
|
||||
return realloc(@alignCast(Align, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||
if (DEBUG) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
|
||||
}
|
||||
|
||||
free(@alignCast(Align, @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
std.process.exit(0);
|
||||
}
|
||||
|
||||
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
|
||||
return memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
|
||||
return memset(dst, value, size);
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
var timer = std.time.Timer.start() catch unreachable;
|
||||
|
||||
// actually call roc to populate the callresult
|
||||
var callresult = RocStr.empty();
|
||||
roc__mainForHost_1_exposed_generic(&callresult);
|
||||
|
||||
const nanos = timer.read();
|
||||
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
|
||||
|
||||
// stdout the result
|
||||
stdout.print("{s}", .{callresult.asSlice()}) catch unreachable;
|
||||
|
||||
callresult.deinit();
|
||||
|
||||
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
platform "echo-in-zig"
|
||||
requires {} { main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
Loading…
Add table
Add a link
Reference in a new issue