dquickts: جاوااسکریپتی که وسط حلقه میخوابه و یه جای دیگه بیدار میشه
خلاصهٔ کاملتر
dquickts یه fork از QuickJS هست؛ همون موتور جاوااسکریپت کوچیک و embeddable (یعنی موتوری که میشه داخل برنامهٔ C خودت جاش بدی) که Fabrice Bellard نوشته. این fork دو قابلیت اصلی بهش اضافه میکنه: durable execution و اجرای مستقیم TypeScript. پایهش نسخهٔ upstream 2026-06-04 هست و هرچی upstream داره، مثل Temporal و Intl آزمایشی، هنوز سر جاشه.
durable execution یعنی میتونی برنامهای که داره اجرا میشه رو، حتی وسط یه حلقهٔ for، متوقف کنی و کل heapش رو به یه بافر بایتی تبدیل کنی. بعد همونو تو یه پروسه یا حتی ماشین دیگه ادامه بدی. snapshot همهچیز رو نگه میداره: آبجکتها، closureها، promiseهای در انتظار، generatorها، ماژولها و حتی call stack کد sync. چون به جای pointer از index استفاده میکنه، heap جدید هر جای حافظه میتونه بشینه. فقط build موتور باید یکی باشه.
کد sync خودش هیچوقت yield نمیکنه، پس معمولاً از interrupt handler تابع JS_RequestSuspend() رو صدا میزنی. موتور تو safepoint بعدی frameها رو به heap میبره و C stack رو خالی میکنه. QuickJS بهطور پیشفرض هر ۱۰٬۰۰۰ branch یه بار این چک رو انجام میده. اگه وسط getter، callback یه forEach یا Proxy trap باشی، درخواست منتظر میمونه تا یه نقطهٔ امنتر. مثال README همینو نشون میده:
val = JS_Eval(ctx, src, strlen(src), "<loop>", JS_EVAL_TYPE_GLOBAL);
if (JS_IsException(val) && JS_IsSuspended(rt)) {
uint8_t *snap = JS_WriteSnapshot(ctx, &len, &host, NULL, 0, 0);
JSRuntime *rt2 = JS_NewRuntime();
val = JS_Resume(ctx); /* continues inside the for loop */
}یه قابلیت جالب دیگه durable host calls هست. یه تابع C میتونه با JS_SuspendCall() کد JS صدازننده رو پارک کنه و میزبان ساعتها بعد، حتی بعد از snapshot و تو یه پروسهٔ دیگه، با JS_ResumeWith() جوابشو بده. از دید اسکریپت این یه فراخوانی sync معمولیه. به گفتهٔ سازندهها کاربردش چیزهایی مثل hibernate کردن اسکریپتهای بیکار، workflowهای durable، تقسیم محاسبه بین workerها و warm start هست.
snapshot کامل حتی برای یه runtime خالی حدود ۱۳ کیلوبایته. برای همین incremental snapshot هم داره: یه base با JS_SNAPSHOT_BASE مینویسی و deltaهای بعدی با JS_SNAPSHOT_DELTA فقط تغییرات رو نگه میدارن. baseها reproducible هستن، یعنی لازم نیست ذخیرهشون کنی و میشه دوباره ساختشون. دستور qjs --drepl FILE دقیقاً همینه: یه REPL که بعد هر دستور session رو ذخیره میکنه و بعد از restart تابعها و متغیرها برمیگردن. session نمونه حدود ۴۰۰ بایت بود.
بخش TypeScript سادهست: parser موقع خوندن typeها رو پاک میکنه، پس فایل .ts بدون transpile اجرا میشه. enum، namespace و decoratorها هم پشتیبانی میشن و یه binding برای Rust (پورت rquickjs) هم همراهشه. محدودیتها هم مهمه: contextهای دارای Intl یا Temporal، SharedArrayBuffer اشتراکی و C closureها snapshot نمیشن و فایل session فقط تو همون build از qjs لود میشه.
نکات کلیدی:
- dquickts روی QuickJS نسخهٔ 2026-06-04 ساخته شده.
- با JS_RequestSuspend() و JS_Resume() میشه کد sync رو وسط حلقه نگه داشت و بعداً ادامه داد.
- JS_WriteSnapshot() و JS_ReadSnapshot() کل runtime رو ذخیره و بازسازی میکنن، حتی تو یه پروسهٔ دیگه.
- snapshot کامل runtime خالی حدود ۱۳ کیلوبایته؛ deltaها فقط تغییرات رو نگه میدارن.
- qjs --drepl FILE یه REPL میده که state اون بعد از restart برمیگرده.
- فایلهای .ts مستقیم اجرا میشن و Intl، Temporal و C closureها قابل snapshot نیستن.




