add timeout and memory limit

This commit is contained in:
ekibun
2022-05-20 00:40:44 +08:00
parent 232ea07e89
commit 32aac42c19
8 changed files with 426 additions and 316 deletions

View File

@@ -8,6 +8,7 @@
## 0.3.7 ## 0.3.7
* add timeout and memory limit
* fixed compiler error in windows release * fixed compiler error in windows release
* fixed crash when encoding Error object * fixed crash when encoding Error object
* updated to latest quickjs * updated to latest quickjs

View File

@@ -160,89 +160,91 @@ LF:157
LH:148 LH:148
end_of_record end_of_record
SF:lib\src\engine.dart SF:lib\src\engine.dart
DA:33,3
DA:39,3 DA:39,3
DA:40,3
DA:41,6
DA:44,3
DA:45,3
DA:46,9
DA:47,3 DA:47,3
DA:48,6 DA:48,3
DA:49,6 DA:49,6
DA:51,3 DA:52,3
DA:52,15 DA:53,3
DA:56,3 DA:54,9
DA:58,3 DA:55,3
DA:60,3 DA:56,6
DA:62,3 DA:57,6
DA:64,6 DA:59,3
DA:60,15
DA:64,3
DA:66,3 DA:66,3
DA:67,3 DA:68,3
DA:68,6
DA:69,6
DA:70,3 DA:70,3
DA:71,6 DA:72,6
DA:72,3
DA:74,3 DA:74,3
DA:75,3 DA:75,3
DA:76,3 DA:76,6
DA:77,3 DA:77,6
DA:78,6 DA:78,3
DA:80,0 DA:79,6
DA:80,3
DA:82,3 DA:82,3
DA:83,3 DA:83,3
DA:84,3 DA:84,3
DA:85,9 DA:85,3
DA:86,3 DA:86,6
DA:88,0 DA:88,0
DA:90,0 DA:90,3
DA:91,0 DA:91,3
DA:92,0 DA:92,3
DA:94,0 DA:93,9
DA:95,0 DA:94,3
DA:96,0 DA:96,0
DA:98,0 DA:98,0
DA:99,0 DA:99,0
DA:100,0 DA:100,0
DA:101,0
DA:102,0 DA:102,0
DA:103,0 DA:103,0
DA:107,3 DA:104,0
DA:108,3 DA:106,0
DA:109,3 DA:107,0
DA:110,3 DA:108,0
DA:111,6 DA:109,0
DA:115,3 DA:110,0
DA:111,0
DA:115,6
DA:116,3 DA:116,3
DA:117,3 DA:117,3
DA:118,3 DA:118,3
DA:119,3 DA:119,9
DA:120,6 DA:120,3
DA:122,3 DA:121,6
DA:124,3
DA:125,3 DA:125,3
DA:126,3 DA:126,3
DA:130,3 DA:127,3
DA:131,3 DA:128,3
DA:129,3
DA:130,6
DA:132,3 DA:132,3
DA:135,6 DA:134,3
DA:135,3
DA:136,3 DA:136,3
DA:137,3 DA:140,3
DA:144,3 DA:141,3
DA:145,9 DA:142,3
DA:145,6
DA:146,3 DA:146,3
DA:151,3 DA:147,3
DA:154,3
DA:155,9
DA:156,3 DA:156,3
DA:157,3 DA:161,3
DA:158,3
DA:164,9
DA:165,3
DA:166,3 DA:166,3
DA:167,3
DA:168,3 DA:168,3
DA:169,3 DA:174,9
LF:81 DA:175,3
LH:67 DA:176,3
DA:178,3
DA:179,3
LF:83
LH:69
end_of_record end_of_record
SF:lib\src\isolate.dart SF:lib\src\isolate.dart
DA:11,9 DA:11,9
@@ -310,87 +312,91 @@ DA:109,6
DA:110,3 DA:110,3
DA:111,3 DA:111,3
DA:112,3 DA:112,3
DA:113,6 DA:113,3
DA:115,3 DA:114,3
DA:118,3 DA:115,6
DA:120,9 DA:117,3
DA:121,6 DA:120,3
DA:124,3 DA:122,9
DA:126,18 DA:123,6
DA:127,3 DA:126,3
DA:128,3 DA:128,18
DA:129,9 DA:129,3
DA:130,3 DA:130,3
DA:131,3 DA:131,9
DA:135,6 DA:132,3
DA:137,3 DA:133,3
DA:137,6
DA:139,3 DA:139,3
DA:140,3 DA:141,3
DA:141,6
DA:142,3 DA:142,3
DA:143,3 DA:143,6
DA:144,3 DA:144,3
DA:147,3 DA:145,3
DA:149,6 DA:146,3
DA:150,3 DA:149,3
DA:151,3 DA:151,6
DA:155,6 DA:152,3
DA:158,0 DA:153,3
DA:159,0 DA:157,6
DA:163,6 DA:160,0
DA:184,3 DA:161,0
DA:190,3 DA:165,6
DA:191,3
DA:192,3 DA:192,3
DA:193,3 DA:200,3
DA:195,3
DA:196,3
DA:197,3
DA:201,3 DA:201,3
DA:202,6 DA:202,3
DA:203,6 DA:203,3
DA:204,3 DA:205,3
DA:206,3
DA:207,3 DA:207,3
DA:208,3 DA:208,3
DA:210,6 DA:209,3
DA:211,3 DA:213,3
DA:212,6 DA:214,6
DA:214,0 DA:215,6
DA:217,0 DA:216,3
DA:219,3
DA:220,3 DA:220,3
DA:221,6 DA:222,6
DA:223,18 DA:223,3
DA:225,0 DA:224,6
DA:226,0
DA:229,0 DA:229,0
DA:230,0 DA:232,3
DA:231,0 DA:233,6
DA:232,0 DA:235,18
DA:234,6 DA:237,0
DA:238,3 DA:241,0
DA:239,3 DA:242,0
DA:240,3 DA:243,0
DA:242,6 DA:244,0
DA:243,3 DA:246,6
DA:244,6
DA:246,3
DA:248,6
DA:249,3
DA:250,3 DA:250,3
DA:251,0 DA:251,3
DA:252,3 DA:252,3
DA:254,6
DA:255,3
DA:256,6
DA:258,3 DA:258,3
DA:263,3 DA:260,6
DA:261,3
DA:262,3
DA:263,0
DA:264,3 DA:264,3
DA:265,6 DA:270,3
DA:266,6 DA:275,3
DA:271,3 DA:276,3
DA:273,6 DA:277,6
DA:274,3 DA:278,6
DA:275,6 DA:283,3
DA:276,0 DA:285,6
DA:277,3 DA:286,3
LF:144 DA:287,6
LH:132 DA:288,0
DA:289,3
LF:148
LH:136
end_of_record end_of_record
SF:lib\src\object.dart SF:lib\src\object.dart
DA:14,3 DA:14,3
@@ -582,182 +588,185 @@ DA:142,0
DA:143,0 DA:143,0
DA:147,9 DA:147,9
DA:148,3 DA:148,3
DA:159,9 DA:160,9
DA:160,3 DA:161,3
DA:172,3 DA:174,3
DA:174,6 DA:176,6
DA:176,9
DA:178,9 DA:178,9
DA:180,3 DA:180,9
DA:181,6 DA:182,3
DA:185,9 DA:183,6
DA:187,3 DA:187,9
DA:192,3 DA:189,3
DA:193,3 DA:194,3
DA:194,6 DA:195,3
DA:195,12 DA:196,6
DA:198,3 DA:197,12
DA:202,6 DA:200,3
DA:203,9 DA:205,6
DA:211,0 DA:206,9
DA:212,0 DA:214,0
DA:223,9 DA:215,0
DA:224,3 DA:227,9
DA:231,3 DA:228,3
DA:234,3 DA:239,9
DA:235,6
DA:238,12
DA:240,3 DA:240,3
DA:241,12
DA:243,6
DA:244,6
DA:245,6
DA:246,12
DA:247,3 DA:247,3
DA:248,18 DA:250,3
DA:249,3 DA:251,6
DA:252,6 DA:254,12
DA:253,6 DA:256,3
DA:254,3 DA:257,12
DA:255,3 DA:259,6
DA:263,9 DA:260,6
DA:264,3 DA:261,6
DA:275,9 DA:262,12
DA:276,3 DA:263,3
DA:283,3 DA:264,18
DA:284,6 DA:265,3
DA:285,6 DA:268,6
DA:286,0 DA:269,6
DA:287,6 DA:270,3
DA:294,9 DA:271,3
DA:295,3 DA:279,9
DA:305,9 DA:280,3
DA:306,3 DA:291,9
DA:320,9 DA:292,3
DA:321,3 DA:299,3
DA:332,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:338,3
DA:339,3 DA:349,3
DA:340,6 DA:355,3
DA:343,3 DA:356,3
DA:347,3 DA:357,6
DA:348,3 DA:360,3
DA:349,21 DA:364,3
DA:356,9 DA:365,3
DA:357,3 DA:366,21
DA:367,9 DA:373,9
DA:368,3 DA:374,3
DA:378,9 DA:384,9
DA:379,3 DA:385,3
DA:390,9 DA:395,9
DA:391,3 DA:396,3
DA:403,9 DA:407,9
DA:404,3 DA:408,3
DA:416,9 DA:420,9
DA:417,3 DA:421,3
DA:429,9 DA:433,9
DA:430,3 DA:434,3
DA:438,3 DA:446,9
DA:442,3 DA:447,3
DA:443,6 DA:455,3
DA:444,3 DA:459,3
DA:453,0 DA:460,6
DA:454,0 DA:461,3
DA:466,9 DA:470,0
DA:467,3 DA:471,0
DA:477,9 DA:483,9
DA:478,3 DA:484,3
DA:490,9 DA:494,9
DA:491,3 DA:495,3
DA:500,3 DA:507,9
DA:505,6 DA:508,3
DA:513,0 DA:517,3
DA:514,0 DA:522,6
DA:523,0 DA:530,0
DA:528,0 DA:531,0
DA:535,9 DA:540,0
DA:536,3 DA:545,0
DA:548,0 DA:552,9
DA:549,0 DA:553,3
DA:561,9 DA:565,0
DA:562,3 DA:566,0
DA:574,9 DA:578,9
DA:575,3 DA:579,3
DA:587,9 DA:591,9
DA:588,3 DA:592,3
DA:600,9 DA:604,9
DA:601,3 DA:605,3
DA:613,9 DA:617,9
DA:614,3 DA:618,3
DA:622,3 DA:630,9
DA:626,6 DA:631,3
DA:627,6 DA:639,3
DA:628,3 DA:643,6
DA:629,6 DA:644,6
DA:637,9 DA:645,3
DA:638,3 DA:646,6
DA:646,3 DA:654,9
DA:650,3
DA:651,6
DA:655,3 DA:655,3
DA:664,9 DA:663,3
DA:665,3 DA:667,3
DA:678,9 DA:668,6
DA:679,3 DA:672,3
DA:692,9 DA:681,9
DA:693,3 DA:682,3
DA:706,9 DA:695,9
DA:707,3 DA:696,3
DA:719,9 DA:709,9
DA:720,3 DA:710,3
DA:732,9 DA:723,9
DA:733,3 DA:724,3
DA:745,9 DA:736,9
DA:746,3 DA:737,3
DA:757,9 DA:749,9
DA:758,3 DA:750,3
DA:771,9 DA:762,9
DA:772,3 DA:763,3
DA:789,9 DA:774,9
DA:790,3 DA:775,3
DA:805,9 DA:788,9
DA:806,3 DA:789,3
DA:818,9 DA:806,9
DA:819,3 DA:807,3
DA:831,9 DA:822,9
DA:832,3 DA:823,3
DA:835,9
DA:836,3
DA:848,9 DA:848,9
DA:849,3 DA:849,3
DA:864,9 DA:865,9
DA:865,3 DA:866,3
DA:874,9 DA:881,9
DA:875,3 DA:882,3
DA:878,12 DA:891,9
DA:885,9 DA:892,3
DA:886,3 DA:895,12
DA:903,9 DA:902,9
DA:904,3 DA:903,3
DA:915,3 DA:920,9
DA:922,15 DA:921,3
DA:923,3
DA:924,9
DA:925,3
DA:926,6
DA:928,6
DA:930,9
DA:931,3
DA:932,3 DA:932,3
DA:933,21 DA:939,15
DA:940,9 DA:940,3
DA:941,3 DA:941,9
DA:951,9 DA:942,3
DA:952,3 DA:943,6
DA:962,9 DA:945,6
DA:963,3 DA:947,9
DA:974,9 DA:948,3
DA:975,3 DA:949,3
DA:987,9 DA:950,21
DA:988,3 DA:957,9
LF:215 DA:958,3
LH:192 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 end_of_record

View File

@@ -1,5 +1,5 @@
/* /*
* @Description: * @Description:
* @Author: ekibun * @Author: ekibun
* @Date: 2020-09-06 18:32:45 * @Date: 2020-09-06 18:32:45
* @LastEditors: ekibun * @LastEditors: ekibun
@@ -33,13 +33,17 @@ extern "C"
return new JSValue(JS_NULL); return new JSValue(JS_NULL);
} }
struct RuntimeOpaque {
JSChannel * channel;
int64_t timeout;
int64_t start;
};
JSModuleDef *js_module_loader( JSModuleDef *js_module_loader(
JSContext *ctx, JSContext *ctx,
const char *module_name, void *opaque) const char *module_name, void *opaque)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); const char *str = (char *)((RuntimeOpaque *)opaque)->channel(ctx, JSChannelType_MODULE, (void *)module_name);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
const char *str = (char *)channel(ctx, JSChannelType_MODULE, (void *)module_name);
if (str == 0) if (str == 0)
return NULL; return NULL;
JSValue func_val = JS_Eval(ctx, str, strlen(str), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); 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) JSValue js_channel(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); RuntimeOpaque *opaque = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt);
void *data[4]; void *data[4];
data[0] = &this_val; data[0] = &this_val;
data[1] = &argc; data[1] = &argc;
data[2] = argv; data[2] = argv;
data[3] = func_data; 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, void js_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
@@ -69,17 +73,26 @@ extern "C"
{ {
if (is_handled) if (is_handled)
return; return;
JSRuntime *rt = JS_GetRuntime(ctx); ((RuntimeOpaque *)opaque)->channel(ctx, JSChannelType_PROMISE_TRACK, &reason);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
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(); JSRuntime *rt = JS_NewRuntime();
JS_SetRuntimeOpaque(rt, (void *)channel); RuntimeOpaque *opaque = new RuntimeOpaque({channel, timeout, 0});
JS_SetHostPromiseRejectionTracker(rt, js_promise_rejection_tracker, nullptr); JS_SetRuntimeOpaque(rt, opaque);
JS_SetModuleLoaderFunc(rt, nullptr, js_module_loader, nullptr); 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; return rt;
} }
@@ -93,13 +106,14 @@ extern "C"
JSClassDef def{ JSClassDef def{
name, name,
// destructor // destructor
[](JSRuntime *rt, JSValue obj) noexcept { [](JSRuntime *rt, JSValue obj) noexcept
{
JSClassID classid = JS_GetClassID(obj); JSClassID classid = JS_GetClassID(obj);
void *opaque = JS_GetOpaque(obj, classid); void *opaque = JS_GetOpaque(obj, classid);
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt); RuntimeOpaque *runtimeOpaque = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt);
if (channel == nullptr) if (runtimeOpaque == nullptr)
return; return;
channel((JSContext *)rt, JSChannelType_FREE_OBJECT, opaque); runtimeOpaque->channel((JSContext *)rt, JSChannelType_FREE_OBJECT, opaque);
}}; }};
int e = JS_NewClass(rt, QJSClassId, &def); int e = JS_NewClass(rt, QJSClassId, &def);
if (e < 0) if (e < 0)
@@ -130,8 +144,16 @@ extern "C"
JS_SetMaxStackSize(rt, stack_size); JS_SetMaxStackSize(rt, stack_size);
} }
DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit)
{
JS_SetMemoryLimit(rt, limit);
}
DLLEXPORT void jsFreeRuntime(JSRuntime *rt) DLLEXPORT void jsFreeRuntime(JSRuntime *rt)
{ {
RuntimeOpaque *opauqe = (RuntimeOpaque *)JS_GetRuntimeOpaque(rt);
if (opauqe)
delete opauqe;
JS_SetRuntimeOpaque(rt, nullptr); JS_SetRuntimeOpaque(rt, nullptr);
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
} }
@@ -143,6 +165,7 @@ extern "C"
DLLEXPORT JSContext *jsNewContext(JSRuntime *rt) DLLEXPORT JSContext *jsNewContext(JSRuntime *rt)
{ {
JS_UpdateStackTop(rt);
JSContext *ctx = JS_NewContext(rt); JSContext *ctx = JS_NewContext(rt);
return ctx; return ctx;
} }
@@ -157,10 +180,16 @@ extern "C"
return JS_GetRuntime(ctx); 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) DLLEXPORT JSValue *jsEval(JSContext *ctx, const char *input, size_t input_len, const char *filename, int32_t eval_flags)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); 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)); JSValue *ret = new JSValue(JS_Eval(ctx, input, input_len, filename, eval_flags));
return ret; return ret;
} }
@@ -261,7 +290,7 @@ extern "C"
DLLEXPORT const char *jsToCString(JSContext *ctx, JSValueConst *val) DLLEXPORT const char *jsToCString(JSContext *ctx, JSValueConst *val)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
JS_UpdateStackTop(rt); js_begin_call(rt);
const char *ret = JS_ToCString(ctx, *val); const char *ret = JS_ToCString(ctx, *val);
return ret; return ret;
} }
@@ -353,7 +382,7 @@ extern "C"
int32_t argc, JSValueConst *argv) int32_t argc, JSValueConst *argv)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); 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)); JSValue *ret = new JSValue(JS_Call(ctx, *func_obj, *this_obj, argc, argv));
return ret; return ret;
} }
@@ -370,7 +399,7 @@ extern "C"
DLLEXPORT int32_t jsExecutePendingJob(JSRuntime *rt) DLLEXPORT int32_t jsExecutePendingJob(JSRuntime *rt)
{ {
JS_UpdateStackTop(rt); js_begin_call(rt);
JSContext *ctx; JSContext *ctx;
int ret = JS_ExecutePendingJob(rt, &ctx); int ret = JS_ExecutePendingJob(rt, &ctx);
return ret; return ret;

View File

@@ -25,7 +25,7 @@ extern "C"
DLLEXPORT JSValue *jsNULL(); 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); 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 jsSetMaxStackSize(JSRuntime *rt, size_t stack_size);
DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit);
DLLEXPORT void jsFreeRuntime(JSRuntime *rt); DLLEXPORT void jsFreeRuntime(JSRuntime *rt);
DLLEXPORT JSValue *jsNewCFunction(JSContext *ctx, JSValue *funcData); DLLEXPORT JSValue *jsNewCFunction(JSContext *ctx, JSValue *funcData);

View File

@@ -21,6 +21,12 @@ class FlutterQjs {
/// Max stack size for quickjs. /// Max stack size for quickjs.
final int? stackSize; 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. /// Message Port for event loop. Close it to stop dispatching event loop.
ReceivePort port = ReceivePort(); ReceivePort port = ReceivePort();
@@ -33,6 +39,8 @@ class FlutterQjs {
FlutterQjs({ FlutterQjs({
this.moduleHandler, this.moduleHandler,
this.stackSize, this.stackSize,
this.timeout,
this.memoryLimit,
this.hostPromiseRejectionHandler, this.hostPromiseRejectionHandler,
}); });
@@ -104,9 +112,11 @@ class FlutterQjs {
} }
return err; return err;
} }
}, port); }, timeout ?? 0, port);
final stackSize = this.stackSize ?? 0; final stackSize = this.stackSize ?? 0;
if (stackSize > 0) jsSetMaxStackSize(rt, stackSize); if (stackSize > 0) jsSetMaxStackSize(rt, stackSize);
final memoryLimit = this.memoryLimit ?? 0;
if (memoryLimit > 0) jsSetMemoryLimit(rt, memoryLimit);
_rt = rt; _rt = rt;
_ctx = jsNewContext(rt); _ctx = jsNewContext(rt);
} }

View File

@@ -156,11 +156,13 @@ typedef _JSChannelNative = Pointer<JSValue> Function(
/// JSRuntime *jsNewRuntime(JSChannel channel) /// JSRuntime *jsNewRuntime(JSChannel channel)
final Pointer<JSRuntime> Function( final Pointer<JSRuntime> Function(
Pointer<NativeFunction<_JSChannelNative>>, Pointer<NativeFunction<_JSChannelNative>>,
int,
) _jsNewRuntime = _qjsLib ) _jsNewRuntime = _qjsLib
.lookup< .lookup<
NativeFunction< NativeFunction<
Pointer<JSRuntime> Function( Pointer<JSRuntime> Function(
Pointer<NativeFunction<_JSChannelNative>>, Pointer<NativeFunction<_JSChannelNative>>,
Int64,
)>>('jsNewRuntime') )>>('jsNewRuntime')
.asFunction(); .asFunction();
@@ -197,9 +199,10 @@ Pointer<JSValue>? channelDispacher(
Pointer<JSRuntime> jsNewRuntime( Pointer<JSRuntime> jsNewRuntime(
_JSChannel callback, _JSChannel callback,
int timeout,
ReceivePort port, ReceivePort port,
) { ) {
final rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher)); final rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher), timeout);
runtimeOpaques[rt] = _RuntimeOpaque(callback, port); runtimeOpaques[rt] = _RuntimeOpaque(callback, port);
return rt; return rt;
} }
@@ -217,6 +220,19 @@ final void Function(
)>>('jsSetMaxStackSize') )>>('jsSetMaxStackSize')
.asFunction(); .asFunction();
/// DLLEXPORT void jsSetMemoryLimit(JSRuntime *rt, size_t limit);
final void Function(
Pointer<JSRuntime>,
int,
) jsSetMemoryLimit = _qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer<JSRuntime>,
IntPtr,
)>>('jsSetMemoryLimit')
.asFunction();
/// void jsFreeRuntime(JSRuntime *rt) /// void jsFreeRuntime(JSRuntime *rt)
final void Function( final void Function(
Pointer<JSRuntime>, Pointer<JSRuntime>,
@@ -282,6 +298,7 @@ final Pointer<JSContext> Function(
Pointer<JSContext> jsNewContext(Pointer<JSRuntime> rt) { Pointer<JSContext> jsNewContext(Pointer<JSRuntime> rt) {
final ctx = _jsNewContext(rt); final ctx = _jsNewContext(rt);
if (ctx.address == 0) throw Exception('Context create failed!');
final runtimeOpaque = runtimeOpaques[rt]; final runtimeOpaque = runtimeOpaques[rt];
if (runtimeOpaque == null) throw Exception('Runtime has been released!'); if (runtimeOpaque == null) throw Exception('Runtime has been released!');
runtimeOpaque._dartObjectClassId = jsNewClass(ctx, 'DartObject'); runtimeOpaque._dartObjectClassId = jsNewClass(ctx, 'DartObject');

View File

@@ -109,6 +109,8 @@ void _runJsIsolate(Map spawnMessage) async {
sendPort.send(port.sendPort); sendPort.send(port.sendPort);
final qjs = FlutterQjs( final qjs = FlutterQjs(
stackSize: spawnMessage[#stackSize], stackSize: spawnMessage[#stackSize],
timeout: spawnMessage[#timeout],
memoryLimit: spawnMessage[#memoryLimit],
hostPromiseRejectionHandler: (reason) { hostPromiseRejectionHandler: (reason) {
sendPort.send({ sendPort.send({
#type: #hostPromiseRejection, #type: #hostPromiseRejection,
@@ -171,6 +173,12 @@ class IsolateQjs {
/// Max stack size for quickjs. /// Max stack size for quickjs.
final int? stackSize; final int? stackSize;
/// Max stack size for quickjs.
final int? timeout;
/// Max memory for quickjs.
final int? memoryLimit;
/// Asynchronously handler to manage js module. /// Asynchronously handler to manage js module.
final _JsAsyncModuleHandler? moduleHandler; final _JsAsyncModuleHandler? moduleHandler;
@@ -184,6 +192,8 @@ class IsolateQjs {
IsolateQjs({ IsolateQjs({
this.moduleHandler, this.moduleHandler,
this.stackSize, this.stackSize,
this.timeout,
this.memoryLimit,
this.hostPromiseRejectionHandler, this.hostPromiseRejectionHandler,
}); });
@@ -195,6 +205,8 @@ class IsolateQjs {
{ {
#port: port.sendPort, #port: port.sendPort,
#stackSize: stackSize, #stackSize: stackSize,
#timeout: timeout,
#memoryLimit: memoryLimit,
}, },
errorsAreFatal: true, errorsAreFatal: true,
); );

View File

@@ -121,6 +121,36 @@ void main() async {
stderr.write(result.stderr); stderr.write(result.stderr);
expect(result.exitCode, 0); 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 { test('module', () async {
final qjs = IsolateQjs( final qjs = IsolateQjs(
moduleHandler: (name) async { moduleHandler: (name) async {