diff --git a/CHANGELOG.md b/CHANGELOG.md index 25384c9..7ba6c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## 0.3.7 +* add timeout and memory limit * fixed compiler error in windows release * fixed crash when encoding Error object * updated to latest quickjs diff --git a/coverage/lcov.info b/coverage/lcov.info index 973b76f..5110821 100644 --- a/coverage/lcov.info +++ b/coverage/lcov.info @@ -160,89 +160,91 @@ LF:157 LH:148 end_of_record SF:lib\src\engine.dart -DA:33,3 DA:39,3 -DA:40,3 -DA:41,6 -DA:44,3 -DA:45,3 -DA:46,9 DA:47,3 -DA:48,6 +DA:48,3 DA:49,6 -DA:51,3 -DA:52,15 -DA:56,3 -DA:58,3 -DA:60,3 -DA:62,3 -DA:64,6 +DA:52,3 +DA:53,3 +DA:54,9 +DA:55,3 +DA:56,6 +DA:57,6 +DA:59,3 +DA:60,15 +DA:64,3 DA:66,3 -DA:67,3 -DA:68,6 -DA:69,6 +DA:68,3 DA:70,3 -DA:71,6 -DA:72,3 +DA:72,6 DA:74,3 DA:75,3 -DA:76,3 -DA:77,3 -DA:78,6 -DA:80,0 +DA:76,6 +DA:77,6 +DA:78,3 +DA:79,6 +DA:80,3 DA:82,3 DA:83,3 DA:84,3 -DA:85,9 -DA:86,3 +DA:85,3 +DA:86,6 DA:88,0 -DA:90,0 -DA:91,0 -DA:92,0 -DA:94,0 -DA:95,0 +DA:90,3 +DA:91,3 +DA:92,3 +DA:93,9 +DA:94,3 DA:96,0 DA:98,0 DA:99,0 DA:100,0 -DA:101,0 DA:102,0 DA:103,0 -DA:107,3 -DA:108,3 -DA:109,3 -DA:110,3 -DA:111,6 -DA:115,3 +DA:104,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:111,0 +DA:115,6 DA:116,3 DA:117,3 DA:118,3 -DA:119,3 -DA:120,6 -DA:122,3 -DA:124,3 +DA:119,9 +DA:120,3 +DA:121,6 DA:125,3 DA:126,3 -DA:130,3 -DA:131,3 +DA:127,3 +DA:128,3 +DA:129,3 +DA:130,6 DA:132,3 -DA:135,6 +DA:134,3 +DA:135,3 DA:136,3 -DA:137,3 -DA:144,3 -DA:145,9 +DA:140,3 +DA:141,3 +DA:142,3 +DA:145,6 DA:146,3 -DA:151,3 +DA:147,3 +DA:154,3 +DA:155,9 DA:156,3 -DA:157,3 -DA:158,3 -DA:164,9 -DA:165,3 +DA:161,3 DA:166,3 +DA:167,3 DA:168,3 -DA:169,3 -LF:81 -LH:67 +DA:174,9 +DA:175,3 +DA:176,3 +DA:178,3 +DA:179,3 +LF:83 +LH:69 end_of_record SF:lib\src\isolate.dart DA:11,9 @@ -310,87 +312,91 @@ DA:109,6 DA:110,3 DA:111,3 DA:112,3 -DA:113,6 -DA:115,3 -DA:118,3 -DA:120,9 -DA:121,6 -DA:124,3 -DA:126,18 -DA:127,3 -DA:128,3 -DA:129,9 +DA:113,3 +DA:114,3 +DA:115,6 +DA:117,3 +DA:120,3 +DA:122,9 +DA:123,6 +DA:126,3 +DA:128,18 +DA:129,3 DA:130,3 -DA:131,3 -DA:135,6 -DA:137,3 +DA:131,9 +DA:132,3 +DA:133,3 +DA:137,6 DA:139,3 -DA:140,3 -DA:141,6 +DA:141,3 DA:142,3 -DA:143,3 +DA:143,6 DA:144,3 -DA:147,3 -DA:149,6 -DA:150,3 -DA:151,3 -DA:155,6 -DA:158,0 -DA:159,0 -DA:163,6 -DA:184,3 -DA:190,3 -DA:191,3 +DA:145,3 +DA:146,3 +DA:149,3 +DA:151,6 +DA:152,3 +DA:153,3 +DA:157,6 +DA:160,0 +DA:161,0 +DA:165,6 DA:192,3 -DA:193,3 -DA:195,3 -DA:196,3 -DA:197,3 +DA:200,3 DA:201,3 -DA:202,6 -DA:203,6 -DA:204,3 +DA:202,3 +DA:203,3 +DA:205,3 +DA:206,3 DA:207,3 DA:208,3 -DA:210,6 -DA:211,3 -DA:212,6 -DA:214,0 -DA:217,0 +DA:209,3 +DA:213,3 +DA:214,6 +DA:215,6 +DA:216,3 +DA:219,3 DA:220,3 -DA:221,6 -DA:223,18 -DA:225,0 +DA:222,6 +DA:223,3 +DA:224,6 +DA:226,0 DA:229,0 -DA:230,0 -DA:231,0 -DA:232,0 -DA:234,6 -DA:238,3 -DA:239,3 -DA:240,3 -DA:242,6 -DA:243,3 -DA:244,6 -DA:246,3 -DA:248,6 -DA:249,3 +DA:232,3 +DA:233,6 +DA:235,18 +DA:237,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:246,6 DA:250,3 -DA:251,0 +DA:251,3 DA:252,3 +DA:254,6 +DA:255,3 +DA:256,6 DA:258,3 -DA:263,3 +DA:260,6 +DA:261,3 +DA:262,3 +DA:263,0 DA:264,3 -DA:265,6 -DA:266,6 -DA:271,3 -DA:273,6 -DA:274,3 -DA:275,6 -DA:276,0 -DA:277,3 -LF:144 -LH:132 +DA:270,3 +DA:275,3 +DA:276,3 +DA:277,6 +DA:278,6 +DA:283,3 +DA:285,6 +DA:286,3 +DA:287,6 +DA:288,0 +DA:289,3 +LF:148 +LH:136 end_of_record SF:lib\src\object.dart DA:14,3 @@ -582,182 +588,185 @@ DA:142,0 DA:143,0 DA:147,9 DA:148,3 -DA:159,9 -DA:160,3 -DA:172,3 -DA:174,6 -DA:176,9 +DA:160,9 +DA:161,3 +DA:174,3 +DA:176,6 DA:178,9 -DA:180,3 -DA:181,6 -DA:185,9 -DA:187,3 -DA:192,3 -DA:193,3 -DA:194,6 -DA:195,12 -DA:198,3 -DA:202,6 -DA:203,9 -DA:211,0 -DA:212,0 -DA:223,9 -DA:224,3 -DA:231,3 -DA:234,3 -DA:235,6 -DA:238,12 +DA:180,9 +DA:182,3 +DA:183,6 +DA:187,9 +DA:189,3 +DA:194,3 +DA:195,3 +DA:196,6 +DA:197,12 +DA:200,3 +DA:205,6 +DA:206,9 +DA:214,0 +DA:215,0 +DA:227,9 +DA:228,3 +DA:239,9 DA:240,3 -DA:241,12 -DA:243,6 -DA:244,6 -DA:245,6 -DA:246,12 DA:247,3 -DA:248,18 -DA:249,3 -DA:252,6 -DA:253,6 -DA:254,3 -DA:255,3 -DA:263,9 -DA:264,3 -DA:275,9 -DA:276,3 -DA:283,3 -DA:284,6 -DA:285,6 -DA:286,0 -DA:287,6 -DA:294,9 -DA:295,3 -DA:305,9 -DA:306,3 -DA:320,9 -DA:321,3 -DA:332,3 +DA:250,3 +DA:251,6 +DA:254,12 +DA:256,3 +DA:257,12 +DA:259,6 +DA:260,6 +DA:261,6 +DA:262,12 +DA:263,3 +DA:264,18 +DA:265,3 +DA:268,6 +DA:269,6 +DA:270,3 +DA:271,3 +DA:279,9 +DA:280,3 +DA:291,9 +DA:292,3 +DA:299,3 +DA:300,6 +DA:301,6 +DA:302,6 +DA:303,0 +DA:304,6 +DA:311,9 +DA:312,3 +DA:322,9 +DA:323,3 +DA:337,9 DA:338,3 -DA:339,3 -DA:340,6 -DA:343,3 -DA:347,3 -DA:348,3 -DA:349,21 -DA:356,9 -DA:357,3 -DA:367,9 -DA:368,3 -DA:378,9 -DA:379,3 -DA:390,9 -DA:391,3 -DA:403,9 -DA:404,3 -DA:416,9 -DA:417,3 -DA:429,9 -DA:430,3 -DA:438,3 -DA:442,3 -DA:443,6 -DA:444,3 -DA:453,0 -DA:454,0 -DA:466,9 -DA:467,3 -DA:477,9 -DA:478,3 -DA:490,9 -DA:491,3 -DA:500,3 -DA:505,6 -DA:513,0 -DA:514,0 -DA:523,0 -DA:528,0 -DA:535,9 -DA:536,3 -DA:548,0 -DA:549,0 -DA:561,9 -DA:562,3 -DA:574,9 -DA:575,3 -DA:587,9 -DA:588,3 -DA:600,9 -DA:601,3 -DA:613,9 -DA:614,3 -DA:622,3 -DA:626,6 -DA:627,6 -DA:628,3 -DA:629,6 -DA:637,9 -DA:638,3 -DA:646,3 -DA:650,3 -DA:651,6 +DA:349,3 +DA:355,3 +DA:356,3 +DA:357,6 +DA:360,3 +DA:364,3 +DA:365,3 +DA:366,21 +DA:373,9 +DA:374,3 +DA:384,9 +DA:385,3 +DA:395,9 +DA:396,3 +DA:407,9 +DA:408,3 +DA:420,9 +DA:421,3 +DA:433,9 +DA:434,3 +DA:446,9 +DA:447,3 +DA:455,3 +DA:459,3 +DA:460,6 +DA:461,3 +DA:470,0 +DA:471,0 +DA:483,9 +DA:484,3 +DA:494,9 +DA:495,3 +DA:507,9 +DA:508,3 +DA:517,3 +DA:522,6 +DA:530,0 +DA:531,0 +DA:540,0 +DA:545,0 +DA:552,9 +DA:553,3 +DA:565,0 +DA:566,0 +DA:578,9 +DA:579,3 +DA:591,9 +DA:592,3 +DA:604,9 +DA:605,3 +DA:617,9 +DA:618,3 +DA:630,9 +DA:631,3 +DA:639,3 +DA:643,6 +DA:644,6 +DA:645,3 +DA:646,6 +DA:654,9 DA:655,3 -DA:664,9 -DA:665,3 -DA:678,9 -DA:679,3 -DA:692,9 -DA:693,3 -DA:706,9 -DA:707,3 -DA:719,9 -DA:720,3 -DA:732,9 -DA:733,3 -DA:745,9 -DA:746,3 -DA:757,9 -DA:758,3 -DA:771,9 -DA:772,3 -DA:789,9 -DA:790,3 -DA:805,9 -DA:806,3 -DA:818,9 -DA:819,3 -DA:831,9 -DA:832,3 +DA:663,3 +DA:667,3 +DA:668,6 +DA:672,3 +DA:681,9 +DA:682,3 +DA:695,9 +DA:696,3 +DA:709,9 +DA:710,3 +DA:723,9 +DA:724,3 +DA:736,9 +DA:737,3 +DA:749,9 +DA:750,3 +DA:762,9 +DA:763,3 +DA:774,9 +DA:775,3 +DA:788,9 +DA:789,3 +DA:806,9 +DA:807,3 +DA:822,9 +DA:823,3 +DA:835,9 +DA:836,3 DA:848,9 DA:849,3 -DA:864,9 -DA:865,3 -DA:874,9 -DA:875,3 -DA:878,12 -DA:885,9 -DA:886,3 -DA:903,9 -DA:904,3 -DA:915,3 -DA:922,15 -DA:923,3 -DA:924,9 -DA:925,3 -DA:926,6 -DA:928,6 -DA:930,9 -DA:931,3 +DA:865,9 +DA:866,3 +DA:881,9 +DA:882,3 +DA:891,9 +DA:892,3 +DA:895,12 +DA:902,9 +DA:903,3 +DA:920,9 +DA:921,3 DA:932,3 -DA:933,21 -DA:940,9 -DA:941,3 -DA:951,9 -DA:952,3 -DA:962,9 -DA:963,3 -DA:974,9 -DA:975,3 -DA:987,9 -DA:988,3 -LF:215 -LH:192 +DA:939,15 +DA:940,3 +DA:941,9 +DA:942,3 +DA:943,6 +DA:945,6 +DA:947,9 +DA:948,3 +DA:949,3 +DA:950,21 +DA:957,9 +DA:958,3 +DA:968,9 +DA:969,3 +DA:979,9 +DA:980,3 +DA:991,9 +DA:992,3 +DA:1004,9 +DA:1005,3 +LF:218 +LH:195 end_of_record diff --git a/cxx/ffi.cpp b/cxx/ffi.cpp index 5d09474..c300694 100644 --- a/cxx/ffi.cpp +++ b/cxx/ffi.cpp @@ -1,5 +1,5 @@ /* - * @Description: + * @Description: * @Author: ekibun * @Date: 2020-09-06 18:32:45 * @LastEditors: ekibun @@ -33,13 +33,17 @@ extern "C" return new JSValue(JS_NULL); } + struct RuntimeOpaque { + JSChannel * channel; + int64_t timeout; + int64_t start; + }; + JSModuleDef *js_module_loader( JSContext *ctx, const char *module_name, void *opaque) { - JSRuntime *rt = JS_GetRuntime(ctx); - JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); - const char *str = (char *)channel(ctx, JSChannelType_MODULE, (void *)module_name); + const char *str = (char *)((RuntimeOpaque *)opaque)->channel(ctx, JSChannelType_MODULE, (void *)module_name); if (str == 0) return NULL; JSValue func_val = JS_Eval(ctx, str, strlen(str), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); @@ -54,13 +58,13 @@ extern "C" JSValue js_channel(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data) { JSRuntime *rt = JS_GetRuntime(ctx); - JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); + RuntimeOpaque *opaque = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt); void *data[4]; data[0] = &this_val; data[1] = &argc; data[2] = argv; data[3] = func_data; - return *(JSValue *)channel(ctx, JSChannelType_METHON, data); + return *(JSValue *)opaque->channel(ctx, JSChannelType_METHON, data); } void js_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, @@ -69,17 +73,26 @@ extern "C" { if (is_handled) return; - JSRuntime *rt = JS_GetRuntime(ctx); - JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); - channel(ctx, JSChannelType_PROMISE_TRACK, &reason); + ((RuntimeOpaque *)opaque)->channel(ctx, JSChannelType_PROMISE_TRACK, &reason); } - DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel) + int js_interrupt_handler(JSRuntime * rt, void * opaque) { + RuntimeOpaque *op = (RuntimeOpaque *)opaque; + if(op->timeout && op->start && (clock() - op->start) > op->timeout * CLOCKS_PER_SEC / 1000) { + op->start = 0; + return 1; + } + return 0; + } + + DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel, int64_t timeout) { JSRuntime *rt = JS_NewRuntime(); - JS_SetRuntimeOpaque(rt, (void *)channel); - JS_SetHostPromiseRejectionTracker(rt, js_promise_rejection_tracker, nullptr); - JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, nullptr); + RuntimeOpaque *opaque = new RuntimeOpaque({channel, timeout, 0}); + JS_SetRuntimeOpaque(rt, opaque); + JS_SetHostPromiseRejectionTracker(rt, js_promise_rejection_tracker, opaque); + JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, opaque); + JS_SetInterruptHandler(rt, js_interrupt_handler, opaque); return rt; } @@ -93,13 +106,14 @@ extern "C" JSClassDef def{ name, // destructor - [](JSRuntime *rt, JSValue obj) noexcept { + [](JSRuntime *rt, JSValue obj) noexcept + { JSClassID classid = JS_GetClassID(obj); void *opaque = JS_GetOpaque(obj, classid); - JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); - if (channel == nullptr) + RuntimeOpaque *runtimeOpaque = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt); + if (runtimeOpaque == nullptr) return; - channel((JSContext *)rt, JSChannelType_FREE_OBJECT, opaque); + runtimeOpaque->channel((JSContext *)rt, JSChannelType_FREE_OBJECT, opaque); }}; int e = JS_NewClass(rt, QJSClassId, &def); if (e < 0) @@ -130,8 +144,16 @@ extern "C" JS_SetMaxStackSize(rt, stack_size); } + DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit) + { + JS_SetMemoryLimit(rt, limit); + } + DLLEXPORT void jsFreeRuntime(JSRuntime *rt) { + RuntimeOpaque *opauqe = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt); + if (opauqe) + delete opauqe; JS_SetRuntimeOpaque(rt, nullptr); JS_FreeRuntime(rt); } @@ -143,6 +165,7 @@ extern "C" DLLEXPORT JSContext *jsNewContext(JSRuntime *rt) { + JS_UpdateStackTop(rt); JSContext *ctx = JS_NewContext(rt); return ctx; } @@ -157,10 +180,16 @@ extern "C" return JS_GetRuntime(ctx); } + void js_begin_call(JSRuntime *rt) { + JS_UpdateStackTop(rt); + RuntimeOpaque * opaque = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt); + if(opaque) opaque->start = clock(); + } + DLLEXPORT JSValue *jsEval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int32_t eval_flags) { JSRuntime *rt = JS_GetRuntime(ctx); - JS_UpdateStackTop(rt); + js_begin_call(rt); JSValue *ret = new JSValue(JS_Eval(ctx, input, input_len, filename, eval_flags)); return ret; } @@ -261,7 +290,7 @@ extern "C" DLLEXPORT const char *jsToCString(JSContext *ctx, JSValueConst *val) { JSRuntime *rt = JS_GetRuntime(ctx); - JS_UpdateStackTop(rt); + js_begin_call(rt); const char *ret = JS_ToCString(ctx, *val); return ret; } @@ -353,7 +382,7 @@ extern "C" int32_t argc, JSValueConst *argv) { JSRuntime *rt = JS_GetRuntime(ctx); - JS_UpdateStackTop(rt); + js_begin_call(rt); JSValue *ret = new JSValue(JS_Call(ctx, *func_obj, *this_obj, argc, argv)); return ret; } @@ -370,7 +399,7 @@ extern "C" DLLEXPORT int32_t jsExecutePendingJob(JSRuntime *rt) { - JS_UpdateStackTop(rt); + js_begin_call(rt); JSContext *ctx; int ret = JS_ExecutePendingJob(rt, &ctx); return ret; diff --git a/cxx/ffi.h b/cxx/ffi.h index 66fddb3..6939105 100644 --- a/cxx/ffi.h +++ b/cxx/ffi.h @@ -25,7 +25,7 @@ extern "C" DLLEXPORT JSValue *jsNULL(); - DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel); + DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel, int64_t timeout); DLLEXPORT uint32_t jsNewClass(JSContext *ctx, const char *name); @@ -35,6 +35,8 @@ extern "C" DLLEXPORT void jsSetMaxStackSize(JSRuntime *rt, size_t stack_size); + DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit); + DLLEXPORT void jsFreeRuntime(JSRuntime *rt); DLLEXPORT JSValue *jsNewCFunction(JSContext *ctx, JSValue *funcData); diff --git a/lib/src/engine.dart b/lib/src/engine.dart index 16c5ed6..82cf412 100644 --- a/lib/src/engine.dart +++ b/lib/src/engine.dart @@ -21,6 +21,12 @@ class FlutterQjs { /// Max stack size for quickjs. final int? stackSize; + /// Max stack size for quickjs. + final int? timeout; + + /// Max memory for quickjs. + final int? memoryLimit; + /// Message Port for event loop. Close it to stop dispatching event loop. ReceivePort port = ReceivePort(); @@ -33,6 +39,8 @@ class FlutterQjs { FlutterQjs({ this.moduleHandler, this.stackSize, + this.timeout, + this.memoryLimit, this.hostPromiseRejectionHandler, }); @@ -104,9 +112,11 @@ class FlutterQjs { } return err; } - }, port); + }, timeout ?? 0, port); final stackSize = this.stackSize ?? 0; if (stackSize > 0) jsSetMaxStackSize(rt, stackSize); + final memoryLimit = this.memoryLimit ?? 0; + if (memoryLimit > 0) jsSetMemoryLimit(rt, memoryLimit); _rt = rt; _ctx = jsNewContext(rt); } diff --git a/lib/src/ffi.dart b/lib/src/ffi.dart index 3522149..b2a0873 100644 --- a/lib/src/ffi.dart +++ b/lib/src/ffi.dart @@ -156,11 +156,13 @@ typedef _JSChannelNative = Pointer Function( /// JSRuntime *jsNewRuntime(JSChannel channel) final Pointer Function( Pointer>, + int, ) _jsNewRuntime = _qjsLib .lookup< NativeFunction< Pointer Function( Pointer>, + Int64, )>>('jsNewRuntime') .asFunction(); @@ -197,9 +199,10 @@ Pointer? channelDispacher( Pointer jsNewRuntime( _JSChannel callback, + int timeout, ReceivePort port, ) { - final rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher)); + final rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher), timeout); runtimeOpaques[rt] = _RuntimeOpaque(callback, port); return rt; } @@ -217,6 +220,19 @@ final void Function( )>>('jsSetMaxStackSize') .asFunction(); +/// DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit); +final void Function( + Pointer, + int, +) jsSetMemoryLimit = _qjsLib + .lookup< + NativeFunction< + Void Function( + Pointer, + IntPtr, + )>>('jsSetMemoryLimit') + .asFunction(); + /// void jsFreeRuntime(JSRuntime *rt) final void Function( Pointer, @@ -282,6 +298,7 @@ final Pointer Function( Pointer jsNewContext(Pointer rt) { final ctx = _jsNewContext(rt); + if (ctx.address == 0) throw Exception('Context create failed!'); final runtimeOpaque = runtimeOpaques[rt]; if (runtimeOpaque == null) throw Exception('Runtime has been released!'); runtimeOpaque._dartObjectClassId = jsNewClass(ctx, 'DartObject'); diff --git a/lib/src/isolate.dart b/lib/src/isolate.dart index 07c77a0..58b8f2d 100644 --- a/lib/src/isolate.dart +++ b/lib/src/isolate.dart @@ -109,6 +109,8 @@ void _runJsIsolate(Map spawnMessage) async { sendPort.send(port.sendPort); final qjs = FlutterQjs( stackSize: spawnMessage[#stackSize], + timeout: spawnMessage[#timeout], + memoryLimit: spawnMessage[#memoryLimit], hostPromiseRejectionHandler: (reason) { sendPort.send({ #type: #hostPromiseRejection, @@ -171,6 +173,12 @@ class IsolateQjs { /// Max stack size for quickjs. final int? stackSize; + /// Max stack size for quickjs. + final int? timeout; + + /// Max memory for quickjs. + final int? memoryLimit; + /// Asynchronously handler to manage js module. final _JsAsyncModuleHandler? moduleHandler; @@ -184,6 +192,8 @@ class IsolateQjs { IsolateQjs({ this.moduleHandler, this.stackSize, + this.timeout, + this.memoryLimit, this.hostPromiseRejectionHandler, }); @@ -195,6 +205,8 @@ class IsolateQjs { { #port: port.sendPort, #stackSize: stackSize, + #timeout: timeout, + #memoryLimit: memoryLimit, }, errorsAreFatal: true, ); diff --git a/test/flutter_qjs_test.dart b/test/flutter_qjs_test.dart index b66b90f..51e5bb6 100644 --- a/test/flutter_qjs_test.dart +++ b/test/flutter_qjs_test.dart @@ -121,6 +121,36 @@ void main() async { stderr.write(result.stderr); expect(result.exitCode, 0); }); + test('infinite loop', () async { + final qjs = FlutterQjs( + timeout: 1000, + ); + qjs.dispatch(); + var result = await qjs.evaluate('1'); + expect(result, 1, reason: 'eval module'); + try { + await qjs.evaluate('while(true) {}'); + throw 'Error not throw'; + } on JSError catch (e) { + expect(e.message, startsWith('InternalError: interrupted'), + reason: 'throw interrupted'); + } + await qjs.close(); + }); + test('memory leak', () async { + final qjs = FlutterQjs( + memoryLimit: 1000000, + ); + qjs.dispatch(); + try { + await qjs.evaluate('new Array(1000000).fill(0)'); + throw 'Error not throw'; + } on JSError catch (e) { + expect(e.message, startsWith('InternalError: out of memory'), + reason: 'throw interrupted'); + } + await qjs.close(); + }); test('module', () async { final qjs = IsolateQjs( moduleHandler: (name) async {