[gjs: 2/9] log: Add gjs_debug_id()



commit 05212069d8751c45050db4fbade25a4af273a462
Author: Philip Chimento <philip chimento gmail com>
Date:   Sat Nov 18 01:36:07 2017 -0800

    log: Add gjs_debug_id()
    
    In order to prevent converting a jsid to a UTF-8 string, only for the
    purpose of putting it in a debug message, we introduce gjs_debug_id() and
    its friends gjs_debug_value(), gjs_debug_string(), and
    gjs_debug_symbol(). If this seems like overkill just to avoid one UTF-8
    conversion, it is; but these functions will be used later in the
    structured logging code.

 gjs/jsapi-util-string.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++++
 gjs/jsapi-util.cpp        |  11 ++--
 gjs/jsapi-util.h          |   5 ++
 gjs/module.cpp            |   7 +--
 4 files changed, 144 insertions(+), 12 deletions(-)
---
diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp
index 9ce43587..dc9777f0 100644
--- a/gjs/jsapi-util-string.cpp
+++ b/gjs/jsapi-util-string.cpp
@@ -24,6 +24,9 @@
 #include <config.h>
 
 #include <algorithm>
+#include <iomanip>
+#include <sstream>
+#include <string>
 #include <string.h>
 
 #include "jsapi-util.h"
@@ -365,3 +368,133 @@ gjs_intern_string_to_id(JSContext  *cx,
     JS::RootedId id(cx, INTERNED_STRING_TO_JSID(cx, str));
     return id;
 }
+
+static std::string
+gjs_debug_flat_string(JSFlatString *fstr)
+{
+    JSLinearString *str = js::FlatStringToLinearString(fstr);
+    size_t len = js::GetLinearStringLength(str);
+
+    JS::AutoCheckCannotGC nogc;
+    if (js::LinearStringHasLatin1Chars(str)) {
+        const JS::Latin1Char *chars = js::GetLatin1LinearStringChars(nogc, str);
+        return std::string(reinterpret_cast<const char *>(chars), len);
+    }
+
+    std::ostringstream out;
+    const char16_t *chars = js::GetTwoByteLinearStringChars(nogc, str);
+    for (size_t ix = 0; ix < len; ix++) {
+        char16_t c = chars[ix];
+        if (c == '\n')
+            out << "\\n";
+        else if (c == '\t')
+            out << "\\t";
+        else if (c >= 32 && c < 127)
+            out << c;
+        else if (c <= 255)
+            out << "\\x" << std::setfill('0') << std::setw(2) << unsigned(c);
+        else
+            out << "\\x" << std::setfill('0') << std::setw(4) << unsigned(c);
+    }
+    return out.str();
+}
+
+std::string
+gjs_debug_string(JSString *str)
+{
+    if (!JS_StringIsFlat(str)) {
+        std::ostringstream out("<non-flat string of length ");
+        out << JS_GetStringLength(str) << '>';
+        return out.str();
+    }
+    return gjs_debug_flat_string(JS_ASSERT_STRING_IS_FLAT(str));
+}
+
+std::string
+gjs_debug_symbol(JS::Symbol * const sym)
+{
+    /* This is OK because JS::GetSymbolCode() and JS::GetSymbolDescription()
+     * can't cause a garbage collection */
+    JS::HandleSymbol handle = JS::HandleSymbol::fromMarkedLocation(&sym);
+    JS::SymbolCode code = JS::GetSymbolCode(handle);
+    JSString *descr = JS::GetSymbolDescription(handle);
+
+    if (size_t(code) < JS::WellKnownSymbolLimit)
+        return gjs_debug_string(descr);
+
+    std::ostringstream out;
+    if (code == JS::SymbolCode::InSymbolRegistry) {
+        out << "Symbol.for(";
+        if (descr)
+            out << gjs_debug_string(descr);
+        else
+            out << "undefined";
+        out << ")";
+        return out.str();
+    }
+    if (code == JS::SymbolCode::UniqueSymbol) {
+        if (descr)
+            out << "Symbol(" << gjs_debug_string(descr) << ")";
+        else
+            out << "<Symbol at " << sym << ">";
+        return out.str();
+    }
+
+    out << "<unexpected symbol code " << uint32_t(code) << ">";
+    return out.str();
+}
+
+std::string
+gjs_debug_value(JS::Value v)
+{
+    std::ostringstream out;
+    if (v.isNull())
+        return "null";
+    if (v.isUndefined())
+        return "undefined";
+    if (v.isInt32()) {
+        out << v.toInt32();
+        return out.str();
+    }
+    if (v.isDouble()) {
+        out << v.toDouble();
+        return out.str();
+    }
+    if (v.isString()) {
+        out << gjs_debug_string(v.toString());
+        return out.str();
+    }
+    if (v.isSymbol()) {
+        out << gjs_debug_symbol(v.toSymbol());
+        return out.str();
+    }
+    if (v.isObject() && js::IsFunctionObject(&v.toObject())) {
+        JSFunction* fun = JS_GetObjectFunction(&v.toObject());
+        JSString *display_name = JS_GetFunctionDisplayId(fun);
+        if (display_name)
+            out << "<function " << gjs_debug_string(display_name);
+        else
+            out << "<unnamed function";
+        out << " at " << fun << '>';
+        return out.str();
+    }
+    if (v.isObject()) {
+        JSObject *obj = &v.toObject();
+        const JSClass* clasp = JS_GetClass(obj);
+        out << "<object " << clasp->name << " at " << obj <<  '>';
+        return out.str();
+    }
+    if (v.isBoolean())
+        return (v.toBoolean() ? "true" : "false");
+    if (v.isMagic())
+        return "<magic>";
+    return "unexpected value";
+}
+
+std::string
+gjs_debug_id(jsid id)
+{
+    if (JSID_IS_STRING(id))
+        return gjs_debug_flat_string(JSID_TO_FLAT_STRING(id));
+    return gjs_debug_value(js::IdToValue(id));
+}
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 5c91dd2a..3f51aac5 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -138,15 +138,12 @@ throw_property_lookup_error(JSContext       *cx,
     /* remember gjs_throw() is a no-op if JS_GetProperty()
      * already set an exception
      */
-    GjsAutoJSChar name;
-    gjs_get_string_id(cx, property_name, &name);
-
     if (description)
-        gjs_throw(cx, "No property '%s' in %s (or %s)", name.get(), description,
-                  reason);
+        gjs_throw(cx, "No property '%s' in %s (or %s)",
+                  gjs_debug_id(property_name).c_str(), description, reason);
     else
-        gjs_throw(cx, "No property '%s' in object %p (or %s)", name.get(),
-                  obj.address(), reason);
+        gjs_throw(cx, "No property '%s' in object %p (or %s)",
+                  gjs_debug_id(property_name).c_str(), obj.get(), reason);
 }
 
 /* Returns whether the object had the property; if the object did
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 8c54ee0a..881a221d 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -385,4 +385,9 @@ bool gjs_object_require_converted_property(JSContext       *cx,
                                                  value);
 }
 
+std::string gjs_debug_string(JSString *str);
+std::string gjs_debug_symbol(JS::Symbol * const sym);
+std::string gjs_debug_value(JS::Value v);
+std::string gjs_debug_id(jsid id);
+
 #endif  /* __GJS_JSAPI_UTIL_H__ */
diff --git a/gjs/module.cpp b/gjs/module.cpp
index cc6657a7..474bba40 100644
--- a/gjs/module.cpp
+++ b/gjs/module.cpp
@@ -163,17 +163,14 @@ class GjsModule {
          * be supported according to ES6. For compatibility with earlier GJS,
          * we treat it as if it were a real property, but warn about it. */
 
-        GjsAutoJSChar prop_name;
-        if (!gjs_get_string_id(cx, id, &prop_name))
-            return false;
-
         g_warning("Some code accessed the property '%s' on the module '%s'. "
                   "That property was defined with 'let' or 'const' inside the "
                   "module. This was previously supported, but is not correct "
                   "according to the ES6 standard. Any symbols to be exported "
                   "from a module must be defined with 'var'. The property "
                   "access will work as previously for the time being, but "
-                  "please fix your code anyway.", prop_name.get(), m_name);
+                  "please fix your code anyway.",
+                  gjs_debug_id(id).c_str(), m_name);
 
         JS::Rooted<JS::PropertyDescriptor> desc(cx);
         return JS_GetPropertyDescriptorById(cx, lexical, id, &desc) &&


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