Skip to content

Commit ff53e25

Browse files
authored
Merge pull request #142 from Peefy/fix-wasm-c-str-mem-alloc
fix: wasm c str memory alloc on the end \0 char
2 parents cb0cf3b + 008b6cb commit ff53e25

File tree

7 files changed

+56
-33
lines changed

7 files changed

+56
-33
lines changed

.github/workflows/wasm-test.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ jobs:
5757

5858
- name: Go example e2e tests
5959
run: cd examples/go && go mod tidy && go run main.go
60+
61+
- name: Node.js example e2e tests
62+
run: cd examples/node && npm install && npm run build
63+
64+
- name: Browser example e2e tests
65+
run: cd examples/browser && npm install && npm run build
6066

6167
- name: Publish Dry Run
6268
if: "startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-')"

wasm/examples/browser/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
],
1616
"dependencies": {
1717
"@kcl-lang/wasm-lib": "file:../..",
18-
"@wasmer/wasi": "^1.2.2"
18+
"@wasmer/wasi": "^1.2.2",
19+
"buffer": "^6.0.3"
1920
},
2021
"devDependencies": {
2122
"copy-webpack-plugin": "^8.1.1",

wasm/examples/go/pkg/module/utils.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ func copyStringToWasmMemory(
1010
) (int32, int32, error) {
1111
bytes := []byte(str)
1212
length := len(bytes)
13-
ptr, err := malloc.Call(store, int32(length))
13+
// C str '\0'
14+
ptr, err := malloc.Call(store, int32(length)+1)
1415
if err != nil {
1516
return 0, 0, err
1617
}
1718
data := memory.UnsafeData(store)
1819
idx := ptr.(int32)
1920
copy(data[idx:(int(idx)+length)], bytes)
21+
// C str '\0'
22+
data[int(idx)+length] = 0
2023
return idx, int32(length), nil
2124
}
2225

wasm/examples/rust/src/lib.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ impl KCLModule {
6565
let free = instance.get_typed_func::<(i32, i32), ()>(&mut store, "kcl_free")?;
6666
let run = instance.get_typed_func::<(i32, i32), i32>(&mut store, "kcl_run")?;
6767
let fmt = instance.get_typed_func::<i32, i32>(&mut store, "kcl_fmt")?;
68-
let runtime_err = instance.get_typed_func::<(i32, i32), i32>(&mut store, "kcl_runtime_err")?;
68+
let runtime_err =
69+
instance.get_typed_func::<(i32, i32), i32>(&mut store, "kcl_runtime_err")?;
6970
Ok(KCLModule {
7071
instance,
7172
store,
@@ -85,25 +86,35 @@ impl KCLModule {
8586
let (source_ptr, source_len) =
8687
copy_string_to_wasm_memory(&mut self.store, &self.malloc, self.memory, &opts.source)?;
8788
let runtime_err_len = 1024;
88-
let (runtime_err_ptr, _) = malloc_bytes_from_wasm_memory(&mut self.store, &self.malloc, runtime_err_len)?;
89+
let (runtime_err_ptr, _) =
90+
malloc_bytes_from_wasm_memory(&mut self.store, &self.malloc, runtime_err_len)?;
8991
let result_str = match self.run.call(&mut self.store, (filename_ptr, source_ptr)) {
9092
Ok(result_ptr) => {
9193
let (result_str, result_len) =
9294
copy_cstr_from_wasm_memory(&mut self.store, self.memory, result_ptr as usize)?;
9395
free_memory(&mut self.store, &self.free, result_ptr, result_len)?;
9496
result_str
95-
},
97+
}
9698
Err(err) => {
97-
self.runtime_err.call(&mut self.store, (runtime_err_ptr, runtime_err_len))?;
98-
let (runtime_err_str, runtime_err_len) =
99-
copy_cstr_from_wasm_memory(&mut self.store, self.memory, runtime_err_ptr as usize)?;
100-
free_memory(&mut self.store, &self.free, runtime_err_ptr, runtime_err_len)?;
99+
self.runtime_err
100+
.call(&mut self.store, (runtime_err_ptr, runtime_err_len))?;
101+
let (runtime_err_str, runtime_err_len) = copy_cstr_from_wasm_memory(
102+
&mut self.store,
103+
self.memory,
104+
runtime_err_ptr as usize,
105+
)?;
106+
free_memory(
107+
&mut self.store,
108+
&self.free,
109+
runtime_err_ptr,
110+
runtime_err_len,
111+
)?;
101112
if runtime_err_str.is_empty() {
102-
return Err(err)
113+
return Err(err);
103114
} else {
104115
runtime_err_str
105116
}
106-
},
117+
}
107118
};
108119
free_memory(&mut self.store, &self.free, filename_ptr, filename_len)?;
109120
free_memory(&mut self.store, &self.free, source_ptr, source_len)?;
@@ -133,21 +144,25 @@ fn copy_string_to_wasm_memory<T>(
133144
let bytes = string.as_bytes();
134145
let length = bytes.len();
135146

136-
let ptr = malloc.call(&mut *store, length as i32)?;
147+
// C str '\0'
148+
let ptr = malloc.call(&mut *store, length as i32 + 1)?;
137149

138150
let data = memory.data_mut(&mut *store);
139151
data[ptr as usize..(ptr as usize + length as usize)].copy_from_slice(bytes);
152+
// C str '\0'
153+
data[ptr as usize + length] = 0;
140154

141-
Ok((ptr, length as usize))
155+
Ok((ptr, length as usize + 1))
142156
}
143157

144158
fn malloc_bytes_from_wasm_memory<T>(
145159
store: &mut Store<T>,
146160
malloc: &TypedFunc<i32, i32>,
147161
length: i32,
148162
) -> Result<(i32, usize)> {
149-
let ptr = malloc.call(&mut *store, length)?;
150-
Ok((ptr, length as usize))
163+
// C str '\0'
164+
let ptr = malloc.call(&mut *store, length + 1)?;
165+
Ok((ptr, length as usize + 1))
151166
}
152167

153168
fn copy_cstr_from_wasm_memory<T>(

wasm/examples/rust/src/tests.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::{FmtOptions, KCLModule, RunOptions};
22
use anyhow::Result;
33

44
const WASM_PATH: &str = "../../kcl.wasm";
5-
const BENCH_COUNT: usize = 20;
65
const SOURCES: &[&str] = &[
76
r#"apiVersion = "apps/v1"
87
kind = "Deployment"
@@ -205,23 +204,25 @@ fn test_run() -> Result<()> {
205204
source: "a = 1".to_string(),
206205
};
207206
let mut module = KCLModule::from_path(WASM_PATH)?;
208-
for _ in 0..BENCH_COUNT {
209-
let result = module.run(&opts)?;
210-
println!("{}", result);
211-
}
207+
let result = module.run(&opts)?;
208+
println!("{}", result);
212209
Ok(())
213210
}
214211

215212
#[test]
216213
fn test_run_examples() -> Result<()> {
214+
// Singleton WASM instance
215+
let mut module = KCLModule::from_path(WASM_PATH)?;
217216
for source in SOURCES {
218217
let opts = RunOptions {
219218
filename: "test.k".to_string(),
220219
source: source.to_string(),
221220
};
222-
let mut module = KCLModule::from_path(WASM_PATH)?;
223221
let result = module.run(&opts)?;
224-
assert!(!result.starts_with("ERROR:"), "source: {source}. result: {result}");
222+
assert!(
223+
!result.starts_with("ERROR:"),
224+
"source: {source}. result: {result}"
225+
);
225226
}
226227
Ok(())
227228
}
@@ -268,9 +269,7 @@ fn test_fmt() -> Result<()> {
268269
source: "a = 1".to_string(),
269270
};
270271
let mut module = KCLModule::from_path(WASM_PATH)?;
271-
for _ in 0..BENCH_COUNT {
272-
let result = module.fmt(&opts)?;
273-
println!("{}", result);
274-
}
272+
let result = module.fmt(&opts)?;
273+
println!("{}", result);
275274
Ok(())
276275
}

wasm/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function copyStringToWasmMemory(
180180
const buffer = new Uint8Array(
181181
exports.memory.buffer,
182182
pointer,
183-
encodedString.length
183+
encodedString.length + 1
184184
);
185185
buffer.set(encodedString);
186186
buffer[encodedString.length] = 0; // Null-terminate the string

wasm/tests/run.test.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,12 @@ nginx = Nginx {
271271
];
272272

273273
test("run example tests", async () => {
274+
const inst = await load();
274275
SNIPPETS.forEach((snippet) => {
275-
load().then((inst) => {
276-
const result = invokeKCLRun(inst, {
277-
filename: "test.k",
278-
source: snippet.value,
279-
});
280-
expect(result).not.toContain("ERROR:");
276+
const result = invokeKCLRun(inst, {
277+
filename: "test.k",
278+
source: snippet.value,
281279
});
280+
expect(result).not.toContain("ERROR:");
282281
});
283282
});

0 commit comments

Comments
 (0)