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
19 changes: 19 additions & 0 deletions .github/workflows/wasm-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,32 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install rust nightly toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 1.79
override: true
components: clippy, rustfmt
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.22

- name: Install dependencies
run: npm install

- name: Build
run: npm run build

- name: Test
run: npm run test

- name: Rust example e2e tests
run: cd examples/rust && cargo test -r

- name: Go example e2e tests
run: cd examples/go && go mod tidy && go run main.go

- name: Publish Dry Run
if: "startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-')"
# Since this command will not exit with non-zero code when file missing,
Expand Down
Binary file modified wasm/examples/browser/kcl.wasm
100644 → 100755
Binary file not shown.
13 changes: 11 additions & 2 deletions wasm/examples/browser/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { load, invokeKCLRun } from "@kcl-lang/wasm-lib";
import { load, invokeKCLRun, invokeKCLFmt } from "@kcl-lang/wasm-lib";

const inst = await load();

async function main() {
const inst = await load();
const result = invokeKCLRun(inst, {
filename: "test.k",
source: `
Expand All @@ -11,6 +12,14 @@ schema Person:
p = Person {name = "Alice"}`,
});
console.log(result);
const fmtResult = invokeKCLFmt(inst, {
source: `
schema Person:
name: str

p = Person {name = "Alice"}`,
});
console.log(fmtResult);
}

main();
4 changes: 2 additions & 2 deletions wasm/examples/browser/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"moduleResolution": "node",
"module": "es2022",
"lib": ["es2022", "dom"],
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
Expand Down
7 changes: 7 additions & 0 deletions wasm/examples/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ func main() {
panic(err)
}
fmt.Println(result)
result, err = m.Fmt(&module.FmtOptions{
Source: "a = 1",
})
if err != nil {
panic(err)
}
fmt.Println(result)
}
38 changes: 38 additions & 0 deletions wasm/examples/go/pkg/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ type RunOptions struct {
Source string
}

type FmtOptions struct {
Source string
}

type KCLModule struct {
Instance *wasmtime.Instance
Store *wasmtime.Store
Memory *wasmtime.Memory
KclMalloc *wasmtime.Func
KclFree *wasmtime.Func
KclRun *wasmtime.Func
KclFmt *wasmtime.Func
}

func New(path string) (*KCLModule, error) {
Expand Down Expand Up @@ -55,13 +60,15 @@ func New(path string) (*KCLModule, error) {
malloc := instance.GetFunc(store, "kcl_malloc")
free := instance.GetFunc(store, "kcl_free")
run := instance.GetFunc(store, "kcl_run")
fmt := instance.GetFunc(store, "kcl_fmt")
return &KCLModule{
Instance: instance,
Store: store,
Memory: memory,
KclMalloc: malloc,
KclFree: free,
KclRun: run,
KclFmt: fmt,
}, nil
}

Expand Down Expand Up @@ -106,3 +113,34 @@ func (m *KCLModule) Run(opts *RunOptions) (string, error) {

return result, nil
}

func (m *KCLModule) Fmt(opts *FmtOptions) (string, error) {
sourcePtr, sourceLen, err := copyStringToWasmMemory(m.Store, m.KclMalloc, m.Memory, opts.Source)
if err != nil {
return "", err
}
defer func() {
err := freeMemory(m.Store, m.KclFree, sourcePtr, sourceLen)
if err != nil {
fmt.Println("Failed to free source memory:", err)
}
}()

resultPtr, err := m.KclFmt.Call(m.Store, sourcePtr)
if err != nil {
return "", err
}

result, _, err := copyCStrFromWasmMemory(m.Store, m.Memory, resultPtr.(int32))
if err != nil {
return "", err
}
defer func() {
err := freeMemory(m.Store, m.KclFree, resultPtr.(int32), int32(len(result)))
if err != nil {
fmt.Println("Failed to free result memory:", err)
}
}()

return result, nil
}
19 changes: 16 additions & 3 deletions wasm/examples/node/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { load, invokeKCLRun } from "@kcl-lang/wasm-lib";
import { load, invokeKCLRun, invokeKCLFmt } from "@kcl-lang/wasm-lib";

async function main() {
async function run() {
const inst = await load();
const result = invokeKCLRun(inst, {
filename: "test.k",
Expand All @@ -13,4 +13,17 @@ p = Person {name = "Alice"}`,
console.log(result);
}

main();
async function fmt() {
const inst = await load();
const result = invokeKCLFmt(inst, {
source: `
schema Person:
name: str

p = Person {name = "Alice"}`,
});
console.log(result);
}

run();
fmt();
58 changes: 55 additions & 3 deletions wasm/examples/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::path::Path;
#[cfg(test)]
mod tests;

use anyhow::Result;
use std::path::Path;
use wasmtime::*;
use wasmtime_wasi::{
preview1::{self, WasiP1Ctx},
Expand All @@ -19,18 +21,26 @@ impl State {
}
}

#[derive(Debug)]
pub struct RunOptions {
pub filename: String,
pub source: String,
}

#[derive(Debug)]
pub struct FmtOptions {
pub source: String,
}

pub struct KCLModule {
pub instance: Instance,
store: Store<State>,
memory: Memory,
malloc: TypedFunc<i32, i32>,
free: TypedFunc<(i32, i32), ()>,
run: TypedFunc<(i32, i32), i32>,
fmt: TypedFunc<i32, i32>,
runtime_err: TypedFunc<(i32, i32), i32>,
}

impl KCLModule {
Expand All @@ -54,13 +64,17 @@ impl KCLModule {
let malloc = instance.get_typed_func::<i32, i32>(&mut store, "kcl_malloc")?;
let free = instance.get_typed_func::<(i32, i32), ()>(&mut store, "kcl_free")?;
let run = instance.get_typed_func::<(i32, i32), i32>(&mut store, "kcl_run")?;
let fmt = instance.get_typed_func::<i32, i32>(&mut store, "kcl_fmt")?;
let runtime_err = instance.get_typed_func::<(i32, i32), i32>(&mut store, "kcl_runtime_err")?;
Ok(KCLModule {
instance,
store,
memory,
malloc,
free,
run,
fmt,
runtime_err,
})
}

Expand All @@ -70,10 +84,39 @@ impl KCLModule {
copy_string_to_wasm_memory(&mut self.store, &self.malloc, self.memory, &opts.filename)?;
let (source_ptr, source_len) =
copy_string_to_wasm_memory(&mut self.store, &self.malloc, self.memory, &opts.source)?;
let result_ptr = self.run.call(&mut self.store, (filename_ptr, source_ptr))?;
let runtime_err_len = 1024;
let (runtime_err_ptr, _) = malloc_bytes_from_wasm_memory(&mut self.store, &self.malloc, runtime_err_len)?;
let result_str = match self.run.call(&mut self.store, (filename_ptr, source_ptr)) {
Ok(result_ptr) => {
let (result_str, result_len) =
copy_cstr_from_wasm_memory(&mut self.store, self.memory, result_ptr as usize)?;
free_memory(&mut self.store, &self.free, result_ptr, result_len)?;
result_str
},
Err(err) => {
self.runtime_err.call(&mut self.store, (runtime_err_ptr, runtime_err_len))?;
let (runtime_err_str, runtime_err_len) =
copy_cstr_from_wasm_memory(&mut self.store, self.memory, runtime_err_ptr as usize)?;
free_memory(&mut self.store, &self.free, runtime_err_ptr, runtime_err_len)?;
if runtime_err_str.is_empty() {
return Err(err)
} else {
runtime_err_str
}
},
};
free_memory(&mut self.store, &self.free, filename_ptr, filename_len)?;
free_memory(&mut self.store, &self.free, source_ptr, source_len)?;
Ok(result_str)
}

/// Run with the wasm module and options.
pub fn fmt(&mut self, opts: &FmtOptions) -> Result<String> {
let (source_ptr, source_len) =
copy_string_to_wasm_memory(&mut self.store, &self.malloc, self.memory, &opts.source)?;
let result_ptr = self.fmt.call(&mut self.store, source_ptr)?;
let (result_str, result_len) =
copy_cstr_from_wasm_memory(&mut self.store, self.memory, result_ptr as usize)?;
free_memory(&mut self.store, &self.free, filename_ptr, filename_len)?;
free_memory(&mut self.store, &self.free, source_ptr, source_len)?;
free_memory(&mut self.store, &self.free, result_ptr, result_len)?;

Expand All @@ -98,6 +141,15 @@ fn copy_string_to_wasm_memory<T>(
Ok((ptr, length as usize))
}

fn malloc_bytes_from_wasm_memory<T>(
store: &mut Store<T>,
malloc: &TypedFunc<i32, i32>,
length: i32,
) -> Result<(i32, usize)> {
let ptr = malloc.call(&mut *store, length)?;
Ok((ptr, length as usize))
}

fn copy_cstr_from_wasm_memory<T>(
store: &mut Store<T>,
memory: Memory,
Expand Down
68 changes: 68 additions & 0 deletions wasm/examples/rust/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::{FmtOptions, KCLModule, RunOptions};
use anyhow::Result;

const WASM_PATH: &str = "../../kcl.wasm";
const BENCH_COUNT: usize = 20;

#[test]
fn test_run() -> Result<()> {
let opts = RunOptions {
filename: "test.k".to_string(),
source: "a = 1".to_string(),
};
let mut module = KCLModule::from_path(WASM_PATH)?;
for _ in 0..BENCH_COUNT {
let result = module.run(&opts)?;
println!("{}", result);
}
Ok(())
}

#[test]
fn test_run_parse_error() -> Result<()> {
let opts = RunOptions {
filename: "test.k".to_string(),
source: "a = ".to_string(),
};
let mut module = KCLModule::from_path(WASM_PATH)?;
let result = module.run(&opts)?;
println!("{}", result);
Ok(())
}

#[test]
fn test_run_type_error() -> Result<()> {
let opts = RunOptions {
filename: "test.k".to_string(),
source: "a: str = 1".to_string(),
};
let mut module = KCLModule::from_path(WASM_PATH)?;
let result = module.run(&opts)?;
println!("{}", result);
Ok(())
}

#[test]
fn test_run_runtime_error() -> Result<()> {
let opts = RunOptions {
filename: "test.k".to_string(),
source: "a = [][0]".to_string(),
};
let mut module = KCLModule::from_path(WASM_PATH)?;
let result = module.run(&opts)?;
println!("{}", result);
Ok(())
}

#[test]
fn test_fmt() -> Result<()> {
let opts = FmtOptions {
source: "a = 1".to_string(),
};
let mut module = KCLModule::from_path(WASM_PATH)?;
for _ in 0..BENCH_COUNT {
let result = module.fmt(&opts)?;
println!("{}", result);
}
Ok(())
}
11 changes: 11 additions & 0 deletions wasm/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Config } from "jest";

const config: Config = {
verbose: true,
transform: { "^.+\\.ts?$": "ts-jest" },
testEnvironment: "node",
testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};

export default config;
Binary file modified wasm/kcl.wasm
100644 → 100755
Binary file not shown.
Loading
Loading