quickjs-emscripten-sync 1.5.1 → 1.5.2
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/dist/index.d.ts +8 -2
- package/dist/quickjs-emscripten-sync.mjs +590 -450
- package/dist/quickjs-emscripten-sync.umd.js +17 -10
- package/package.json +6 -6
- package/src/contextex.test.ts +43 -0
- package/src/contextex.ts +65 -0
- package/src/edge.test.ts +70 -0
- package/src/index.test.ts +36 -39
- package/src/index.ts +6 -2
- package/src/marshal/index.test.ts +2 -2
- package/src/vmmap.ts +1 -1
- package/src/wrapper.ts +44 -32
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(m,v){typeof exports=="object"&&typeof module!="undefined"?v(exports):typeof define=="function"&&define.amd?define(["exports"],v):(m=typeof globalThis!="undefined"?globalThis:m||self,v(m.QuickjsEmscriptenSync={}))})(this,function(m){"use strict";var ve=Object.defineProperty;var we=(m,v,j)=>v in m?ve(m,v,{enumerable:!0,configurable:!0,writable:!0,value:j}):m[v]=j;var c=(m,v,j)=>(we(m,typeof v!="symbol"?v+"":v,j),j);const v=n=>{const e=new j(n);return new Proxy(n,{get(t,s,r){return s in e?e[s]:Reflect.get(t,s,r)}})};class j{constructor(e){c(this,"context");c(this,"fn");c(this,"fnGenerator");c(this,"fnCounter",Number.MIN_SAFE_INTEGER);c(this,"fnMap",new Map);c(this,"newFunction",(e,t)=>{this.fnCounter++;const s=this.fnCounter;return this.fnMap.set(s,t),this.context.unwrapResult(this.context.callFunction(this.fnGenerator,this.context.undefined,this.context.newString(e),this.context.newNumber(t.length),this.context.newNumber(s),this.fn))});this.context=e;const t=this.fnMap;this.fn=this.context.newFunction("",function(s,...r){const o=e.getNumber(s),i=t.get(o);if(!i)throw new Error("function is not registered");return i.call(this,...r)}),this.fnGenerator=e.unwrapResult(e.evalCode(`((name, length, id, f) => {
|
|
2
|
+
const fn = function(...args) {
|
|
3
|
+
return f.call(this, id, ...args);
|
|
4
|
+
};
|
|
5
|
+
fn.name = name;
|
|
6
|
+
fn.length = length;
|
|
7
|
+
return fn;
|
|
8
|
+
})`))}disposeEx(){this.fnGenerator.dispose(),this.fn.dispose()}}const k=[[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 U(n,e){const t=n.unwrapResult(n.evalCode(e)),s=(r,...o)=>n.unwrapResult(n.callFunction(t,r!=null?r:n.undefined,...o));return s.dispose=()=>t.dispose(),s.alive=!0,Object.defineProperty(s,"alive",{get:()=>t.alive}),s}function _(n,e,t,...s){const r=U(n,e);try{return r(t,...s)}finally{r.dispose()}}function C(n,e,t){return n.dump(_(n,"Object.is",void 0,e,t))}function W(n,e,t){return n.dump(_(n,"(a, b) => a instanceof b",void 0,e,t))}function F(n,e){return n.dump(_(n,'a => typeof a === "object" && a !== null || typeof a === "function"',void 0,e))}function H(n,e){const t=JSON.stringify(e);return t?_(n,"JSON.parse",void 0,n.newString(t)):n.undefined}function z(n,e){try{return e(n)}finally{for(const t of n)t.alive&&t.dispose()}}function $([n,e],t){try{return t(n)}finally{e&&n.dispose()}}function O(n,e){try{return e(...n.map(t=>t[0]))}finally{for(const[t,s]of n)s&&t.dispose()}}function q(n){return"handle"in n}function Q(n){return q(n)?n.handle:n}function V(n,e,t,s){var o;let r;for(const i of s)if(r=i(e,n),r)break;return r?(o=t(e,r))!=null?o:r:void 0}function L(n,e){return typeof n!="symbol"?void 0:_(e,"d => Symbol(d)",void 0,n.description?e.newString(n.description):e.undefined)}function X(n,e){return n instanceof Date?_(e,"d => new Date(d)",void 0,e.newNumber(n.getTime())):void 0}const Y=[L,X];function N(n){return typeof n=="function"&&/^class\s/.test(Function.prototype.toString.call(n))}function g(n){return typeof n=="function"||typeof n=="object"&&n!==null}function S(n,e){const t=new Set,s=r=>{if(!(!g(r)||t.has(r)||(e==null?void 0:e(r,t))===!1)){if(t.add(r),Array.isArray(r)){for(const o of r)s(o);return}if(typeof r=="object"){const o=Object.getPrototypeOf(r);o&&o!==Object.prototype&&s(o)}for(const o of Object.values(Object.getOwnPropertyDescriptors(r)))"value"in o&&s(o.value),"get"in o&&s(o.get),"set"in o&&s(o.set)}};return s(n),t}function Z(n,e){return S(n,e?(t,s)=>s.size<e:void 0).size}function K(){let n=()=>{},e=()=>{};return{promise:new Promise((s,r)=>{n=s,e=r}),resolve:n,reject:e}}function A(n,e,t,s){const r=n.newObject(),o=(a,u)=>{const p=s(a),l=typeof u.value=="undefined"?void 0:s(u.value),f=typeof u.get=="undefined"?void 0:s(u.get),d=typeof u.set=="undefined"?void 0:s(u.set);n.newObject().consume(h=>{Object.entries(u).forEach(([y,b])=>{const w=y==="value"?l:y==="get"?f:y==="set"?d:b?n.true:n.false;w&&n.setProp(h,y,w)}),n.setProp(r,p,h)})},i=Object.getOwnPropertyDescriptors(e);Object.entries(i).forEach(([a,u])=>o(a,u)),Object.getOwnPropertySymbols(i).forEach(a=>o(a,i[a])),_(n,"Object.defineProperties",void 0,t,r).dispose(),r.dispose()}function x(n,e,t,s,r,o){var u;if(typeof e!="function")return;const i=n.newFunction(e.name,function(...p){const l=s(this),f=p.map(d=>s(d));if(N(e)&&g(l)){const d=new e(...f);return Object.entries(d).forEach(([h,y])=>{n.setProp(this,h,t(y))}),this}return t(o?o(e,l,f):e.apply(l,f))}).consume(p=>_(n,`Cls => {
|
|
2
9
|
const fn = function(...args) { return Cls.apply(this, args); };
|
|
3
10
|
fn.name = Cls.name;
|
|
4
11
|
fn.length = Cls.length;
|
|
5
12
|
return fn;
|
|
6
|
-
}`,void 0,p)),
|
|
13
|
+
}`,void 0,p)),a=(u=r(e,i))!=null?u:i;return A(n,e,i,t),a}function ee(n,e,t){var o;const s=H(n,e);return(o=t(e,s))!=null?o:s}function te(n,e,t,s){var u;if(typeof e!="object"||e===null)return;const r=Array.isArray(e)?n.newArray():n.newObject(),o=(u=s(e,r))!=null?u:r,i=Object.getPrototypeOf(e),a=i&&i!==Object.prototype&&i!==Array.prototype?t(i):void 0;return a&&_(n,"Object.setPrototypeOf",void 0,o,a).dispose(),A(n,e,r,t),o}function ne(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"object":return e===null?n.null:void 0}}function re(n,e,t,s){var o;if(!(e instanceof Promise))return;const r=n.newPromise();return e.then(i=>r.resolve(t(i)),i=>r.reject(t(i))),(o=s(e,r))!=null?o:r.handle}function E(n,e){var l,f,d,h,y;const{ctx:t,unmarshal:s,isMarshalable:r,find:o,pre:i}=e;{const b=ne(t,n);if(b)return b}{const b=o(n);if(b)return b}const a=r==null?void 0:r(n);if(a===!1)return t.undefined;const u=(b,w)=>i(b,w,a);if(a==="json")return ee(t,n,u);const p=b=>E(b,e);return(y=(h=(d=(f=V(t,n,u,[...Y,...(l=e.custom)!=null?l:[]]))!=null?f:re(t,n,p,u))!=null?d:x(t,n,p,s,u,e.preApply))!=null?h:te(t,n,p,u))!=null?y:t.undefined}function se(n,e,t,s){var o;let r;for(const i of s)if(r=i(e,n),r)break;return r?(o=t(r,e))!=null?o:r:void 0}function oe(n,e){if(e.typeof(n)!=="symbol")return;const t=e.getString(e.getProp(n,"description"));return Symbol(t)}function ie(n,e){if(!e.dump(_(e,"a => a instanceof Date",void 0,n)))return;const t=e.getNumber(_(e,"a => a.getTime()",void 0,n));return new Date(t)}const ue=[oe,ie];function D(n,e,t,s){n.newFunction("",(r,o)=>{const[i]=s(r);if(typeof i!="string"&&typeof i!="number"&&typeof i!="symbol")return;const a=[["value",!0],["get",!0],["set",!0],["configurable",!1],["enumerable",!1],["writable",!1]].reduce((u,[p,l])=>{const f=n.getProp(o,p),d=n.typeof(f);if(d==="undefined")return u;if(!l&&d==="boolean")return u[p]=n.dump(n.getProp(o,p)),u;const[h,y]=s(f);return y&&f.dispose(),u[p]=h,u},{});Object.defineProperty(t,i,a)}).consume(r=>{_(n,`(o, fn) => {
|
|
7
14
|
const descs = Object.getOwnPropertyDescriptors(o);
|
|
8
15
|
Object.entries(descs).forEach(([k, v]) => fn(k, v));
|
|
9
16
|
Object.getOwnPropertySymbols(descs).forEach(k => fn(k, descs[k]));
|
|
10
|
-
}`,void 0,e,
|
|
17
|
+
}`,void 0,e,r).dispose()})}function ae(n,e,t,s,r){var a;if(n.typeof(e)!=="function")return;const o=function(...u){return O([t(this),...u.map(p=>t(p))],(p,...l)=>{if(new.target){const[y]=s(_(n,"(Cls, ...args) => new Cls(...args)",p,e,...l));return Object.defineProperties(this,Object.getOwnPropertyDescriptors(y)),this}const f=n.unwrapResult(n.callFunction(e,p,...l)),[d,h]=s(f);return h&&f.dispose(),d})},i=(a=r(o,e))!=null?a:o;return D(n,e,o,s),i}function pe(n,e,t,s){var a;if(n.typeof(e)!=="object"||n.unwrapResult(n.evalCode("o => o === null")).consume(u=>n.dump(n.unwrapResult(n.callFunction(u,n.undefined,e)))))return;const r=_(n,"Array.isArray",void 0,e).consume(u=>n.dump(u))?[]:{},o=(a=s(r,e))!=null?a:r,i=_(n,`o => {
|
|
11
18
|
const p = Object.getPrototypeOf(o);
|
|
12
19
|
return !p || p === Object.prototype || p === Array.prototype ? undefined : p;
|
|
13
|
-
}`,void 0,e).consume(
|
|
20
|
+
}`,void 0,e).consume(u=>{if(n.typeof(u)==="undefined")return;const[p]=t(u);return p});return typeof i=="object"&&Object.setPrototypeOf(o,i),D(n,e,r,t),o}function fe(n,e){const t=n.typeof(e);return t==="undefined"||t==="number"||t==="string"||t==="boolean"?[n.dump(e),!0]:t==="object"&&n.unwrapResult(n.evalCode("a => a === null")).consume(r=>n.dump(n.unwrapResult(n.callFunction(r,n.undefined,e))))?[null,!0]:[void 0,!1]}function ce(n,e,t,s){var p;if(!le(n,e))return;const r=K(),[o,i]=t(r.resolve),[a,u]=t(r.reject);return _(n,"(p, res, rej) => { p.then(res, rej); }",void 0,e,o,a),i&&o.dispose(),u&&a.dispose(),(p=s(r.promise,e))!=null?p:r.promise}function le(n,e){return e.owner?n.unwrapResult(n.evalCode("Promise")).consume(t=>e.owner?W(n,e,t):!1):!1}function B(n,e){const[t]=T(n,e);return t}function T(n,e){var u,p,l,f;const{ctx:t,marshal:s,find:r,pre:o}=e;{const[d,h]=fe(t,n);if(h)return[d,!1]}{const d=r(n);if(d)return[d,!0]}const i=d=>T(d,e);return[(f=(l=(p=se(t,n,o,[...ue,...(u=e.custom)!=null?u:[]]))!=null?p:ce(t,n,s,o))!=null?l:ae(t,n,s,i,o))!=null?f:pe(t,n,i,o),!1]}class R{constructor(e){c(this,"ctx");c(this,"_map1",new Map);c(this,"_map2",new Map);c(this,"_map3",new Map);c(this,"_map4",new Map);c(this,"_counterMap",new Map);c(this,"_disposables",new Set);c(this,"_mapGet");c(this,"_mapSet");c(this,"_mapDelete");c(this,"_mapClear");c(this,"_counter",Number.MIN_SAFE_INTEGER);this.ctx=e;const t=e.unwrapResult(e.evalCode(`() => {
|
|
14
21
|
const mapSym = new Map();
|
|
15
22
|
let map = new WeakMap();
|
|
16
23
|
let map2 = new WeakMap();
|
|
@@ -33,7 +40,7 @@
|
|
|
33
40
|
map2 = new WeakMap();
|
|
34
41
|
}
|
|
35
42
|
};
|
|
36
|
-
}`)).consume(
|
|
43
|
+
}`)).consume(s=>this._call(s,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,s,r){var a;if(!t.alive||r&&!r.alive)return!1;const o=(a=this.get(e))!=null?a:this.get(s);if(o)return o===t||o===r;const i=this._counter++;return this._map1.set(e,i),this._map3.set(i,t),this._counterMap.set(i,e),s&&(this._map2.set(s,i),r&&this._map4.set(i,r)),this.ctx.newNumber(i).consume(u=>{this._call(this._mapSet,void 0,t,u,r!=null?r: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 r;const t=(r=this._map1.get(e))!=null?r:this._map2.get(e),s=typeof t=="number"?this._map3.get(t):void 0;if(s){if(!s.alive){this.delete(e);return}return s}}getByHandle(e){if(e.alive)return this._counterMap.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._map1.keys()}delete(e,t){var i;const s=(i=this._map1.get(e))!=null?i:this._map2.get(e);if(typeof s=="undefined")return;const r=this._map3.get(s),o=this._map4.get(s);this._call(this._mapDelete,void 0,...[r,o].filter(a=>!!(a!=null&&a.alive))),this._map1.delete(e),this._map2.delete(e),this._map3.delete(s),this._map4.delete(s);for(const[a,u]of this._map1)if(u===s){this._map1.delete(a);break}for(const[a,u]of this._map2)if(u===s){this._map2.delete(a);break}for(const[a,u]of this._counterMap)if(u===e){this._counterMap.delete(a);break}t&&(r!=null&&r.alive&&r.dispose(),o!=null&&o.alive&&o.dispose())}deleteByHandle(e,t){const s=this.getByHandle(e);typeof s!="undefined"&&this.delete(s,t)}clear(){this._counter=0,this._map1.clear(),this._map2.clear(),this._map3.clear(),this._map4.clear(),this._counterMap.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._map3.values())e.alive&&e.dispose();for(const e of this._map4.values())e.alive&&e.dispose();this._disposables.clear(),this.clear()}get size(){return this._map1.size}[Symbol.iterator](){const e=this._map1.keys();return{next:()=>{for(;;){const t=e.next();if(t.done)return{value:void 0,done:!0};const s=this._map1.get(t.value);if(typeof s=="undefined")continue;const r=this._map3.get(s),o=this._map4.get(s);if(!r)continue;const i=this._get2(s);return{value:[t.value,r,i,o],done:!1}}}}}_get2(e){for(const[t,s]of this._map2)if(s===e)return t}_call(e,t,...s){return this.ctx.unwrapResult(this.ctx.callFunction(e,typeof t=="undefined"?this.ctx.undefined:t,...s))}}function de(n,e,t,s,r,o,i){if(!g(e)||e instanceof Promise||e instanceof Date||i&&!i(e))return;if(he(e,t))return e;const a=new Proxy(e,{get(u,p){return p===t?u:Reflect.get(u,p)},set(u,p,l,f){var y;const d=P(l,t),h=(y=o==null?void 0:o(f))!=null?y:"host";return h!=="vm"&&!Reflect.set(u,p,d,f)||h==="host"||!n.alive||O([r(f),r(p),r(d)],(b,w,G)=>{const[J,_e]=M(n,b,s);_e?J.consume(be=>n.setProp(be,w,G)):n.setProp(J,w,G)}),!0},deleteProperty(u,p){var f;const l=(f=o==null?void 0:o(a))!=null?f:"host";return O([r(a),r(p)],(d,h)=>{const[y,b]=M(n,d,s);if(l==="vm"||Reflect.deleteProperty(u,p)){if(l==="host"||!n.alive)return!0;b?y.consume(w=>_(n,"(a, b) => delete a[b]",void 0,w,h)):_(n,"(a, b) => delete a[b]",void 0,y,h)}return!0})}});return a}function me(n,e,t,s,r,o,i){if(!F(n,e)||i&&!i(e,n))return[void 0,!1];if(I(n,e,s))return[e,!1];const a=l=>{const f=o==null?void 0:o(r(l));return typeof f=="string"?n.newString(f):n.undefined},u=(l,f,d)=>{const h=r(l);if(!h)return;const y=r(f);if(y==="__proto__")return;const b=r(d);P(h,t)[y]=b},p=(l,f)=>{const d=r(l);if(!d)return;const h=r(f);delete P(d,t)[h]};return n.newFunction("proxyFuncs",(l,...f)=>{switch(n.getNumber(l)){case 1:return a(f[0]);case 2:return u(f[0],f[1],f[2]);case 3:return p(f[0],f[1])}return n.undefined}).consume(l=>[_(n,`(target, sym, proxyFuncs) => {
|
|
37
44
|
const rec = new Proxy(target, {
|
|
38
45
|
get(obj, key, receiver) {
|
|
39
46
|
return key === sym ? obj : Reflect.get(obj, key, receiver)
|
|
@@ -42,23 +49,23 @@
|
|
|
42
49
|
const v = typeof value === "object" && value !== null || typeof value === "function"
|
|
43
50
|
? value[sym] ?? value
|
|
44
51
|
: value;
|
|
45
|
-
const sync =
|
|
52
|
+
const sync = proxyFuncs(1, receiver) ?? "vm";
|
|
46
53
|
if (sync === "host" || Reflect.set(obj, key, v, receiver)) {
|
|
47
54
|
if (sync !== "vm") {
|
|
48
|
-
|
|
55
|
+
proxyFuncs(2, receiver, key, v);
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
return true;
|
|
52
59
|
},
|
|
53
60
|
deleteProperty(obj, key) {
|
|
54
|
-
const sync =
|
|
61
|
+
const sync = proxyFuncs(1, rec) ?? "vm";
|
|
55
62
|
if (sync === "host" || Reflect.deleteProperty(obj, key)) {
|
|
56
63
|
if (sync !== "vm") {
|
|
57
|
-
|
|
64
|
+
proxyFuncs(3, rec, key);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
return true;
|
|
61
68
|
},
|
|
62
69
|
});
|
|
63
70
|
return rec;
|
|
64
|
-
}`,void 0,e,
|
|
71
|
+
}`,void 0,e,s,l),!0])}function P(n,e){var t;return g(n)&&(t=n[e])!=null?t:n}function M(n,e,t){return I(n,e,t)?[n.getProp(e,t),!0]:[e,!1]}function he(n,e){return g(n)&&!!n[e]}function I(n,e,t){return!!n.dump(_(n,'(a, s) => (a instanceof Promise) || (a instanceof Date) || (typeof a === "object" && a !== null || typeof a === "function") && !!a[s]',void 0,e,t))}class ye{constructor(e,t){c(this,"context");c(this,"_map");c(this,"_registeredMap");c(this,"_registeredMapDispose",new Set);c(this,"_sync",new Set);c(this,"_temporalSync",new Set);c(this,"_symbol",Symbol());c(this,"_symbolHandle");c(this,"_options");c(this,"_isMarshalable",e=>{var s,r;const t=(s=this._options)==null?void 0:s.isMarshalable;return(r=typeof t=="function"?t(this._unwrap(e)):t)!=null?r:"json"});c(this,"_marshalFind",e=>{var r,o,i;const t=this._unwrap(e);return(i=(o=(r=this._registeredMap.get(e))!=null?r:t!==e?this._registeredMap.get(t):void 0)!=null?o:this._map.get(e))!=null?i:t!==e?this._map.get(t):void 0});c(this,"_marshalPre",(e,t,s)=>{var r;if(s!=="json")return(r=this._register(e,Q(t),this._map))==null?void 0:r[1]});c(this,"_marshalPreApply",(e,t,s)=>{const r=g(t)?this._unwrap(t):void 0;r&&this._temporalSync.add(r);try{return e.apply(t,s)}finally{r&&this._temporalSync.delete(r)}});c(this,"_marshal",e=>{var r,o;const t=this._registeredMap.get(e);if(t)return[t,!1];const s=E((r=this._wrap(e))!=null?r:e,{ctx:this.context,unmarshal:this._unmarshal,isMarshalable:this._isMarshalable,find:this._marshalFind,pre:this._marshalPre,preApply:this._marshalPreApply,custom:(o=this._options)==null?void 0:o.customMarshaller});return[s,!this._map.hasHandle(s)]});c(this,"_preUnmarshal",(e,t)=>{var s;return(s=this._register(e,t,void 0,!0))==null?void 0:s[0]});c(this,"_unmarshalFind",e=>{var t;return(t=this._registeredMap.getByHandle(e))!=null?t:this._map.getByHandle(e)});c(this,"_unmarshal",e=>{var r;const t=this._registeredMap.getByHandle(e);if(typeof t!="undefined")return t;const[s]=this._wrapHandle(e);return B(s!=null?s:e,{ctx:this.context,marshal:this._marshal,find:this._unmarshalFind,pre:this._preUnmarshal,custom:(r=this._options)==null?void 0:r.customUnmarshaller})});c(this,"_syncMode",e=>{const t=this._unwrap(e);return this._sync.has(t)||this._temporalSync.has(t)?"both":void 0});c(this,"_unwrapIfNotSynced",e=>{const t=this._unwrap(e);return t instanceof Promise||!this._sync.has(t)?t:e});var s;t!=null&&t.compat&&!("runtime"in e)&&(e.runtime={hasPendingJob:()=>e.hasPendingJob(),executePendingJobs:r=>e.executePendingJobs(r)}),this.context=t!=null&&t.experimentalContextEx?v(e):e,this._options=t,this._symbolHandle=e.unwrapResult(e.evalCode("Symbol()")),this._map=new R(e),this._registeredMap=new R(e),this.registerAll((s=t==null?void 0:t.registeredObjects)!=null?s:k)}dispose(){var e,t;this._map.dispose(),this._registeredMap.dispose(),this._symbolHandle.dispose(),(t=(e=this.context).disposeEx)==null||t.call(e)}evalCode(e){const t=this.context.evalCode(e);return this._unwrapResultAndUnmarshal(t)}executePendingJobs(e){const t=this.context.runtime.executePendingJobs(e);if("value"in t)return t.value;throw this._unwrapIfNotSynced(t.error.consume(this._unmarshal))}expose(e){for(const[t,s]of Object.entries(e))$(this._marshal(s),r=>{this.context.setProp(this.context.global,t,r)})}sync(e){const t=this._wrap(e);return typeof t=="undefined"?e:(S(t,s=>{const r=this._unwrap(s);this._sync.add(r)}),t)}register(e,t){if(this._registeredMap.has(e))return;const s=typeof t=="string"?this._unwrapResult(this.context.evalCode(t)):t;C(this.context,s,this.context.undefined)||(typeof t=="string"&&this._registeredMapDispose.add(e),this._registeredMap.set(e,s))}registerAll(e){for(const[t,s]of e)this.register(t,s)}unregister(e,t){this._registeredMap.delete(e,this._registeredMapDispose.has(e)||t),this._registeredMapDispose.delete(e)}unregisterAll(e,t){for(const s of e)this.unregister(s,t)}startSync(e){if(!g(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(e.error.consume(this._unmarshal))}_unwrapResultAndUnmarshal(e){if(e)return this._unwrapIfNotSynced(this._unwrapResult(e).consume(this._unmarshal))}_register(e,t,s=this._map,r){if(this._registeredMap.has(e)||this._registeredMap.hasHandle(t))return;let o=this._wrap(e);const[i]=this._wrapHandle(t),a=e instanceof Promise;if(!i||!o&&!a)return;a&&(o=e);const u=this._unwrap(e),[p,l]=this._unwrapHandle(t);if(s.set(o,i,u,p))r&&this._sync.add(u);else throw l&&p.dispose(),new Error("already registered");return[o,i]}_wrap(e){var t;return de(this.context,e,this._symbol,this._symbolHandle,this._marshal,this._syncMode,(t=this._options)==null?void 0:t.isWrappable)}_unwrap(e){return P(e,this._symbol)}_wrapHandle(e){var t;return me(this.context,e,this._symbol,this._symbolHandle,this._unmarshal,this._syncMode,(t=this._options)==null?void 0:t.isHandleWrappable)}_unwrapHandle(e){return M(this.context,e,this._symbolHandle)}}m.Arena=ye,m.VMMap=R,m.call=_,m.complexity=Z,m.consumeAll=z,m.defaultRegisteredObjects=k,m.eq=C,m.isES2015Class=N,m.isHandleObject=F,m.isObject=g,m.json=H,m.marshal=E,m.unmarshal=B,m.walkObject=S,Object.defineProperty(m,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.5.
|
|
4
|
+
"version": "1.5.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./dist/quickjs-emscripten-sync.umd.js",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"quickjs-emscripten": "*"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@vitest/coverage-c8": "^0.
|
|
34
|
+
"@vitest/coverage-c8": "^0.28.5",
|
|
35
35
|
"eslint": "^8.22.0",
|
|
36
|
-
"eslint-config-reearth": "^0.1
|
|
36
|
+
"eslint-config-reearth": "^0.2.1",
|
|
37
37
|
"prettier": "^2.7.1",
|
|
38
38
|
"quickjs-emscripten": "^0.21.0",
|
|
39
39
|
"typescript": "^4.8.2",
|
|
40
|
-
"vite": "^
|
|
41
|
-
"vite-plugin-dts": "^
|
|
42
|
-
"vitest": "^0.
|
|
40
|
+
"vite": "^4.1.2",
|
|
41
|
+
"vite-plugin-dts": "^2.0.0-beta.1",
|
|
42
|
+
"vitest": "^0.28.5"
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getQuickJS } from "quickjs-emscripten";
|
|
2
|
+
import { expect, test, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { ContextEx, wrapContext } from "./contextex";
|
|
5
|
+
|
|
6
|
+
test("wrapContext", async () => {
|
|
7
|
+
const ctx = (await getQuickJS()).newContext();
|
|
8
|
+
const c = wrapContext(ctx);
|
|
9
|
+
c.disposeEx?.();
|
|
10
|
+
ctx.dispose();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("ContextEx", async () => {
|
|
14
|
+
try {
|
|
15
|
+
const ctx = (await getQuickJS()).newContext();
|
|
16
|
+
const spy = vi.spyOn(ctx, "newFunction");
|
|
17
|
+
const ctxex = new ContextEx(ctx);
|
|
18
|
+
|
|
19
|
+
const handle = ctxex.newFunction("", function (handle) {
|
|
20
|
+
expect(ctx.getString(this)).toBe("this");
|
|
21
|
+
expect(ctx.getNumber(handle)).toBe(100);
|
|
22
|
+
return ctx.newString("result");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < 10000; i++) {
|
|
26
|
+
const res = ctx.unwrapResult(
|
|
27
|
+
ctx.callFunction(handle, ctx.newString("this"), ctx.newNumber(100)),
|
|
28
|
+
);
|
|
29
|
+
expect(ctx.getString(res)).toBe("result");
|
|
30
|
+
res.dispose();
|
|
31
|
+
}
|
|
32
|
+
expect(spy).toBeCalledTimes(1);
|
|
33
|
+
|
|
34
|
+
handle.dispose();
|
|
35
|
+
ctxex.disposeEx();
|
|
36
|
+
ctx.dispose();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// prevent freezing node when printing QuickJSContext
|
|
39
|
+
delete (err as any).context;
|
|
40
|
+
console.error(err);
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
});
|
package/src/contextex.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { QuickJSContext, QuickJSHandle, VmFunctionImplementation } from "quickjs-emscripten";
|
|
2
|
+
|
|
3
|
+
export type QuickJSContextEx = QuickJSContext & {
|
|
4
|
+
disposeEx?: () => void;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const wrapContext = (ctx: QuickJSContext): QuickJSContextEx => {
|
|
8
|
+
const ctxex = new ContextEx(ctx);
|
|
9
|
+
return new Proxy(ctx, {
|
|
10
|
+
get(target, p, receiver) {
|
|
11
|
+
return p in ctxex ? (ctxex as any)[p] : Reflect.get(target, p, receiver);
|
|
12
|
+
},
|
|
13
|
+
}) as QuickJSContextEx;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class ContextEx {
|
|
17
|
+
context: QuickJSContext;
|
|
18
|
+
fn: QuickJSHandle;
|
|
19
|
+
fnGenerator: QuickJSHandle;
|
|
20
|
+
fnCounter = Number.MIN_SAFE_INTEGER;
|
|
21
|
+
fnMap = new Map<number, VmFunctionImplementation<QuickJSHandle>>();
|
|
22
|
+
|
|
23
|
+
constructor(context: QuickJSContext) {
|
|
24
|
+
this.context = context;
|
|
25
|
+
const fnMap = this.fnMap;
|
|
26
|
+
this.fn = this.context.newFunction("", function (idHandle, ...argHandles) {
|
|
27
|
+
const id = context.getNumber(idHandle);
|
|
28
|
+
const f = fnMap.get(id);
|
|
29
|
+
if (!f) throw new Error("function is not registered");
|
|
30
|
+
return f.call(this, ...argHandles);
|
|
31
|
+
});
|
|
32
|
+
this.fnGenerator = context.unwrapResult(
|
|
33
|
+
context.evalCode(`((name, length, id, f) => {
|
|
34
|
+
const fn = function(...args) {
|
|
35
|
+
return f.call(this, id, ...args);
|
|
36
|
+
};
|
|
37
|
+
fn.name = name;
|
|
38
|
+
fn.length = length;
|
|
39
|
+
return fn;
|
|
40
|
+
})`),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
disposeEx(): void {
|
|
45
|
+
this.fnGenerator.dispose();
|
|
46
|
+
this.fn.dispose();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Similar to the original newFunction, but no matter how many new functions are generated, newFunction is called only once. */
|
|
50
|
+
newFunction = (name: string, fn: VmFunctionImplementation<QuickJSHandle>): QuickJSHandle => {
|
|
51
|
+
this.fnCounter++;
|
|
52
|
+
const id = this.fnCounter;
|
|
53
|
+
this.fnMap.set(id, fn);
|
|
54
|
+
return this.context.unwrapResult(
|
|
55
|
+
this.context.callFunction(
|
|
56
|
+
this.fnGenerator,
|
|
57
|
+
this.context.undefined,
|
|
58
|
+
this.context.newString(name),
|
|
59
|
+
this.context.newNumber(fn.length),
|
|
60
|
+
this.context.newNumber(id),
|
|
61
|
+
this.fn,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
}
|
package/src/edge.test.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getQuickJS } from "quickjs-emscripten";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { Arena } from ".";
|
|
5
|
+
|
|
6
|
+
// tests for edge cases
|
|
7
|
+
|
|
8
|
+
describe("edge cases", () => {
|
|
9
|
+
// this test takes more than about 20s
|
|
10
|
+
test("getter", async () => {
|
|
11
|
+
const ctx = (await getQuickJS()).newContext();
|
|
12
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
13
|
+
|
|
14
|
+
const called: string[] = [];
|
|
15
|
+
const obj = { c: 0 };
|
|
16
|
+
const exposed = {
|
|
17
|
+
get a() {
|
|
18
|
+
called.push("a");
|
|
19
|
+
return {
|
|
20
|
+
get b() {
|
|
21
|
+
called.push("b");
|
|
22
|
+
return obj;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const cb: { current?: () => any } = {};
|
|
28
|
+
const register = (fn: () => any) => {
|
|
29
|
+
cb.current = fn;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
arena.expose({ exposed, register });
|
|
33
|
+
expect(called).toEqual([]);
|
|
34
|
+
|
|
35
|
+
arena.evalCode(`register(() => exposed.a.b.c);`);
|
|
36
|
+
expect(cb.current?.()).toBe(0);
|
|
37
|
+
expect(called).toEqual(["a", "b"]);
|
|
38
|
+
|
|
39
|
+
obj.c = 1;
|
|
40
|
+
expect(cb.current?.()).toBe(1); // this line causes an error when context is disposed
|
|
41
|
+
expect(called).toEqual(["a", "b", "a", "b"]);
|
|
42
|
+
|
|
43
|
+
arena.dispose();
|
|
44
|
+
// ctx.dispose(); // reports an error
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("many newFunction", async () => {
|
|
48
|
+
const rt = (await getQuickJS()).newRuntime();
|
|
49
|
+
const ctx = rt.newContext();
|
|
50
|
+
const arena = new Arena(ctx, {
|
|
51
|
+
isMarshalable: true,
|
|
52
|
+
// enable this option to solve this problem
|
|
53
|
+
experimentalContextEx: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
arena.expose({
|
|
57
|
+
hoge: () => {},
|
|
58
|
+
});
|
|
59
|
+
// should have an object as an arg
|
|
60
|
+
const fn = arena.evalCode(`() => { hoge([]); }`);
|
|
61
|
+
// error happens from 3926 times
|
|
62
|
+
for (let i = 0; i < 10000; i++) {
|
|
63
|
+
fn();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
arena.dispose();
|
|
67
|
+
ctx.dispose();
|
|
68
|
+
rt.dispose();
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/index.test.ts
CHANGED
|
@@ -218,6 +218,42 @@ describe("evalCode", () => {
|
|
|
218
218
|
arena.dispose();
|
|
219
219
|
ctx.dispose();
|
|
220
220
|
});
|
|
221
|
+
|
|
222
|
+
test("async function", async () => {
|
|
223
|
+
const ctx = (await getQuickJS()).newContext();
|
|
224
|
+
const arena = new Arena(ctx, { isMarshalable: true });
|
|
225
|
+
|
|
226
|
+
const consolelog = vi.fn();
|
|
227
|
+
arena.expose({
|
|
228
|
+
console: {
|
|
229
|
+
log: consolelog,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
arena.evalCode(`
|
|
234
|
+
const someAsyncOperation = async () => "hello";
|
|
235
|
+
const execute = async () => {
|
|
236
|
+
try {
|
|
237
|
+
const res = await someAsyncOperation();
|
|
238
|
+
console.log(res);
|
|
239
|
+
} catch (e) {
|
|
240
|
+
console.log(e);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
execute();
|
|
244
|
+
`);
|
|
245
|
+
expect(consolelog).toBeCalledTimes(0);
|
|
246
|
+
expect(arena.executePendingJobs()).toBe(2);
|
|
247
|
+
|
|
248
|
+
arena.executePendingJobs();
|
|
249
|
+
|
|
250
|
+
expect(consolelog).toBeCalledTimes(1);
|
|
251
|
+
expect(consolelog).toBeCalledWith("hello");
|
|
252
|
+
expect(arena.executePendingJobs()).toBe(0);
|
|
253
|
+
|
|
254
|
+
arena.dispose();
|
|
255
|
+
ctx.dispose();
|
|
256
|
+
});
|
|
221
257
|
});
|
|
222
258
|
|
|
223
259
|
describe("expose without sync", () => {
|
|
@@ -576,42 +612,3 @@ describe("isMarshalable option", () => {
|
|
|
576
612
|
ctx.dispose();
|
|
577
613
|
});
|
|
578
614
|
});
|
|
579
|
-
|
|
580
|
-
describe("edge cases", () => {
|
|
581
|
-
test("getter", async () => {
|
|
582
|
-
const ctx = (await getQuickJS()).newContext();
|
|
583
|
-
const arena = new Arena(ctx, { isMarshalable: true });
|
|
584
|
-
|
|
585
|
-
const called: string[] = [];
|
|
586
|
-
const obj = { c: 0 };
|
|
587
|
-
const exposed = {
|
|
588
|
-
get a() {
|
|
589
|
-
called.push("a");
|
|
590
|
-
return {
|
|
591
|
-
get b() {
|
|
592
|
-
called.push("b");
|
|
593
|
-
return obj;
|
|
594
|
-
},
|
|
595
|
-
};
|
|
596
|
-
},
|
|
597
|
-
};
|
|
598
|
-
const cb: { current?: () => any } = {};
|
|
599
|
-
const register = (fn: () => any) => {
|
|
600
|
-
cb.current = fn;
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
arena.expose({ exposed, register });
|
|
604
|
-
expect(called).toEqual([]);
|
|
605
|
-
|
|
606
|
-
arena.evalCode(`register(() => exposed.a.b.c);`);
|
|
607
|
-
expect(cb.current?.()).toBe(0);
|
|
608
|
-
expect(called).toEqual(["a", "b"]);
|
|
609
|
-
|
|
610
|
-
obj.c = 1;
|
|
611
|
-
expect(cb.current?.()).toBe(1); // this line causes an error when context is disposed
|
|
612
|
-
expect(called).toEqual(["a", "b", "a", "b"]);
|
|
613
|
-
|
|
614
|
-
arena.dispose();
|
|
615
|
-
// ctx.dispose(); // reports an error
|
|
616
|
-
});
|
|
617
|
-
});
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
VmCallResult,
|
|
7
7
|
} from "quickjs-emscripten";
|
|
8
8
|
|
|
9
|
+
import { wrapContext, QuickJSContextEx } from "./contextex";
|
|
9
10
|
import { defaultRegisteredObjects } from "./default";
|
|
10
11
|
import marshal from "./marshal";
|
|
11
12
|
import unmarshal from "./unmarshal";
|
|
@@ -48,13 +49,15 @@ export type Options = {
|
|
|
48
49
|
isHandleWrappable?: (handle: QuickJSHandle, ctx: QuickJSContext) => boolean;
|
|
49
50
|
/** Compatibility with quickjs-emscripten prior to v0.15. Inject code for compatibility into context at Arena class initialization time. */
|
|
50
51
|
compat?: boolean;
|
|
52
|
+
/** Experimental: use QuickJSContextEx, which wraps existing QuickJSContext. */
|
|
53
|
+
experimentalContextEx?: boolean;
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* The Arena class manages all generated handles at once by quickjs-emscripten and automatically converts objects between the host and the QuickJS VM.
|
|
55
58
|
*/
|
|
56
59
|
export class Arena {
|
|
57
|
-
context:
|
|
60
|
+
context: QuickJSContextEx;
|
|
58
61
|
_map: VMMap;
|
|
59
62
|
_registeredMap: VMMap;
|
|
60
63
|
_registeredMapDispose: Set<any> = new Set();
|
|
@@ -74,7 +77,7 @@ export class Arena {
|
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
this.context = ctx;
|
|
80
|
+
this.context = options?.experimentalContextEx ? wrapContext(ctx) : ctx;
|
|
78
81
|
this._options = options;
|
|
79
82
|
this._symbolHandle = ctx.unwrapResult(ctx.evalCode(`Symbol()`));
|
|
80
83
|
this._map = new VMMap(ctx);
|
|
@@ -89,6 +92,7 @@ export class Arena {
|
|
|
89
92
|
this._map.dispose();
|
|
90
93
|
this._registeredMap.dispose();
|
|
91
94
|
this._symbolHandle.dispose();
|
|
95
|
+
this.context.disposeEx?.();
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
/**
|
|
@@ -153,7 +153,7 @@ test("class", async () => {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
expect(A.name).toBe("
|
|
156
|
+
expect(A.name).toBe("A");
|
|
157
157
|
const handle = marshal(A);
|
|
158
158
|
if (!map) throw new Error("map is undefined");
|
|
159
159
|
|
|
@@ -167,7 +167,7 @@ test("class", async () => {
|
|
|
167
167
|
|
|
168
168
|
expect(ctx.typeof(handle)).toBe("function");
|
|
169
169
|
expect(ctx.dump(ctx.getProp(handle, "length"))).toBe(1);
|
|
170
|
-
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("
|
|
170
|
+
expect(ctx.dump(ctx.getProp(handle, "name"))).toBe("A");
|
|
171
171
|
const staticA = ctx.getProp(handle, "a");
|
|
172
172
|
expect(instanceOf(ctx, staticA, handle)).toBe(true);
|
|
173
173
|
expect(ctx.dump(ctx.getProp(staticA, "a"))).toBe(100);
|