allow 'await' in the REPL and added os.sleepAsync()
This commit is contained in:
parent
8de4538ff3
commit
e44b793e38
5 changed files with 123 additions and 48 deletions
|
@ -368,6 +368,9 @@ optional properties:
|
||||||
@item backtrace_barrier
|
@item backtrace_barrier
|
||||||
Boolean (default = false). If true, error backtraces do not list the
|
Boolean (default = false). If true, error backtraces do not list the
|
||||||
stack frames below the evalScript.
|
stack frames below the evalScript.
|
||||||
|
@item async
|
||||||
|
Boolean (default = false). If true, @code{await} is accepted in the
|
||||||
|
script and a promise is returned.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item loadScript(filename)
|
@item loadScript(filename)
|
||||||
|
@ -769,6 +772,12 @@ write_fd]} or null in case of error.
|
||||||
@item sleep(delay_ms)
|
@item sleep(delay_ms)
|
||||||
Sleep during @code{delay_ms} milliseconds.
|
Sleep during @code{delay_ms} milliseconds.
|
||||||
|
|
||||||
|
@item sleepAsync(delay_ms)
|
||||||
|
Asynchronouse sleep during @code{delay_ms} milliseconds. Returns a promise. Example:
|
||||||
|
@example
|
||||||
|
await os.sleepAsync(500);
|
||||||
|
@end example
|
||||||
|
|
||||||
@item now()
|
@item now()
|
||||||
Return a timestamp in milliseconds with more precision than
|
Return a timestamp in milliseconds with more precision than
|
||||||
@code{Date.now()}. The time origin is unspecified and is normally not
|
@code{Date.now()}. The time origin is unspecified and is normally not
|
||||||
|
|
|
@ -751,6 +751,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
JSValue ret;
|
JSValue ret;
|
||||||
JSValueConst options_obj;
|
JSValueConst options_obj;
|
||||||
BOOL backtrace_barrier = FALSE;
|
BOOL backtrace_barrier = FALSE;
|
||||||
|
BOOL is_async = FALSE;
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
|
@ -758,6 +759,9 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
|
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
|
||||||
"backtrace_barrier"))
|
"backtrace_barrier"))
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
|
if (get_bool_option(ctx, &is_async, options_obj,
|
||||||
|
"async"))
|
||||||
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = JS_ToCStringLen(ctx, &len, argv[0]);
|
str = JS_ToCStringLen(ctx, &len, argv[0]);
|
||||||
|
@ -770,6 +774,8 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
|
||||||
flags = JS_EVAL_TYPE_GLOBAL;
|
flags = JS_EVAL_TYPE_GLOBAL;
|
||||||
if (backtrace_barrier)
|
if (backtrace_barrier)
|
||||||
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
|
flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER;
|
||||||
|
if (is_async)
|
||||||
|
flags |= JS_EVAL_FLAG_ASYNC;
|
||||||
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
|
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
|
||||||
JS_FreeCString(ctx, str);
|
JS_FreeCString(ctx, str);
|
||||||
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
|
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
|
||||||
|
@ -2082,6 +2088,38 @@ static JSClassDef js_os_timer_class = {
|
||||||
.gc_mark = js_os_timer_mark,
|
.gc_mark = js_os_timer_mark,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* return a promise */
|
||||||
|
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
|
||||||
|
int argc, JSValueConst *argv)
|
||||||
|
{
|
||||||
|
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||||
|
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
|
||||||
|
int64_t delay;
|
||||||
|
JSOSTimer *th;
|
||||||
|
JSValue promise, resolving_funcs[2];
|
||||||
|
|
||||||
|
if (JS_ToInt64(ctx, &delay, argv[0]))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
promise = JS_NewPromiseCapability(ctx, resolving_funcs);
|
||||||
|
if (JS_IsException(promise))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
th = js_mallocz(ctx, sizeof(*th));
|
||||||
|
if (!th) {
|
||||||
|
JS_FreeValue(ctx, promise);
|
||||||
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
||||||
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
th->has_object = FALSE;
|
||||||
|
th->timeout = get_time_ms() + delay;
|
||||||
|
th->func = JS_DupValue(ctx, resolving_funcs[0]);
|
||||||
|
list_add_tail(&th->link, &ts->os_timers);
|
||||||
|
JS_FreeValue(ctx, resolving_funcs[0]);
|
||||||
|
JS_FreeValue(ctx, resolving_funcs[1]);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
static void call_handler(JSContext *ctx, JSValueConst func)
|
static void call_handler(JSContext *ctx, JSValueConst func)
|
||||||
{
|
{
|
||||||
JSValue ret, func1;
|
JSValue ret, func1;
|
||||||
|
@ -3648,6 +3686,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||||
JS_CFUNC_DEF("now", 0, js_os_now ),
|
JS_CFUNC_DEF("now", 0, js_os_now ),
|
||||||
JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
|
JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ),
|
||||||
JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
|
JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ),
|
||||||
|
JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ),
|
||||||
JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
|
JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ),
|
||||||
JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
|
JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ),
|
||||||
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
|
JS_CFUNC_DEF("chdir", 0, js_os_chdir ),
|
||||||
|
|
|
@ -33827,7 +33827,7 @@ static __exception int js_parse_program(JSParseState *s)
|
||||||
emit_op(s, OP_get_loc);
|
emit_op(s, OP_get_loc);
|
||||||
emit_u16(s, fd->eval_ret_idx);
|
emit_u16(s, fd->eval_ret_idx);
|
||||||
|
|
||||||
emit_op(s, OP_return);
|
emit_return(s, TRUE);
|
||||||
} else {
|
} else {
|
||||||
emit_return(s, FALSE);
|
emit_return(s, FALSE);
|
||||||
}
|
}
|
||||||
|
@ -33959,7 +33959,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
fd->module = m;
|
fd->module = m;
|
||||||
if (m != NULL) {
|
if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) {
|
||||||
fd->in_function_body = TRUE;
|
fd->in_function_body = TRUE;
|
||||||
fd->func_kind = JS_FUNC_ASYNC;
|
fd->func_kind = JS_FUNC_ASYNC;
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,6 +307,9 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
||||||
/* don't include the stack frames before this eval in the Error() backtraces */
|
/* don't include the stack frames before this eval in the Error() backtraces */
|
||||||
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
||||||
|
/* allow top-level await in normal script. JS_Eval() returns a
|
||||||
|
promise. Only allowed with JS_EVAL_TYPE_GLOBAL */
|
||||||
|
#define JS_EVAL_FLAG_ASYNC (1 << 7)
|
||||||
|
|
||||||
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
||||||
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
||||||
|
|
116
repl.js
116
repl.js
|
@ -118,6 +118,7 @@ import * as os from "os";
|
||||||
var utf8 = true;
|
var utf8 = true;
|
||||||
var show_time = false;
|
var show_time = false;
|
||||||
var show_colors = true;
|
var show_colors = true;
|
||||||
|
var eval_start_time;
|
||||||
var eval_time = 0;
|
var eval_time = 0;
|
||||||
|
|
||||||
var mexpr = "";
|
var mexpr = "";
|
||||||
|
@ -814,10 +815,8 @@ import * as os from "os";
|
||||||
prompt += ps2;
|
prompt += ps2;
|
||||||
} else {
|
} else {
|
||||||
if (show_time) {
|
if (show_time) {
|
||||||
var t = Math.round(eval_time) + " ";
|
var t = eval_time / 1000;
|
||||||
eval_time = 0;
|
prompt += t.toFixed(6) + " ";
|
||||||
t = dupstr("0", 5 - t.length) + t;
|
|
||||||
prompt += t.substring(0, t.length - 4) + "." + t.substring(t.length - 4);
|
|
||||||
}
|
}
|
||||||
plen = prompt.length;
|
plen = prompt.length;
|
||||||
prompt += ps1;
|
prompt += ps1;
|
||||||
|
@ -1224,37 +1223,6 @@ import * as os from "os";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function eval_and_print(expr) {
|
|
||||||
var result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (eval_mode === "math")
|
|
||||||
expr = '"use math"; void 0;' + expr;
|
|
||||||
var now = (new Date).getTime();
|
|
||||||
/* eval as a script */
|
|
||||||
result = std.evalScript(expr, { backtrace_barrier: true });
|
|
||||||
eval_time = (new Date).getTime() - now;
|
|
||||||
std.puts(colors[styles.result]);
|
|
||||||
print(result);
|
|
||||||
std.puts("\n");
|
|
||||||
std.puts(colors.none);
|
|
||||||
/* set the last result */
|
|
||||||
g._ = result;
|
|
||||||
} catch (error) {
|
|
||||||
std.puts(colors[styles.error_msg]);
|
|
||||||
if (error instanceof Error) {
|
|
||||||
console.log(error);
|
|
||||||
if (error.stack) {
|
|
||||||
std.puts(error.stack);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std.puts("Throw: ");
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
std.puts(colors.none);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cmd_start() {
|
function cmd_start() {
|
||||||
if (!config_numcalc) {
|
if (!config_numcalc) {
|
||||||
if (has_jscalc)
|
if (has_jscalc)
|
||||||
|
@ -1281,29 +1249,32 @@ import * as os from "os";
|
||||||
}
|
}
|
||||||
|
|
||||||
function readline_handle_cmd(expr) {
|
function readline_handle_cmd(expr) {
|
||||||
handle_cmd(expr);
|
if (!handle_cmd(expr)) {
|
||||||
cmd_readline_start();
|
cmd_readline_start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* return true if async termination */
|
||||||
function handle_cmd(expr) {
|
function handle_cmd(expr) {
|
||||||
var colorstate, cmd;
|
var colorstate, cmd;
|
||||||
|
|
||||||
if (expr === null) {
|
if (expr === null) {
|
||||||
expr = "";
|
expr = "";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (expr === "?") {
|
if (expr === "?") {
|
||||||
help();
|
help();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
cmd = extract_directive(expr);
|
cmd = extract_directive(expr);
|
||||||
if (cmd.length > 0) {
|
if (cmd.length > 0) {
|
||||||
if (!handle_directive(cmd, expr))
|
if (!handle_directive(cmd, expr)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
expr = expr.substring(cmd.length + 1);
|
expr = expr.substring(cmd.length + 1);
|
||||||
}
|
}
|
||||||
if (expr === "")
|
if (expr === "")
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (mexpr)
|
if (mexpr)
|
||||||
expr = mexpr + '\n' + expr;
|
expr = mexpr + '\n' + expr;
|
||||||
|
@ -1312,20 +1283,73 @@ import * as os from "os";
|
||||||
level = colorstate[1];
|
level = colorstate[1];
|
||||||
if (pstate) {
|
if (pstate) {
|
||||||
mexpr = expr;
|
mexpr = expr;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
mexpr = "";
|
mexpr = "";
|
||||||
|
|
||||||
if (has_bignum) {
|
if (has_bignum) {
|
||||||
BigFloatEnv.setPrec(eval_and_print.bind(null, expr),
|
/* XXX: async is not supported in this case */
|
||||||
|
BigFloatEnv.setPrec(eval_and_print_start.bind(null, expr, false),
|
||||||
prec, expBits);
|
prec, expBits);
|
||||||
} else {
|
} else {
|
||||||
eval_and_print(expr);
|
eval_and_print_start(expr, true);
|
||||||
}
|
}
|
||||||
level = 0;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function eval_and_print_start(expr, is_async) {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (eval_mode === "math")
|
||||||
|
expr = '"use math"; void 0;' + expr;
|
||||||
|
eval_start_time = os.now();
|
||||||
|
/* eval as a script */
|
||||||
|
result = std.evalScript(expr, { backtrace_barrier: true, async: is_async });
|
||||||
|
if (is_async) {
|
||||||
|
/* result is a promise */
|
||||||
|
result.then(print_eval_result, print_eval_error);
|
||||||
|
} else {
|
||||||
|
print_eval_result(result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
print_eval_error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_eval_result(result) {
|
||||||
|
eval_time = os.now() - eval_start_time;
|
||||||
|
std.puts(colors[styles.result]);
|
||||||
|
print(result);
|
||||||
|
std.puts("\n");
|
||||||
|
std.puts(colors.none);
|
||||||
|
/* set the last result */
|
||||||
|
g._ = result;
|
||||||
|
|
||||||
|
handle_cmd_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_eval_error(error) {
|
||||||
|
std.puts(colors[styles.error_msg]);
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.log(error);
|
||||||
|
if (error.stack) {
|
||||||
|
std.puts(error.stack);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std.puts("Throw: ");
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
std.puts(colors.none);
|
||||||
|
|
||||||
|
handle_cmd_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_cmd_end() {
|
||||||
|
level = 0;
|
||||||
/* run the garbage collector after each command */
|
/* run the garbage collector after each command */
|
||||||
std.gc();
|
std.gc();
|
||||||
|
cmd_readline_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorize_js(str) {
|
function colorize_js(str) {
|
||||||
|
|
Loading…
Reference in a new issue