adding test for embed

This commit is contained in:
Jakub Doka 2024-10-13 20:01:18 +02:00
parent 0f8a720fe8
commit af147b3cb6
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
10 changed files with 113 additions and 50 deletions

View file

@ -104,3 +104,14 @@ button {
button:hover:not(:active) { button:hover:not(:active) {
background: white; background: white;
} }
div#code-editor {
display: flex;
position: relative;
span#code-size {
position: absolute;
right: 2px;
font-size: 12px;
}
}

View file

@ -40,17 +40,19 @@ function compileCode(instance, code, fuel) {
/**@type{WebAssembly.Instance}*/ let fmtInstance; /**@type{WebAssembly.Instance}*/ let fmtInstance;
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let fmtInstaceFuture; /**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let fmtInstaceFuture;
/** @param {string} code @param {"fmt" | "minify"} action async function getFmtInstance() {
* @returns {Promise<string>} */
async function modifyCode(code, action) {
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {}); fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
fmtInstance ??= (await fmtInstaceFuture).instance; return fmtInstance ??= (await fmtInstaceFuture).instance;
}
/** @param {WebAssembly.Instance} instance @param {string} code @param {"fmt" | "minify"} action
* @returns {string | undefined} */
function modifyCode(instance, code, action) {
let { let {
INPUT, INPUT_LEN, INPUT, INPUT_LEN,
OUTPUT, OUTPUT_LEN, OUTPUT, OUTPUT_LEN,
memory, fmt, minify memory, fmt, minify
} = fmtInstance.exports; } = instance.exports;
if (!(true if (!(true
&& memory instanceof WebAssembly.Memory && memory instanceof WebAssembly.Memory
@ -71,15 +73,15 @@ async function modifyCode(code, action) {
dw.setUint32(INPUT_LEN.value, code.length, true); dw.setUint32(INPUT_LEN.value, code.length, true);
new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code)); new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code));
return runWasmFunction(fmtInstance, action === "fmt" ? fmt : minify) ? return runWasmFunction(instance, action === "fmt" ? fmt : minify) ?
bufToString(memory, OUTPUT, OUTPUT_LEN) : "invalid code"; bufToString(memory, OUTPUT, OUTPUT_LEN) : undefined;
} }
/** @param {WebAssembly.Instance} instance @param {CallableFunction} func @param {any[]} args /** @param {WebAssembly.Instance} instance @param {CallableFunction} func @param {any[]} args
* @returns {boolean} */ * @returns {boolean} */
function runWasmFunction(instance, func, ...args) { function runWasmFunction(instance, func, ...args) {
const prev = performance.now(); //const prev = performance.now();
const { PANIC_MESSAGE, PANIC_MESSAGE_LEN, memory, stack_pointer } = instance.exports; const { PANIC_MESSAGE, PANIC_MESSAGE_LEN, memory, stack_pointer } = instance.exports;
if (!(true if (!(true
&& memory instanceof WebAssembly.Memory && memory instanceof WebAssembly.Memory
@ -101,7 +103,7 @@ function runWasmFunction(instance, func, ...args) {
stack_pointer.value = ptr; stack_pointer.value = ptr;
return false; return false;
} finally { } finally {
console.log("compiletion took:", performance.now() - prev); //console.log("compiletion took:", performance.now() - prev);
} }
} }
@ -147,17 +149,22 @@ function wireUp(target) {
/** @type {{ [key: string]: (content: string) => Promise<string> | string }} */ /** @type {{ [key: string]: (content: string) => Promise<string> | string }} */
const applyFns = { const applyFns = {
timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(), timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(),
fmt: (content) => modifyCode(content, "fmt").then(c => c), fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"),
}; };
/** @param {HTMLElement} target */ /** @param {HTMLElement} target */
async function bindCodeEdit(target) { async function bindCodeEdit(target) {
const edit = target.querySelector("#code-edit"); const edit = target.querySelector("#code-edit");
if (!(edit instanceof HTMLTextAreaElement)) return; if (!(edit instanceof HTMLTextAreaElement)) return;
const codeSize = target.querySelector("#code-size");
if (!(codeSize instanceof HTMLSpanElement)) never();
const MAX_CODE_SIZE = parseInt(codeSize.innerHTML);
if (Number.isNaN(MAX_CODE_SIZE)) never();
const errors = target.querySelector("#compiler-output"); const errors = target.querySelector("#compiler-output");
if (!(errors instanceof HTMLPreElement)) never(); if (!(errors instanceof HTMLPreElement)) never();
const hbc = await getHbcInstance(); const hbc = await getHbcInstance();
const fmt = await getFmtInstance();
const debounce = 100; const debounce = 100;
let timeout = 0; let timeout = 0;
@ -166,9 +173,14 @@ async function bindCodeEdit(target) {
timeout = setTimeout(() => { timeout = setTimeout(() => {
const buf = packPosts([ const buf = packPosts([
{ path: "local.hb", code: edit.value }, { path: "local.hb", code: edit.value },
{ path: "lam.hb", code: "foo:=10" },
]); ]);
errors.textContent = compileCode(hbc, buf, 1); errors.textContent = compileCode(hbc, buf, 1);
const minified_size = modifyCode(fmt, edit.value, "minify")?.length;
if (minified_size) {
codeSize.textContent = (MAX_CODE_SIZE - minified_size) + "";
const perc = Math.min(100, Math.floor(100 * (minified_size / MAX_CODE_SIZE)));
codeSize.style.color = `color-mix(in srgb, white, var(--error) ${perc}%)`;
}
timeout = 0; timeout = 0;
}, debounce); }, debounce);
}); });
@ -246,8 +258,9 @@ if (window.location.hostname === 'localhost') {
(async function test() { (async function test() {
{ {
const code = "main:=fn():void{return}"; const code = "main:=fn():void{return}";
const fmtd = await modifyCode(code, "fmt") ?? never(); const inst = await getFmtInstance()
const prev = await modifyCode(fmtd, "minify") ?? never(); const fmtd = modifyCode(inst, code, "fmt") ?? never();
const prev = modifyCode(inst, fmtd, "minify") ?? never();
if (code != prev) console.error(code, prev); if (code != prev) console.error(code, prev);
} }
{ {

View file

@ -12,7 +12,7 @@ use {
const MAX_NAME_LENGTH: usize = 32; const MAX_NAME_LENGTH: usize = 32;
const MAX_POSTNAME_LENGTH: usize = 64; const MAX_POSTNAME_LENGTH: usize = 64;
//const MAX_CODE_LENGTH: usize = 1024 * 4; const MAX_CODE_LENGTH: usize = 1024 * 4;
const SESSION_DURATION_SECS: u64 = 60 * 60; const SESSION_DURATION_SECS: u64 = 60 * 60;
type Redirect<const COUNT: usize = 1> = AppendHeaders<[(&'static str, &'static str); COUNT]>; type Redirect<const COUNT: usize = 1> = AppendHeaders<[(&'static str, &'static str); COUNT]>;
@ -153,7 +153,11 @@ impl Page for Post {
<input name="author" type="text" value={session.name} hidden> <input name="author" type="text" value={session.name} hidden>
<input name="name" type="text" placeholder="name" value=name <input name="name" type="text" placeholder="name" value=name
required maxlength=MAX_POSTNAME_LENGTH> required maxlength=MAX_POSTNAME_LENGTH>
<textarea id="code-edit" name="code" placeholder="code" rows=1 required>code</textarea> <div id="code-editor">
<textarea id="code-edit" name="code" placeholder="code" rows=1 required
style="flex: 1">code</textarea>
<span id="code-size">MAX_CODE_LENGTH</span>
</div>
<input type="submit" value="submit"> <input type="submit" value="submit">
<pre id="compiler-output"></pre> <pre id="compiler-output"></pre>
</form> </form>

View file

@ -104,5 +104,5 @@ unsafe fn compile_and_run(mut fuel: usize) {
} }
} }
log::error!("memory consumption: {}b / {}b", ALLOCATOR.used(), ARENA_CAP); //log::error!("memory consumption: {}b / {}b", ALLOCATOR.used(), ARENA_CAP);
} }

View file

@ -251,6 +251,7 @@ main := fn(): int {
align_of_Type_in_bytes := @alignof(foo.Type) align_of_Type_in_bytes := @alignof(foo.Type)
hardcoded_pointer := @as(^u8, @bitcast(10)) hardcoded_pointer := @as(^u8, @bitcast(10))
ecall_that_returns_int := @as(int, @eca(1, foo.Type.(10, 20), 5, 6)) ecall_that_returns_int := @as(int, @eca(1, foo.Type.(10, 20), 5, 6))
embedded_array := @as([u8; 15], @embed("text.txt"))
return @inline(foo.foo) return @inline(foo.foo)
} }
@ -262,6 +263,9 @@ Type := struct {
} }
foo := fn(): int return 0 foo := fn(): int return 0
// in module: text.txt
arbitrary text
``` ```
- `@use(<string>)`: imports a module based of string, the string is passed to a loader that can be customized, default loader uses following syntax: - `@use(<string>)`: imports a module based of string, the string is passed to a loader that can be customized, default loader uses following syntax:

View file

@ -12,7 +12,7 @@ use {
TypedReloc, Types, HEADER_SIZE, TypedReloc, Types, HEADER_SIZE,
}, },
alloc::{boxed::Box, string::String, vec::Vec}, alloc::{boxed::Box, string::String, vec::Vec},
core::{fmt::Display, u16}, core::fmt::Display,
}; };
type Offset = u32; type Offset = u32;
@ -2843,8 +2843,9 @@ mod tests {
_ = log::set_logger(&crate::fs::Logger); _ = log::set_logger(&crate::fs::Logger);
log::set_max_level(log::LevelFilter::Debug); log::set_max_level(log::LevelFilter::Debug);
let mut codegen = let (files, embeds) = crate::test_parse_files(ident, input);
super::Codegen { files: crate::test_parse_files(ident, input), ..Default::default() }; let mut codegen = super::Codegen { files, ..Default::default() };
codegen.push_embeds(embeds);
codegen.generate(0); codegen.generate(0);
let mut out = Vec::new(); let mut out = Vec::new();

View file

@ -41,7 +41,6 @@ use {
core::{cell::Cell, ops::Range}, core::{cell::Cell, ops::Range},
hashbrown::hash_map, hashbrown::hash_map,
hbbytecode as instrs, hbbytecode as instrs,
std::println,
}; };
#[macro_use] #[macro_use]
@ -1081,7 +1080,6 @@ impl Types {
match entry { match entry {
hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value, hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value,
hash_map::RawEntryMut::Vacant(v) => { hash_map::RawEntryMut::Vacant(v) => {
println!("waht");
self.ins.ptrs.push(ptr); self.ins.ptrs.push(ptr);
v.insert( v.insert(
ctx_map::Key { ctx_map::Key {
@ -1345,7 +1343,7 @@ pub fn run_test(
} }
#[cfg(test)] #[cfg(test)]
fn test_parse_files(ident: &'static str, input: &'static str) -> Vec<parser::Ast> { fn test_parse_files(ident: &'static str, input: &'static str) -> (Vec<parser::Ast>, Vec<Vec<u8>>) {
use { use {
self::parser::FileKind, self::parser::FileKind,
std::{borrow::ToOwned, string::ToString}, std::{borrow::ToOwned, string::ToString},
@ -1377,32 +1375,51 @@ fn test_parse_files(ident: &'static str, input: &'static str) -> Vec<parser::Ast
let input = find_block(input, ident); let input = find_block(input, ident);
let mut module_map = Vec::new(); let mut module_map = Vec::new();
let mut embed_map = Vec::new();
let mut last_start = 0; let mut last_start = 0;
let mut last_module_name = "test"; let mut last_module_name = "test.hb";
for (i, m) in input.match_indices("// in module: ") { for (i, m) in input.match_indices("// in module: ") {
fmt::test::format(ident, input[last_start..i].trim()); if last_module_name.ends_with(".hb") {
module_map.push((last_module_name, &input[last_start..i])); fmt::test::format(ident, input[last_start..i].trim());
module_map.push((last_module_name, &input[last_start..i]));
} else {
embed_map.push((last_module_name, &input[last_start..i]));
}
let (module_name, _) = input[i + m.len()..].split_once('\n').unwrap(); let (module_name, _) = input[i + m.len()..].split_once('\n').unwrap();
last_module_name = module_name; last_module_name = module_name;
last_start = i + m.len() + module_name.len() + 1; last_start = i + m.len() + module_name.len() + 1;
} }
fmt::test::format(ident, input[last_start..].trim());
module_map.push((last_module_name, input[last_start..].trim()));
let mut loader = |path: &str, _: &str, kind| { if last_module_name.ends_with(".hb") {
assert_eq!(kind, FileKind::Module); fmt::test::format(ident, input[last_start..].trim());
module_map module_map.push((last_module_name, &input[last_start..]));
} else {
embed_map.push((last_module_name, &input[last_start..]));
}
let mut loader = |path: &str, _: &str, kind| match kind {
FileKind::Module => module_map
.iter() .iter()
.position(|&(name, _)| name == path) .position(|&(name, _)| name == path)
.map(|i| i as parser::FileId) .map(|i| i as parser::FileId)
.ok_or("Not Found".to_string()) .ok_or("Module Not Found".to_string()),
FileKind::Embed => embed_map
.iter()
.position(|&(name, _)| name == path)
.map(|i| i as parser::FileId)
.ok_or("Embed Not Found".to_string()),
}; };
let mut ctx = parser::ParserCtx::default(); let mut ctx = parser::ParserCtx::default();
module_map (
.iter() module_map
.map(|&(path, content)| parser::Ast::new(path, content.to_owned(), &mut ctx, &mut loader)) .iter()
.collect() .map(|&(path, content)| {
parser::Ast::new(path, content.to_owned(), &mut ctx, &mut loader)
})
.collect(),
embed_map.iter().map(|&(_, content)| content.to_owned().into_bytes()).collect(),
)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -2861,8 +2861,8 @@ mod tests {
_ = log::set_logger(&crate::fs::Logger); _ = log::set_logger(&crate::fs::Logger);
log::set_max_level(log::LevelFilter::Info); log::set_max_level(log::LevelFilter::Info);
let mut codegen = let (files, _embeds) = crate::test_parse_files(ident, input);
super::Codegen { files: crate::test_parse_files(ident, input), ..Default::default() }; let mut codegen = super::Codegen { files, ..Default::default() };
codegen.generate(); codegen.generate();

View file

@ -1,6 +1,6 @@
main: main:
ADDI64 r254, r254, -88d ADDI64 r254, r254, -96d
ST r31, r254, 16a, 72h ST r31, r254, 16a, 80h
LI64 r32, 10d LI64 r32, 10d
LI64 r33, 30d LI64 r33, 30d
LI64 r34, 40d LI64 r34, 40d
@ -18,11 +18,13 @@ main:
LI64 r6, 6d LI64 r6, 6d
ECA ECA
CP r39, r1 CP r39, r1
LRA r40, r0, :arbitrary text
LI64 r1, 0d LI64 r1, 0d
LD r31, r254, 16a, 72h LD r31, r254, 16a, 80h
ADDI64 r254, r254, 88d ADDI64 r254, r254, 96d
JALA r0, r31, 0a JALA r0, r31, 0a
ev: Ecall ev: Ecall
code size: 233 code size: 255
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -6,20 +6,31 @@ drop:
ADDI64 r254, r254, 16d ADDI64 r254, r254, 16d
JALA r0, r31, 0a JALA r0, r31, 0a
main: main:
ADDI64 r254, r254, -40d ADDI64 r254, r254, -48d
ST r31, r254, 8a, 32h ST r31, r254, 8a, 40h
LI64 r32, 1d LI64 r32, 1d
ST r32, r254, 0a, 8h ST r32, r254, 0a, 8h
ADDI64 r32, r254, 0d ADDI64 r32, r254, 0d
LI64 r33, 1000d
CP r34, r32
CP r35, r33
MULI64 r35, r35, 8d
ADD64 r34, r34, r35
ADDI64 r32, r34, -16d
CP r34, r33
ADDI64 r34, r34, -2d
CP r35, r34
MULI64 r35, r35, 8d
SUB64 r32, r32, r35
CP r2, r32 CP r2, r32
JAL r31, r0, :modify JAL r31, r0, :modify
LD r2, r254, 0a, 8h LD r2, r254, 0a, 8h
JAL r31, r0, :drop JAL r31, r0, :drop
CP r33, r32 CP r34, r32
LD r34, r33, 0a, 8h LD r35, r34, 0a, 8h
ADDI64 r1, r34, -2d ADDI64 r1, r35, -2d
LD r31, r254, 8a, 32h LD r31, r254, 8a, 40h
ADDI64 r254, r254, 40d ADDI64 r254, r254, 48d
JALA r0, r31, 0a JALA r0, r31, 0a
modify: modify:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
@ -31,6 +42,6 @@ modify:
LD r31, r254, 0a, 32h LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 308 code size: 382
ret: 0 ret: 0
status: Ok(()) status: Ok(())