Comments (8)
I try to simplify the call of dispose()
in another more general way @crysislinux
import { QuickJSHandle, QuickJSVm } from 'quickjs-emscripten'
const QuickJSVmScopeSymbol = Symbol('QuickJSVmScope')
/**
* 为 QuickJSVm 添加局部作用域,局部作用域的所有方法调用不再需要手动释放内存
* @param vm
* @param handle
*/
export function withScope<F extends (vm: QuickJSVm) => any>(
vm: QuickJSVm,
handle: F,
): {
value: ReturnType<F>
dispose(): void
} {
let disposes: (() => void)[] = []
function wrap(handle: QuickJSHandle) {
disposes.push(() => handle.alive && handle.dispose())
return handle
}
//避免多层代理
const isProxy = !!Reflect.get(vm, QuickJSVmScopeSymbol)
function dispose() {
if (isProxy) {
Reflect.get(vm, QuickJSVmScopeSymbol)()
return
}
disposes.forEach((dispose) => dispose())
//手动释放闭包变量的内存
disposes.length = 0
}
const value = handle(
isProxy
? vm
: new Proxy(vm, {
get(
target: QuickJSVm,
p: keyof QuickJSVm | typeof QuickJSVmScopeSymbol,
): any {
if (p === QuickJSVmScopeSymbol) {
return dispose
}
//锁定所有方法的 this 值为 QuickJSVm 对象而非 Proxy 对象
const res = Reflect.get(target, p, target)
if (
p.startsWith('new') ||
['getProp', 'unwrapResult'].includes(p)
) {
return (...args: any[]): QuickJSHandle => {
return wrap(Reflect.apply(res, target, args))
}
}
if (['evalCode', 'callFunction'].includes(p)) {
return (...args: any[]) => {
const res = (target[p] as any)(...args)
disposes.push(() => {
const handle = res.error ?? res.value
handle.alive && handle.dispose()
})
return res
}
}
if (typeof res === 'function') {
return (...args: any[]) => {
return Reflect.apply(res, target, args)
}
}
return res
},
}),
)
return { value, dispose }
}
withScope(vm, (vm) => {
const _hello = vm.newFunction('hello', () => {})
const _object = vm.newObject()
vm.setProp(_object, 'hello', _hello)
vm.setProp(_object, 'name', vm.newString('liuli'))
expect(vm.dump(vm.getProp(_object, 'hello'))).not.toBeNull()
vm.setProp(vm.global, 'VM_GLOBAL', _object)
}).dispose()
from quickjs-emscripten.
Here's a quick (untested) example, based on js-sandbox-demo
.
const disposables = [];
const trueHandle = vm.unwrapResult(vm.evalCode('true'));
const falseHandle = vm.unwrapResult(vm.evalCode('false'));
const nullHandle = vm.unwrapResult(vm.evalCode('null'));
function marshal(target) {
switch (typeof target) {
case 'number': {
const handle = vm.newNumber(target);
disposables.push(handle);
return handle;
}
case 'string': {
const handle = vm.newString(target);
disposables.push(handle);
return handle;
}
case 'undefined': {
return vm.undefined;
}
case 'boolean': {
return target ? trueHandle : falseHandle;
}
case 'object': {
if (target === null) {
return nullHandle;
}
if (Array.isArray(target)) {
const array = vm.unwrapResult(vm.evalCode('([])'));
disposables.push(array);
target.forEach((item) => {
const marshaledItem = marshal(item);
disposables.push(marshaledItem);
const push = vm.getProp(array, 'push');
vm.callFunction(push, array, marshaledItem);
});
return array;
}
const obj = vm.newObject();
disposables.push(obj);
Object.keys(target).forEach((key) => {
const value = target[key];
const marshaledKey = marshal(key);
const marshaledValue = marshal(value);
vm.setProp(obj, marshaledKey, marshaledValue);
});
return obj;
}
default: {
throw new Error(`${typeof target} marshaling is not supported`);
}
}
}
// Call this when done, to dispose all handles
function dispose() {
disposables.forEach((disposable) => disposable.alive && disposable.dispose())
}
// Define globals in vm
const globals = {
// Can pass nested values
};
Object.keys(globals).forEach(key => {
vm.setProp(
vm.global,
key,
marshal(globals[key])
);
});
from quickjs-emscripten.
@crysislinux I'll add VM-wide constants vm.true
, vm.false
, and vm.null
the way we currrently have vm.undefined
.
I can also expose a vm.newArray()
that creates empty arrays.
Personally I use JSON.stringify
to copy data into the VM for testing purposes. I haven't evaluated if recursive marshalling like @eliot-akira's example is faster or slower than JSON.stringify - it could be that the GC pressure of creating all the Lifetime
wrapper objects is more expensive than the JSON.stringify / code parsing stuff. Probably moot for small object, but something to keep in mind for your testing if data copy speed is a bottleneck for your application.
from quickjs-emscripten.
Thank you both very much. It's good to know those tricks are common solutions to the problems 😀
from quickjs-emscripten.
@justjake I have another question unrelated this one, I guess call vm.dispose()
at the end is enough(without calling dispose for every handle) if the VM is a short-live VM, right?
from quickjs-emscripten.
@crysislinux unfortunately no - you still need to free every handle. We could possibly track every owned handle inside the vm
object, and do the handle.dispose()
calls inside vm.dispose()
, but as the API is currently written, you must dispose all the handles. The quickjs C code will assert the handles are correctly disposed.
from quickjs-emscripten.
Just published 0.4.0 which includes vm.newArray()
and support for working with array indices as native numbers.
from quickjs-emscripten.
@justjake Thanks😋
from quickjs-emscripten.
Related Issues (20)
- Tests fail on s390x machines HOT 5
- Proxy log receiver blocking error HOT 1
- Async Regression "TypeError: Object not disposable" in v0.26.0 HOT 4
- wish to add a new api (QuickJSHandle.keepAlive) HOT 2
- `getQuickJS()` callback never resolves? HOT 3
- [vite - MIME type Error]It's cannot work in vite HOT 2
- How to access wasm module's Memory? HOT 3
- Feature request: Expose GetModuleExport APIs HOT 4
- Timeout not working HOT 6
- [feature request] Upgrade to v2024-01-13 HOT 3
- TypeError: Failed to construct 'URL': Invalid URL HOT 7
- Loading WasmMemory into module HOT 11
- Determinism of the library HOT 2
- Distributed packages have no license HOT 2
- `evalCode` won't return error in ES module mode HOT 3
- Can't run cloudflare example HOT 1
- `Out of bounds memory access` Error only on ARM Safari HOT 10
- Having troubles with ArrayBuffer HOT 1
- Inconsistent behavior when running in browser (and web worker) HOT 2
- In context of `QuickJSAsyncRuntime` and `QuickJSAsyncContext`, the `callFunction()` not working as expected HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from quickjs-emscripten.