[gjs] importer: Useful toString for importer and module objects



commit 4c74e71f38f245184ecaa7d6761d9f049c72dad0
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Fri Dec 3 12:51:09 2010 -0500

    importer: Useful toString for importer and module objects
    
    SpiderMonkey doesn't have a useful default toString(): [object Object].
    When debugging, it's useful to have information like the imported path of
    a module, which we previously didn't display.
    
    Original patch by Jasper St. Pierre; rewritten for current GJS by Philip
    Chimento.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=636283

 gjs/context.cpp                    |    2 +-
 gjs/importer.cpp                   |   91 +++++++++++++++++++++++++++++++++--
 gjs/jsapi-util.h                   |    1 +
 installed-tests/js/testImporter.js |   10 ++++
 4 files changed, 97 insertions(+), 7 deletions(-)
---
diff --git a/gjs/context.cpp b/gjs/context.cpp
index f75f742..1c3b829 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -86,7 +86,7 @@ static const char *const_strings[] = {
     "gi", "versions", "overrides",
     "_init", "_instance_init", "_new_internal", "new",
     "message", "code", "stack", "fileName", "lineNumber", "name",
-    "x", "y", "width", "height",
+    "x", "y", "width", "height", "__modulePath__"
 };
 
 G_STATIC_ASSERT(G_N_ELEMENTS(const_strings) == GJS_STRING_LAST);
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index ce6ee14..2f54be8 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -53,6 +53,33 @@ extern struct JSClass gjs_importer_class;
 GJS_DEFINE_PRIV_FROM_JS(Importer, gjs_importer_class)
 
 static bool
+importer_to_string(JSContext *cx,
+                   unsigned   argc,
+                   JS::Value *vp)
+{
+    GJS_GET_THIS(cx, argc, vp, args, importer);
+    const JSClass *klass = JS_GetClass(importer);
+
+    JS::RootedValue module_path(cx);
+    if (!gjs_object_get_property_const(cx, importer, GJS_STRING_MODULE_PATH,
+                                       &module_path))
+        return false;
+
+    g_autofree char *path = NULL;
+    if (module_path.isNull()) {
+        path = g_strdup("root");
+    } else {
+        if (!gjs_string_to_utf8(cx, module_path, &path))
+            return false;
+    }
+
+    g_autofree char *output = g_strdup_printf("[%s %s]", klass->name, path);
+
+    args.rval().setString(JS_NewStringCopyZ(cx, output));
+    return true;
+}
+
+static bool
 define_meta_properties(JSContext       *context,
                        JS::HandleObject module_obj,
                        const char      *full_path,
@@ -80,26 +107,46 @@ define_meta_properties(JSContext       *context,
             return false;
     }
 
+    /* Null is used instead of undefined to make sure we don't try to invoke
+     * a "resolve" operation. */
     JS::RootedValue module_name_val(context, JS::NullValue());
     JS::RootedValue parent_module_val(context, JS::NullValue());
+    JS::RootedValue module_path(context, JS::NullValue());
     if (parent_is_module) {
         module_name_val.setString(JS_NewStringCopyZ(context, module_name));
         parent_module_val.setObject(*parent);
+
+        JS::RootedValue parent_module_path(context);
+        if (!gjs_object_get_property_const(context, parent,
+                                           GJS_STRING_MODULE_PATH,
+                                           &parent_module_path))
+            return false;
+
+        g_autofree char *module_path_buf = NULL;
+        if (parent_module_path.isNull()) {
+            module_path_buf = g_strdup(module_name);
+        } else {
+            g_autofree char *parent_path = NULL;
+            if (!gjs_string_to_utf8(context, parent_module_path, &parent_path))
+                return false;
+            module_path_buf = g_strdup_printf("%s.%s", parent_path, module_name);
+        }
+        module_path.setString(JS_NewStringCopyZ(context, module_path_buf));
     }
 
+    /* don't set ENUMERATE since we wouldn't want to copy these symbols to any
+     * other object for example. */
     if (!JS_DefineProperty(context, module_obj,
                            "__moduleName__", module_name_val,
-                           /* don't set ENUMERATE since we wouldn't want to copy
-                            * this symbol to any other object for example.
-                            */
                            JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
     if (!JS_DefineProperty(context, module_obj,
                            "__parentModule__", parent_module_val,
-                           /* don't set ENUMERATE since we wouldn't want to copy
-                            * this symbol to any other object for example.
-                            */
+                           JSPROP_READONLY | JSPROP_PERMANENT))
+        return false;
+
+    if (!JS_DefineProperty(context, module_obj, "__modulePath__", module_path,
                            JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
@@ -211,6 +258,29 @@ cancel_import(JSContext       *context,
 }
 
 static bool
+module_to_string(JSContext *cx,
+                 unsigned   argc,
+                 JS::Value *vp)
+{
+    GJS_GET_THIS(cx, argc, vp, args, module);
+
+    JS::RootedValue module_path(cx);
+    if (!gjs_object_get_property_const(cx, module, GJS_STRING_MODULE_PATH,
+                                       &module_path))
+        return false;
+
+    g_assert(!module_path.isNull());
+
+    g_autofree char *path = NULL;
+    if (!gjs_string_to_utf8(cx, module_path, &path))
+        return false;
+    g_autofree char *output = g_strdup_printf("[GjsModule %s]", path);
+
+    args.rval().setString(JS_NewStringCopyZ(cx, output));
+    return true;
+}
+
+static bool
 import_native_file(JSContext       *context,
                    JS::HandleObject obj,
                    const char      *name)
@@ -225,6 +295,10 @@ import_native_file(JSContext       *context,
     if (!define_meta_properties(context, module_obj, NULL, name, obj))
         return false;
 
+    if (!JS_DefineFunction(context, module_obj, "toString", module_to_string,
+                           0, 0))
+        return false;
+
     if (JS_IsExceptionPending(context)) {
         /* I am not sure whether this can happen, but if it does we want to trap it.
          */
@@ -382,6 +456,10 @@ import_file_on_module(JSContext       *context,
     if (!define_meta_properties(context, module_obj, full_path, name, obj))
         goto out;
 
+    if (!JS_DefineFunction(context, module_obj, "toString", module_to_string,
+                           0, 0))
+        goto out;
+
     if (!seal_import(context, obj, name))
         goto out;
 
@@ -889,6 +967,7 @@ JSPropertySpec gjs_importer_proto_props[] = {
 };
 
 JSFunctionSpec gjs_importer_proto_funcs[] = {
+    JS_FS("toString", importer_to_string, 0, 0),
     JS_FS_END
 };
 
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 79d64fb..7564865 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -473,6 +473,7 @@ typedef enum {
   GJS_STRING_Y,
   GJS_STRING_WIDTH,
   GJS_STRING_HEIGHT,
+  GJS_STRING_MODULE_PATH,
   GJS_STRING_LAST
 } GjsConstString;
 
diff --git a/installed-tests/js/testImporter.js b/installed-tests/js/testImporter.js
index 26be7ed..0f01019 100644
--- a/installed-tests/js/testImporter.js
+++ b/installed-tests/js/testImporter.js
@@ -57,6 +57,11 @@ describe('Importer', function () {
         expect(imports).toBeDefined();
     });
 
+    it('has a toString representation', function () {
+        expect(imports.toString()).toEqual('[GjsFileImporter root]');
+        expect(subA.toString()).toEqual('[GjsFileImporter subA]');
+    });
+
     it('throws an import error when trying to import a nonexistent module', function () {
         expect(() => imports.nonexistentModuleName)
             .toThrow(jasmine.objectContaining({ name: 'ImportError' }));
@@ -92,6 +97,11 @@ describe('Importer', function () {
         expect(subFoobar.bar).toEqual('This is bar');
     });
 
+    it('imports modules with a toString representation', function () {
+        expect(foobar.toString()).toEqual('[GjsModule foobar]');
+        expect(subFoobar.toString()).toEqual('[GjsModule subA.subB.foobar]');
+    });
+
     it('does not share the same object for a module on a different path', function () {
         foobar.somethingElse = 'Should remain';
         expect(subFoobar.somethingElse).not.toBeDefined();


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