1 // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 // SPDX-FileCopyrightText: 2018 Philip Chimento <philip.chimento@gmail.com>
3 // SPDX-FileContributor: Philip Chimento <philip.chimento@gmail.com>
4
5 #include <config.h> // for HAVE_READLINE_READLINE_H, HAVE_UNISTD_H
6
7 #include <stdint.h>
8 #include <stdio.h> // for feof, fflush, fgets, stdin, stdout
9
10 #ifdef HAVE_READLINE_READLINE_H
11 # include <readline/history.h>
12 # include <readline/readline.h>
13 #endif
14
15 #include <glib.h>
16
17 #include <js/CallArgs.h>
18 #include <js/PropertySpec.h>
19 #include <js/RootingAPI.h>
20 #include <js/TypeDecls.h>
21 #include <js/Utility.h> // for UniqueChars
22 #include <js/Value.h>
23 #include <jsapi.h> // for JS_DefineFunctions, JS_NewStringCopyZ
24
25 #include "gjs/atoms.h"
26 #include "gjs/context-private.h"
27 #include "gjs/context.h"
28 #include "gjs/global.h"
29 #include "gjs/jsapi-util-args.h"
30 #include "gjs/jsapi-util.h"
31 #include "gjs/macros.h"
32
33 #include "util/console.h"
34
35 GJS_JSAPI_RETURN_CONVENTION
quit(JSContext * cx,unsigned argc,JS::Value * vp)36 static bool quit(JSContext* cx, unsigned argc, JS::Value* vp) {
37 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
38
39 int32_t exitcode;
40 if (!gjs_parse_call_args(cx, "quit", args, "i", "exitcode", &exitcode))
41 return false;
42
43 GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
44 gjs->exit(exitcode);
45 return false; // without gjs_throw() == "throw uncatchable exception"
46 }
47
48 GJS_JSAPI_RETURN_CONVENTION
do_readline(JSContext * cx,unsigned argc,JS::Value * vp)49 static bool do_readline(JSContext* cx, unsigned argc, JS::Value* vp) {
50 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
51
52 JS::UniqueChars prompt;
53 if (!gjs_parse_call_args(cx, "readline", args, "|s", "prompt", &prompt))
54 return false;
55
56 GjsAutoChar line;
57 do {
58 const char* real_prompt = prompt ? prompt.get() : "db> ";
59 #ifdef HAVE_READLINE_READLINE_H
60 if (gjs_console_is_tty(stdin_fd)) {
61 line = readline(real_prompt);
62 } else {
63 #else
64 {
65 #endif // HAVE_READLINE_READLINE_H
66 char buf[256];
67 g_print("%s", real_prompt);
68 fflush(stdout);
69 if (!fgets(buf, sizeof buf, stdin))
70 buf[0] = '\0';
71 line.reset(g_strdup(g_strchomp(buf)));
72
73 if (!gjs_console_is_tty(stdin_fd)) {
74 if (feof(stdin)) {
75 g_print("[quit due to end of input]\n");
76 line.reset(g_strdup("quit"));
77 } else {
78 g_print("%s\n", line.get());
79 }
80 }
81 }
82
83 /* EOF, return null */
84 if (!line) {
85 args.rval().setUndefined();
86 return true;
87 }
88 } while (line && line.get()[0] == '\0');
89
90 /* Add line to history and convert it to a JSString so that we can pass it
91 * back as the return value */
92 #ifdef HAVE_READLINE_READLINE_H
93 add_history(line);
94 #endif
95 args.rval().setString(JS_NewStringCopyZ(cx, line));
96 return true;
97 }
98
99 // clang-format off
100 static JSFunctionSpec debugger_funcs[] = {
101 JS_FN("quit", quit, 1, GJS_MODULE_PROP_FLAGS),
102 JS_FN("readline", do_readline, 1, GJS_MODULE_PROP_FLAGS),
103 JS_FS_END
104 };
105 // clang-format on
106
107 void gjs_context_setup_debugger_console(GjsContext* gjs) {
108 auto cx = static_cast<JSContext*>(gjs_context_get_native_context(gjs));
109
110 JS::RootedObject debuggee(cx, gjs_get_import_global(cx));
111 JS::RootedObject debugger_global(
112 cx, gjs_create_global_object(cx, GjsGlobalType::DEBUGGER));
113
114 // Enter realm of the debugger and initialize it with the debuggee
115 JSAutoRealm ar(cx, debugger_global);
116 JS::RootedObject debuggee_wrapper(cx, debuggee);
117 if (!JS_WrapObject(cx, &debuggee_wrapper)) {
118 gjs_log_exception(cx);
119 return;
120 }
121
122 const GjsAtoms& atoms = GjsContextPrivate::atoms(cx);
123 JS::RootedValue v_wrapper(cx, JS::ObjectValue(*debuggee_wrapper));
124 if (!JS_SetPropertyById(cx, debugger_global, atoms.debuggee(), v_wrapper) ||
125 !JS_DefineFunctions(cx, debugger_global, debugger_funcs) ||
126 !gjs_define_global_properties(cx, debugger_global,
127 GjsGlobalType::DEBUGGER, "GJS debugger",
128 "debugger"))
129 gjs_log_exception(cx);
130 }
131