[gjs/wip/fix-imports] Revert recent import changes



commit eb3edba5f7e4a68f9e81a276ba74c64038def0ea
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Tue Oct 1 23:08:56 2013 -0400

    Revert recent import changes
    
    These don't do well with circular imports, so revert them for now.
    I'll have to land a revised patch soon.

 gi/object.c            |   10 +-
 gi/repo.c              |    9 +-
 gi/repo.h              |    2 +-
 gjs/byteArray.c        |   13 +--
 gjs/byteArray.h        |    2 +-
 gjs/context.c          |    6 +-
 gjs/gi.c               |    4 +-
 gjs/gi.h               |    4 +-
 gjs/importer.c         |  373 ++++++++++++++++++++++++++++++++----------------
 gjs/native.c           |   75 ++++++++--
 gjs/native.h           |   30 +++-
 modules/cairo-module.h |    2 +-
 modules/cairo.c        |    7 +-
 modules/console.c      |   11 +-
 modules/console.h      |    2 +-
 modules/modules.c      |    6 +-
 modules/system.c       |    8 +-
 modules/system.h       |    2 +-
 18 files changed, 372 insertions(+), 194 deletions(-)
---
diff --git a/gi/object.c b/gi/object.c
index 039b65f..0549254 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -2676,16 +2676,10 @@ static JSFunctionSpec module_funcs[] = {
 
 JSBool
 gjs_define_private_gi_stuff(JSContext *context,
-                            JSObject **module_out)
+                            JSObject  *module_obj)
 {
-    JSObject *module;
-
-    module = JS_NewObject (context, NULL, NULL, NULL);
-
-    if (!JS_DefineFunctions(context, module, &module_funcs[0]))
+    if (!JS_DefineFunctions(context, module_obj, &module_funcs[0]))
         return JS_FALSE;
 
-    *module_out = module;
-
     return JS_TRUE;
 }
diff --git a/gi/repo.c b/gi/repo.c
index 993290a..3b10d90 100644
--- a/gi/repo.c
+++ b/gi/repo.c
@@ -345,13 +345,18 @@ repo_new(JSContext *context)
 
 JSBool
 gjs_define_repo(JSContext  *context,
-                JSObject  **module_out,
+                JSObject   *module_obj,
                 const char *name)
 {
     JSObject *repo;
 
     repo = repo_new(context);
-    *module_out = repo;
+
+    if (!JS_DefineProperty(context, module_obj,
+                           name, OBJECT_TO_JSVAL(repo),
+                           NULL, NULL,
+                           GJS_MODULE_PROP_FLAGS))
+        return JS_FALSE;
 
     return JS_TRUE;
 }
diff --git a/gi/repo.h b/gi/repo.h
index 60d5be4..940a6e7 100644
--- a/gi/repo.h
+++ b/gi/repo.h
@@ -33,7 +33,7 @@
 G_BEGIN_DECLS
 
 JSBool      gjs_define_repo                     (JSContext      *context,
-                                                 JSObject      **module_out,
+                                                 JSObject       *module_obj,
                                                  const char     *name);
 const char* gjs_info_type_name                  (GIInfoType      type);
 JSObject*   gjs_lookup_private_namespace        (JSContext      *context);
diff --git a/gjs/byteArray.c b/gjs/byteArray.c
index 1145b9e..4597d68 100644
--- a/gjs/byteArray.c
+++ b/gjs/byteArray.c
@@ -910,14 +910,10 @@ static JSFunctionSpec gjs_byte_array_module_funcs[] = {
 };
 
 JSBool
-gjs_define_byte_array_stuff(JSContext  *context,
-                            JSObject  **module_out)
+gjs_define_byte_array_stuff(JSContext      *context,
+                            JSObject       *in_object)
 {
-    JSObject *module;
-
-    module = JS_NewObject (context, NULL, NULL, NULL);
-
-    gjs_byte_array_prototype = JS_InitClass(context, module,
+    gjs_byte_array_prototype = JS_InitClass(context, in_object,
                              NULL,
                              &gjs_byte_array_class,
                              gjs_byte_array_constructor,
@@ -930,9 +926,8 @@ gjs_define_byte_array_stuff(JSContext  *context,
     if (gjs_byte_array_prototype == NULL)
         return JS_FALSE;
 
-    if (!JS_DefineFunctions(context, module, &gjs_byte_array_module_funcs[0]))
+    if (!JS_DefineFunctions(context, in_object, &gjs_byte_array_module_funcs[0]))
         return JS_FALSE;
 
-    *module_out = module;
     return JS_TRUE;
 }
diff --git a/gjs/byteArray.h b/gjs/byteArray.h
index c3b4234..2d0dadc 100644
--- a/gjs/byteArray.h
+++ b/gjs/byteArray.h
@@ -38,7 +38,7 @@ JSBool    gjs_typecheck_bytearray        (JSContext     *context,
                                           JSBool         throw);
 
 JSBool        gjs_define_byte_array_stuff    (JSContext  *context,
-                                              JSObject  **module_out);
+                                              JSObject   *in_object);
 
 JSObject *    gjs_byte_array_from_byte_array (JSContext  *context,
                                               GByteArray *array);
diff --git a/gjs/context.c b/gjs/context.c
index cb2fe1b..c9353d7 100644
--- a/gjs/context.c
+++ b/gjs/context.c
@@ -354,9 +354,9 @@ gjs_context_class_init(GjsContextClass *klass)
         g_free (priv_typelib_dir);
     }
 
-    gjs_register_native_module("byteArray", gjs_define_byte_array_stuff);
-    gjs_register_native_module("_gi", gjs_define_private_gi_stuff);
-    gjs_register_native_module("gi", gjs_define_gi_stuff);
+    gjs_register_native_module("byteArray", gjs_define_byte_array_stuff, 0);
+    gjs_register_native_module("_gi", gjs_define_private_gi_stuff, 0);
+    gjs_register_native_module("gi", gjs_define_gi_stuff, GJS_NATIVE_SUPPLIES_MODULE_OBJ);
 
     gjs_register_static_modules();
 }
diff --git a/gjs/gi.c b/gjs/gi.c
index 6ff4847..0e43122 100644
--- a/gjs/gi.c
+++ b/gjs/gi.c
@@ -33,7 +33,7 @@
 
 JSBool
 gjs_define_gi_stuff(JSContext      *context,
-                    JSObject      **module_out)
+                    JSObject       *module_obj)
 {
-    return gjs_define_repo(context, module_out, "gi");
+    return gjs_define_repo(context, module_obj, "gi");
 }
diff --git a/gjs/gi.h b/gjs/gi.h
index 837d03d..c8da00f 100644
--- a/gjs/gi.h
+++ b/gjs/gi.h
@@ -31,9 +31,9 @@
 G_BEGIN_DECLS
 
 JSBool        gjs_define_gi_stuff     (JSContext      *context,
-                                       JSObject      **module_out);
+                                       JSObject       *module_obj);
 JSBool        gjs_define_private_gi_stuff   (JSContext     *context,
-                                             JSObject     **module_out);
+                                             JSObject      *module_obj);
 
 G_END_DECLS
 
diff --git a/gjs/importer.c b/gjs/importer.c
index f7fbd7e..c0651e2 100644
--- a/gjs/importer.c
+++ b/gjs/importer.c
@@ -31,8 +31,6 @@
 #include <gjs/compat.h>
 #include <gjs/runtime.h>
 
-#include <gio/gio.h>
-
 #include <string.h>
 
 #define MODULE_INIT_FILENAME "__init__.js"
@@ -130,6 +128,107 @@ import_directory(JSContext   *context,
 }
 
 static JSBool
+finish_import(JSContext  *context,
+              const char *name)
+{
+    if (JS_IsExceptionPending(context)) {
+        /* I am not sure whether this can happen, but if it does we want to trap it.
+         */
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "Module '%s' reported an exception but gjs_import_native_module() returned TRUE",
+                  name);
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
+}
+
+static JSBool
+define_import(JSContext  *context,
+              JSObject   *obj,
+              JSObject   *module_obj,
+              const char *name)
+{
+    if (!JS_DefineProperty(context, obj,
+                           name, OBJECT_TO_JSVAL(module_obj),
+                           NULL, NULL,
+                           GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) {
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "Failed to define '%s' in importer",
+                  name);
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
+}
+
+/* Make the property we set in define_import permament;
+ * we do this after the import succesfully completes.
+ */
+static JSBool
+seal_import(JSContext  *context,
+            JSObject   *obj,
+            const char *name)
+{
+    JSBool found;
+    unsigned attrs;
+
+    if (!JS_GetPropertyAttributes(context, obj, name,
+                                  &attrs, &found) || !found) {
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "Failed to get attributes to seal '%s' in importer",
+                  name);
+        return JS_FALSE;
+    }
+
+    attrs |= JSPROP_PERMANENT;
+
+    if (!JS_SetPropertyAttributes(context, obj, name,
+                                  attrs, &found) || !found) {
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "Failed to set attributes to seal '%s' in importer",
+                  name);
+        return JS_FALSE;
+    }
+
+    return JS_TRUE;
+}
+
+/* An import failed. Delete the property pointing to the import
+ * from the parent namespace. In complicated situations this might
+ * not be sufficient to get us fully back to a sane state. If:
+ *
+ *  - We import module A
+ *  - module A imports module B
+ *  - module B imports module A, storing a reference to the current
+ *    module A module object
+ *  - module A subsequently throws an exception
+ *
+ * Then module B is left imported, but the imported module B has
+ * a reference to the failed module A module object. To handle this
+ * we could could try to track the entire "import operation" and
+ * roll back *all* modifications made to the namespace objects.
+ * It's not clear that the complexity would be worth the small gain
+ * in robustness. (You can still come up with ways of defeating
+ * the attempt to clean up.)
+ */
+static void
+cancel_import(JSContext  *context,
+              JSObject   *obj,
+              const char *name)
+{
+    gjs_debug(GJS_DEBUG_IMPORTER,
+              "Cleaning up from failed import of '%s'",
+              name);
+
+    if (!JS_DeleteProperty(context, obj, name)) {
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "Failed to delete '%s' in importer",
+                  name);
+    }
+}
+
+static JSBool
 import_native_file(JSContext  *context,
                    JSObject   *obj,
                    const char *name)
@@ -139,67 +238,102 @@ import_native_file(JSContext  *context,
 
     gjs_debug(GJS_DEBUG_IMPORTER, "Importing '%s'", name);
 
-    if (!gjs_import_native_module(context, name, &module_obj))
-        goto out;
+    module_obj = JS_NewObject(context, NULL, NULL, NULL);
+    if (module_obj == NULL) {
+        return JS_FALSE;
+    }
+
+    /* We store the module object into the parent module before
+     * initializing the module. If the module has the
+     * GJS_NATIVE_SUPPLIES_MODULE_OBJ flag, it will just overwrite
+     * the reference we stored when it initializes.
+     */
+    if (!define_import(context, obj, module_obj, name))
+        return JS_FALSE;
 
     if (!define_meta_properties(context, module_obj, NULL, name, obj))
         goto out;
 
-    if (JS_IsExceptionPending(context)) {
-        /* I am not sure whether this can happen, but if it does we want to trap it.
-         */
-        gjs_debug(GJS_DEBUG_IMPORTER,
-                  "Module '%s' reported an exception but gjs_import_native_module() returned TRUE",
-                  name);
+    if (!gjs_import_native_module(context, module_obj, name))
         goto out;
-    }
 
-    if (!JS_DefineProperty(context, obj,
-                           name, OBJECT_TO_JSVAL(module_obj),
-                           NULL, NULL, GJS_MODULE_PROP_FLAGS))
+    if (!finish_import(context, name))
+        goto out;
+
+    if (!seal_import(context, obj, name))
         goto out;
 
     retval = JS_TRUE;
 
  out:
+    if (!retval)
+        cancel_import(context, obj, name);
+
     return retval;
 }
 
-static JSBool
-import_file(JSContext  *context,
-            const char *name,
-            GFile      *file,
-            JSObject  **module_out)
+static JSObject *
+load_module_init(JSContext  *context,
+                 JSObject   *in_object,
+                 const char *full_path)
 {
-    JSBool ret = JS_FALSE;
-    JSObject *module_obj;
-    char *script = NULL;
-    char *full_path = NULL;
-    gsize script_len = 0;
+    char *script;
+    gsize script_len;
     jsval script_retval;
-    GError *error = NULL;
+    JSObject *module_obj;
+    GError *error;
+    JSBool found;
+    jsid module_init_name;
+
+    /* First we check if js module has already been loaded  */
+    module_init_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
+                                                    GJS_STRING_MODULE_INIT);
+    if (JS_HasPropertyById(context, in_object, module_init_name, &found) && found) {
+        jsval module_obj_val;
+
+        if (JS_GetPropertyById(context,
+                               in_object,
+                               module_init_name,
+                               &module_obj_val)) {
+            return JSVAL_TO_OBJECT(module_obj_val);
+        }
+    }
 
     module_obj = JS_NewObject(context, NULL, NULL, NULL);
-    if (module_obj == NULL)
-        goto out;
+    if (module_obj == NULL) {
+        return JS_FALSE;
+    }
 
-    if (!(g_file_load_contents(file, NULL, &script, &script_len, NULL, &error))) {
-        if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY) &&
-            !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY) &&
-            !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    /* https://bugzilla.mozilla.org/show_bug.cgi?id=599651 means we
+     * can't just pass in the global as the parent */
+    JS_SetParent(context, module_obj,
+                 gjs_get_import_global (context));
+
+    /* Define module in importer for future use and to avoid module_obj
+     * object to be garbage collected during the evaluation of the script */
+    JS_DefinePropertyById(context, in_object,
+                          module_init_name, OBJECT_TO_JSVAL(module_obj),
+                          NULL, NULL,
+                          GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT);
+
+    script_len = 0;
+    error = NULL;
+
+    if (!g_file_get_contents(full_path, &script, &script_len, &error)) {
+        if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_ISDIR) &&
+            !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR) &&
+            !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
             gjs_throw_g_error(context, error);
         else
             g_error_free(error);
 
-        goto out;
+        return NULL;
     }
 
     g_assert(script != NULL);
 
     gjs_debug(GJS_DEBUG_IMPORTER, "Importing %s", full_path);
 
-    full_path = g_file_get_parse_name (file);
-
     if (!JS_EvaluateScript(context,
                            module_obj,
                            script,
@@ -207,6 +341,7 @@ import_file(JSContext  *context,
                            full_path,
                            1, /* line number */
                            &script_retval)) {
+        g_free(script);
 
         /* If JSOPTION_DONT_REPORT_UNCAUGHT is set then the exception
          * would be left set after the evaluate and not go to the error
@@ -214,62 +349,18 @@ import_file(JSContext  *context,
          */
         if (JS_IsExceptionPending(context)) {
             gjs_debug(GJS_DEBUG_IMPORTER,
-                      "Module '%s' left an exception set",
-                      name);
+                      "Module " MODULE_INIT_FILENAME " left an exception set");
             gjs_log_and_keep_exception(context);
         } else {
             gjs_throw(context,
-                         "JS_EvaluateScript() returned FALSE but did not set exception");
+                      "JS_EvaluateScript() returned FALSE but did not set exception");
         }
 
-        goto out;
+        return NULL;
     }
 
-    ret = JS_TRUE;
-
- out:
     g_free(script);
-    g_free(full_path);
-    *module_out = module_obj;
-    return ret;
-}
 
-static JSObject *
-load_module_init(JSContext  *context,
-                 JSObject   *in_object,
-                 const char *full_path)
-{
-    JSObject *module_obj;
-    JSBool found;
-    jsid module_init_name;
-    GFile *file;
-
-    /* First we check if js module has already been loaded  */
-    module_init_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
-                                                    GJS_STRING_MODULE_INIT);
-    if (JS_HasPropertyById(context, in_object, module_init_name, &found) && found) {
-        jsval module_obj_val;
-
-        if (JS_GetPropertyById(context,
-                               in_object,
-                               module_init_name,
-                               &module_obj_val)) {
-            return JSVAL_TO_OBJECT(module_obj_val);
-        }
-    }
-
-    file = g_file_new_for_commandline_arg(full_path);
-    if (!import_file (context, "__init__", file, &module_obj))
-        goto out;
-
-    if (!JS_DefinePropertyById(context, in_object,
-                               module_init_name, OBJECT_TO_JSVAL(module_obj),
-                               NULL, NULL,
-                               GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT))
-        goto out;
-
- out:
-    g_object_unref (file);
     return module_obj;
 }
 
@@ -314,35 +405,87 @@ load_module_elements(JSContext *context,
 }
 
 static JSBool
-import_file_on_module(JSContext  *context,
-                      JSObject   *obj,
-                      const char *name,
-                      GFile      *file)
+import_file(JSContext  *context,
+            JSObject   *obj,
+            const char *name,
+            const char *full_path)
 {
+    char *script;
+    gsize script_len;
     JSObject *module_obj;
+    GError *error;
+    jsval script_retval;
     JSBool retval = JS_FALSE;
-    gchar *full_path;
-
-    full_path = g_file_get_parse_name (file);
 
     gjs_debug(GJS_DEBUG_IMPORTER,
               "Importing '%s'", full_path);
 
-    if (!import_file (context, name, file, &module_obj))
-        goto out;
+    module_obj = JS_NewObject(context, NULL, NULL, NULL);
+    if (module_obj == NULL) {
+        return JS_FALSE;
+    }
+
+    if (!define_import(context, obj, module_obj, name))
+        return JS_FALSE;
 
     if (!define_meta_properties(context, module_obj, full_path, name, obj))
         goto out;
 
-    if (!JS_DefineProperty(context, obj,
-                           name, OBJECT_TO_JSVAL(module_obj),
-                           NULL, NULL, GJS_MODULE_PROP_FLAGS))
+    script_len = 0;
+    error = NULL;
+
+    if (!(g_file_get_contents(full_path, &script, &script_len, &error))) {
+        if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_ISDIR) &&
+            !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR) &&
+            !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+            gjs_throw_g_error(context, error);
+        else
+            g_error_free(error);
+
+        goto out;
+    }
+
+    g_assert(script != NULL);
+
+    if (!JS_EvaluateScript(context,
+                           module_obj,
+                           script,
+                           script_len,
+                           full_path,
+                           1, /* line number */
+                           &script_retval)) {
+        g_free(script);
+
+        /* If JSOPTION_DONT_REPORT_UNCAUGHT is set then the exception
+         * would be left set after the evaluate and not go to the error
+         * reporter function.
+         */
+        if (JS_IsExceptionPending(context)) {
+            gjs_debug(GJS_DEBUG_IMPORTER,
+                      "Module '%s' left an exception set",
+                      name);
+            gjs_log_and_keep_exception(context);
+        } else {
+            gjs_throw(context,
+                         "JS_EvaluateScript() returned FALSE but did not set exception");
+        }
+
+        goto out;
+    }
+
+    g_free(script);
+
+    if (!finish_import(context, name))
+        goto out;
+
+    if (!seal_import(context, obj, name))
         goto out;
 
     retval = JS_TRUE;
 
  out:
-    g_free (full_path);
+    if (!retval)
+        cancel_import(context, obj, name);
 
     return retval;
 }
@@ -364,8 +507,6 @@ do_import(JSContext  *context,
     JSBool result;
     GPtrArray *directories;
     jsid search_path_name;
-    GFile *gfile;
-    gboolean exists;
 
     search_path_name = gjs_runtime_get_const_string(JS_GetRuntime(context),
                                                     GJS_STRING_SEARCH_PATH);
@@ -465,9 +606,8 @@ do_import(JSContext  *context,
             g_free(full_path);
         full_path = g_build_filename(dirname, name,
                                      NULL);
-        gfile = g_file_new_for_commandline_arg(full_path);
 
-        if (g_file_query_file_type(gfile, 0, NULL) == G_FILE_TYPE_DIRECTORY) {
+        if (g_file_test(full_path, G_FILE_TEST_IS_DIR)) {
             gjs_debug(GJS_DEBUG_IMPORTER,
                       "Adding directory '%s' to child importer '%s'",
                       full_path, name);
@@ -479,8 +619,6 @@ do_import(JSContext  *context,
             full_path = NULL;
         }
 
-        g_object_unref(gfile);
-
         /* If we just added to directories, we know we don't need to
          * check for a file.  If we added to directories on an earlier
          * iteration, we want to ignore any files later in the
@@ -495,31 +633,24 @@ do_import(JSContext  *context,
         g_free(full_path);
         full_path = g_build_filename(dirname, filename,
                                      NULL);
-        gfile = g_file_new_for_commandline_arg(full_path);
-        exists = g_file_query_exists(gfile, NULL);
 
-        if (!exists) {
-            gjs_debug(GJS_DEBUG_IMPORTER,
-                      "JS import '%s' not found in %s",
-                      name, dirname);
-
-            g_object_unref(gfile);
-            continue;
-        }
+        if (g_file_test(full_path, G_FILE_TEST_EXISTS)) {
+            if (import_file(context, obj, name, full_path)) {
+                gjs_debug(GJS_DEBUG_IMPORTER,
+                          "successfully imported module '%s'", name);
+                result = JS_TRUE;
+            }
 
-        if (import_file_on_module (context, obj, name, gfile)) {
-            gjs_debug(GJS_DEBUG_IMPORTER,
-                      "successfully imported module '%s'", name);
-            result = JS_TRUE;
+            /* Don't keep searching path if we fail to load the file for
+             * reasons other than it doesn't exist... i.e. broken files
+             * block searching for nonbroken ones
+             */
+            goto out;
         }
 
-        g_object_unref(gfile);
-
-        /* Don't keep searching path if we fail to load the file for
-         * reasons other than it doesn't exist... i.e. broken files
-         * block searching for nonbroken ones
-         */
-        goto out;
+        gjs_debug(GJS_DEBUG_IMPORTER,
+                  "JS import '%s' not found in %s",
+                  name, dirname);
     }
 
     if (directories != NULL) {
diff --git a/gjs/native.c b/gjs/native.c
index 5041dbe..05d5936 100644
--- a/gjs/native.c
+++ b/gjs/native.c
@@ -32,14 +32,30 @@
 #include "jsapi-util.h"
 #include "runtime.h"
 
+typedef struct {
+    GjsDefineModuleFunc func;
+    GjsNativeFlags flags;
+} GjsNativeModule;
+
 static GHashTable *modules = NULL;
 
+static void
+native_module_free(void *data)
+{
+    g_slice_free(GjsNativeModule, data);
+}
+
 void
-gjs_register_native_module (const char          *module_id,
-                            GjsDefineModuleFunc  func)
+gjs_register_native_module (const char            *module_id,
+                            GjsDefineModuleFunc  func,
+                            GjsNativeFlags       flags)
 {
-    if (modules == NULL)
-        modules = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+    GjsNativeModule *module;
+
+    if (modules == NULL) {
+        modules = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                        g_free, native_module_free);
+    }
 
     if (g_hash_table_lookup(modules, module_id) != NULL) {
         g_warning("A second native module tried to register the same id '%s'",
@@ -47,13 +63,34 @@ gjs_register_native_module (const char          *module_id,
         return;
     }
 
-    g_hash_table_replace(modules, g_strdup(module_id), func);
+    module = g_slice_new(GjsNativeModule);
+    module->func = func;
+    module->flags = flags;
+
+    g_hash_table_replace(modules,
+                         g_strdup(module_id),
+                         module);
 
     gjs_debug(GJS_DEBUG_NATIVE,
               "Registered native JS module '%s'",
               module_id);
 }
 
+static JSObject*
+module_get_parent(JSContext *context,
+                  JSObject  *module_obj)
+{
+    jsval value;
+
+    if (gjs_object_get_property_const(context, module_obj, GJS_STRING_PARENT_MODULE, &value) &&
+        !JSVAL_IS_NULL(value) &&
+        JSVAL_IS_OBJECT(value)) {
+        return JSVAL_TO_OBJECT(value);
+    } else {
+        return NULL;
+    }
+}
+
 /**
  * gjs_is_registered_native_module:
  * @context:
@@ -83,27 +120,39 @@ gjs_is_registered_native_module(JSContext  *context,
  * Return a native module that's been preloaded.
  */
 JSBool
-gjs_import_native_module(JSContext   *context,
-                         const char  *name,
-                         JSObject   **module_out)
+gjs_import_native_module(JSContext *context,
+                         JSObject  *module_obj,
+                         const char *name)
 {
-    GjsDefineModuleFunc func;
+    GjsNativeModule *native_module;
+    JSObject *parent;
 
     gjs_debug(GJS_DEBUG_NATIVE,
               "Defining native module '%s'",
               name);
 
     if (modules != NULL)
-        func = g_hash_table_lookup(modules, name);
+        native_module = g_hash_table_lookup(modules, name);
     else
-        func = NULL;
+        native_module = NULL;
 
-    if (!func) {
+    if (!native_module) {
         gjs_throw(context,
                   "No native module '%s' has registered itself",
                   name);
         return JS_FALSE;
     }
 
-    return func (context, module_out);
+    if (native_module->flags & GJS_NATIVE_SUPPLIES_MODULE_OBJ) {
+
+        /* In this case we just throw away "module_obj" eventually,
+         * since the native module defines itself in the parent of
+         * module_obj directly.
+         */
+        parent = module_get_parent(context, module_obj);
+        return (* native_module->func) (context, parent);
+    } else {
+        return (* native_module->func) (context, module_obj);
+    }
 }
+
diff --git a/gjs/native.h b/gjs/native.h
index d2f4b4a..902f7bc 100644
--- a/gjs/native.h
+++ b/gjs/native.h
@@ -33,12 +33,30 @@
 
 G_BEGIN_DECLS
 
-typedef JSBool (* GjsDefineModuleFunc) (JSContext  *context,
-                                        JSObject  **module_out);
+typedef enum {
+    /* This means that the GjsDefineModuleFunc defines the module
+     * name in the parent module, as opposed to the normal process
+     * where the GjsDefineModuleFunc defines module contents. When
+     * importing imports.foo.bar, this flag means the native module is
+     * given foo and defines bar in it, while normally the native
+     * module is given bar and defines stuff in that.
+     *
+     * The purpose of this is to allow a module with lazy properties
+     * by allowing module objects to be custom classes. It's used for
+     * the gobject-introspection module for example.
+     */
+    GJS_NATIVE_SUPPLIES_MODULE_OBJ = 1 << 0
+
+} GjsNativeFlags;
+
+
+typedef JSBool (* GjsDefineModuleFunc) (JSContext *context,
+                                        JSObject  *module_obj);
 
 /* called on context init */
 void   gjs_register_native_module (const char            *module_id,
-                                   GjsDefineModuleFunc  func);
+                                   GjsDefineModuleFunc  func,
+                                   GjsNativeFlags       flags);
 
 /* called by importer.c to to check for already loaded modules */
 gboolean gjs_is_registered_native_module(JSContext  *context,
@@ -46,9 +64,9 @@ gboolean gjs_is_registered_native_module(JSContext  *context,
                                          const char *name);
 
 /* called by importer.c to load a statically linked native module */
-JSBool gjs_import_native_module (JSContext  *context,
-                                 const char *name,
-                                 JSObject   **module_out);
+JSBool gjs_import_native_module (JSContext *context,
+                                 JSObject  *module_obj,
+                                 const char *name);
 
 G_END_DECLS
 
diff --git a/modules/cairo-module.h b/modules/cairo-module.h
index 9b5e2d5..beb6a56 100644
--- a/modules/cairo-module.h
+++ b/modules/cairo-module.h
@@ -24,6 +24,6 @@
 #define __CAIRO_MODULE_H__
 
 JSBool           gjs_js_define_cairo_stuff              (JSContext       *context,
-                                                         JSObject       **module_out);
+                                                         JSObject        *module);
 
 #endif /* __CAIRO_MODULE_H__ */
diff --git a/modules/cairo.c b/modules/cairo.c
index 0565169..d72e39c 100644
--- a/modules/cairo.c
+++ b/modules/cairo.c
@@ -45,14 +45,11 @@ gjs_cairo_check_status(JSContext      *context,
 
 JSBool
 gjs_js_define_cairo_stuff(JSContext *context,
-                          JSObject **module_out)
+                          JSObject  *module)
 {
     jsval obj;
-    JSObject *module;
     JSObject *surface_proto, *pattern_proto, *gradient_proto;
 
-    module = JS_NewObject (context, NULL, NULL, NULL);
-
     obj = gjs_cairo_context_create_proto(context, module,
                                          "Context", NULL);
     if (JSVAL_IS_NULL(obj))
@@ -125,7 +122,5 @@ gjs_js_define_cairo_stuff(JSContext *context,
     if (JSVAL_IS_NULL(obj))
         return JS_FALSE;
 
-    *module_out = module;
-
     return JS_TRUE;
 }
diff --git a/modules/console.c b/modules/console.c
index 1424b68..a1805b3 100644
--- a/modules/console.c
+++ b/modules/console.c
@@ -226,19 +226,14 @@ gjs_console_interact(JSContext *context,
 }
 
 JSBool
-gjs_define_console_stuff(JSContext  *context,
-                         JSObject  **module_out)
+gjs_define_console_stuff(JSContext *context,
+                         JSObject  *module_obj)
 {
-    JSObject *module;
-
-    module = JS_NewObject (context, NULL, NULL, NULL);
-
-    if (!JS_DefineFunction(context, module,
+    if (!JS_DefineFunction(context, module_obj,
                            "interact",
                            (JSNative) gjs_console_interact,
                            1, GJS_MODULE_PROP_FLAGS))
         return JS_FALSE;
 
-    *module_out = module;
     return JS_TRUE;
 }
diff --git a/modules/console.h b/modules/console.h
index bb1e2da..39fae94 100644
--- a/modules/console.h
+++ b/modules/console.h
@@ -31,7 +31,7 @@
 G_BEGIN_DECLS
 
 JSBool        gjs_define_console_stuff     (JSContext      *context,
-                                            JSObject      **module_out);
+                                            JSObject       *in_object);
 JSBool        gjs_console_interact         (JSContext      *context,
                                             unsigned        argc,
                                             jsval          *vp);
diff --git a/modules/modules.c b/modules/modules.c
index aae3569..479fb4c 100644
--- a/modules/modules.c
+++ b/modules/modules.c
@@ -37,8 +37,8 @@ void
 gjs_register_static_modules (void)
 {
 #ifdef ENABLE_CAIRO
-    gjs_register_native_module("cairoNative", gjs_js_define_cairo_stuff);
+    gjs_register_native_module("cairoNative", gjs_js_define_cairo_stuff, 0);
 #endif
-    gjs_register_native_module("system", gjs_js_define_system_stuff);
-    gjs_register_native_module("console", gjs_define_console_stuff);
+    gjs_register_native_module("system", gjs_js_define_system_stuff, 0);
+    gjs_register_native_module("console", gjs_define_console_stuff, 0);
 }
diff --git a/modules/system.c b/modules/system.c
index 41523c5..6fc1e21 100644
--- a/modules/system.c
+++ b/modules/system.c
@@ -129,16 +129,13 @@ static JSFunctionSpec module_funcs[] = {
 };
 
 JSBool
-gjs_js_define_system_stuff(JSContext  *context,
-                           JSObject  **module_out)
+gjs_js_define_system_stuff(JSContext *context,
+                           JSObject  *module)
 {
     GjsContext *gjs_context;
     char *program_name;
     jsval value;
     JSBool retval;
-    JSObject *module;
-
-    module = JS_NewObject (context, NULL, NULL, NULL);
 
     if (!JS_DefineFunctions(context, module, &module_funcs[0]))
         return JS_FALSE;
@@ -176,7 +173,6 @@ gjs_js_define_system_stuff(JSContext  *context,
 
  out:
     g_free(program_name);
-    *module_out = module;
 
     return retval;
 }
diff --git a/modules/system.h b/modules/system.h
index 608de0e..ca093a6 100644
--- a/modules/system.h
+++ b/modules/system.h
@@ -32,7 +32,7 @@
 G_BEGIN_DECLS
 
 JSBool        gjs_js_define_system_stuff     (JSContext      *context,
-                                              JSObject      **module_out);
+                                              JSObject       *in_object);
 
 G_END_DECLS
 


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