quickjs-emscripten-sync 1.8.4 → 1.9.1
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 +231 -150
- package/dist/index.d.ts +50 -7
- package/dist/quickjs-emscripten-sync.js +652 -387
- package/dist/quickjs-emscripten-sync.umd.cjs +17 -8
- package/package.json +5 -2
- package/src/index.test.ts +250 -5
- package/src/index.ts +126 -9
- package/src/jsonleak.test.ts +178 -0
- package/src/marshal/custom.ts +16 -2
- package/src/marshal/function.ts +6 -2
- package/src/marshal/hostref.ts +18 -0
- package/src/marshal/index.ts +25 -2
- package/src/marshal/mapset.ts +37 -0
- package/src/marshal/object.ts +4 -1
- package/src/marshal/primitive.ts +2 -11
- package/src/marshal/properties.ts +19 -6
- package/src/unmarshal/custom.ts +35 -4
- package/src/unmarshal/function.ts +2 -2
- package/src/unmarshal/hostref.ts +21 -0
- package/src/unmarshal/index.ts +21 -1
- package/src/unmarshal/mapset.ts +43 -0
- package/src/unmarshal/object.ts +16 -20
- package/src/unmarshal/primitive.ts +6 -17
- package/src/unmarshal/properties.ts +7 -6
- package/src/vmmap.ts +59 -69
- package/src/vmutil.ts +97 -12
- package/src/wrapper.ts +97 -30
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(_,b){typeof exports=="object"&&typeof module!="undefined"?b(exports):typeof define=="function"&&define.amd?define(["exports"],b):(_=typeof globalThis!="undefined"?globalThis:_||self,b(_.QuickjsEmscriptenSync={}))})(this,(function(_){"use strict";var Fe=Object.defineProperty;var ee=Object.getOwnPropertySymbols;var Ie=Object.prototype.hasOwnProperty,Ne=Object.prototype.propertyIsEnumerable;var J=(_,b,g)=>b in _?Fe(_,b,{enumerable:!0,configurable:!0,writable:!0,value:g}):_[b]=g,te=(_,b)=>{for(var g in b||(b={}))Ie.call(b,g)&&J(_,g,b[g]);if(ee)for(var g of ee(b))Ne.call(b,g)&&J(_,g,b[g]);return _};var p=(_,b,g)=>J(_,typeof b!="symbol"?b+"":b,g);var ne=(_,b,g)=>new Promise((A,k)=>{var I=O=>{try{C(g.next(O))}catch(h){k(h)}},R=O=>{try{C(g.throw(O))}catch(h){k(h)}},C=O=>O.done?A(O.value):Promise.resolve(O.value).then(I,R);C((g=g.apply(_,b)).next())});const b=n=>{const e=new g(n);return new Proxy(n,{get(t,r,s){return r in e?e[r]:Reflect.get(t,r,s)}})};class g{constructor(e){p(this,"context");p(this,"fn");p(this,"fnGenerator");p(this,"fnCounter",Number.MIN_SAFE_INTEGER);p(this,"fnMap",new Map);p(this,"newFunction",(e,t)=>{this.fnCounter++;const r=this.fnCounter;return this.fnMap.set(r,t),this.context.unwrapResult(this.context.callFunction(this.fnGenerator,this.context.undefined,this.context.newString(e),this.context.newNumber(t.length),this.context.newNumber(r),this.fn))});this.context=e;const t=this.fnMap;this.fn=this.context.newFunction("",function(r,...s){const i=e.getNumber(r),o=t.get(i);if(!o)throw new Error("function is not registered");return o.call(this,...s)}),this.fnGenerator=e.unwrapResult(e.evalCode(`((name, length, id, f) => {
|
|
2
2
|
const fn = function(...args) {
|
|
3
3
|
return f.call(this, id, ...args);
|
|
4
4
|
};
|
|
5
5
|
fn.name = name;
|
|
6
6
|
fn.length = length;
|
|
7
7
|
return fn;
|
|
8
|
-
})`))}disposeEx(){this.fnGenerator.dispose(),this.fn.dispose()}}const
|
|
8
|
+
})`))}disposeEx(){this.fnGenerator.dispose(),this.fn.dispose()}}const A=[[Symbol,"Symbol"],[Symbol.prototype,"Symbol.prototype"],[Object,"Object"],[Object.prototype,"Object.prototype"],[Function,"Function"],[Function.prototype,"Function.prototype"],[Boolean,"Boolean"],[Boolean.prototype,"Boolean.prototype"],[Array,"Array"],[Array.prototype,"Array.prototype"],[Error,"Error"],[Error.prototype,"Error.prototype"],[EvalError,"EvalError"],[EvalError.prototype,"EvalError.prototype"],[RangeError,"RangeError"],[RangeError.prototype,"RangeError.prototype"],[ReferenceError,"ReferenceError"],[ReferenceError.prototype,"ReferenceError.prototype"],[SyntaxError,"SyntaxError"],[SyntaxError.prototype,"SyntaxError.prototype"],[TypeError,"TypeError"],[TypeError.prototype,"TypeError.prototype"],[URIError,"URIError"],[URIError.prototype,"URIError.prototype"],...Object.getOwnPropertyNames(Symbol).filter(n=>typeof Symbol[n]=="symbol").map(n=>[Symbol[n],`Symbol.${n}`])];function k(n,e){if("error"in e&&e.error){const{error:t}=e;let r;try{r=n.dump(t)}catch(i){}finally{t.alive&&t.dispose()}const s=new Error(typeof r=="object"&&r&&"message"in r?String(r.message):r!==void 0?String(r):"quickjs-emscripten-sync: VM evaluation failed");throw typeof r=="object"&&r&&("name"in r&&(s.name=String(r.name)),"stack"in r&&(s.stack=String(r.stack))),s}return e.value}function I(n,e){const t=k(n,n.evalCode(e)),r=(i,...o)=>k(n,n.callFunction(t,i!=null?i:n.undefined,...o)),s=()=>t.dispose();return r.dispose=s,r[Symbol.dispose]=s,Object.defineProperty(r,"alive",{get:()=>t.alive}),r}const R=new WeakMap;function C(n){R.has(n)||R.set(n,new Map)}function O(n){const e=R.get(n);if(e){for(const t of e.values())t.alive&&t.dispose();R.delete(n)}}function h(n,e,t,...r){const s=R.get(n);if(!s){const o=I(n,e);try{return o(t,...r)}finally{o.dispose()}}let i=s.get(e);return(!i||!i.alive)&&(i=k(n,n.evalCode(e)),s.set(e,i)),k(n,n.callFunction(i,t!=null?t:n.undefined,...r))}function re(n,e,t){return n.dump(h(n,"(a, b) => a instanceof b",void 0,e,t))}function W(n,e){const t=n.typeof(e);return t==="function"||t==="object"&&!n.sameValue(e,n.null)}function z(n,e){const t=JSON.stringify(e);return t?n.newString(t).consume(r=>h(n,"JSON.parse",void 0,r)):n.undefined}function P(n,e){try{return e(n)}finally{n.alive&&n.dispose()}}function $(n,e){try{return e(n)}finally{for(const t of n)t.alive&&t.dispose()}}function se([n,e],t){try{return t(n)}finally{e&&n.dispose()}}function B(n,e){try{return e(...n.map(t=>t[0]))}finally{for(const[t,r]of n)r&&t.dispose()}}function ie(n){return"handle"in n}function N(n){return ie(n)?n.handle:n}function oe(n,e,t,r){var i;let s;for(const o of r)if(s=o(e,n),s)break;return s?(i=t(e,s))!=null?i:s:void 0}function ue(n,e){return typeof n!="symbol"?void 0:h(e,"d => Symbol(d)",void 0,n.description?e.newString(n.description):e.undefined)}function ae(n,e){return n instanceof Date?h(e,"d => new Date(d)",void 0,e.newNumber(n.getTime())):void 0}function fe(n,e){if(n instanceof ArrayBuffer)return e.newArrayBuffer(n.slice(0));if(ArrayBuffer.isView(n)){const t=new Uint8Array(n.buffer,n.byteOffset,n.byteLength).slice();return $([e.newArrayBuffer(t.buffer),e.newString(n.constructor.name)],([r,s])=>h(e,"(buf, name) => new globalThis[name](buf)",void 0,r,s))}}const ce=[ue,ae,fe];function L(n){return typeof n=="function"&&/^class\s/.test(Function.prototype.toString.call(n))}function H(n){return typeof n=="function"||typeof n=="object"&&n!==null}function D(n,e){const t=new Set,r=s=>{if(!(!H(s)||t.has(s)||(e==null?void 0:e(s,t))===!1)){if(t.add(s),Array.isArray(s)){for(const i of s)r(i);return}if(typeof s=="object"){const i=Object.getPrototypeOf(s);i&&i!==Object.prototype&&r(i)}for(const i of Object.values(Object.getOwnPropertyDescriptors(s)))"value"in i&&r(i.value),"get"in i&&r(i.get),"set"in i&&r(i.set)}};return r(n),t}function le(n,e){return D(n,e?(t,r)=>r.size<e:void 0).size}function pe(){let n=()=>{},e=()=>{};return{promise:new Promise((r,s)=>{n=r,e=s}),resolve:n,reject:e}}function K(n,e,t,r,s=()=>{}){const i=n.newObject(),o=[],f=(a,u)=>{const l=r(a),y=typeof u.value=="undefined"?void 0:r(u.value),m=typeof u.get=="undefined"?void 0:r(u.get),c=typeof u.set=="undefined"?void 0:r(u.set);y&&o.push(y),m&&o.push(m),c&&o.push(c),n.newObject().consume(d=>{Object.entries(u).forEach(([v,w])=>{const j=v==="value"?y:v==="get"?m:v==="set"?c:w?n.true:n.false;j&&n.setProp(d,v,j)}),n.setProp(i,l,d)})};try{const a=Object.getOwnPropertyDescriptors(e);Object.entries(a).forEach(([u,l])=>f(u,l)),Object.getOwnPropertySymbols(a).forEach(u=>f(u,a[u])),h(n,"Object.defineProperties",void 0,t,i).dispose();for(const u of o)s(u)}finally{i.dispose()}}function de(n,e,t,r,s,i,o=()=>{}){var u;if(typeof e!="function")return;const f=n.newFunction(e.name,function(...l){const y=r(this),m=l.map(c=>r(c));if(L(e)&&H(y)){const c=new e(...m);return Object.entries(c).forEach(([d,v])=>{const w=t(v);n.setProp(this,d,w),o(w)}),this}return t(i?i(e,y,m):e.apply(y,m))}).consume(l=>h(n,`Cls => {
|
|
9
9
|
const fn = function(...args) { return Cls.apply(this, args); };
|
|
10
10
|
fn.name = Cls.name;
|
|
11
11
|
fn.length = Cls.length;
|
|
12
12
|
return fn;
|
|
13
|
-
}`,void 0,
|
|
13
|
+
}`,void 0,l)),a=(u=s(e,f))!=null?u:f;return K(n,e,f,t,o),a}function ye(n,e,t){if(!(typeof e!="object"&&typeof e!="function")&&e!==null)return t(e,n.newHostRef(e).handle)}function he(n,e,t){var i;const r=z(n,e);return(i=t(e,r))!=null?i:r}function me(n,e,t,r,s=()=>{}){var i,o;if(e instanceof Map){const f=h(n,"() => new Map()"),a=(i=r(e,f))!=null?i:f;for(const[u,l]of e){const y=t(u),m=t(l);h(n,"(m, k, v) => m.set(k, v)",void 0,f,y,m).dispose(),s(y),s(m)}return a}if(e instanceof Set){const f=h(n,"() => new Set()"),a=(o=r(e,f))!=null?o:f;for(const u of e){const l=t(u);h(n,"(s, v) => s.add(v)",void 0,f,l).dispose(),s(l)}return a}}function _e(n,e,t,r,s=()=>{}){var u;if(typeof e!="object"||e===null)return;const i=Array.isArray(e)?n.newArray():n.newObject(),o=(u=r(e,i))!=null?u:i,f=Object.getPrototypeOf(e),a=f&&f!==Object.prototype&&f!==Array.prototype?t(f):void 0;return a&&(h(n,"Object.setPrototypeOf",void 0,o,a).dispose(),s(a)),K(n,e,i,t,s),o}function ve(n,e){switch(typeof e){case"undefined":return n.undefined;case"number":return n.newNumber(e);case"string":return n.newString(e);case"boolean":return e?n.true:n.false;case"bigint":return n.newBigInt(e);case"object":return e===null?n.null:void 0}}function be(n,e,t,r){var i;if(!(e instanceof Promise))return;const s=n.newPromise();return e.then(o=>s.resolve(t(o)),o=>s.reject(t(o))),(i=r(e,s))!=null?i:s.handle}function U(n,e){var y,m,c,d,v,w,j,T;const{ctx:t,unmarshal:r,isMarshalable:s,find:i,pre:o}=e;{const S=ve(t,n);if(S)return typeof n=="bigint"&&((y=e.registerTransient)==null||y.call(e,S)),S}{const S=i(n);if(S)return S}if((m=e.marshalByReference)!=null&&m.call(e,n)&&e.registerHostRef){const S=ye(t,n,e.registerHostRef);if(S)return S}const f=s==null?void 0:s(n);if(f===!1)return t.undefined;const a=(S,M)=>o(S,M,f);if(f==="json")return he(t,n,a);const u=S=>U(S,e),l=e.disposeTransient;return(T=(j=(w=(v=(d=oe(t,n,a,[...ce,...(c=e.custom)!=null?c:[]]))!=null?d:be(t,n,u,a))!=null?v:de(t,n,u,r,a,e.preApply,l))!=null?w:me(t,n,u,a,l))!=null?j:_e(t,n,u,a,l))!=null?T:t.undefined}function we(n,e,t,r){var i;let s;for(const o of r)if(s=o(e,n),s)break;return s?(i=t(s,e))!=null?i:s:void 0}function ge(n,e){if(e.typeof(n)!=="symbol")return;const t=e.getString(e.getProp(n,"description"));return Symbol(t)}function je(n,e){if(!P(h(e,"a => a instanceof Date",void 0,n),r=>e.dump(r)))return;const t=P(h(e,"a => a.getTime()",void 0,n),r=>e.getNumber(r));return new Date(t)}function Se(n,e){if(P(h(e,"a => a instanceof ArrayBuffer",void 0,n),t=>e.dump(t))){const t=e.getArrayBuffer(n),r=t.value.slice();return t.dispose(),r.buffer}if(P(h(e,"a => ArrayBuffer.isView(a)",void 0,n),t=>e.dump(t))){const t=P(h(e,"a => a.constructor.name",void 0,n),f=>e.getString(f)),r=globalThis[t];if(typeof r!="function")return;const s=h(e,"a => a.buffer.slice(a.byteOffset, a.byteOffset + a.byteLength)",void 0,n),i=e.getArrayBuffer(s),o=i.value.slice();return i.dispose(),s.dispose(),new r(o.buffer)}}const Pe=[ge,je,Se];function Q(n,e,t,r){P(n.newFunction("",(s,i)=>{const[o]=r(s);if(typeof o!="string"&&typeof o!="number"&&typeof o!="symbol")return;const f=[["value",!0],["get",!0],["set",!0],["configurable",!1],["enumerable",!1],["writable",!1]].reduce((a,[u,l])=>{const y=n.getProp(i,u),m=n.typeof(y);if(m==="undefined")return a;if(!l&&m==="boolean")return a[u]=n.dump(n.getProp(i,u)),a;const[c,d]=r(y);return d&&y.dispose(),a[u]=c,a},{});Object.defineProperty(t,o,f)}),s=>{h(n,`(o, fn) => {
|
|
14
14
|
const descs = Object.getOwnPropertyDescriptors(o);
|
|
15
15
|
Object.entries(descs).forEach(([k, v]) => fn(k, v));
|
|
16
16
|
Object.getOwnPropertySymbols(descs).forEach(k => fn(k, descs[k]));
|
|
17
|
-
}`,void 0,e,s).dispose()})}function
|
|
17
|
+
}`,void 0,e,s).dispose()})}function Te(n,e,t,r,s){var f;if(n.typeof(e)!=="function")return;const i=function(...a){return B([t(this),...a.map(u=>t(u))],(u,...l)=>{if(new.target){const[d]=r(h(n,"(Cls, ...args) => new Cls(...args)",u,e,...l));return Object.defineProperties(this,Object.getOwnPropertyDescriptors(d)),this}const y=k(n,n.callFunction(e,u,...l)),[m,c]=r(y);return c&&y.dispose(),m})},o=(f=s(i,e))!=null?f:i;return Q(n,e,i,r),o}function q(n,e){const t=n.toHostRef(e);if(t)try{return{value:t.value}}finally{t.dispose()}}function ke(n,e,t,r){const s=P(h(n,"a => a instanceof Map",void 0,e),a=>n.dump(a)),i=!s&&P(h(n,"a => a instanceof Set",void 0,e),a=>n.dump(a));if(!s&&!i)return;const o=s?new Map:new Set;r(o,e);const f=n.unwrapResult(n.getIterator(e));try{for(const a of f){const u=n.unwrapResult(a);if(s){const l=n.getProp(u,0),y=n.getProp(u,1),[m,c]=t(l),[d,v]=t(y);c&&l.dispose(),v&&y.dispose(),o.set(m,d),u.dispose()}else{const[l,y]=t(u);y&&u.dispose(),o.add(l)}}}finally{f.alive&&f.dispose()}return o}function Oe(n,e,t,r){var f;if(n.typeof(e)!=="object"||n.sameValue(e,n.null))return;const s=P(h(n,"Array.isArray",void 0,e),a=>n.dump(a))?[]:{},i=(f=r(s,e))!=null?f:s,o=P(h(n,`o => {
|
|
18
18
|
const p = Object.getPrototypeOf(o);
|
|
19
19
|
return !p || p === Object.prototype || p === Array.prototype ? undefined : p;
|
|
20
|
-
}`,void 0,e)
|
|
20
|
+
}`,void 0,e),a=>{if(n.typeof(a)==="undefined")return;const[u]=t(a);return u});return typeof o=="object"&&Object.setPrototypeOf(i,o),Q(n,e,s,t),i}function He(n,e){const t=n.typeof(e);return t==="undefined"||t==="number"||t==="string"||t==="boolean"?[n.dump(e),!0]:t==="bigint"?[n.getBigInt(e),!0]:t==="object"&&n.sameValue(e,n.null)?[null,!0]:[void 0,!1]}function Ee(n,e,t,r){var u;if(!Re(n,e))return;const s=pe(),[i,o]=t(s.resolve),[f,a]=t(s.reject);return h(n,"(p, res, rej) => { p.then(res, rej); }",void 0,e,i,f),o&&i.dispose(),a&&f.dispose(),(u=r(s.promise,e))!=null?u:s.promise}function Re(n,e){return e.owner?n.unwrapResult(n.evalCode("Promise")).consume(t=>e.owner?re(n,e,t):!1):!1}function X(n,e){const[t]=Y(n,e);return t}function Y(n,e){var l,y,m;const{ctx:t,marshal:r,find:s,pre:i}=e;{const[c,d]=He(t,n);if(d)return[c,!1]}{const c=s(n);if(c)return[c,!0]}const o=c=>Y(c,e);if(e.hostRef){const c=q(t,n);if(c)return[c.value,!0]}const f=we(t,n,i,[...Pe,...(l=e.custom)!=null?l:[]]);if(f)return[f,!0];const a=ke(t,n,o,i);return a?[a,!0]:[(m=(y=Ee(t,n,r,i))!=null?y:Te(t,n,r,o,i))!=null?m:Oe(t,n,o,i),!1]}class V{constructor(e){p(this,"ctx");p(this,"_keyToId",new Map);p(this,"_key2ToId",new Map);p(this,"_idToHandle",new Map);p(this,"_idToHandle2",new Map);p(this,"_idToKey",new Map);p(this,"_idToKey2",new Map);p(this,"_disposables",new Set);p(this,"_mapGet");p(this,"_mapSet");p(this,"_mapDelete");p(this,"_mapClear");p(this,"_nextId",Number.MIN_SAFE_INTEGER);this.ctx=e;const t=e.unwrapResult(e.evalCode(`() => {
|
|
21
21
|
const mapSym = new Map();
|
|
22
22
|
let map = new WeakMap();
|
|
23
23
|
let map2 = new WeakMap();
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
map2 = new WeakMap();
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
-
}`)).consume(r=>this._call(r,void 0));this._mapGet=e.getProp(t,"get"),this._mapSet=e.getProp(t,"set"),this._mapDelete=e.getProp(t,"delete"),this._mapClear=e.getProp(t,"clear"),t.dispose(),this._disposables.add(this._mapGet),this._disposables.add(this._mapSet),this._disposables.add(this._mapDelete),this._disposables.add(this._mapClear)}set(e,t,r,s){var
|
|
43
|
+
}`)).consume(r=>this._call(r,void 0));this._mapGet=e.getProp(t,"get"),this._mapSet=e.getProp(t,"set"),this._mapDelete=e.getProp(t,"delete"),this._mapClear=e.getProp(t,"clear"),t.dispose(),this._disposables.add(this._mapGet),this._disposables.add(this._mapSet),this._disposables.add(this._mapDelete),this._disposables.add(this._mapClear)}set(e,t,r,s){var f;if(!t.alive||s&&!s.alive)return!1;const i=(f=this.get(e))!=null?f:this.get(r);if(i)return i===t||i===s;const o=this._nextId++;return this._keyToId.set(e,o),this._idToHandle.set(o,t),this._idToKey.set(o,e),r&&(this._key2ToId.set(r,o),this._idToKey2.set(o,r),s&&this._idToHandle2.set(o,s)),this.ctx.newNumber(o).consume(a=>{this._call(this._mapSet,void 0,t,a,s!=null?s:this.ctx.undefined)}),!0}merge(e){if(e)for(const t of e)t&&t[1]&&this.set(t[0],t[1],t[2],t[3])}get(e){var s;const t=(s=this._keyToId.get(e))!=null?s:this._key2ToId.get(e),r=typeof t=="number"?this._idToHandle.get(t):void 0;if(r){if(!r.alive){this.delete(e);return}return r}}getByHandle(e){if(e.alive)return this._idToKey.get(this.ctx.getNumber(this._call(this._mapGet,void 0,e)))}has(e){return!!this.get(e)}hasHandle(e){return typeof this.getByHandle(e)!="undefined"}keys(){return this._keyToId.keys()}delete(e,t){var a;const r=(a=this._keyToId.get(e))!=null?a:this._key2ToId.get(e);if(typeof r=="undefined")return;const s=this._idToHandle.get(r),i=this._idToHandle2.get(r);this._call(this._mapDelete,void 0,...[s,i].filter(u=>!!(u!=null&&u.alive)));const o=this._idToKey.get(r),f=this._idToKey2.get(r);typeof o!="undefined"&&this._keyToId.delete(o),typeof f!="undefined"&&this._key2ToId.delete(f),this._idToHandle.delete(r),this._idToHandle2.delete(r),this._idToKey.delete(r),this._idToKey2.delete(r),t&&(s!=null&&s.alive&&s.dispose(),i!=null&&i.alive&&i.dispose())}deleteByHandle(e,t){const r=this.getByHandle(e);typeof r!="undefined"&&this.delete(r,t)}clear(){this._nextId=0,this._keyToId.clear(),this._key2ToId.clear(),this._idToHandle.clear(),this._idToHandle2.clear(),this._idToKey.clear(),this._idToKey2.clear(),this._mapClear.alive&&this._call(this._mapClear,void 0)}dispose(){for(const e of this._disposables.values())e.alive&&e.dispose();for(const e of this._idToHandle.values())e.alive&&e.dispose();for(const e of this._idToHandle2.values())e.alive&&e.dispose();this._disposables.clear(),this.clear()}get size(){return this._keyToId.size}[Symbol.iterator](){const e=this._keyToId.keys();return{next:()=>{for(;;){const t=e.next();if(t.done)return{value:void 0,done:!0};const r=this._keyToId.get(t.value);if(typeof r=="undefined")continue;const s=this._idToHandle.get(r),i=this._idToHandle2.get(r);if(!s)continue;const o=this._idToKey2.get(r);return{value:[t.value,s,o,i],done:!1}}}}}_call(e,t,...r){return k(this.ctx,this.ctx.callFunction(e,typeof t=="undefined"?this.ctx.undefined:t,...r))}}function Me(n,e,t,r,s,i,o,f=!0){if(!H(e)||e instanceof Promise||e instanceof Date||e instanceof ArrayBuffer||ArrayBuffer.isView(e)||e instanceof Map||e instanceof Set||o&&!o(e))return;if(Ae(e,t)||!f)return e;const a=new Proxy(e,{get(u,l){return l===t?u:Reflect.get(u,l)},set(u,l,y,m){var v;const c=E(y,t),d=(v=i==null?void 0:i(m))!=null?v:"host";return d!=="vm"&&!Reflect.set(u,l,c)||d==="host"||!n.alive||B([s(m),s(l),s(c)],(w,j,T)=>{const[S,M]=F(n,w,r);M?S.consume(G=>n.setProp(G,j,T)):n.setProp(S,j,T)}),!0},deleteProperty(u,l){var m;const y=(m=i==null?void 0:i(a))!=null?m:"host";return B([s(a),s(l)],(c,d)=>{const[v,w]=F(n,c,r);if(y==="vm"||Reflect.deleteProperty(u,l)){if(y==="host"||!n.alive)return!0;w?v.consume(j=>h(n,"(a, b) => delete a[b]",void 0,j,d)):h(n,"(a, b) => delete a[b]",void 0,v,d)}return!0})},defineProperty(u,l,y){var d;const m=(d=i==null?void 0:i(a))!=null?d:"host",c=te({},y);return"value"in c&&(c.value=E(c.value,t)),typeof c.get=="function"&&(c.get=E(c.get,t)),typeof c.set=="function"&&(c.set=E(c.set,t)),m!=="vm"&&!Reflect.defineProperty(u,l,c)?!1:(m==="host"||!n.alive||B([s(a),s(l),s(c)],(v,w,j)=>{const[T,S]=F(n,v,r),M=G=>h(n,"(o, k, d) => { Object.defineProperty(o, k, d); }",void 0,G,w,j).dispose();S?T.consume(M):M(T)}),!0)}});return a}function Ce(n,e,t,r,s,i,o,f=!0){if(!W(n,e)||o&&!o(e,n))return[void 0,!1];if(Z(n,e,r))return[e,!1];if(!f)return[e,!1];const a=c=>{const d=i==null?void 0:i(s(c));return typeof d=="string"?n.newString(d):n.undefined},u=(c,d,v)=>{const w=s(c);if(!w)return;const j=s(d);if(j==="__proto__")return;const T=s(v);E(w,t)[j]=T},l=(c,d)=>{const v=s(c);if(!v)return;const w=s(d);Reflect.deleteProperty(E(v,t),w)},y=(c,d,v)=>{const w=s(c);if(!w)return;const j=s(d);if(j==="__proto__")return;const T=s(v);Object.defineProperty(E(w,t),j,T)},m=n.newFunction("proxyFuncs",(c,...d)=>{switch(n.getNumber(c)){case 1:return a(d[0]);case 2:return u(d[0],d[1],d[2]);case 3:return l(d[0],d[1]);case 4:return y(d[0],d[1],d[2])}return n.undefined});return P(m,c=>[h(n,`(target, sym, proxyFuncs) => {
|
|
44
44
|
const rec = new Proxy(target, {
|
|
45
45
|
get(obj, key, receiver) {
|
|
46
46
|
return key === sym ? obj : Reflect.get(obj, key, receiver)
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
? value[sym] ?? value
|
|
51
51
|
: value;
|
|
52
52
|
const sync = proxyFuncs(1, receiver) ?? "vm";
|
|
53
|
-
if (sync === "host" || Reflect.set(obj, key, v
|
|
53
|
+
if (sync === "host" || Reflect.set(obj, key, v)) {
|
|
54
54
|
if (sync !== "vm") {
|
|
55
55
|
proxyFuncs(2, receiver, key, v);
|
|
56
56
|
}
|
|
@@ -66,6 +66,15 @@
|
|
|
66
66
|
}
|
|
67
67
|
return true;
|
|
68
68
|
},
|
|
69
|
+
defineProperty(obj, key, descriptor) {
|
|
70
|
+
const sync = proxyFuncs(1, rec) ?? "vm";
|
|
71
|
+
if (sync === "host" || Reflect.defineProperty(obj, key, descriptor)) {
|
|
72
|
+
if (sync !== "vm") {
|
|
73
|
+
proxyFuncs(4, rec, key, descriptor);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
69
78
|
});
|
|
70
79
|
return rec;
|
|
71
|
-
}`,void 0,e,r,
|
|
80
|
+
}`,void 0,e,r,c),!0])}function E(n,e){var t;return H(n)&&(t=n[e])!=null?t:n}function F(n,e,t){return Z(n,e,t)?[n.getProp(e,t),!0]:[e,!1]}function Ae(n,e){return H(n)&&!!n[e]}function Z(n,e,t){return!!n.dump(h(n,'(a, s) => (a instanceof Promise) || (a instanceof Date) || (a instanceof ArrayBuffer) || (ArrayBuffer.isView(a)) || (a instanceof Map) || (a instanceof Set) || (typeof a === "object" && a !== null || typeof a === "function") && !!a[s]',void 0,e,t))}class x{constructor(e,t){p(this,"context");p(this,"_map");p(this,"_registeredMap");p(this,"_registeredMapDispose",new Set);p(this,"_transientHandles",new Set);p(this,"_sync",new Set);p(this,"_temporalSync",new Set);p(this,"_symbol",Symbol());p(this,"_symbolHandle");p(this,"_options");p(this,"_isMarshalable",e=>{var r,s;const t=(r=this._options)==null?void 0:r.isMarshalable;return(s=typeof t=="function"?t(this._unwrap(e)):t)!=null?s:"json"});p(this,"_marshalByReference",e=>{var t,r;return!!((r=(t=this._options)==null?void 0:t.marshalByReference)!=null&&r.call(t,this._unwrap(e)))});p(this,"_registerHostRef",(e,t)=>{const r=this._unwrap(e),s=this._map.get(r);return s||(this._map.set(r,t),t)});p(this,"_marshalFind",e=>{var s,i,o;const t=this._unwrap(e);return(o=(i=(s=this._registeredMap.get(e))!=null?s:t!==e?this._registeredMap.get(t):void 0)!=null?i:this._map.get(e))!=null?o:t!==e?this._map.get(t):void 0});p(this,"_marshalPre",(e,t,r)=>{if(r==="json"){this._registerTransient(N(t));return}const s=this._register(e,N(t),this._map);if(s)return s[1];this._registeredMap.has(e)||this._registerTransient(N(t))});p(this,"_registerTransient",e=>{this._transientHandles.add(e)});p(this,"_disposeTransient",e=>{this._transientHandles.delete(e)&&e.alive&&e.dispose()});p(this,"_marshalPreApply",(e,t,r)=>{const s=H(t)?this._unwrap(t):void 0;s&&this._temporalSync.add(s);try{return e.apply(t,r)}finally{s&&this._temporalSync.delete(s)}});p(this,"_marshal",e=>{var o,f,a,u;const t=this._registeredMap.get(e);if(t)return[t,!1];const r=this._marshalByReference(e)?this._unwrap(e):(o=this._wrap(e))!=null?o:e,s=U(r,{ctx:this.context,unmarshal:this._unmarshal,isMarshalable:this._isMarshalable,marshalByReference:this._marshalByReference,registerHostRef:this._registerHostRef,find:this._marshalFind,pre:this._marshalPre,registerTransient:this._registerTransient,disposeTransient:this._disposeTransient,preApply:this._marshalPreApply,custom:(f=this._options)==null?void 0:f.customMarshaller});this._transientHandles.delete(s);const i=(u=(a=this._options)==null?void 0:a.syncEnabled)!=null?u:!0;return[s,!i||!this._map.hasHandle(s)]});p(this,"_preUnmarshal",(e,t)=>{var r,s,i;return(i=this._register(e,t,void 0,(s=(r=this._options)==null?void 0:r.syncEnabled)!=null?s:!0))==null?void 0:i[0]});p(this,"_unmarshalFind",e=>{var t;return(t=this._registeredMap.getByHandle(e))!=null?t:this._map.getByHandle(e)});p(this,"_unmarshal",e=>{var s,i,o;const t=this._registeredMap.getByHandle(e);if(typeof t!="undefined")return t;if((s=this._options)!=null&&s.marshalByReference){const f=q(this.context,e);if(f)return f.value}const[r]=this._wrapHandle(e);return X(r!=null?r:e,{ctx:this.context,marshal:this._marshal,find:this._unmarshalFind,pre:this._preUnmarshal,custom:(i=this._options)==null?void 0:i.customUnmarshaller,hostRef:!!((o=this._options)!=null&&o.marshalByReference)})});p(this,"_syncMode",e=>{const t=this._unwrap(e);return this._sync.has(t)||this._temporalSync.has(t)?"both":void 0});p(this,"_unwrapIfNotSynced",e=>{const t=this._unwrap(e);return t instanceof Promise||!this._sync.has(t)?t:e});var r;t!=null&&t.compat&&!("runtime"in e)&&(e.runtime={hasPendingJob:()=>e.hasPendingJob(),executePendingJobs:s=>e.executePendingJobs(s)}),this.context=t!=null&&t.experimentalContextEx?b(e):e,C(this.context),this._options=t,this._symbolHandle=e.unwrapResult(e.evalCode("Symbol()")),this._map=new V(e),this._registeredMap=new V(e),this.registerAll((r=t==null?void 0:t.registeredObjects)!=null?r:A)}dispose(){var e,t;for(const r of this._transientHandles)r.alive&&r.dispose();this._transientHandles.clear(),this._map.dispose(),this._registeredMap.dispose(),this._symbolHandle.dispose(),O(this.context),(t=(e=this.context).disposeEx)==null||t.call(e)}[Symbol.dispose](){this.dispose()}evalCode(e){const t=this.context.evalCode(e);return this._unwrapResultAndUnmarshal(t)}evalModule(e,t="module.js"){const r=this.context.evalCode(e,t,{type:"module"});return this._unwrapResultAndUnmarshal(r)}executePendingJobs(e){const t=this.context.runtime.executePendingJobs(e);if("value"in t)return t.value;throw this._unwrapIfNotSynced(P(t.error,this._unmarshal))}setMemoryLimit(e){this.context.runtime.setMemoryLimit(e)}setMaxStackSize(e){this.context.runtime.setMaxStackSize(e)}getMemoryUsage(){const e=this.context.runtime.computeMemoryUsage();try{return this.context.dump(e)}finally{e.dispose()}}dumpMemoryUsage(){return this.context.runtime.dumpMemoryUsage()}expose(e){for(const[t,r]of Object.entries(e))se(this._marshal(r),s=>{this.context.setProp(this.context.global,t,s)})}sync(e){const t=this._wrap(e);return typeof t=="undefined"?e:(D(t,r=>{const s=this._unwrap(r);this._sync.add(s)}),t)}register(e,t){if(this._registeredMap.has(e))return;const r=typeof t=="string"?this._unwrapResult(this.context.evalCode(t)):t;this.context.sameValue(r,this.context.undefined)||(typeof t=="string"&&this._registeredMapDispose.add(e),this._registeredMap.set(e,r))}registerAll(e){for(const[t,r]of e)this.register(t,r)}unregister(e,t){this._registeredMap.delete(e,this._registeredMapDispose.has(e)||t),this._registeredMapDispose.delete(e)}unregisterAll(e,t){for(const r of e)this.unregister(r,t)}startSync(e){if(!H(e))return;const t=this._unwrap(e);this._sync.add(t)}endSync(e){this._sync.delete(this._unwrap(e))}_unwrapResult(e){if("value"in e)return e.value;throw this._unwrapIfNotSynced(P(e.error,this._unmarshal))}_unwrapResultAndUnmarshal(e){if(e)return this._unwrapIfNotSynced(P(this._unwrapResult(e),this._unmarshal))}_register(e,t,r=this._map,s){if(this._registeredMap.has(e)||this._registeredMap.hasHandle(t))return;let i=this._wrap(e);const[o]=this._wrapHandle(t),f=e instanceof Promise;if(!o||!i&&!f)return;f&&(i=e);const a=this._unwrap(e),[u,l]=this._unwrapHandle(t);if(r.set(i,o,a,u))s&&this._sync.add(a);else throw l&&u.dispose(),new Error("already registered");return[i,o]}_wrap(e){var t,r,s;return Me(this.context,e,this._symbol,this._symbolHandle,this._marshal,this._syncMode,(t=this._options)==null?void 0:t.isWrappable,(s=(r=this._options)==null?void 0:r.syncEnabled)!=null?s:!0)}_unwrap(e){return E(e,this._symbol)}_wrapHandle(e){var t,r,s;return Ce(this.context,e,this._symbol,this._symbolHandle,this._unmarshal,this._syncMode,(t=this._options)==null?void 0:t.isHandleWrappable,(s=(r=this._options)==null?void 0:r.syncEnabled)!=null?s:!0)}_unwrapHandle(e){return F(this.context,e,this._symbolHandle)}}class Be extends x{constructor(t,r){super(t,r);p(this,"asyncContext");this.asyncContext=t}evalCodeAsync(t,r){return ne(this,null,function*(){const s=yield this.asyncContext.evalCodeAsync(t,r);return this._unwrapResultAndUnmarshal(s)})}}_.Arena=x,_.AsyncArena=Be,_.VMMap=V,_.call=h,_.complexity=le,_.consumeAll=$,_.defaultRegisteredObjects=A,_.isES2015Class=L,_.isHandleObject=W,_.isObject=H,_.json=z,_.marshal=U,_.unmarshal=X,_.walkObject=D,Object.defineProperty(_,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickjs-emscripten-sync",
|
|
3
3
|
"author": "rot1024",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -34,17 +34,20 @@
|
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsc && vite build",
|
|
36
36
|
"test": "vitest",
|
|
37
|
+
"bench": "vitest bench --run",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
37
39
|
"lint": "eslint ."
|
|
38
40
|
},
|
|
39
41
|
"peerDependencies": {
|
|
40
42
|
"quickjs-emscripten": "*"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
45
|
+
"@jitl/quickjs-wasmfile-debug-sync": "^0.32.0",
|
|
43
46
|
"@vitest/coverage-v8": "^4.1.6",
|
|
44
47
|
"eslint": "^9.39.4",
|
|
45
48
|
"eslint-config-reearth": "^0.3.8",
|
|
46
49
|
"prettier": "^3.8.3",
|
|
47
|
-
"quickjs-emscripten": "^0.
|
|
50
|
+
"quickjs-emscripten": "^0.32.0",
|
|
48
51
|
"typescript": "^5.9.3",
|
|
49
52
|
"vite": "^7.3.3",
|
|
50
53
|
"vite-plugin-dts": "^4.3.0",
|
package/src/index.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { getQuickJS } from "quickjs-emscripten";
|
|
1
|
+
import { getQuickJS, newAsyncContext } from "quickjs-emscripten";
|
|
2
2
|
import { describe, expect, test, vi } from "vitest";
|
|
3
3
|
|
|
4
4
|
import { isWrapped } from "./wrapper";
|
|
5
5
|
|
|
6
|
-
import { Arena } from ".";
|
|
6
|
+
import { Arena, AsyncArena } from ".";
|
|
7
7
|
|
|
8
8
|
describe("readme", () => {
|
|
9
9
|
test("first", async () => {
|
|
@@ -999,11 +999,15 @@ describe("memory management", () => {
|
|
|
999
999
|
|
|
1000
1000
|
expect(data.items.length).toBe(10);
|
|
1001
1001
|
|
|
1002
|
-
// But not unlimited data
|
|
1002
|
+
// But not unlimited data. The allocation happens in a local (non-synced)
|
|
1003
|
+
// structure on purpose: a hard out-of-memory hit while mutating a synced
|
|
1004
|
+
// global mid-flight leaves the VM unrecoverable, so it would not be safe to
|
|
1005
|
+
// keep using or cleanly dispose afterwards.
|
|
1003
1006
|
expect(() => {
|
|
1004
1007
|
arena.evalCode(`
|
|
1005
|
-
|
|
1006
|
-
|
|
1008
|
+
const huge = [];
|
|
1009
|
+
for (let i = 0; i < 1000000; i++) {
|
|
1010
|
+
huge.push({ id: i, data: "x".repeat(1000) });
|
|
1007
1011
|
}
|
|
1008
1012
|
`);
|
|
1009
1013
|
}).toThrow();
|
|
@@ -1032,3 +1036,244 @@ describe("intrinsics configuration", () => {
|
|
|
1032
1036
|
runtime.dispose();
|
|
1033
1037
|
});
|
|
1034
1038
|
});
|
|
1039
|
+
|
|
1040
|
+
describe("syncEnabled", () => {
|
|
1041
|
+
const objCount = (ctx: any) => {
|
|
1042
|
+
const h = ctx.runtime.computeMemoryUsage();
|
|
1043
|
+
const c = ctx.dump(h).obj_count as number;
|
|
1044
|
+
h.dispose();
|
|
1045
|
+
return c;
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const growth = async (syncEnabled: boolean) => {
|
|
1049
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1050
|
+
const arena = new Arena(ctx, { isMarshalable: true, registeredObjects: [], syncEnabled });
|
|
1051
|
+
|
|
1052
|
+
arena.expose({ fnFromHost: () => ({ id: "x", data: Math.random() }) });
|
|
1053
|
+
arena.evalCode(`globalThis.run = () => { for (let i = 0; i < 200; i++) fnFromHost(); }`);
|
|
1054
|
+
|
|
1055
|
+
arena.evalCode(`run()`);
|
|
1056
|
+
const before = objCount(ctx);
|
|
1057
|
+
arena.evalCode(`run()`);
|
|
1058
|
+
const after = objCount(ctx);
|
|
1059
|
+
|
|
1060
|
+
arena.dispose();
|
|
1061
|
+
ctx.dispose();
|
|
1062
|
+
return after - before;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
test("syncEnabled: false does not retain marshalled objects", async () => {
|
|
1066
|
+
// returned objects are not retained, so repeated runs don't grow memory
|
|
1067
|
+
expect(await growth(false)).toBeLessThan(50);
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
test("syncEnabled: true retains marshalled objects (default)", async () => {
|
|
1071
|
+
// with sync on, every returned object is kept for identity, so memory grows
|
|
1072
|
+
expect(await growth(true)).toBeGreaterThan(150);
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
describe("AsyncArena", () => {
|
|
1077
|
+
test("evalCodeAsync returns values", async () => {
|
|
1078
|
+
const ctx = await newAsyncContext();
|
|
1079
|
+
const arena = new AsyncArena(ctx, { isMarshalable: true });
|
|
1080
|
+
|
|
1081
|
+
expect(await arena.evalCodeAsync(`1 + 2`)).toBe(3);
|
|
1082
|
+
expect(await arena.evalCodeAsync(`({ a: 1, b: [2, 3] })`)).toEqual({ a: 1, b: [2, 3] });
|
|
1083
|
+
|
|
1084
|
+
arena.dispose();
|
|
1085
|
+
ctx.dispose();
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
test("evalCodeAsync re-throws errors", async () => {
|
|
1089
|
+
const ctx = await newAsyncContext();
|
|
1090
|
+
const arena = new AsyncArena(ctx, { isMarshalable: true });
|
|
1091
|
+
|
|
1092
|
+
await expect(arena.evalCodeAsync(`throw new Error("boom")`)).rejects.toThrow("boom");
|
|
1093
|
+
|
|
1094
|
+
arena.dispose();
|
|
1095
|
+
ctx.dispose();
|
|
1096
|
+
});
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
describe("Symbol.dispose", () => {
|
|
1100
|
+
test("using disposes the arena", async () => {
|
|
1101
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1102
|
+
{
|
|
1103
|
+
using arena = new Arena(ctx, { isMarshalable: true });
|
|
1104
|
+
expect(arena.evalCode(`1 + 1`)).toBe(2);
|
|
1105
|
+
}
|
|
1106
|
+
// arena is disposed here; the context can still be disposed cleanly
|
|
1107
|
+
ctx.dispose();
|
|
1108
|
+
});
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
describe("defineProperty sync", () => {
|
|
1112
|
+
test("host -> VM", async () => {
|
|
1113
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1114
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1115
|
+
const data = arena.sync<any>({});
|
|
1116
|
+
arena.expose({ data });
|
|
1117
|
+
|
|
1118
|
+
Object.defineProperty(data, "x", { value: 42, enumerable: true, configurable: true });
|
|
1119
|
+
expect(arena.evalCode(`data.x`)).toBe(42);
|
|
1120
|
+
|
|
1121
|
+
arena.dispose();
|
|
1122
|
+
ctx.dispose();
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
test("VM -> host", async () => {
|
|
1126
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1127
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1128
|
+
const data = arena.sync<any>({});
|
|
1129
|
+
arena.expose({ data });
|
|
1130
|
+
|
|
1131
|
+
arena.evalCode(
|
|
1132
|
+
`Object.defineProperty(data, "y", { value: 7, enumerable: true, configurable: true })`,
|
|
1133
|
+
);
|
|
1134
|
+
expect(data.y).toBe(7);
|
|
1135
|
+
|
|
1136
|
+
arena.dispose();
|
|
1137
|
+
ctx.dispose();
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
test("syncs accessor descriptors host -> VM", async () => {
|
|
1141
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1142
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1143
|
+
const data = arena.sync<any>({});
|
|
1144
|
+
arena.expose({ data });
|
|
1145
|
+
|
|
1146
|
+
Object.defineProperty(data, "g", { get: () => 99, enumerable: true, configurable: true });
|
|
1147
|
+
expect(arena.evalCode(`data.g`)).toBe(99);
|
|
1148
|
+
|
|
1149
|
+
arena.dispose();
|
|
1150
|
+
ctx.dispose();
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
describe("marshalByReference", () => {
|
|
1155
|
+
test("passes objects through the VM by reference (identity preserved)", async () => {
|
|
1156
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1157
|
+
const secret = {
|
|
1158
|
+
hidden: 42,
|
|
1159
|
+
method() {
|
|
1160
|
+
return this.hidden;
|
|
1161
|
+
},
|
|
1162
|
+
};
|
|
1163
|
+
const arena = new Arena(ctx, {
|
|
1164
|
+
isMarshalable: true,
|
|
1165
|
+
marshalByReference: t => t === secret,
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
arena.expose({
|
|
1169
|
+
getSecret: () => secret,
|
|
1170
|
+
useSecret: (s: typeof secret) => s.method(),
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
// VM receives the opaque ref and passes it back; host resolves the original
|
|
1174
|
+
expect(arena.evalCode(`useSecret(getSecret())`)).toBe(42);
|
|
1175
|
+
|
|
1176
|
+
// host -> VM -> host keeps identity
|
|
1177
|
+
const echo = arena.evalCode<(x: typeof secret) => typeof secret>(`x => x`);
|
|
1178
|
+
expect(echo(secret)).toBe(secret);
|
|
1179
|
+
|
|
1180
|
+
// the guest cannot read into the opaque ref
|
|
1181
|
+
expect(
|
|
1182
|
+
arena.evalCode(`(() => { try { return getSecret().hidden; } catch { return "threw"; } })()`),
|
|
1183
|
+
).not.toBe(42);
|
|
1184
|
+
|
|
1185
|
+
arena.dispose();
|
|
1186
|
+
ctx.dispose();
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
test("resolves host refs nested in objects", async () => {
|
|
1190
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1191
|
+
const secret = { token: "abc" };
|
|
1192
|
+
const arena = new Arena(ctx, {
|
|
1193
|
+
isMarshalable: true,
|
|
1194
|
+
marshalByReference: t => t === secret,
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
arena.expose({ wrap: () => ({ inner: secret, label: "x" }) });
|
|
1198
|
+
const out = arena.evalCode(`wrap()`);
|
|
1199
|
+
expect(out.label).toBe("x");
|
|
1200
|
+
expect(out.inner).toBe(secret);
|
|
1201
|
+
|
|
1202
|
+
arena.dispose();
|
|
1203
|
+
ctx.dispose();
|
|
1204
|
+
});
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
describe("BigInt", () => {
|
|
1208
|
+
test("roundtrip", async () => {
|
|
1209
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1210
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1211
|
+
|
|
1212
|
+
expect(arena.evalCode(`123n`)).toBe(123n);
|
|
1213
|
+
expect(arena.evalCode(`2n ** 64n`)).toBe(2n ** 64n);
|
|
1214
|
+
|
|
1215
|
+
const double = arena.evalCode<(x: bigint) => bigint>(`x => x * 2n`);
|
|
1216
|
+
expect(double(21n)).toBe(42n);
|
|
1217
|
+
expect(double(9007199254740993n)).toBe(18014398509481986n);
|
|
1218
|
+
|
|
1219
|
+
arena.dispose();
|
|
1220
|
+
ctx.dispose();
|
|
1221
|
+
});
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
describe("ArrayBuffer and TypedArray", () => {
|
|
1225
|
+
test("roundtrip", async () => {
|
|
1226
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1227
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1228
|
+
|
|
1229
|
+
const fromVM = arena.evalCode(`new Uint8Array([1, 2, 3, 250])`);
|
|
1230
|
+
expect(fromVM).toBeInstanceOf(Uint8Array);
|
|
1231
|
+
expect(Array.from(fromVM)).toEqual([1, 2, 3, 250]);
|
|
1232
|
+
|
|
1233
|
+
const buf = arena.evalCode(`new ArrayBuffer(4)`);
|
|
1234
|
+
expect(buf).toBeInstanceOf(ArrayBuffer);
|
|
1235
|
+
expect(buf.byteLength).toBe(4);
|
|
1236
|
+
|
|
1237
|
+
const echo = arena.evalCode<(x: Uint8Array) => Uint8Array>(`x => x`);
|
|
1238
|
+
const back = echo(new Uint8Array([9, 8, 7]));
|
|
1239
|
+
expect(back).toBeInstanceOf(Uint8Array);
|
|
1240
|
+
expect(Array.from(back)).toEqual([9, 8, 7]);
|
|
1241
|
+
|
|
1242
|
+
const sum = arena.evalCode<(a: Float64Array) => number>(
|
|
1243
|
+
`a => a.reduce((x, y) => x + y, 0)`,
|
|
1244
|
+
);
|
|
1245
|
+
expect(sum(new Float64Array([1.5, 2.5, 3]))).toBe(7);
|
|
1246
|
+
|
|
1247
|
+
arena.dispose();
|
|
1248
|
+
ctx.dispose();
|
|
1249
|
+
});
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
describe("Map and Set", () => {
|
|
1253
|
+
test("roundtrip", async () => {
|
|
1254
|
+
const ctx = (await getQuickJS()).newContext();
|
|
1255
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
1256
|
+
|
|
1257
|
+
const map = arena.evalCode(`new Map([["a", 1], ["b", { n: 2 }]])`);
|
|
1258
|
+
expect(map).toBeInstanceOf(Map);
|
|
1259
|
+
expect(map.get("a")).toBe(1);
|
|
1260
|
+
expect(map.get("b")).toEqual({ n: 2 });
|
|
1261
|
+
|
|
1262
|
+
const set = arena.evalCode(`new Set([1, 2, 3])`);
|
|
1263
|
+
expect(set).toBeInstanceOf(Set);
|
|
1264
|
+
expect([...set]).toEqual([1, 2, 3]);
|
|
1265
|
+
|
|
1266
|
+
const mapSum = arena.evalCode<(m: Map<string, number>) => number>(
|
|
1267
|
+
`m => { let s = 0; for (const v of m.values()) s += v; return s; }`,
|
|
1268
|
+
);
|
|
1269
|
+
expect(mapSum(new Map([["x", 10], ["y", 20]]))).toBe(30);
|
|
1270
|
+
|
|
1271
|
+
const echo = arena.evalCode<(s: Set<string>) => Set<string>>(`s => s`);
|
|
1272
|
+
const back = echo(new Set(["p", "q"]));
|
|
1273
|
+
expect(back).toBeInstanceOf(Set);
|
|
1274
|
+
expect([...back]).toEqual(["p", "q"]);
|
|
1275
|
+
|
|
1276
|
+
arena.dispose();
|
|
1277
|
+
ctx.dispose();
|
|
1278
|
+
});
|
|
1279
|
+
});
|