[gjs/107-connect-pretty-print] WIP: connect up prettyPrint() to console.interact and log




commit 97ff56b5552c00af08c99b7af5f6628bdd61b424
Author: Philip Chimento <philip chimento gmail com>
Date:   Wed Mar 17 22:17:46 2021 -0700

    WIP: connect up prettyPrint() to console.interact and log

 gjs/atoms.h                          |   1 +
 gjs/jsapi-util.cpp                   | 115 -----------------------------------
 gjs/jsapi-util.h                     |   3 -
 modules/console.cpp                  |  50 +++++++++++++--
 modules/print.cpp                    |  18 +++---
 modules/script/_bootstrap/default.js |  26 +++++++-
 test/gjs-tests.cpp                   |  46 --------------
 7 files changed, 81 insertions(+), 178 deletions(-)
---
diff --git a/gjs/atoms.h b/gjs/atoms.h
index 25c9904d..013b80be 100644
--- a/gjs/atoms.h
+++ b/gjs/atoms.h
@@ -72,6 +72,7 @@ class JSTracer;
 
 #define FOR_EACH_SYMBOL_ATOM(macro) \
     macro(hook_up_vfunc, "__GObject__hook_up_vfunc") \
+    macro(pretty_print, "__prettyPrint") \
     macro(private_ns_marker, "__gjsPrivateNS") \
     macro(signal_find, "__GObject__signal_find") \
     macro(signals_block, "__GObject__signals_block") \
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 6262ac2d..9f8d63c1 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -235,121 +235,6 @@ JSObject* gjs_define_string_array(JSContext* context,
     return array;
 }
 
-/**
- * gjs_string_readable:
- *
- * Return a string that can be read back by gjs-console; for
- * JS strings that contain valid Unicode, we return a UTF-8 formatted
- * string.  Otherwise, we return one where non-ASCII-printable bytes
- * are \x escaped.
- *
- */
-[[nodiscard]] static std::string gjs_string_readable(JSContext* context,
-                                                     JS::HandleString string) {
-    std::string buf(1, '"');
-
-    JS::UniqueChars chars(JS_EncodeStringToUTF8(context, string));
-    if (!chars) {
-        /* I'm not sure this code will actually ever be reached except in the
-         * case of OOM, since JS_EncodeStringToUTF8() seems to happily output
-         * non-valid UTF-8 bytes. However, let's leave this in, since
-         * SpiderMonkey may decide to do validation in the future. */
-
-        /* Find out size of buffer to allocate, not counting 0-terminator */
-        size_t len = JS_PutEscapedString(context, NULL, 0, string, '"');
-        char *escaped = g_new(char, len + 1);
-
-        JS_PutEscapedString(context, escaped, len, string, '"');
-        buf += escaped;
-        g_free(escaped);
-    } else {
-        buf += chars.get();
-    }
-
-    return buf + '"';
-}
-
-[[nodiscard]] static std::string _gjs_g_utf8_make_valid(const char* name) {
-    const char *remainder, *invalid;
-    int remaining_bytes, valid_bytes;
-
-    g_return_val_if_fail (name != NULL, NULL);
-
-    remainder = name;
-    remaining_bytes = strlen (name);
-
-    if (remaining_bytes == 0)
-        return std::string(name);
-
-    std::string buf;
-    buf.reserve(remaining_bytes);
-    while (remaining_bytes != 0) {
-        if (g_utf8_validate (remainder, remaining_bytes, &invalid))
-            break;
-        valid_bytes = invalid - remainder;
-
-        buf.append(remainder, valid_bytes);
-        /* append U+FFFD REPLACEMENT CHARACTER */
-        buf += "\357\277\275";
-
-        remaining_bytes -= valid_bytes + 1;
-        remainder = invalid + 1;
-    }
-
-    buf += remainder;
-
-    g_assert(g_utf8_validate(buf.c_str(), -1, nullptr));
-
-    return buf;
-}
-
-/**
- * gjs_value_debug_string:
- * @context:
- * @value: Any JavaScript value
- *
- * Returns: A UTF-8 encoded string describing @value
- */
-std::string gjs_value_debug_string(JSContext* context, JS::HandleValue value) {
-    /* Special case debug strings for strings */
-    if (value.isString()) {
-        JS::RootedString str(context, value.toString());
-        return gjs_string_readable(context, str);
-    }
-
-    JS::RootedString str(context, JS::ToString(context, value));
-
-    if (!str) {
-        JS_ClearPendingException(context);
-        str = JS_ValueToSource(context, value);
-    }
-
-    if (!str) {
-        if (value.isObject()) {
-            /* Specifically the Call object (see jsfun.c in spidermonkey)
-             * does not have a toString; there may be others also.
-             */
-            const JSClass *klass = JS_GetClass(&value.toObject());
-            if (klass != NULL) {
-                str = JS_NewStringCopyZ(context, klass->name);
-                JS_ClearPendingException(context);
-                if (!str)
-                    return "[out of memory copying class name]";
-            } else {
-                gjs_log_exception(context);
-                return "[unknown object]";
-            }
-        } else {
-            return "[unknown non-object]";
-        }
-    }
-
-    g_assert(str);
-
-    JS::UniqueChars bytes = JS_EncodeStringToUTF8(context, str);
-    return _gjs_g_utf8_make_valid(bytes.get());
-}
-
 /**
  * gjs_log_exception_full:
  * @cx: the #JSContext
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 11c23776..447fa0fa 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -423,9 +423,6 @@ bool gjs_log_exception_uncaught(JSContext* cx);
 bool gjs_log_exception_full(JSContext* cx, JS::HandleValue exc,
                             JS::HandleString message, GLogLevelFlags level);
 
-[[nodiscard]] std::string gjs_value_debug_string(JSContext* cx,
-                                                 JS::HandleValue value);
-
 void gjs_warning_reporter(JSContext*, JSErrorReport* report);
 
 GJS_JSAPI_RETURN_CONVENTION
diff --git a/modules/console.cpp b/modules/console.cpp
index 2ef511aa..b44711ad 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -28,6 +28,7 @@
 #include <glib/gprintf.h>  // for g_fprintf
 
 #include <js/CallArgs.h>
+#include <js/CharacterEncoding.h>  // for JS_EncodeStringToUTF8
 #include <js/CompilationAndEvaluation.h>
 #include <js/CompileOptions.h>
 #include <js/ErrorReport.h>
@@ -35,6 +36,9 @@
 #include <js/RootingAPI.h>
 #include <js/SourceText.h>
 #include <js/TypeDecls.h>
+#include <js/Utility.h>  // for UniqueChars
+#include <js/Value.h>
+#include <js/ValueArray.h>
 #include <js/Warnings.h>
 #include <jsapi.h>  // for JS_IsExceptionPending, Exce...
 
@@ -148,13 +152,27 @@ struct AutoCatchCtrlC {};
     return true;
 }
 
+std::string print_string_value(JSContext* cx, JS::HandleValue v_string) {
+    if (!v_string.isString())
+        return "[unexpected result from printing value]";
+
+    JS::RootedString printed_string(cx, v_string.toString());
+    JS::AutoSaveExceptionState exc_state(cx);
+    JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, printed_string));
+    exc_state.restore();
+    if (!chars)
+        return "[error printing value]";
+
+    return chars.get();
+}
+
 /* 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.)
  */
-[[nodiscard]] static bool gjs_console_eval_and_print(JSContext* cx,
-                                                     const std::string& bytes,
-                                                     int lineno) {
+[[nodiscard]] static bool gjs_console_eval_and_print(
+    JSContext* cx, JS::HandleObject global, JS::HandleObject pretty_print,
+    const std::string& bytes, int lineno) {
     JS::SourceText<mozilla::Utf8Unit> source;
     if (!source.init(cx, bytes.c_str(), bytes.size(),
                      JS::SourceOwnership::Borrowed))
@@ -175,7 +193,22 @@ struct AutoCatchCtrlC {};
     if (result.isUndefined())
         return true;
 
-    g_fprintf(stdout, "%s\n", gjs_value_debug_string(cx, result).c_str());
+    JS::AutoSaveExceptionState exc_state(cx);
+    JS::RootedValue v_printed_string(cx);
+    JS::RootedValue v_global(cx, JS::ObjectValue(*global));
+    bool ok = JS::Call(cx, v_global, pretty_print, JS::HandleValueArray(result),
+                       &v_printed_string);
+    if (!ok)
+        gjs_log_exception(cx);
+    exc_state.restore();
+
+    if (ok) {
+        g_fprintf(stdout, "%s\n",
+                  print_string_value(cx, v_printed_string).c_str());
+    } else {
+        g_fprintf(stdout, "[error printing value]\n");
+    }
+
     return true;
 }
 
@@ -198,6 +231,12 @@ gjs_console_interact(JSContext *context,
 
     JS::SetWarningReporter(context, gjs_console_warning_reporter);
 
+    JS::RootedObject pretty_print(context);
+    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
+    if (!gjs_object_require_property(context, global, "global",
+                                     atoms.pretty_print(), &pretty_print))
+        return false;
+
     AutoCatchCtrlC ctrl_c;
 
     // Separate initialization from declaration because of possible overwriting
@@ -253,7 +292,8 @@ gjs_console_interact(JSContext *context,
         bool ok;
         {
             AutoReportException are(context);
-            ok = gjs_console_eval_and_print(context, buffer, startline);
+            ok = gjs_console_eval_and_print(context, global, pretty_print,
+                                            buffer, startline);
         }
         exit_warning = false;
 
diff --git a/modules/print.cpp b/modules/print.cpp
index cb0f1e3b..9b2e870f 100644
--- a/modules/print.cpp
+++ b/modules/print.cpp
@@ -12,22 +12,18 @@
 #include <js/CallArgs.h>
 #include <js/CharacterEncoding.h>  // for JS_EncodeStringToUTF8
 #include <js/Conversions.h>
-#include <js/GCPolicyAPI.h>
 #include <js/PropertySpec.h>  // for JS_FN, JSFunctionSpec, JS_FS_END
 #include <js/RootingAPI.h>
 #include <js/TypeDecls.h>
 #include <js/Utility.h>  // for UniqueChars
+#include <js/Value.h>
 #include <jsapi.h>
 
+#include "gjs/atoms.h"
+#include "gjs/context-private.h"
 #include "gjs/jsapi-util.h"
 #include "modules/print.h"
 
-// Avoid static_assert in MSVC builds
-namespace JS {
-template <>
-struct GCPolicy<void*> : public IgnoreGCPolicy<void*> {};
-}
-
 GJS_JSAPI_RETURN_CONVENTION
 static bool gjs_log(JSContext* cx, unsigned argc, JS::Value* vp) {
     JS::CallArgs argv = JS::CallArgsFromVp(argc, vp);
@@ -155,5 +151,11 @@ bool gjs_define_print_stuff(JSContext* context,
     module.set(JS_NewPlainObject(context));
     if (!module)
         return false;
-    return JS_DefineFunctions(context, module, funcs);
+
+    const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
+    JS::RootedValue v_pretty_print_symbol(
+        context, JS::SymbolValue(atoms.pretty_print().toSymbol()));
+    return JS_DefineProperty(context, module, "prettyPrintSymbol",
+                             v_pretty_print_symbol, GJS_MODULE_PROP_FLAGS) &&
+           JS_DefineFunctions(context, module, funcs);
 }
diff --git a/modules/script/_bootstrap/default.js b/modules/script/_bootstrap/default.js
index 952d7fe3..ef98fd09 100644
--- a/modules/script/_bootstrap/default.js
+++ b/modules/script/_bootstrap/default.js
@@ -5,7 +5,25 @@
 (function (exports) {
     'use strict';
 
-    const {print, printerr, log, logError} = imports._print;
+    const {
+        print,
+        printerr,
+        log: nativeLog,
+        logError: nativeLogError,
+        prettyPrintSymbol,
+    } = imports._print;
+
+    function prettyPrint(value) {
+        // to be filled in
+    }
+
+    function log(...args) {
+        return nativeLog(args.map(prettyPrint).join(' '));
+    }
+
+    function logError(e, ...args) {
+        return nativeLogError(e, args.map(prettyPrint).join(' '));
+    }
 
     Object.defineProperties(exports, {
         ARGV: {
@@ -40,5 +58,11 @@
             writable: true,
             value: logError,
         },
+        [prettyPrintSymbol]: {
+            configurable: false,
+            enumerable: true,
+            writable: true,
+            value: prettyPrint,
+        },
     });
 })(globalThis);
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index e63fc430..f63ea399 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -17,13 +17,11 @@
 #include <glib.h>
 #include <glib/gstdio.h>  // for g_unlink
 
-#include <js/Array.h>
 #include <js/CharacterEncoding.h>
 #include <js/RootingAPI.h>
 #include <js/TypeDecls.h>
 #include <js/Utility.h>  // for UniqueChars
 #include <js/Value.h>
-#include <js/ValueArray.h>
 #include <jsapi.h>
 #include <jspubtd.h>  // for JSProto_Number
 
@@ -536,44 +534,6 @@ static void test_jsapi_util_string_to_ucs4(GjsUnitTestFixture* fx,
     g_free(chars);
 }
 
-static void test_jsapi_util_debug_string_valid_utf8(GjsUnitTestFixture* fx,
-                                                    const void*) {
-    JS::RootedValue v_string(fx->cx);
-    g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, &v_string));
-
-    std::string debug_output = gjs_value_debug_string(fx->cx, v_string);
-
-    g_assert_cmpstr("\"" VALID_UTF8_STRING "\"", ==, debug_output.c_str());
-}
-
-static void test_jsapi_util_debug_string_invalid_utf8(GjsUnitTestFixture* fx,
-                                                      const void*) {
-    g_test_skip("SpiderMonkey doesn't validate UTF-8 after encoding it");
-
-    JS::RootedValue v_string(fx->cx);
-    const char16_t invalid_unicode[] = { 0xffff, 0xffff };
-    v_string.setString(JS_NewUCStringCopyN(fx->cx, invalid_unicode, 2));
-
-    std::string debug_output = gjs_value_debug_string(fx->cx, v_string);
-    // g_assert_cmpstr("\"\\xff\\xff\\xff\\xff\"", ==, debug_output.c_str());
-}
-
-static void test_jsapi_util_debug_string_object_with_complicated_to_string(
-    GjsUnitTestFixture* fx, const void*) {
-    const char16_t desserts[] = {
-        0xd83c, 0xdf6a,  /* cookie */
-        0xd83c, 0xdf69,  /* doughnut */
-    };
-    JS::RootedValueArray<2> contents(fx->cx);
-    contents[0].setString(JS_NewUCStringCopyN(fx->cx, desserts, 2));
-    contents[1].setString(JS_NewUCStringCopyN(fx->cx, desserts + 2, 2));
-    JS::RootedObject array(fx->cx, JS::NewArrayObject(fx->cx, contents));
-    JS::RootedValue v_array(fx->cx, JS::ObjectValue(*array));
-    std::string debug_output = gjs_value_debug_string(fx->cx, v_array);
-
-    g_assert_cmpstr(u8"🍪,🍩", ==, debug_output.c_str());
-}
-
 static void
 gjstest_test_func_util_misc_strv_concat_null(void)
 {
@@ -919,12 +879,6 @@ main(int    argc,
                         test_jsapi_util_string_char16_data);
     ADD_JSAPI_UTIL_TEST("string/to_ucs4",
                         test_jsapi_util_string_to_ucs4);
-    ADD_JSAPI_UTIL_TEST("debug_string/valid-utf8",
-                        test_jsapi_util_debug_string_valid_utf8);
-    ADD_JSAPI_UTIL_TEST("debug_string/invalid-utf8",
-                        test_jsapi_util_debug_string_invalid_utf8);
-    ADD_JSAPI_UTIL_TEST("debug_string/object-with-complicated-to-string",
-                        test_jsapi_util_debug_string_object_with_complicated_to_string);
 
     ADD_JSAPI_UTIL_TEST("gi/args/safe-integer/max",
                         gjstest_test_safe_integer_max);


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