| // Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "li.h" | |
| #include <stdio.h> | |
| #include <iterator> | |
| #include "src/lib/line_input/line_input.h" | |
| #include "third_party/quickjs/list.h" | |
| namespace shell { | |
| namespace { | |
| JSClassID js_repl_class_id; | |
| JSClassDef js_repl_class = { | |
| "Repl", | |
| .finalizer = nullptr, | |
| }; | |
| // Expects no arguments | |
| JSValue NewRepl(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 0) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = new repl::Repl(ctx, "li > "); | |
| JSValue obj = JS_NewObjectClass(ctx, js_repl_class_id); | |
| if (JS_IsException(obj)) { | |
| return obj; | |
| } | |
| JS_SetOpaque(obj, repl); | |
| return obj; | |
| } | |
| // Expects 1 argument: a repl::Repl object | |
| JSValue CloseRepl(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 1) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| delete repl; | |
| return JS_NewBool(ctx, true); | |
| } | |
| // Expects 3 arguments: a repl::Repl object, a byte buffer and the number of relevant bytes | |
| // in the byte buffer | |
| JSValue OnInput(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 3) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| size_t num_bytes; | |
| uint8_t *bytes = JS_GetArrayBuffer(ctx, &num_bytes, argv[1]); | |
| if (!bytes) { | |
| return JS_ThrowTypeError(ctx, "Expected an ArrayBuffer"); | |
| } | |
| int len; | |
| if (JS_ToInt32(ctx, &len, argv[2])) { | |
| return JS_EXCEPTION; | |
| } | |
| bool exit_shell = repl->FeedInput(bytes, len); | |
| return JS_NewBool(ctx, exit_shell); | |
| } | |
| // Expects 1 argument: a repl::Repl object | |
| // evaluates repl->cmd_, and returns an array [error_in_script (boolean), script_result] | |
| JSValue GetAndEvalCmd(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 1) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| const char *cmd = repl->GetCmd(); | |
| JSValue script_result = JS_Eval(ctx, cmd, strlen(cmd), "<evalScript>", JS_EVAL_TYPE_GLOBAL); | |
| bool error_in_script = false; | |
| if (JS_IsException(script_result)) { | |
| js_std_dump_error(ctx); | |
| error_in_script = true; | |
| } | |
| JSValue result = JS_NewArray(ctx); | |
| JS_SetPropertyUint32(ctx, result, 0, JS_NewBool(ctx, error_in_script)); | |
| JS_SetPropertyUint32(ctx, result, 1, script_result); | |
| return result; | |
| } | |
| // Expects 2 argument: a repl::Repl object and a JS object, the result of a script evaluation | |
| // (that is not an error) | |
| JSValue ShowOutput(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 2) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| const char *output = JS_ToCString(ctx, argv[1]); | |
| repl->Write(output); | |
| repl->Write("\n"); | |
| return JS_NewBool(ctx, true); | |
| } | |
| // Expects 1 argument: a repl::Repl object | |
| JSValue ShowPrompt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 1) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| repl->ShowPrompt(); | |
| return JS_NewBool(ctx, true); | |
| } | |
| // Expects 1 argument: a repl::Repl object | |
| JSValue GetLine(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { | |
| if (argc != 1) { | |
| return JS_EXCEPTION; | |
| } | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(argv[0], js_repl_class_id)); | |
| return JS_NewString(ctx, repl->GetLine()); | |
| } | |
| const JSCFunctionListEntry js_li_funcs[] = { | |
| JS_CFUNC_DEF("createRepl", 0, NewRepl), JS_CFUNC_DEF("onInput", 3, OnInput), | |
| JS_CFUNC_DEF("closeRepl", 1, CloseRepl), JS_CFUNC_DEF("getAndEvalCmd", 1, GetAndEvalCmd), | |
| JS_CFUNC_DEF("showPrompt", 1, ShowPrompt), JS_CFUNC_DEF("showOutput", 2, ShowOutput), | |
| JS_CFUNC_DEF("getLine", 1, GetLine)}; | |
| int LiRunOnInit(JSContext *ctx, JSModuleDef *m) { | |
| /* Repl Input class */ | |
| JS_NewClassID(&js_repl_class_id); | |
| JS_NewClass(JS_GetRuntime(ctx), js_repl_class_id, &js_repl_class); | |
| return JS_SetModuleExportList(ctx, m, js_li_funcs, std::size(js_li_funcs)); | |
| }; | |
| } // namespace | |
| namespace li { | |
| JSModuleDef *LiModuleInit(JSContext *ctx, const char *module_name) { | |
| JSModuleDef *m = JS_NewCModule(ctx, module_name, LiRunOnInit); | |
| if (!m) { | |
| return nullptr; | |
| } | |
| JS_AddModuleExportList(ctx, m, js_li_funcs, std::size(js_li_funcs)); | |
| return m; | |
| } | |
| // Expects one argument: the repl as a JSValue | |
| repl::Repl *GetRepl(JSContext *ctx, JSValueConst repl_js) { | |
| repl::Repl *repl = reinterpret_cast<repl::Repl *>(JS_GetOpaque(repl_js, js_repl_class_id)); | |
| return repl; | |
| } | |
| } // namespace li | |
| } // namespace shell |