@live-codes/odin-wasm 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -1
- package/assets/odin.js +2302 -0
- package/dist/odin-wasm.global.js +6 -6
- package/package.json +1 -1
- package/scripts/sync-assets.mjs +12 -4
- package/src/api.js +23 -0
- package/src/asset-receipts.js +2 -1
- package/src/engine.js +29 -7
package/dist/odin-wasm.global.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var odinWasm=(()=>{var W=Object.defineProperty;var
|
|
2
|
-
`);i>=0&&((r===1?f:
|
|
3
|
-
`).map(e=>e.trimEnd()).filter(e=>e.trim()!=="");function
|
|
4
|
-
`),
|
|
5
|
-
`),
|
|
6
|
-
`,output:""},
|
|
1
|
+
var odinWasm=(()=>{var W=Object.defineProperty;var gt=Object.getOwnPropertyDescriptor;var mt=Object.getOwnPropertyNames;var St=Object.prototype.hasOwnProperty;var wt=(t,e)=>{for(var n in e)W(t,n,{get:e[n],enumerable:!0})},_t=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let f of mt(e))!St.call(t,f)&&f!==n&&W(t,f,{get:()=>e[f],enumerable:!(a=gt(e,f))||a.enumerable});return t};var yt=t=>_t(W({},"__esModule",{value:!0}),t);var $t={};wt($t,{TARGETS:()=>Yt,createCompiler:()=>Lt});var V=Object.freeze({"odin.wasm":Object.freeze({bytes:3763439,sha256:"5044e383aa2415927cd518a4d3e6fdbcc6c99154ac90d212e154f18c092deef3"}),"odin-src.pack.gz":Object.freeze({bytes:2502169,sha256:"567b1c0c1b50d3e557d119927ade3373dda91364acae3302f2cf01950a8dd8f9"}),"odin.js":Object.freeze({bytes:72772,sha256:"e918ba1614af23f02299976122d5edf4c994873ce4c6cdab621d42f3e9cb9733"})});var Gt=Object.freeze(Object.keys(V)),Ut="baseUrl is required here. The assets that ship in this package can only be read where there is a filesystem, and a browser cannot reach a file inside an npm package \u2014 copy them somewhere your page can fetch with `npx --package @live-codes/odin-wasm odin-wasm-copy-assets <dir>` and pass that directory as baseUrl.";function et(t,e){if(t.baseUrl!=null&&t.baseUrl!=="")return At(t);if(!e)throw new Error(Ut);return Ct(e)}function At(t){let e=Tt(t.baseUrl);return{kind:"hosted",key:`hosted\0${e}`,description:e,baseUrl:e,async readAsset(n,a){let f=new URL(n,e),h=await fetch(f);if(!h.ok)throw new Error(`Failed to load the runtime asset ${f}: ${h.status}`);return rt(n,await bt(h,a))}}}function Ct(t){return{kind:"packaged",key:`packaged\0${t.root.href}`,description:`the assets packaged with this library (${t.root.href})`,async readAsset(e){let n;try{n=await t.readFile(e)}catch(a){throw new Error(`Failed to read the packaged asset ${e} from ${t.root.href}: ${a.message}`,{cause:a})}return rt(e,n)}}}function Tt(t){let e;try{e=t instanceof URL?t:new URL(t,globalThis.document?.baseURI)}catch(n){throw new Error(`baseUrl must be an absolute http(s) URL, or relative to the page in a browser: ${n.message}`,{cause:n})}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new Error(`baseUrl must be an absolute http(s) URL, or relative to the page in a browser: got "${t}"`);return e.href.endsWith("/")?e.href:`${e.href}/`}async function bt(t,e){if(!e||!t.body)return new Uint8Array(await t.arrayBuffer());let n=Number(t.headers.get("content-length"))||0,a=t.body.getReader(),f=[],h=0;for(e(0,n);;){let{done:c,value:g}=await a.read();if(c)break;f.push(g),h+=g.length,e(h,n)}let m=new Uint8Array(h),y=0;for(let c of f)m.set(c,y),y+=c.length;return m}async function rt(t,e){let n=V[t];if(!n)throw new Error(`No pinned receipt for the runtime asset ${t}`);if(e.byteLength!==n.bytes)throw new Error(`The runtime asset ${t} is ${e.byteLength} bytes, expected ${n.bytes}`);let a=await xt(e);if(a!==n.sha256)throw new Error(`The runtime asset ${t} failed SHA-256 verification: expected ${n.sha256}, got ${a}`);return e}async function xt(t){let e=globalThis.crypto?.subtle;if(!e)throw new Error("Verifying the runtime assets needs crypto.subtle: a secure context in the browser, or Node 20 and later.");let n=await e.digest("SHA-256",t);return[...new Uint8Array(n)].map(a=>a.toString(16).padStart(2,"0")).join("")}async function nt(t){if(typeof DecompressionStream!="function")throw new Error("Inflating the standard library pack needs DecompressionStream");let e=new Blob([t]).stream().pipeThrough(new DecompressionStream("gzip"));return new Uint8Array(await new Response(e).arrayBuffer())}var B=class extends Error{constructor(e){super(`WASI process exited with code ${e}`),this.code=e}},R=t=>{let e=[];for(let n of String(t).split("/"))if(!(n===""||n===".")){if(n===".."){e.pop();continue}e.push(n)}return e.join("/")},H=t=>{let e=t.lastIndexOf("/");return e<0?"":t.slice(0,e)},M=new TextEncoder,kt=new TextDecoder,v=class{constructor(){this.files=new Map,this.dirs=new Set([""])}mkdirAll(e){let n=R(e)===""?[]:R(e).split("/"),a="";for(let f of n)a=a===""?f:`${a}/${f}`,this.dirs.add(a)}writeFile(e,n){let a=R(e);this.mkdirAll(H(a));let f=n instanceof Uint8Array?n:new Uint8Array(n);this.files.set(a,{data:f,size:f.length})}readFile(e){let n=this.files.get(R(e));return n?n.data.subarray(0,n.size):null}exists(e){let n=R(e);return this.files.has(n)||this.dirs.has(n)}listDir(e){let n=R(e),a=n===""?"":`${n}/`,f=[];for(let h of this.dirs)h!==""&&h.startsWith(a)&&h.indexOf("/",a.length)<0&&h!==n&&f.push({name:h.slice(a.length),isDir:!0});for(let h of this.files.keys())h.startsWith(a)&&h.indexOf("/",a.length)<0&&f.push({name:h.slice(a.length),isDir:!1});return f.sort((h,m)=>h.name<m.name?-1:h.name>m.name?1:0),f}},Ft=t=>t instanceof Uint8Array?t:new Uint8Array(t),X=(t,e={})=>{let n=e.args??[],a=e.env??[],f=e.stdout??(()=>{}),h=e.stderr??(()=>{}),m=Ft(M.encode(e.stdin??"")),y=0,c=new Map,g=4;c.set(0,{kind:"stdin"}),c.set(1,{kind:"stdout"}),c.set(2,{kind:"stderr"}),c.set(3,{kind:"dir",path:""});let S=["","",""],u=null,E=()=>new Uint8Array(u.buffer),l=()=>new DataView(u.buffer),_=(r,o)=>kt.decode(E().subarray(r,r+o)),F=(r,o)=>{let s=S[r]+o,i=s.lastIndexOf(`
|
|
2
|
+
`);i>=0&&((r===1?f:h)(s.slice(0,i+1)),s=s.slice(i+1)),S[r]=s},O=()=>{for(let r of[1,2])S[r]!==""&&((r===1?f:h)(S[r]),S[r]="")},k=(r,o,s)=>{let i=l();i.setBigUint64(r+0,0n,!0),i.setBigUint64(r+8,0n,!0),i.setUint8(r+16,o),i.setBigUint64(r+24,1n,!0),i.setBigUint64(r+32,BigInt(s),!0),i.setBigUint64(r+40,0n,!0),i.setBigUint64(r+48,0n,!0),i.setBigUint64(r+56,0n,!0)},C=(r,o)=>{if(t.dirs.has(r))return k(o,3,0),0;let s=t.files.get(r);return s?(k(o,4,s.size),0):44},P=(r,o)=>{if(o<=r.data.length)return;let s=Math.max(r.data.length*2,4096);for(;s<o;)s*=2;let i=new Uint8Array(s);i.set(r.data.subarray(0,r.size)),r.data=i},D=(r,o,s,i)=>{let d=l(),p=E(),w=0;for(let A=0;A<s;A++){let T=d.getUint32(o+A*8,!0),U=d.getUint32(o+A*8+4,!0),b=Math.max(0,Math.min(U,r.size-i));if(p.set(r.data.subarray(i,i+b),T),i+=b,w+=b,b<U)break}return w},Z=(r,o,s,i)=>{let d=l(),p=E(),w=0;for(let A=0;A<s;A++){let T=d.getUint32(o+A*8,!0),U=d.getUint32(o+A*8+4,!0);P(r,i+U),r.data.set(p.subarray(T,T+U),i),i+=U,r.size=Math.max(r.size,i),w+=U}return w},tt=(r,o,s)=>{let i=l(),d=E(),p=s;for(let w=0;w<r.length;w++){let A=M.encode(r[w]);i.setUint32(o+w*4,p,!0),d.set(A,p),d[p+A.length]=0,p+=A.length+1}return 0},$=(r,o,s)=>{let i=c.get(r);if(!i||i.kind!=="dir")return null;let d=_(o,s);return R(d.startsWith("/")?d:`${i.path}/${d}`)};return{imports:{args_sizes_get(r,o){let s=l();return s.setUint32(r,n.length,!0),s.setUint32(o,n.reduce((i,d)=>i+M.encode(d).length+1,0),!0),0},args_get(r,o){return tt(n,r,o)},environ_sizes_get(r,o){let s=l();return s.setUint32(r,a.length,!0),s.setUint32(o,a.reduce((i,d)=>i+M.encode(d).length+1,0),!0),0},environ_get(r,o){return tt(a,r,o)},clock_res_get(r,o){return l().setBigUint64(o,1000n,!0),0},clock_time_get(r,o,s){let i=r===0?BigInt(Date.now())*1000000n:BigInt(Math.round((globalThis.performance?.now()??Date.now())*1e3))*1000n;return l().setBigUint64(s,i,!0),0},fd_close(r){return c.has(r)?(r>3&&c.delete(r),0):8},fd_fdstat_get(r,o){let s=c.get(r);if(!s)return 8;let i=l(),d=s.kind==="dir"?3:s.kind==="file"?4:2;return i.setUint8(o,d),i.setUint16(o+2,0,!0),i.setBigUint64(o+8,0xffffffffffffffffn,!0),i.setBigUint64(o+16,0xffffffffffffffffn,!0),0},fd_filestat_get(r,o){let s=c.get(r);return s?(s.kind==="dir"?k(o,3,0):s.kind==="file"?k(o,4,s.file.size):k(o,2,0),0):8},fd_filestat_set_size(r,o){let s=c.get(r);if(!s||s.kind!=="file")return 8;let i=Number(o);return P(s.file,i),i>s.file.size&&s.file.data.fill(0,s.file.size,i),s.file.size=i,0},fd_prestat_get(r,o){if(r!==3)return 8;let s=l();return s.setUint8(o,0),s.setUint32(o+4,1,!0),0},fd_prestat_dir_name(r,o,s){return r!==3?8:(s>=1&&(E()[o]=47),0)},fd_read(r,o,s,i){let d=c.get(r);if(!d)return 8;if(d.kind==="stdin"){let w=D({data:m,size:m.length},o,s,y);return y+=w,l().setUint32(i,w,!0),0}if(d.kind!=="file")return 8;let p=D(d.file,o,s,d.offset);return d.offset+=p,l().setUint32(i,p,!0),0},fd_pread(r,o,s,i,d){let p=c.get(r);if(!p||p.kind!=="file")return 8;let w=D(p.file,o,s,Number(i));return l().setUint32(d,w,!0),0},fd_write(r,o,s,i){let d=c.get(r);if(!d)return 8;if(d.kind==="stdout"||d.kind==="stderr"){let w=l(),A=0;for(let T=0;T<s;T++){let U=w.getUint32(o+T*8,!0),b=w.getUint32(o+T*8+4,!0);F(r,_(U,b)),A+=b}return w.setUint32(i,A,!0),0}if(d.kind!=="file")return 8;let p=Z(d.file,o,s,d.offset);return d.offset+=p,l().setUint32(i,p,!0),0},fd_pwrite(r,o,s,i,d){let p=c.get(r);if(!p||p.kind!=="file")return 8;let w=Z(p.file,o,s,Number(i));return l().setUint32(d,w,!0),0},fd_seek(r,o,s,i){let d=c.get(r);if(!d||d.kind!=="file")return 8;let p=Number(o),w=0;if(s===1)w=d.offset;else if(s===2)w=d.file.size;else if(s!==0)return 28;return w+p<0?28:(d.offset=w+p,l().setBigUint64(i,BigInt(d.offset),!0),0)},fd_readdir(r,o,s,i,d){let p=c.get(r);if(!p||p.kind!=="dir")return 8;p.entries||(p.entries=t.listDir(p.path));let w=l(),A=E(),T=0;for(let U=Number(i);U<p.entries.length;U++){let b=p.entries[U],x=M.encode(b.name),I=new Uint8Array(24+x.length),N=new DataView(I.buffer);N.setBigUint64(0,BigInt(U+1),!0),N.setBigUint64(8,BigInt(U+1),!0),N.setUint32(16,x.length,!0),N.setUint8(20,b.isDir?3:4),I.set(x,24);let j=Math.min(I.length,s-T);if(A.set(I.subarray(0,j),o+T),T+=j,j<I.length)break}return w.setUint32(d,T,!0),0},path_create_directory(r,o,s){let i=$(r,o,s);return i===null?8:t.exists(i)?20:t.dirs.has(H(i))?(t.dirs.add(i),0):44},path_filestat_get(r,o,s,i,d){let p=$(r,s,i);return p===null?8:C(p,d)},path_open(r,o,s,i,d,p,w,A,T){let U=$(r,s,i);if(U===null)return 8;let b=l();if(t.dirs.has(U)){if(d&4&&d&1)return 20;let N=g++;return c.set(N,{kind:"dir",path:U}),b.setUint32(T,N,!0),0}if(d&2)return t.files.has(U)?54:44;let x=t.files.get(U);if(x){if(d&4&&d&1)return 20;d&8&&(x.data=new Uint8Array(0),x.size=0)}else{if(!(d&1)||!t.dirs.has(H(U)))return 44;x={data:new Uint8Array(0),size:0},t.files.set(U,x)}let I=g++;return c.set(I,{kind:"file",path:U,file:x,offset:A&1?x.size:0}),b.setUint32(T,I,!0),0},path_remove_directory(r,o,s){let i=$(r,o,s);return i===null?8:t.dirs.has(i)?t.listDir(i).length>0?55:(t.dirs.delete(i),0):t.files.has(i)?54:44},path_unlink_file(r,o,s){let i=$(r,o,s);return i===null?8:t.files.has(i)?(t.files.delete(i),0):t.dirs.has(i)?31:44},fd_sync(){return 0},fd_datasync(){return 0},fd_advise(){return 0},fd_tell(r,o){let s=c.get(r);return!s||s.kind!=="file"?8:(l().setBigUint64(o,BigInt(s.offset),!0),0)},fd_renumber(r,o){return!c.has(r)||!c.has(o)?8:(c.set(o,c.get(r)),c.delete(r),0)},fd_allocate:()=>52,fd_fdstat_set_flags:()=>52,fd_fdstat_set_rights:()=>52,fd_filestat_set_times:()=>52,path_filestat_set_times:()=>52,path_link:()=>52,path_readlink:()=>52,path_rename:()=>52,path_symlink:()=>52,proc_raise:()=>52,sock_accept:()=>52,sock_recv:()=>52,sock_send:()=>52,sock_shutdown:()=>52,proc_exit(r){throw new B(r)},random_get(r,o){return globalThis.crypto.getRandomValues(E().subarray(r,r+o)),0},sched_yield(){return 0},poll_oneoff(){return 52}},setMemory(r){u=r},flush:O}},q=(t,e)=>{let n=new WebAssembly.Instance(t,{wasi_snapshot_preview1:e.imports});e.setMemory(n.exports.memory);let a=0;try{n.exports._start()}catch(f){if(f instanceof B)a=f.code;else throw e.flush(),f}return e.flush(),a};var L=class extends Error{constructor(e){super(`The program exited (code ${e}).`),this.name="JsExit",this.code=e}},Ot=new TextEncoder,It=new TextDecoder,st=t=>new Proxy(t,{get:(e,n)=>typeof n=="string"&&!(n in e)?()=>0:e[n]});function it({input:t="",args:e=[],stdout:n=()=>{},stderr:a=()=>{}}={}){let f=typeof t=="string"?Ot.encode(t):new Uint8Array(t),h=0,m=null,y=()=>new Uint8Array(m.buffer),c=(u,E)=>It.decode(y().subarray(u,u+E)),g=new Proxy({},{get:(u,E)=>typeof E=="string"?()=>0:void 0}),S={write(u,E,l){let _=c(E,l);(u===2?a:n)(_)},read(u,E,l){if(u!==0)return 0;let _=Math.max(0,Math.min(l,f.length-h));return _>0&&(y().set(f.subarray(h,h+_),E),h+=_),_},trap(){throw new L(1)},abort(){throw new L(1)},alert(){},evaluate(u,E){let l=c(u,E);(0,globalThis.eval)(l)},open(){},time_now:()=>BigInt(Date.now()),tick_now:()=>globalThis.performance?.now?.()??Date.now(),time_sleep(){},sqrt:Math.sqrt,sin:Math.sin,cos:Math.cos,pow:Math.pow,fmuladd:(u,E,l)=>u*E+l,ln:Math.log,exp:Math.exp,ldexp:(u,E)=>u*2**E,rand_bytes(u,E){y().set(globalThis.crypto.getRandomValues(new Uint8Array(E)),u)}};return{imports:{env:st({}),odin_env:st(S),odin_dom:g,webgl:g,webgl2:g},setMemory(u){m=u},args:e}}var J="/odin",ot="/out/main.wasm",z=["wasi_wasm32","js_wasm32"],Nt=new TextEncoder,at=()=>{let t=[],e=[],n=[];return{stdout:a=>{t.push(a),n.push(a)},stderr:a=>{e.push(a),n.push(a)},stdoutText:()=>t.join(""),stderrText:()=>e.join(""),outputText:()=>n.join("")}},Rt=t=>t.split(`
|
|
3
|
+
`).map(e=>e.trimEnd()).filter(e=>e.trim()!==""),ct=t=>t.crash?[t.crash]:t.diagnostics.length>0?t.diagnostics:[`The compiler produced no module (exit code ${t.exitCode}).`];function lt({compilerModule:t,libraryEntries:e}){let n=()=>{let c=new v;for(let[g,S]of e)c.writeFile(`${J}/${g}`,S);return c.mkdirAll(`${J}/vendor`),c},a=({code:c,target:g,compileArgs:S,fileName:u})=>{let E=n();E.writeFile(`/src/${u}`,Nt.encode(c)),E.mkdirAll("/out");let l=[],_=[],F=["odin","build","/src",`-out:${ot}`,"-backend:wasm",`-target:${g}`,...S],O=1,k="";try{O=q(t,X(E,{args:F,env:[`ODIN_ROOT=${J}`],stdout:C=>l.push(C),stderr:C=>_.push(C)}))}catch(C){k=C?.message??String(C)}return{exitCode:O,artifact:O===0?E.readFile(ot):null,diagnostics:Rt(l.join("")+_.join("")),crash:k}},f=({code:c,target:g,compileArgs:S=[],fileName:u="main.odin"})=>{let E=Y(),l=a({code:c,target:g,compileArgs:[...S],fileName:u}),_=Math.round(Y()-E);return l.artifact===null||l.artifact.length===0?{ok:!1,wasm:null,errors:ct(l),compileMs:_}:{ok:!0,wasm:l.artifact,errors:[],compileMs:_}},h=(c,{input:g,args:S})=>{let u=at(),E=0;try{E=q(new WebAssembly.Module(c.slice()),X(new v,{args:["main.wasm",...S],stdin:g,stdout:u.stdout,stderr:u.stderr}))}catch(l){l instanceof B?E=l.code:(u.stderr(`The program trapped: ${l?.message??l}
|
|
4
|
+
`),E=1)}return{exitCode:E,...dt(u)}},m=async(c,{input:g,args:S})=>{let u=at(),E=it({input:g,args:["main.wasm",...S],stdout:u.stdout,stderr:u.stderr}),l=0;try{let{instance:_}=await WebAssembly.instantiate(c.slice(),E.imports);E.setMemory(_.exports.memory),_.exports._start&&_.exports._start(),_.exports._end&&_.exports._end()}catch(_){_ instanceof L?l=_.code:(u.stderr(`The program trapped: ${_?.message??_}
|
|
5
|
+
`),l=1)}return{exitCode:l,...dt(u)}};return{compileAndRun:async({code:c,input:g="",target:S,args:u=[],compileArgs:E=[],fileName:l="main.odin"})=>{let _=Y(),F=a({code:c,target:S,compileArgs:[...E],fileName:l}),O=Math.round(Y()-_);if(F.artifact===null||F.artifact.length===0)return{stdout:"",stderr:"",output:"",errors:ct(F),exitCode:null,compileMs:O,runMs:null};let k=Y(),C;try{C=S==="js_wasm32"?await m(F.artifact,{input:g,args:u}):h(F.artifact,{input:g,args:u})}catch(D){C={exitCode:1,stdout:"",stderr:`The program could not run: ${D?.message??D}
|
|
6
|
+
`,output:""},C.output=C.stderr}let P=Math.round(Y()-k);return{stdout:C.stdout,stderr:C.stderr,output:C.output,errors:[],exitCode:C.exitCode,compileMs:O,runMs:P}},compileOnly:f}}var dt=t=>({stdout:t.stdoutText(),stderr:t.stderrText(),output:t.outputText()}),Y=()=>globalThis.performance?.now?.()??Date.now();var ut="ODINPOC1";function ft(t){let e=t instanceof Uint8Array?t:new Uint8Array(t);if(new TextDecoder().decode(e.subarray(0,8))!==ut)throw new Error(`The standard library pack is not a ${ut} container.`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),a=new TextDecoder,f=n.getUint32(8,!0),h=[],m=12;for(let y=0;y<f;y++){let c=n.getUint32(m,!0);m+=4;let g=a.decode(e.subarray(m,m+c));m+=c;let S=n.getUint32(m,!0);m+=4,h.push([g,e.subarray(m,m+S)]),m+=S}return h}var G=new Map;async function Dt(t,e){let n=y=>(c,g)=>e?.({name:y,loaded:c,total:g}),a=await t.readAsset("odin.wasm",n("odin.wasm")),f=await t.readAsset("odin-src.pack.gz",n("odin-src.pack.gz")),h=await WebAssembly.compile(a),m=ft(await nt(f));return lt({compilerModule:h,libraryEntries:m})}function Et(t,e){let n=G.get(t.key);return n===void 0&&(n={key:t.key,refs:0,promise:Dt(t,e)},G.set(t.key,n),n.promise.catch(()=>G.delete(t.key))),n.refs+=1,n}function K(t){t.refs-=1,t.refs<=0&&G.delete(t.key)}var Bt={wasi:"wasi_wasm32","wasi-wasm32":"wasi_wasm32",js:"js_wasm32","js-wasm32":"js_wasm32"};function Q(t,e){let n=t??e;if(n==null)return"wasi_wasm32";let a=Bt[n]??n;if(!z.includes(a))throw new Error(`Unknown target "${n}". Expected one of ${z.join(", ")}.`);return a}function ht({packaged:t}){async function e(n={}){let a=Q(n.target),f=et(n,t),h=Et(f,n.onProgress),m;try{m=await h.promise}catch(g){throw K(h),g}let y={target:a,args:n.args??[],compileArgs:n.compileArgs??[],fileName:n.fileName??"main.odin"},c=!1;return{get target(){return y.target},get targets(){return[...z]},async run(g,S,u={}){if(c)throw new Error("This compiler has been disposed.");if(typeof g!="string")throw new Error("run() needs the program source as its first argument.");return m.compileAndRun({code:g,input:S??"",target:Q(u.target,y.target),args:u.args??y.args,compileArgs:[...y.compileArgs,...u.compileArgs??[]],fileName:u.fileName??y.fileName})},async compile(g,S={}){if(c)throw new Error("This compiler has been disposed.");if(typeof g!="string")throw new Error("compile() needs the program source as its first argument.");return m.compileOnly({code:g,target:Q(S.target,y.target),compileArgs:[...y.compileArgs,...S.compileArgs??[]],fileName:S.fileName??y.fileName})},dispose(){c||(c=!0,K(h))}}}return{createCompiler:e,TARGETS:z}}var pt=ht({packaged:null}),Lt=pt.createCompiler,{TARGETS:Yt}=pt;return yt($t);})();
|
package/package.json
CHANGED
package/scripts/sync-assets.mjs
CHANGED
|
@@ -11,17 +11,25 @@ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
|
11
11
|
import { createHash } from 'node:crypto';
|
|
12
12
|
|
|
13
13
|
const ROOT_ASSETS = new URL('../../../assets/', import.meta.url);
|
|
14
|
+
const PUBLIC = new URL('../../../public/', import.meta.url);
|
|
14
15
|
const PACKAGE_ASSETS = new URL('../assets/', import.meta.url);
|
|
15
16
|
const RECEIPTS = new URL('../src/asset-receipts.js', import.meta.url);
|
|
16
17
|
|
|
17
|
-
//
|
|
18
|
-
const
|
|
18
|
+
// What ships in assets/, in the order the package loads it, and where each comes from.
|
|
19
|
+
const ASSETS = [
|
|
20
|
+
['odin.wasm', new URL('odin.wasm', ROOT_ASSETS)],
|
|
21
|
+
['odin-src.pack.gz', new URL('odin-src.pack.gz', ROOT_ASSETS)],
|
|
22
|
+
// Odin's own JS runtime, which is what a js_wasm32 program runs on when it needs the DOM — a
|
|
23
|
+
// worker has no `document`, so the host instantiates on the page. The repository vendors it for
|
|
24
|
+
// its own page; this is the same file.
|
|
25
|
+
['odin.js', new URL('odin.js', PUBLIC)],
|
|
26
|
+
];
|
|
19
27
|
|
|
20
28
|
await mkdir(PACKAGE_ASSETS, { recursive: true });
|
|
21
29
|
|
|
22
30
|
const receipts = [];
|
|
23
|
-
for (const name of
|
|
24
|
-
await copyFile(
|
|
31
|
+
for (const [name, from] of ASSETS) {
|
|
32
|
+
await copyFile(from, new URL(name, PACKAGE_ASSETS));
|
|
25
33
|
const bytes = await readFile(new URL(name, PACKAGE_ASSETS));
|
|
26
34
|
receipts.push({
|
|
27
35
|
name,
|
package/src/api.js
CHANGED
|
@@ -104,6 +104,29 @@ export function createApi({ packaged }) {
|
|
|
104
104
|
});
|
|
105
105
|
},
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Compile without running, for a host that runs the module itself.
|
|
109
|
+
*
|
|
110
|
+
* A page that needs the DOM cannot use `run`: a worker has no `document`, so the module has to
|
|
111
|
+
* be instantiated on the page with Odin's own JS runtime. This hands back the module for that.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} code - the Odin source.
|
|
114
|
+
* @param {object} [options] - per-call `target`, `compileArgs`, `fileName`.
|
|
115
|
+
* @returns {Promise<{ok: boolean, wasm: Uint8Array|null, errors: string[], compileMs: number}>}
|
|
116
|
+
*/
|
|
117
|
+
async compile(code, options = {}) {
|
|
118
|
+
if (disposed) throw new Error('This compiler has been disposed.');
|
|
119
|
+
if (typeof code !== 'string') {
|
|
120
|
+
throw new Error('compile() needs the program source as its first argument.');
|
|
121
|
+
}
|
|
122
|
+
return engine.compileOnly({
|
|
123
|
+
code,
|
|
124
|
+
target: resolveTarget(options.target, defaults.target),
|
|
125
|
+
compileArgs: [...defaults.compileArgs, ...(options.compileArgs ?? [])],
|
|
126
|
+
fileName: options.fileName ?? defaults.fileName,
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
|
|
107
130
|
/** Drop this compiler's hold on the shared engine. Further runs throw. */
|
|
108
131
|
dispose() {
|
|
109
132
|
if (disposed) return;
|
package/src/asset-receipts.js
CHANGED
|
@@ -8,5 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
export const ASSET_RECEIPTS = Object.freeze({
|
|
10
10
|
'odin.wasm': Object.freeze({ bytes: 3763439, sha256: '5044e383aa2415927cd518a4d3e6fdbcc6c99154ac90d212e154f18c092deef3' }),
|
|
11
|
-
'odin-src.pack.gz': Object.freeze({ bytes: 2502169, sha256: '567b1c0c1b50d3e557d119927ade3373dda91364acae3302f2cf01950a8dd8f9' })
|
|
11
|
+
'odin-src.pack.gz': Object.freeze({ bytes: 2502169, sha256: '567b1c0c1b50d3e557d119927ade3373dda91364acae3302f2cf01950a8dd8f9' }),
|
|
12
|
+
'odin.js': Object.freeze({ bytes: 72772, sha256: 'e918ba1614af23f02299976122d5edf4c994873ce4c6cdab621d42f3e9cb9733' })
|
|
12
13
|
});
|
package/src/engine.js
CHANGED
|
@@ -44,6 +44,14 @@ const diagnosticLines = (text) =>
|
|
|
44
44
|
.map((line) => line.trimEnd())
|
|
45
45
|
.filter((line) => line.trim() !== '');
|
|
46
46
|
|
|
47
|
+
/** Why a compile produced no module, as the lines a caller reads. */
|
|
48
|
+
const compileFailure = (compiled) =>
|
|
49
|
+
compiled.crash
|
|
50
|
+
? [compiled.crash]
|
|
51
|
+
: compiled.diagnostics.length > 0
|
|
52
|
+
? compiled.diagnostics
|
|
53
|
+
: [`The compiler produced no module (exit code ${compiled.exitCode}).`];
|
|
54
|
+
|
|
47
55
|
/**
|
|
48
56
|
* @param {object} assets
|
|
49
57
|
* @param {WebAssembly.Module} assets.compilerModule - the Odin compiler
|
|
@@ -99,6 +107,25 @@ export function createEngine({ compilerModule, libraryEntries }) {
|
|
|
99
107
|
};
|
|
100
108
|
};
|
|
101
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Compile only, returning the module for the caller to run.
|
|
112
|
+
*
|
|
113
|
+
* A page that needs the DOM has to instantiate the program on the main thread — a worker has no
|
|
114
|
+
* `document`, and Odin's JS runtime is written for the page — so the caller takes the module
|
|
115
|
+
* instead of an output string.
|
|
116
|
+
*
|
|
117
|
+
* @returns {{ok: boolean, wasm: Uint8Array|null, errors: string[], compileMs: number}}
|
|
118
|
+
*/
|
|
119
|
+
const compileOnly = ({ code, target, compileArgs = [], fileName = 'main.odin' }) => {
|
|
120
|
+
const started = now();
|
|
121
|
+
const compiled = compile({ code, target, compileArgs: [...compileArgs], fileName });
|
|
122
|
+
const compileMs = Math.round(now() - started);
|
|
123
|
+
if (compiled.artifact === null || compiled.artifact.length === 0) {
|
|
124
|
+
return { ok: false, wasm: null, errors: compileFailure(compiled), compileMs };
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, wasm: compiled.artifact, errors: [], compileMs };
|
|
127
|
+
};
|
|
128
|
+
|
|
102
129
|
const runWasiTarget = (artifact, { input, args }) => {
|
|
103
130
|
const captured = capture();
|
|
104
131
|
let exitCode = 0;
|
|
@@ -155,12 +182,7 @@ export function createEngine({ compilerModule, libraryEntries }) {
|
|
|
155
182
|
const compileMs = Math.round(now() - compileStarted);
|
|
156
183
|
|
|
157
184
|
if (compiled.artifact === null || compiled.artifact.length === 0) {
|
|
158
|
-
|
|
159
|
-
? [compiled.crash]
|
|
160
|
-
: compiled.diagnostics.length > 0
|
|
161
|
-
? compiled.diagnostics
|
|
162
|
-
: [`The compiler produced no module (exit code ${compiled.exitCode}).`];
|
|
163
|
-
return { stdout: '', stderr: '', output: '', errors, exitCode: null, compileMs, runMs: null };
|
|
185
|
+
return { stdout: '', stderr: '', output: '', errors: compileFailure(compiled), exitCode: null, compileMs, runMs: null };
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
const runStarted = now();
|
|
@@ -186,7 +208,7 @@ export function createEngine({ compilerModule, libraryEntries }) {
|
|
|
186
208
|
};
|
|
187
209
|
};
|
|
188
210
|
|
|
189
|
-
return { compileAndRun };
|
|
211
|
+
return { compileAndRun, compileOnly };
|
|
190
212
|
}
|
|
191
213
|
|
|
192
214
|
const capturedText = (captured) => ({
|