LCOV - code coverage report
Current view: top level - modules - console.cpp (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 0.0 % 137 0
Test Date: 2024-04-16 04:37:39 Functions: 0.0 % 12 0
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0.0 % 70 0

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :             : /* vim: set ts=8 sw=4 et tw=78: */
       3                 :             : // SPDX-License-Identifier: MPL-1.1 OR GPL-2.0-or-later OR LGPL-2.1-or-later
       4                 :             : // SPDX-FileCopyrightText: 1998 Netscape Communications Corporation
       5                 :             : 
       6                 :             : #include <config.h>  // for HAVE_READLINE_READLINE_H
       7                 :             : 
       8                 :             : #ifdef HAVE_SIGNAL_H
       9                 :             : #    include <setjmp.h>
      10                 :             : #    include <signal.h>
      11                 :             : #    ifdef _WIN32
      12                 :             : #        define sigjmp_buf jmp_buf
      13                 :             : #        define siglongjmp(e, v) longjmp (e, v)
      14                 :             : #        define sigsetjmp(v, m) setjmp (v)
      15                 :             : #    endif
      16                 :             : #endif
      17                 :             : 
      18                 :             : #ifdef HAVE_READLINE_READLINE_H
      19                 :             : #    include <stdio.h>  // include before readline/readline.h
      20                 :             : 
      21                 :             : #    include <readline/history.h>
      22                 :             : #    include <readline/readline.h>
      23                 :             : #endif
      24                 :             : 
      25                 :             : #include <string>
      26                 :             : 
      27                 :             : #include <glib.h>
      28                 :             : #include <glib/gprintf.h>  // for g_fprintf
      29                 :             : 
      30                 :             : #include <js/CallAndConstruct.h>
      31                 :             : #include <js/CallArgs.h>
      32                 :             : #include <js/CharacterEncoding.h>  // for JS_EncodeStringToUTF8
      33                 :             : #include <js/CompilationAndEvaluation.h>
      34                 :             : #include <js/CompileOptions.h>
      35                 :             : #include <js/ErrorReport.h>
      36                 :             : #include <js/Exception.h>
      37                 :             : #include <js/GlobalObject.h>  // for CurrentGlobalOrNull
      38                 :             : #include <js/PropertyAndElement.h>
      39                 :             : #include <js/RootingAPI.h>
      40                 :             : #include <js/SourceText.h>
      41                 :             : #include <js/TypeDecls.h>
      42                 :             : #include <js/Utility.h>  // for UniqueChars
      43                 :             : #include <js/Value.h>
      44                 :             : #include <js/ValueArray.h>
      45                 :             : #include <js/Warnings.h>
      46                 :             : #include <jsapi.h>  // for JS_NewPlainObject
      47                 :             : 
      48                 :             : #include "gjs/atoms.h"
      49                 :             : #include "gjs/context-private.h"
      50                 :             : #include "gjs/global.h"
      51                 :             : #include "gjs/jsapi-util.h"
      52                 :             : #include "gjs/macros.h"
      53                 :             : #include "modules/console.h"
      54                 :             : 
      55                 :             : namespace mozilla {
      56                 :             : union Utf8Unit;
      57                 :             : }
      58                 :             : 
      59                 :           0 : static void gjs_console_warning_reporter(JSContext*, JSErrorReport* report) {
      60                 :           0 :     JS::PrintError(stderr, report, /* reportWarnings = */ true);
      61                 :           0 : }
      62                 :             : 
      63                 :             : /* Based on js::shell::AutoReportException from SpiderMonkey. */
      64                 :             : class AutoReportException {
      65                 :             :     JSContext *m_cx;
      66                 :             : 
      67                 :             : public:
      68                 :           0 :     explicit AutoReportException(JSContext *cx) : m_cx(cx) {}
      69                 :             : 
      70                 :           0 :     ~AutoReportException() {
      71         [ #  # ]:           0 :         if (!JS_IsExceptionPending(m_cx))
      72                 :           0 :             return;
      73                 :             : 
      74                 :             :         /* Get exception object before printing and clearing exception. */
      75                 :           0 :         JS::ExceptionStack exnStack(m_cx);
      76                 :           0 :         JS::ErrorReportBuilder report(m_cx);
      77   [ #  #  #  # ]:           0 :         if (!JS::StealPendingExceptionStack(m_cx, &exnStack) ||
      78         [ #  # ]:           0 :             !report.init(m_cx, exnStack,
      79                 :             :                          JS::ErrorReportBuilder::NoSideEffects)) {
      80                 :           0 :             g_printerr("(Unable to print exception)\n");
      81                 :           0 :             JS_ClearPendingException(m_cx);
      82                 :           0 :             return;
      83                 :             :         }
      84                 :             : 
      85                 :           0 :         g_assert(!report.report()->isWarning());
      86                 :             : 
      87                 :           0 :         JS::PrintError(stderr, report, /* reportWarnings = */ false);
      88                 :             : 
      89         [ #  # ]:           0 :         if (exnStack.stack()) {
      90                 :             :             JS::UniqueChars stack_str{
      91                 :           0 :                 format_saved_frame(m_cx, exnStack.stack(), 2)};
      92         [ #  # ]:           0 :             if (!stack_str) {
      93                 :           0 :                 g_printerr("(Unable to print stack trace)\n");
      94                 :             :             } else {
      95                 :             :                 GjsAutoChar encoded_stack_str{g_filename_from_utf8(
      96                 :           0 :                     stack_str.get(), -1, nullptr, nullptr, nullptr)};
      97         [ #  # ]:           0 :                 if (!encoded_stack_str)
      98                 :           0 :                     g_printerr("(Unable to print stack trace)\n");
      99                 :             :                 else
     100                 :           0 :                     g_printerr("%s", stack_str.get());
     101                 :           0 :             }
     102                 :           0 :         }
     103                 :             : 
     104                 :           0 :         JS_ClearPendingException(m_cx);
     105   [ #  #  #  # ]:           0 :     }
     106                 :             : };
     107                 :             : 
     108                 :             : 
     109                 :             : // Adapted from https://stackoverflow.com/a/17035073/172999
     110                 :             : class AutoCatchCtrlC {
     111                 :             : #ifdef HAVE_SIGNAL_H
     112                 :             :     void (*m_prev_handler)(int);
     113                 :             : 
     114                 :           0 :     static void handler(int signal) {
     115         [ #  # ]:           0 :         if (signal == SIGINT)
     116                 :           0 :             siglongjmp(jump_buffer, 1);
     117                 :           0 :     }
     118                 :             : 
     119                 :             :  public:
     120                 :             :     static sigjmp_buf jump_buffer;
     121                 :             : 
     122                 :           0 :     AutoCatchCtrlC() {
     123                 :           0 :         m_prev_handler = signal(SIGINT, &AutoCatchCtrlC::handler);
     124                 :           0 :     }
     125                 :             : 
     126                 :           0 :     ~AutoCatchCtrlC() {
     127         [ #  # ]:           0 :         if (m_prev_handler != SIG_ERR)
     128                 :           0 :             signal(SIGINT, m_prev_handler);
     129                 :           0 :     }
     130                 :             : 
     131                 :           0 :     void raise_default() {
     132         [ #  # ]:           0 :         if (m_prev_handler != SIG_ERR)
     133                 :           0 :             signal(SIGINT, m_prev_handler);
     134                 :           0 :         raise(SIGINT);
     135                 :           0 :     }
     136                 :             : #endif  // HAVE_SIGNAL_H
     137                 :             : };
     138                 :             : 
     139                 :             : #ifdef HAVE_SIGNAL_H
     140                 :             : sigjmp_buf AutoCatchCtrlC::jump_buffer;
     141                 :             : #endif  // HAVE_SIGNAL_H
     142                 :             : 
     143                 :           0 : [[nodiscard]] static bool gjs_console_readline(char** bufp,
     144                 :             :                                                const char* prompt) {
     145                 :             : #ifdef HAVE_READLINE_READLINE_H
     146                 :             :     char *line;
     147                 :           0 :     line = readline(prompt);
     148         [ #  # ]:           0 :     if (!line)
     149                 :           0 :         return false;
     150         [ #  # ]:           0 :     if (line[0] != '\0')
     151                 :           0 :         add_history(line);
     152                 :           0 :     *bufp = line;
     153                 :             : #else   // !HAVE_READLINE_READLINE_H
     154                 :             :     char line[256];
     155                 :             :     fprintf(stdout, "%s", prompt);
     156                 :             :     fflush(stdout);
     157                 :             :     if (!fgets(line, sizeof line, stdin))
     158                 :             :         return false;
     159                 :             :     *bufp = g_strdup(line);
     160                 :             : #endif  // !HAVE_READLINE_READLINE_H
     161                 :           0 :     return true;
     162                 :             : }
     163                 :             : 
     164                 :           0 : std::string print_string_value(JSContext* cx, JS::HandleValue v_string) {
     165         [ #  # ]:           0 :     if (!v_string.isString())
     166                 :           0 :         return "[unexpected result from printing value]";
     167                 :             : 
     168                 :           0 :     JS::RootedString printed_string(cx, v_string.toString());
     169                 :           0 :     JS::AutoSaveExceptionState exc_state(cx);
     170                 :           0 :     JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, printed_string));
     171                 :           0 :     exc_state.restore();
     172         [ #  # ]:           0 :     if (!chars)
     173                 :           0 :         return "[error printing value]";
     174                 :             : 
     175                 :           0 :     return chars.get();
     176                 :           0 : }
     177                 :             : 
     178                 :             : /* Return value of false indicates an uncatchable exception, rather than any
     179                 :             :  * exception. (This is because the exception should be auto-printed around the
     180                 :             :  * invocation of this function.)
     181                 :             :  */
     182                 :           0 : [[nodiscard]] static bool gjs_console_eval_and_print(JSContext* cx,
     183                 :             :                                                      JS::HandleObject global,
     184                 :             :                                                      const std::string& bytes,
     185                 :             :                                                      int lineno) {
     186                 :           0 :     JS::SourceText<mozilla::Utf8Unit> source;
     187         [ #  # ]:           0 :     if (!source.init(cx, bytes.c_str(), bytes.size(),
     188                 :             :                      JS::SourceOwnership::Borrowed))
     189                 :           0 :         return false;
     190                 :             : 
     191                 :           0 :     JS::CompileOptions options(cx);
     192                 :           0 :     options.setFileAndLine("typein", lineno);
     193                 :             : 
     194                 :           0 :     JS::RootedValue result(cx);
     195         [ #  # ]:           0 :     if (!JS::Evaluate(cx, options, source, &result)) {
     196         [ #  # ]:           0 :         if (!JS_IsExceptionPending(cx))
     197                 :           0 :             return false;
     198                 :             :     }
     199                 :             : 
     200                 :           0 :     GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
     201                 :           0 :     gjs->schedule_gc_if_needed();
     202                 :             : 
     203         [ #  # ]:           0 :     if (result.isUndefined())
     204                 :           0 :         return true;
     205                 :             : 
     206                 :           0 :     JS::AutoSaveExceptionState exc_state(cx);
     207                 :           0 :     JS::RootedValue v_printed_string(cx);
     208                 :             :     JS::RootedValue v_pretty_print(
     209                 :           0 :         cx, gjs_get_global_slot(global, GjsGlobalSlot::PRETTY_PRINT_FUNC));
     210                 :           0 :     bool ok = JS::Call(cx, global, v_pretty_print, JS::HandleValueArray(result),
     211                 :             :                        &v_printed_string);
     212         [ #  # ]:           0 :     if (!ok)
     213                 :           0 :         gjs_log_exception(cx);
     214                 :           0 :     exc_state.restore();
     215                 :             : 
     216         [ #  # ]:           0 :     if (ok) {
     217                 :           0 :         g_fprintf(stdout, "%s\n",
     218                 :           0 :                   print_string_value(cx, v_printed_string).c_str());
     219                 :             :     } else {
     220                 :           0 :         g_fprintf(stdout, "[error printing value]\n");
     221                 :             :     }
     222                 :             : 
     223                 :           0 :     return true;
     224                 :           0 : }
     225                 :             : 
     226                 :             : GJS_JSAPI_RETURN_CONVENTION
     227                 :             : static bool
     228                 :           0 : gjs_console_interact(JSContext *context,
     229                 :             :                      unsigned   argc,
     230                 :             :                      JS::Value *vp)
     231                 :             : {
     232                 :           0 :     JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
     233                 :             :     volatile bool eof, exit_warning;  // accessed after setjmp()
     234                 :           0 :     JS::RootedObject global{context, JS::CurrentGlobalOrNull(context)};
     235                 :             :     char* temp_buf;
     236                 :             :     volatile int lineno;     // accessed after setjmp()
     237                 :             :     volatile int startline;  // accessed after setjmp()
     238                 :             : 
     239                 :             : #ifndef HAVE_READLINE_READLINE_H
     240                 :             :     int rl_end = 0;  // nonzero if using readline and any text is typed in
     241                 :             : #endif
     242                 :             : 
     243                 :           0 :     JS::SetWarningReporter(context, gjs_console_warning_reporter);
     244                 :             : 
     245                 :           0 :     AutoCatchCtrlC ctrl_c;
     246                 :             : 
     247                 :             :     // Separate initialization from declaration because of possible overwriting
     248                 :             :     // when siglongjmp() jumps into this function
     249                 :           0 :     eof = exit_warning = false;
     250                 :           0 :     temp_buf = nullptr;
     251                 :           0 :     lineno = 1;
     252                 :             :     do {
     253                 :             :         /*
     254                 :             :          * Accumulate lines until we get a 'compilable unit' - one that either
     255                 :             :          * generates an error (before running out of source) or that compiles
     256                 :             :          * cleanly.  This should be whenever we get a complete statement that
     257                 :             :          * coincides with the end of a line.
     258                 :             :          */
     259                 :           0 :         startline = lineno;
     260                 :           0 :         std::string buffer;
     261                 :             :         do {
     262                 :             : #ifdef HAVE_SIGNAL_H
     263                 :             :             // sigsetjmp() returns 0 if control flow encounters it normally, and
     264                 :             :             // nonzero if it's been jumped to. In the latter case, use a while
     265                 :             :             // loop so that we call sigsetjmp() a second time to reinit the jump
     266                 :             :             // buffer.
     267         [ #  # ]:           0 :             while (sigsetjmp(AutoCatchCtrlC::jump_buffer, 1) != 0) {
     268                 :           0 :                 g_fprintf(stdout, "\n");
     269   [ #  #  #  #  :           0 :                 if (buffer.empty() && rl_end == 0) {
                   #  # ]
     270         [ #  # ]:           0 :                     if (!exit_warning) {
     271                 :           0 :                         g_fprintf(stdout,
     272                 :             :                                   "(To exit, press Ctrl+C again or Ctrl+D)\n");
     273                 :           0 :                         exit_warning = true;
     274                 :             :                     } else {
     275                 :           0 :                         ctrl_c.raise_default();
     276                 :             :                     }
     277                 :             :                 } else {
     278                 :           0 :                     exit_warning = false;
     279                 :             :                 }
     280                 :           0 :                 buffer.clear();
     281                 :           0 :                 startline = lineno = 1;
     282                 :             :             }
     283                 :             : #endif  // HAVE_SIGNAL_H
     284                 :             : 
     285         [ #  # ]:           0 :             if (!gjs_console_readline(
     286         [ #  # ]:           0 :                     &temp_buf, startline == lineno ? "gjs> " : ".... ")) {
     287                 :           0 :                 eof = true;
     288                 :           0 :                 break;
     289                 :             :             }
     290                 :           0 :             buffer += temp_buf;
     291                 :           0 :             buffer += "\n";
     292                 :           0 :             g_free(temp_buf);
     293                 :           0 :             lineno++;
     294         [ #  # ]:           0 :         } while (!JS_Utf8BufferIsCompilableUnit(context, global, buffer.c_str(),
     295                 :             :                                                 buffer.size()));
     296                 :             : 
     297                 :             :         bool ok;
     298                 :             :         {
     299                 :           0 :             AutoReportException are(context);
     300                 :           0 :             ok = gjs_console_eval_and_print(context, global, buffer, startline);
     301                 :           0 :         }
     302                 :           0 :         exit_warning = false;
     303                 :             : 
     304                 :           0 :         GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
     305   [ #  #  #  # ]:           0 :         ok = gjs->run_jobs_fallible() && ok;
     306                 :             : 
     307         [ #  # ]:           0 :         if (!ok) {
     308                 :             :             /* If this was an uncatchable exception, throw another uncatchable
     309                 :             :              * exception on up to the surrounding JS::Evaluate() in main(). This
     310                 :             :              * happens when you run gjs-console and type imports.system.exit(0);
     311                 :             :              * at the prompt. If we don't throw another uncatchable exception
     312                 :             :              * here, then it's swallowed and main() won't exit. */
     313                 :           0 :             return false;
     314                 :             :         }
     315   [ #  #  #  # ]:           0 :     } while (!eof);
     316                 :             : 
     317                 :           0 :     g_fprintf(stdout, "\n");
     318                 :             : 
     319                 :           0 :     argv.rval().setUndefined();
     320                 :           0 :     return true;
     321                 :           0 : }
     322                 :             : 
     323                 :             : bool
     324                 :           0 : gjs_define_console_stuff(JSContext              *context,
     325                 :             :                          JS::MutableHandleObject module)
     326                 :             : {
     327                 :           0 :     module.set(JS_NewPlainObject(context));
     328                 :           0 :     const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
     329                 :           0 :     return JS_DefineFunctionById(context, module, atoms.interact(),
     330                 :             :                                  gjs_console_interact, 1,
     331                 :           0 :                                  GJS_MODULE_PROP_FLAGS);
     332                 :             : }
        

Generated by: LCOV version 2.0-1