From c9b85f9004b7a5d4a4cad68bdf4eb2c1e75d811e Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Sun, 13 Oct 2024 22:24:57 +0200 Subject: [PATCH] fixing sizeof not storing values --- depell/src/index.js | 123 +++++++++++++++++++++++++++++++++----------- lang/README.md | 2 +- lang/src/codegen.rs | 4 +- lang/src/lib.rs | 1 - 4 files changed, 96 insertions(+), 34 deletions(-) diff --git a/depell/src/index.js b/depell/src/index.js index 2cd75fa..f49e5d9 100644 --- a/depell/src/index.js +++ b/depell/src/index.js @@ -12,9 +12,9 @@ async function getHbcInstance() { const stack_pointer_offset = 1 << 20; -/** @param {WebAssembly.Instance} instance @param {Uint8Array} code @param {number} fuel +/** @param {WebAssembly.Instance} instance @param {Post[]} packages @param {number} fuel * @returns {string} */ -function compileCode(instance, code, fuel) { +function compileCode(instance, packages, fuel) { let { INPUT, INPUT_LEN, LOG_MESSAGES, LOG_MESSAGES_LEN, @@ -30,9 +30,8 @@ function compileCode(instance, code, fuel) { && typeof compile_and_run === "function" )) never(); - const dw = new DataView(memory.buffer); - dw.setUint32(INPUT_LEN.value, code.length, true); - new Uint8Array(memory.buffer, INPUT.value).set(code); + const codeLength = packPosts(packages, new DataView(memory.buffer, INPUT.value)); + new DataView(memory.buffer).setUint32(INPUT_LEN.value, codeLength, true); runWasmFunction(instance, compile_and_run, fuel); return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN); @@ -111,19 +110,16 @@ function runWasmFunction(instance, func, ...args) { * @property {string} path * @property {string} code */ -/** @param {Post[]} posts @returns {Uint8Array} */ -function packPosts(posts) { - let len = 0; for (const post of posts) len += 2 + post.path.length + 2 + post.code.length; - - const buf = new Uint8Array(len), view = new DataView(buf.buffer), enc = new TextEncoder(); - len = 0; for (const post of posts) { +/** @param {Post[]} posts @param {DataView} view @returns {number} */ +function packPosts(posts, view) { + const enc = new TextEncoder(), buf = new Uint8Array(view.buffer, view.byteOffset); + let len = 0; for (const post of posts) { view.setUint16(len, post.path.length, true); len += 2; buf.set(enc.encode(post.path), len); len += post.path.length; view.setUint16(len, post.code.length, true); len += 2; buf.set(enc.encode(post.code), len); len += post.code.length; } - - return buf; + return len; } /** @param {WebAssembly.Memory} mem @@ -146,35 +142,98 @@ function wireUp(target) { bindCodeEdit(target); } -/** @type {{ [key: string]: (content: string) => Promise | string }} */ -const applyFns = { - timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(), - fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"), -}; +const importRe = /@use\s*\(\s*"(([^"]|\\")+)"\s*\)/g; +/** @param {string} code @param {string[]} matches @returns {string[]} */ +function findImports(code, matches) { + matches.length = 0; + for (const match of code.matchAll(importRe)) { + matches.push(match[1]); + } + + matches.sort(); + + let c = 0; + for (let i = 1; i < matches.length; i++) { + if (matches[c] != matches[i]) { + matches[++c] = matches[i]; + } + } + matches.length = Math.min(matches.length, c + 1); + + return matches; +} + /** @param {HTMLElement} target */ async function bindCodeEdit(target) { const edit = target.querySelector("#code-edit"); if (!(edit instanceof HTMLTextAreaElement)) return; + const codeSize = target.querySelector("#code-size"); - if (!(codeSize instanceof HTMLSpanElement)) never(); + const errors = target.querySelector("#compiler-output"); + if (!(true + && codeSize instanceof HTMLSpanElement + && errors instanceof HTMLPreElement + )) never(); + const MAX_CODE_SIZE = parseInt(codeSize.innerHTML); if (Number.isNaN(MAX_CODE_SIZE)) never(); - const errors = target.querySelector("#compiler-output"); - if (!(errors instanceof HTMLPreElement)) never(); - - const hbc = await getHbcInstance(); - const fmt = await getFmtInstance(); + const hbc = await getHbcInstance(), fmt = await getFmtInstance(); + const prevImports = []; + const matches = []; + /**@type{Post[]}*/ + const packages = [{ path: "local.hb", code: "" }]; const debounce = 100; + /**@type{AbortController|undefined}*/ + let cancelation = undefined; let timeout = 0; + edit.addEventListener("input", () => { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { - const buf = packPosts([ - { path: "local.hb", code: edit.value }, - ]); - errors.textContent = compileCode(hbc, buf, 1); + prevImports.length = 0; prevImports.push(...matches); + const imports = findImports(edit.value, matches); + let changed = imports.length !== prevImports.length; + for (let i = 0; i < imports.length && !changed; i++) { + changed ||= imports[i] !== prevImports[i]; + } + + if (changed && imports.length !== 0) { + if (cancelation) cancelation.abort(); + cancelation = new AbortController(); + errors.textContent = "fetching: " + imports.join(", "); + fetch(`/code`, { + method: "POST", + signal: cancelation.signal, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(imports), + }).then(async e => { + try { + const json = await e.json(); + if (e.status == 200) { + packages.length = 1; + packages.push(...json); + cancelation = undefined; + edit.dispatchEvent(new InputEvent("input")); + } else { + errors.textContent = "failed to fetch: " + json.join(", "); + } + } catch (er) { + errors.textContent = "completely failed to fetch (" + + e.status + "): " + imports.join(", "); + console.error(e); + } + }); + } + + if (cancelation && imports.length !== 0) { + return; + } + + packages[0].code = edit.value; + + errors.textContent = compileCode(hbc, packages, 1); const minified_size = modifyCode(fmt, edit.value, "minify")?.length; if (minified_size) { codeSize.textContent = (MAX_CODE_SIZE - minified_size) + ""; @@ -187,6 +246,11 @@ async function bindCodeEdit(target) { edit.dispatchEvent(new InputEvent("input")); } +/** @type {{ [key: string]: (content: string) => Promise | string }} */ +const applyFns = { + timestamp: (content) => new Date(parseInt(content) * 1000).toLocaleString(), + fmt: (content) => getFmtInstance().then(i => modifyCode(i, content, "fmt") ?? "invalid code"), +}; /** @param {HTMLElement} target */ function execApply(target) { for (const elem of target.querySelectorAll('[apply]')) { @@ -268,8 +332,7 @@ if (window.location.hostname === 'localhost') { path: "foo.hb", code: "main:=fn():int{return 42}", }]; - const buf = packPosts(posts); - const res = compileCode(await getHbcInstance(), buf, 1) ?? never(); + const res = compileCode(await getHbcInstance(), posts, 1) ?? never(); const expected = "exit code: 42\n"; if (expected != res) console.error(expected, res); } diff --git a/lang/README.md b/lang/README.md index c304781..badae54 100644 --- a/lang/README.md +++ b/lang/README.md @@ -174,7 +174,7 @@ main := fn(): int { return 9001 } - finst := Ty2.{ty: .{a: 4, b: 1}, c: 3} + finst := Ty2.{ty: .{a: @bitcast(@sizeof(u32)), b: 1}, c: 3} inst := odher_pass(finst) if inst.c == 3 { return pass(&inst.ty) diff --git a/lang/src/codegen.rs b/lang/src/codegen.rs index 926f127..4429c21 100644 --- a/lang/src/codegen.rs +++ b/lang/src/codegen.rs @@ -1004,11 +1004,11 @@ impl Codegen { } E::Directive { name: "sizeof", args: [ty], .. } => { let ty = self.ty(ty); - return Some(Value::imm(self.tys.size_of(ty) as _)); + Some(Value::imm(self.tys.size_of(ty) as _)) } E::Directive { name: "alignof", args: [ty], .. } => { let ty = self.ty(ty); - return Some(Value::imm(self.tys.align_of(ty) as _)); + Some(Value::imm(self.tys.align_of(ty) as _)) } E::Directive { name: "intcast", args: [val], .. } => { let Some(ty) = ctx.ty else { diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 4cda36e..0c73da3 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -1389,7 +1389,6 @@ fn test_parse_files(ident: &'static str, input: &'static str) -> (Vec