blob: 54570705e23e3e0f41bb00d97a0d48852414e277 [file] [log] [blame]
// 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 "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, countof(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, countof(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