[gjs/wip/ptomato/mozjs52: 37/40] console: Refactor read-eval-print loop



commit 5ff7e387e4dbb0d8bb0b69e8a51a87fe27356eb8
Author: Philip Chimento <philip chimento gmail com>
Date:   Mon Jun 5 23:30:18 2017 -0700

    console: Refactor read-eval-print loop
    
    This makes the REPL look more like the code from the SpiderMonkey shell
    in mozjs52. This is because in SpiderMonkey 52, error reporter callbacks
    and JS_ReportPendingException() are going away. We can anticipate this
    change by introducing an AutoReportException class which prints any
    pending exception when it goes out of scope. (We will do something
    similar in the rest of GJS, but the interactive console prints errors
    differently, so it requires slightly different code.)

 modules/console.cpp |  132 ++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 98 insertions(+), 34 deletions(-)
---
diff --git a/modules/console.cpp b/modules/console.cpp
index df0d5f8..224a635 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -58,7 +58,7 @@
 #include "gjs/jsapi-wrapper.h"
 
 static void
-gjs_console_error_reporter(JSContext *cx, const char *message, JSErrorReport *report)
+gjs_console_print_error(const char *message, JSErrorReport *report)
 {
     /* Code modified from SpiderMonkey js/src/jscntxt.cpp, js::PrintError() */
 
@@ -134,6 +134,59 @@ gjs_console_error_reporter(JSContext *cx, const char *message, JSErrorReport *re
     g_free(prefix);
 }
 
+static void
+gjs_console_error_reporter(JSContext *cx, const char *message, JSErrorReport *report)
+{
+    gjs_console_print_error(message, report);
+}
+
+/* Based on js::shell::AutoReportException from SpiderMonkey. Different from
+ * GjsAutoReportException because we have different error reporting code in the
+ * interactive shell. */
+class AutoReportException {
+    JSContext *m_cx;
+
+public:
+    explicit AutoReportException(JSContext *cx) : m_cx(cx) {}
+
+    ~AutoReportException() {
+        if (!JS_IsExceptionPending(m_cx))
+            return;
+
+        /* Get exception object before printing and clearing exception. */
+        JS::RootedValue v_exn(m_cx);
+        (void) JS_GetPendingException(m_cx, &v_exn);
+
+        JS_ClearPendingException(m_cx);
+
+        JS::RootedObject exn(m_cx, &v_exn.toObject());
+        JSErrorReport *report = JS_ErrorFromException(m_cx, exn);
+        if (!report) {
+            fprintf(stderr, "out of memory initializing ErrorReport\n");
+            fflush(stderr);
+            JS_ClearPendingException(m_cx);
+            return;
+        }
+
+        MOZ_ASSERT(!JSREPORT_IS_WARNING(report->flags));
+
+        gjs_console_print_error(report);
+
+        {
+            JS::AutoSaveExceptionState savedExc(m_cx);
+            JS::RootedObject stack(m_cx, ExceptionStackOrNull(exn));
+            if (stack) {
+                JS::RootedString stack_trace(m_cx);
+                if (!JS::BuildStackString(m_cx, stack, &stack_trace, 2))
+                    fputs("(Unable to print stack trace)\n", stderr);
+            }
+            savedExc.restore();
+        }
+
+        JS_ClearPendingException(m_cx);
+    }
+};
+
 #ifdef HAVE_READLINE_READLINE_H
 static bool
 gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt)
@@ -161,6 +214,44 @@ gjs_console_readline(JSContext *cx, char **bufp, FILE *file, const char *prompt)
 }
 #endif
 
+/* Return value of false indicates an uncatchable exception, rather than any
+ * exception. (This is because the exception should be auto-printed around the
+ * invocation of this function.)
+ */
+static bool
+gjs_console_eval_and_print(JSContext  *cx,
+                           const char *bytes,
+                           size_t      length,
+                           int         lineno)
+{
+    JS::CompileOptions options(cx);
+    options.setUTF8(true)
+           .setFileAndLine("typein", lineno);
+
+    JS::RootedValue result(cx);
+    if (!JS::Evaluate(cx, options, bytes, length, &result)) {
+        if (!JS_IsExceptionPending(cx))
+            return false;
+    }
+
+    gjs_schedule_gc_if_needed(cx);
+
+    if (result.isUndefined())
+        return true;
+
+    JS::RootedString str(cx, JS::ToString(cx, result));
+    if (!str)
+        return true;
+
+    char *display_str;
+    display_str = gjs_value_debug_string(cx, result);
+    if (display_str) {
+        g_fprintf(stdout, "%s\n", display_str);
+        g_free(display_str);
+    }
+    return true;
+}
+
 static bool
 gjs_console_interact(JSContext *context,
                      unsigned   argc,
@@ -168,8 +259,6 @@ gjs_console_interact(JSContext *context,
 {
     JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
     bool eof = false;
-    JS::RootedValue result(context);
-    JS::RootedString str(context);
     JS::RootedObject global(context, gjs_get_import_global(context));
     GString *buffer = NULL;
     char *temp_buf = NULL;
@@ -202,44 +291,18 @@ gjs_console_interact(JSContext *context,
         } while (!JS_BufferIsCompilableUnit(context, global,
                                             buffer->str, buffer->len));
 
-        JS::CompileOptions options(context);
-        options.setUTF8(true)
-               .setFileAndLine("typein", startline);
-        if (!JS::Evaluate(context, options, buffer->str, buffer->len,
-                          &result)) {
+        AutoReportException are(context);
+        if (!gjs_console_eval_and_print(context, buffer->str, buffer->len,
+                                        startline)) {
             /* If this was an uncatchable exception, throw another uncatchable
              * exception on up to the surrounding JS::Evaluate() in main(). This
              * happens when you run gjs-console and type imports.system.exit(0);
              * at the prompt. If we don't throw another uncatchable exception
              * here, then it's swallowed and main() won't exit. */
-            if (!JS_IsExceptionPending(context)) {
-                argv.rval().set(result);
-                return false;
-            }
-        }
-
-        gjs_schedule_gc_if_needed(context);
-
-        if (JS_GetPendingException(context, &result)) {
-            if (!JS_ReportPendingException(context))
-                return false;
-            goto next;
-        } else if (result.isUndefined()) {
-            goto next;
-        } else {
-            str = JS::ToString(context, result);
-        }
-
-        if (str) {
-            char *display_str;
-            display_str = gjs_value_debug_string(context, result);
-            if (display_str != NULL) {
-                g_fprintf(stdout, "%s\n", display_str);
-                g_free(display_str);
-            }
+            g_string_free(buffer, true);
+            return false;
         }
 
- next:
         g_string_free(buffer, true);
     } while (!eof);
 
@@ -248,6 +311,7 @@ gjs_console_interact(JSContext *context,
     if (file != stdin)
         fclose(file);
 
+    argv.rval().setUndefined();
     return true;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]