[gjs/wip/imports-rewrite: 5/5] bootstrap: Add a JS implementation of the imports system



commit 75b676e8164eb09e3da1a9bd4ceae15c9c1073a1
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu Jan 2 15:59:16 2014 -0500

    bootstrap: Add a JS implementation of the imports system
    
    Re-implement the old importer with a Proxy in bootstrap.js, reducing the
    footprint and making the code significantly easier to understand.

 Makefile-modules.am              |    7 +-
 Makefile.am                      |    5 +-
 gi/repo.cpp                      |  384 +-------------
 gi/repo.h                        |    3 -
 gi/union.cpp                     |    1 -
 gjs/bootstrap.cpp                |   42 ++
 gjs/context.cpp                  |   30 +-
 gjs/context.h                    |    2 +
 gjs/gi.h                         |    2 -
 gjs/gjs-module.h                 |    1 -
 gjs/importer.cpp                 | 1118 --------------------------------------
 gjs/importer.h                   |   50 --
 gjs/jsapi-util.h                 |    7 -
 modules/bootstrap.js             |  143 +++++-
 modules/importer.cpp             |  153 ++++++
 gjs/gi.cpp => modules/importer.h |   25 +-
 modules/modules.cpp              |    2 +
 17 files changed, 377 insertions(+), 1598 deletions(-)
---
diff --git a/Makefile-modules.am b/Makefile-modules.am
index 4b6e356..4151c5d 100644
--- a/Makefile-modules.am
+++ b/Makefile-modules.am
@@ -1,6 +1,5 @@
 
-NATIVE_MODULES = libconsole.la libsystem.la libmodules_resources.la
-
+NATIVE_MODULES = libconsole.la libsystem.la libmodules_resources.la libimporter.la
 if ENABLE_CAIRO
 NATIVE_MODULES += libcairoNative.la
 endif
@@ -56,3 +55,7 @@ libsystem_la_SOURCES = modules/system.h modules/system.cpp
 libconsole_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS)
 libconsole_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(READLINE_LIBS)
 libconsole_la_SOURCES = modules/console.h modules/console.cpp
+
+libimporter_la_CPPFLAGS = $(JS_NATIVE_MODULE_CFLAGS)
+libimporter_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD)
+libimporter_la_SOURCES = modules/importer.h modules/importer.cpp
diff --git a/Makefile.am b/Makefile.am
index 835bb86..5774462 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,7 +35,6 @@ nobase_gjs_module_include_HEADERS =   \
        gjs/compat.h            \
        gjs/coverage.h \
        gjs/byteArray.h         \
-       gjs/importer.h          \
        gjs/jsapi-util.h        \
        gjs/runtime.h           \
        gjs/type-module.h       \
@@ -112,10 +111,8 @@ libgjs_la_SOURCES =                \
        gjs/byteArray.cpp               \
        gjs/context.cpp         \
        gjs/bootstrap.cpp       \
-       gjs/importer.cpp                \
        gjs/gi.h                \
-       gjs/gi.cpp              \
-       gjs/coverage.cpp \
+       gjs/coverage.cpp        \
        gjs/jsapi-private.cpp   \
        gjs/jsapi-util.cpp      \
        gjs/jsapi-dynamic-class.cpp \
diff --git a/gi/repo.cpp b/gi/repo.cpp
index fd665ed..2b56e75 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -39,6 +39,7 @@
 
 #include <gjs/compat.h>
 #include <gjs/jsapi-private.h>
+#include <gjs/runtime.h>
 
 #include <util/log.h>
 #include <util/misc.h>
@@ -46,298 +47,6 @@
 #include <girepository.h>
 #include <string.h>
 
-typedef struct {
-    void *dummy;
-
-} Repo;
-
-extern struct JSClass gjs_repo_class;
-
-GJS_DEFINE_PRIV_FROM_JS(Repo, gjs_repo_class)
-
-static JSObject * lookup_override_function(JSContext *, jsid);
-
-static JSBool
-get_version_for_ns (JSContext *context,
-                    JSObject  *repo_obj,
-                    jsid       ns_id,
-                    char     **version)
-{
-    jsid versions_name;
-    jsval versions_val;
-    JSObject *versions;
-    jsval version_val;
-
-    versions_name = gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS);
-    if (!gjs_object_require_property(context, repo_obj, "GI repository object", versions_name, 
&versions_val) ||
-        !JSVAL_IS_OBJECT(versions_val)) {
-        gjs_throw(context, "No 'versions' property in GI repository object");
-        return JS_FALSE;
-    }
-
-    versions = JSVAL_TO_OBJECT(versions_val);
-
-    *version = NULL;
-    if (JS_GetPropertyById(context, versions, ns_id, &version_val) &&
-        JSVAL_IS_STRING(version_val)) {
-        gjs_string_to_utf8(context, version_val, version);
-    }
-
-    return JS_TRUE;
-}
-
-static JSBool
-resolve_namespace_object(JSContext  *context,
-                         JSObject   *repo_obj,
-                         jsid        ns_id,
-                         const char *ns_name)
-{
-    char *version = NULL;
-    JSObject *override;
-    jsval result;
-    JSObject *gi_namespace = NULL;
-    JSBool ret = JS_FALSE;
-
-    JS_BeginRequest(context);
-
-    if (!get_version_for_ns(context, repo_obj, ns_id, &version))
-        goto out;
-
-    /* Defines a property on "obj" (the javascript repo object)
-     * with the given namespace name, pointing to that namespace
-     * in the repo.
-     */
-    if (!gjs_import_gi_module(context, ns_name, version, &gi_namespace))
-        goto out;
-
-    JS_AddObjectRoot(context, &gi_namespace);
-
-    /* Define the property early, to avoid reentrancy issues if
-       the override module looks for namespaces that import this */
-    if (!JS_DefineProperty(context, repo_obj,
-                           ns_name, OBJECT_TO_JSVAL(gi_namespace),
-                           NULL, NULL,
-                           GJS_MODULE_PROP_FLAGS))
-        g_error("no memory to define ns property");
-
-    override = lookup_override_function(context, ns_id);
-    if (override && !JS_CallFunctionValue (context,
-                                           gi_namespace, /* thisp */
-                                           OBJECT_TO_JSVAL(override), /* callee */
-                                           0, /* argc */
-                                           NULL, /* argv */
-                                           &result))
-        goto out;
-
-    gjs_debug(GJS_DEBUG_GNAMESPACE,
-              "Defined namespace '%s' %p in GIRepository %p", ns_name, gi_namespace, repo_obj);
-
-    ret = JS_TRUE;
-    gjs_schedule_gc_if_needed(context);
-
- out:
-    g_free(version);
-    if (gi_namespace)
-        JS_RemoveObjectRoot(context, &gi_namespace);
-    JS_EndRequest(context);
-    return ret;
-}
-
-/*
- * Like JSResolveOp, but flags provide contextual information as follows:
- *
- *  JSRESOLVE_QUALIFIED   a qualified property id: obj.id or obj[id], not id
- *  JSRESOLVE_ASSIGNING   obj[id] is on the left-hand side of an assignment
- *  JSRESOLVE_DETECTING   'if (o.p)...' or similar detection opcode sequence
- *  JSRESOLVE_DECLARING   var, const, or function prolog declaration opcode
- *  JSRESOLVE_CLASSNAME   class name used when constructing
- *
- * The *objp out parameter, on success, should be null to indicate that id
- * was not resolved; and non-null, referring to obj or one of its prototypes,
- * if id was resolved.
- */
-static JSBool
-repo_new_resolve(JSContext *context,
-                 JS::HandleObject obj,
-                 JS::HandleId id,
-                 unsigned flags,
-                 JS::MutableHandleObject objp)
-{
-    Repo *priv;
-    char *name;
-    JSBool ret = JS_TRUE;
-
-    if (!gjs_get_string_id(context, id, &name))
-        return JS_TRUE; /* not resolved, but no error */
-
-    /* let Object.prototype resolve these */
-    if (strcmp(name, "valueOf") == 0 ||
-        strcmp(name, "toString") == 0)
-        goto out;
-
-    priv = priv_from_js(context, obj);
-    gjs_debug_jsprop(GJS_DEBUG_GREPO, "Resolve prop '%s' hook obj %p priv %p", name, *obj, priv);
-
-    if (priv == NULL) /* we are the prototype, or have the wrong class */
-        goto out;
-
-    if (!resolve_namespace_object(context, obj, id, name)) {
-        ret = JS_FALSE;
-    } else {
-        objp.set(obj); /* store the object we defined the prop in */
-    }
-
- out:
-    g_free(name);
-    return ret;
-}
-
-GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(repo)
-
-static void
-repo_finalize(JSFreeOp *fop,
-              JSObject *obj)
-{
-    Repo *priv;
-
-    priv = (Repo*) JS_GetPrivate(obj);
-    gjs_debug_lifecycle(GJS_DEBUG_GREPO,
-                        "finalize, obj %p priv %p", obj, priv);
-    if (priv == NULL)
-        return; /* we are the prototype, not a real instance */
-
-    GJS_DEC_COUNTER(repo);
-    g_slice_free(Repo, priv);
-}
-
-/* The bizarre thing about this vtable is that it applies to both
- * instances of the object, and to the prototype that instances of the
- * class have.
- */
-struct JSClass gjs_repo_class = {
-    "GIRepository", /* means "new GIRepository()" works */
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_NEW_RESOLVE,
-    JS_PropertyStub,
-    JS_DeletePropertyStub,
-    JS_PropertyStub,
-    JS_StrictPropertyStub,
-    JS_EnumerateStub,
-    (JSResolveOp) repo_new_resolve, /* needs cast since it's the new resolve signature */
-    JS_ConvertStub,
-    repo_finalize,
-    JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSPropertySpec gjs_repo_proto_props[] = {
-    { NULL }
-};
-
-JSFunctionSpec gjs_repo_proto_funcs[] = {
-    { NULL }
-};
-
-static JSObject*
-repo_new(JSContext *context)
-{
-    Repo *priv;
-    JSObject *repo;
-    JSObject *global;
-    JSObject *versions;
-    JSObject *private_ns;
-    JSBool found;
-    jsid versions_name, private_ns_name;
-
-    global = gjs_get_import_global(context);
-
-    if (!JS_HasProperty(context, global, gjs_repo_class.name, &found))
-        return NULL;
-    if (!found) {
-        JSObject *prototype;
-        prototype = JS_InitClass(context, global,
-                                 /* parent prototype JSObject* for
-                                  * prototype; NULL for
-                                  * Object.prototype
-                                  */
-                                 NULL,
-                                 &gjs_repo_class,
-                                 /* constructor for instances (NULL for
-                                  * none - just name the prototype like
-                                  * Math - rarely correct)
-                                  */
-                                 gjs_repo_constructor,
-                                 /* number of constructor args */
-                                 0,
-                                 /* props of prototype */
-                                 &gjs_repo_proto_props[0],
-                                 /* funcs of prototype */
-                                 &gjs_repo_proto_funcs[0],
-                                 /* props of constructor, MyConstructor.myprop */
-                                 NULL,
-                                 /* funcs of constructor, MyConstructor.myfunc() */
-                                 NULL);
-        if (prototype == NULL)
-            g_error("Can't init class %s", gjs_repo_class.name);
-
-        gjs_debug(GJS_DEBUG_GREPO, "Initialized class %s prototype %p",
-                  gjs_repo_class.name, prototype);
-    }
-
-    repo = JS_NewObject(context, &gjs_repo_class, NULL, global);
-    if (repo == NULL) {
-        gjs_throw(context, "No memory to create repo object");
-        return NULL;
-    }
-
-    priv = g_slice_new0(Repo);
-
-    GJS_INC_COUNTER(repo);
-
-    g_assert(priv_from_js(context, repo) == NULL);
-    JS_SetPrivate(repo, priv);
-
-    gjs_debug_lifecycle(GJS_DEBUG_GREPO,
-                        "repo constructor, obj %p priv %p", repo, priv);
-
-    versions = JS_NewObject(context, NULL, NULL, global);
-    versions_name = gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS);
-    JS_DefinePropertyById(context, repo,
-                          versions_name,
-                          OBJECT_TO_JSVAL(versions),
-                          NULL, NULL,
-                          JSPROP_PERMANENT);
-
-    private_ns = JS_NewObject(context, NULL, NULL, global);
-    private_ns_name = gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER);
-    JS_DefinePropertyById(context, repo,
-                          private_ns_name,
-                          OBJECT_TO_JSVAL(private_ns),
-                          NULL, NULL, JSPROP_PERMANENT);
-
-    /* FIXME - hack to make namespaces load, since
-     * gobject-introspection does not yet search a path properly.
-     */
-    {
-        jsval value;
-        JS_GetProperty(context, repo, "GLib", &value);
-    }
-
-    return repo;
-}
-
-JSBool
-gjs_define_repo(JSContext  *context,
-                JSObject  **module_out,
-                const char *name)
-{
-    JSObject *repo;
-
-    repo = repo_new(context);
-    *module_out = repo;
-
-    return JS_TRUE;
-}
-
 static JSBool
 gjs_define_constant(JSContext      *context,
                     JSObject       *in_object,
@@ -533,7 +242,6 @@ JSObject*
 gjs_lookup_private_namespace(JSContext *context)
 {
     jsid ns_name;
-
     ns_name = gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER);
     return gjs_lookup_namespace_object_by_name(context, ns_name);
 }
@@ -559,95 +267,29 @@ gjs_lookup_namespace_object(JSContext  *context,
     return gjs_lookup_namespace_object_by_name(context, ns_name);
 }
 
-static JSObject*
-lookup_override_function(JSContext  *context,
-                         jsid        ns_name)
-{
-    jsval importer;
-    jsval overridespkg;
-    jsval module;
-    jsval function;
-    jsid overrides_name, object_init_name;
-
-    JS_BeginRequest(context);
-
-    importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS);
-    g_assert(JSVAL_IS_OBJECT(importer));
-
-    overridespkg = JSVAL_VOID;
-    overrides_name = gjs_context_get_const_string(context, GJS_STRING_GI_OVERRIDES);
-    if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(importer), "importer",
-                                     overrides_name, &overridespkg) ||
-        !JSVAL_IS_OBJECT(overridespkg))
-        goto fail;
-
-    module = JSVAL_VOID;
-    if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(overridespkg), "GI repository object", 
ns_name, &module)
-        || !JSVAL_IS_OBJECT(module))
-        goto fail;
-
-    object_init_name = gjs_context_get_const_string(context, GJS_STRING_GOBJECT_INIT);
-    if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(module), "override module",
-                                     object_init_name, &function) ||
-        !JSVAL_IS_OBJECT(function))
-        goto fail;
-
-    JS_EndRequest(context);
-    return JSVAL_TO_OBJECT(function);
-
- fail:
-    JS_ClearPendingException(context);
-    JS_EndRequest(context);
-    return NULL;
-}
-
 JSObject*
 gjs_lookup_namespace_object_by_name(JSContext      *context,
                                     jsid            ns_name)
 {
-    JSObject *repo_obj;
-    jsval importer;
-    jsval girepository;
-    jsval ns_obj;
-    jsid gi_name;
+    char *name;
+    char *script;
+    jsval ns_val;
+    JSObject *ns_obj = NULL;
 
     JS_BeginRequest(context);
 
-    importer = gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS);
-    g_assert(JSVAL_IS_OBJECT(importer));
-
-    girepository = JSVAL_VOID;
-    gi_name = gjs_context_get_const_string(context, GJS_STRING_GI_MODULE);
-    if (!gjs_object_require_property(context, JSVAL_TO_OBJECT(importer), "importer",
-                                     gi_name, &girepository) ||
-        !JSVAL_IS_OBJECT(girepository)) {
-        gjs_log_exception(context);
-        gjs_throw(context, "No gi property in importer");
-        goto fail;
-    }
-
-    repo_obj = JSVAL_TO_OBJECT(girepository);
-
-    if (!gjs_object_require_property(context, repo_obj, "GI repository object", ns_name, &ns_obj)) {
-        goto fail;
-    }
-
-    if (!JSVAL_IS_OBJECT(ns_obj)) {
-        char *name;
-
-        gjs_get_string_id(context, ns_name, &name);
-        gjs_throw(context, "Namespace '%s' is not an object?", name);
+    if (!gjs_get_string_id(context, ns_name, &name))
+        goto out;
 
-        g_free(name);
-        goto fail;
-    }
+    script = g_strdup_printf("imports.gi.%s;", name);
+    if (!gjs_eval_with_scope(context, NULL, script, -1, "<internal>", &ns_val))
+        goto out;
 
-    JS_EndRequest(context);
-    return JSVAL_TO_OBJECT(ns_obj);
+    ns_obj = JSVAL_TO_OBJECT(ns_val);
 
- fail:
+ out:
     JS_EndRequest(context);
-    return NULL;
+    return ns_obj;
 }
 
 const char*
diff --git a/gi/repo.h b/gi/repo.h
index ba1fd90..c2b277e 100644
--- a/gi/repo.h
+++ b/gi/repo.h
@@ -32,9 +32,6 @@
 
 G_BEGIN_DECLS
 
-JSBool      gjs_define_repo                     (JSContext      *context,
-                                                 JSObject      **module_out,
-                                                 const char     *name);
 const char* gjs_info_type_name                  (GIInfoType      type);
 JSObject*   gjs_lookup_private_namespace        (JSContext      *context);
 JSObject*   gjs_lookup_namespace_object         (JSContext      *context,
diff --git a/gi/union.cpp b/gi/union.cpp
index 23ca458..1a28bff 100644
--- a/gi/union.cpp
+++ b/gi/union.cpp
@@ -25,7 +25,6 @@
 
 #include <string.h>
 
-/* include first for logging related #define used in repo.h */
 #include <util/log.h>
 
 #include "union.h"
diff --git a/gjs/bootstrap.cpp b/gjs/bootstrap.cpp
index b049ec4..406c26f 100644
--- a/gjs/bootstrap.cpp
+++ b/gjs/bootstrap.cpp
@@ -26,9 +26,48 @@
 #include <gjs/gjs.h>
 
 #include "bootstrap.h"
+#include "native.h"
 
 #include <gio/gio.h>
 
+/* The bootstrap process is the thing that sets up the import system.
+ * As such, we give it a hook to import any native modules it may need.
+ *
+ * The rest of the functionality that the bootstrap code needs should be
+ * in independent native modules which can be imported by this API,
+ * rather than in the bootstrap environment.
+ */
+
+static JSBool
+import_native_module(JSContext *context,
+                     unsigned   argc,
+                     jsval     *vp)
+{
+    JSBool ret = JS_FALSE;
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    char *module_name = NULL;
+    JSObject *module_obj;
+
+    if (!gjs_parse_call_args(context, "importNativeModule", "s", args,
+                             "moduleName", &module_name))
+        goto out;
+
+    if (!gjs_import_native_module(context, module_name, &module_obj))
+        goto out;
+
+    ret = JS_TRUE;
+    args.rval().setObjectOrNull(module_obj);
+
+ out:
+    g_free(module_name);
+    return ret;
+}
+
+static JSFunctionSpec environment_funcs[] = {
+    { "importNativeModule", JSOP_WRAPPER (import_native_module), 1, GJS_MODULE_PROP_FLAGS },
+    { NULL },
+};
+
 static gboolean
 define_bootstrap_environment(JSContext  *context,
                              JSObject  **environment_out)
@@ -38,6 +77,9 @@ define_bootstrap_environment(JSContext  *context,
     if (!environment)
         return FALSE;
 
+    if (!JS_DefineFunctions(context, environment, &environment_funcs[0]))
+        return FALSE;
+
     *environment_out = environment;
     return TRUE;
 }
diff --git a/gjs/context.cpp b/gjs/context.cpp
index d1bd77c..20d12df 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -27,7 +27,6 @@
 
 #include "context-private.h"
 #include "bootstrap.h"
-#include "importer.h"
 #include "jsapi-private.h"
 #include "jsapi-util.h"
 #include "native.h"
@@ -78,10 +77,8 @@ struct _GjsContext {
 /* Keep this consistent with GjsConstString */
 static const char *const_strings[] = {
     "constructor", "prototype", "length",
-    "imports", "__parentModule__", "__init__", "searchPath",
     "__gjsKeepAlive", "__gjsPrivateNS",
-    "gi", "versions", "overrides",
-    "_init", "_instance_init", "_new_internal", "new",
+    "gi", "_init", "_instance_init", "_new_internal", "new",
     "message", "code", "stack", "fileName", "lineNumber", "name",
     "x", "y", "width", "height",
 };
@@ -321,7 +318,6 @@ gjs_context_class_init(GjsContextClass *klass)
 
     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_static_modules();
 }
@@ -443,24 +439,6 @@ gjs_context_constructed(GObject *object)
     if (!JS_DefineFunctions(js_context->context, js_context->global, &global_funcs[0]))
         g_error("Failed to define properties on the global object");
 
-    /* We create the global-to-runtime root importer with the
-     * passed-in search path. If someone else already created
-     * the root importer, this is a no-op.
-     */
-    if (!gjs_create_root_importer(js_context->context,
-                                  js_context->search_path ?
-                                  (const char**) js_context->search_path :
-                                  NULL,
-                                  TRUE))
-        g_error("Failed to create root importer");
-
-    /* Now copy the global root importer (which we just created,
-     * if it didn't exist) to our global object
-     */
-    if (!gjs_define_root_importer(js_context->context,
-                                  js_context->global))
-        g_error("Failed to point 'imports' property at root importer");
-
     if (!gjs_run_bootstrap(js_context->context))
         g_error("Failed to bootstrap GJS context");
 
@@ -793,6 +771,12 @@ gjs_get_import_global(JSContext *context)
     return gjs_context->global;
 }
 
+const char **
+gjs_context_get_search_path(GjsContext *context)
+{
+    return (const char **) context->search_path;
+}
+
 G_CONST_RETURN char * G_CONST_RETURN *
 gjs_get_search_path(void)
 {
diff --git a/gjs/context.h b/gjs/context.h
index 175ccca..54969a0 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -77,6 +77,8 @@ void            gjs_context_gc                    (GjsContext  *context);
 
 void            gjs_dumpstack                     (void);
 
+const char **   gjs_context_get_search_path       (GjsContext *context);
+
 G_CONST_RETURN char * G_CONST_RETURN * gjs_get_search_path (void);
 
 G_END_DECLS
diff --git a/gjs/gi.h b/gjs/gi.h
index 837d03d..786fc61 100644
--- a/gjs/gi.h
+++ b/gjs/gi.h
@@ -30,8 +30,6 @@
 
 G_BEGIN_DECLS
 
-JSBool        gjs_define_gi_stuff     (JSContext      *context,
-                                       JSObject      **module_out);
 JSBool        gjs_define_private_gi_stuff   (JSContext     *context,
                                              JSObject     **module_out);
 
diff --git a/gjs/gjs-module.h b/gjs/gjs-module.h
index f85f89b..f644139 100644
--- a/gjs/gjs-module.h
+++ b/gjs/gjs-module.h
@@ -27,7 +27,6 @@
 #include <gjs/gjs.h>
 #include <gjs/native.h>
 #include <gjs/mem.h>
-#include <gjs/importer.h>
 #include <gjs/runtime.h>
 #include <gjs/jsapi-util.h>
 
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 6e98713..35ba939 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -46,7 +46,6 @@ enum {
 };
 
 typedef enum {
-    GJS_GLOBAL_SLOT_IMPORTS,
     GJS_GLOBAL_SLOT_KEEP_ALIVE,
     GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE,
     GJS_GLOBAL_SLOT_LAST,
@@ -407,15 +406,9 @@ typedef enum {
   GJS_STRING_CONSTRUCTOR,
   GJS_STRING_PROTOTYPE,
   GJS_STRING_LENGTH,
-  GJS_STRING_IMPORTS,
-  GJS_STRING_PARENT_MODULE,
-  GJS_STRING_MODULE_INIT,
-  GJS_STRING_SEARCH_PATH,
   GJS_STRING_KEEP_ALIVE_MARKER,
   GJS_STRING_PRIVATE_NS_MARKER,
   GJS_STRING_GI_MODULE,
-  GJS_STRING_GI_VERSIONS,
-  GJS_STRING_GI_OVERRIDES,
   GJS_STRING_GOBJECT_INIT,
   GJS_STRING_INSTANCE_INIT,
   GJS_STRING_NEW_INTERNAL,
diff --git a/modules/bootstrap.js b/modules/bootstrap.js
index 45ab533..7e27a5f 100644
--- a/modules/bootstrap.js
+++ b/modules/bootstrap.js
@@ -1,6 +1,143 @@
-(function(exports) {
+(function(exports, importNativeModule) {
     "use strict";
 
-    // Do early initialization here.
+    const Importer = importNativeModule('_importer');
+    const Gio = Importer.importGIModule('Gio', '2.0');
 
-})(window);
+    function runOverridesForGIModule(module, moduleID) {
+        let overridesModule = imports.overrides[moduleID];
+        if (!overridesModule)
+            return;
+
+        let initFunc = overridesModule._init;
+        if (!initFunc)
+            return;
+
+        initFunc.call(module);
+    }
+
+    function importGIModuleWithOverrides(parent, moduleID, moduleVersion) {
+        let module = Importer.importGIModule(moduleID, moduleVersion);
+        parent[moduleID] = module;
+        runOverridesForGIModule(module, moduleID);
+    }
+
+    function installImports() {
+        // Implement the global "imports" object.
+
+        // imports.gi
+        let gi = new Proxy({
+            versions: {},
+            __gjsPrivateNS: {},
+        }, {
+            get: function(target, name) {
+                if (!target[name]) {
+                    let version = target.versions[name] || null;
+                    importGIModuleWithOverrides(target, name, version);
+                }
+
+                return target[name];
+            },
+        });
+
+        function importModule(module, file) {
+            let success, script;
+            try {
+                [success, script] = file.load_contents(null);
+            } catch(e) {
+                return null;
+            }
+
+            // Don't catch errors for the eval, as those should propagate
+            // back up to the user...
+            Importer.evalWithScope(module, script, file.get_parse_name());
+            return module;
+        }
+
+        function importFile(parent, name, file) {
+            let module = {};
+            parent[name] = module;
+            module.__file__ = file.get_parse_name();
+            module.__moduleName__ = name;
+            module.__parentModule__ = parent;
+            importModule(module, file);
+        }
+
+        function importDirectory(parent, name) {
+            let searchPath = parent.searchPath.map(function(path) {
+                return path + '/' + name;
+            }).filter(function(path) {
+                let file = Gio.File.new_for_commandline_arg(path);
+                let type = file.query_file_type(Gio.FileQueryInfoFlags.NONE, null);
+                return (type == Gio.FileType.DIRECTORY);
+            });
+
+            let module = createSearchPathImporter();
+            parent[name] = module;
+            module.searchPath = searchPath;
+            module.__moduleName__ = name;
+            module.__parentModule__ = parent;
+
+            tryImport(module, '__init__');
+        }
+
+        function tryImport(proxy, name) {
+            function tryPath(path) {
+                let file, type;
+                file = Gio.File.new_for_commandline_arg(path);
+                type = file.query_file_type(Gio.FileQueryInfoFlags.NONE, null);
+                if (type == Gio.FileType.DIRECTORY) {
+                    importDirectory(proxy, name);
+                    return true;
+                } else {
+                    file = Gio.File.new_for_commandline_arg(path + '.js');
+                    if (file.query_exists(null)) {
+                        importFile(proxy, name, file);
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            for (let path of proxy.searchPath) {
+                let modulePath = path + '/' + name;
+                if (tryPath(modulePath))
+                    return;
+            }
+        }
+
+        function createSearchPathImporter() {
+            let proxy = new Proxy({ __init__: {} }, {
+                get: function(target, name) {
+                    if (target.__init__[name])
+                        return target.__init__[name];
+
+                    if (!target[name])
+                        tryImport(proxy, name);
+
+                    return target[name];
+                },
+            });
+            return proxy;
+        }
+
+        let rootDirectoryImporter = createSearchPathImporter();
+        rootDirectoryImporter.searchPath = Importer.getBuiltinSearchPath();
+
+        // root importer, checks for native modules
+        let rootImporter = new Proxy(rootDirectoryImporter, {
+            get: function(target, name) {
+                if (!target[name])
+                    target[name] = importNativeModule(name);
+                if (!target[name])
+                    target[name] = rootDirectoryImporter[name];
+                return target[name];
+            },
+        });
+        rootImporter.gi = gi;
+
+        exports.imports = rootImporter;
+    }
+    installImports();
+
+})(window, importNativeModule);
diff --git a/modules/importer.cpp b/modules/importer.cpp
new file mode 100644
index 0000000..10b3ed8
--- /dev/null
+++ b/modules/importer.cpp
@@ -0,0 +1,153 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <config.h>
+
+#include "importer.h"
+#include <gjs/gjs-module.h>
+#include <gjs/byteArray.h>
+#include "gi/ns.h"
+
+static JSBool
+import_gi_module(JSContext *context,
+                 unsigned   argc,
+                 jsval     *vp)
+{
+    JSBool ret = JS_FALSE;
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    jsval retval = JSVAL_VOID;
+    char *module_name = NULL;
+    char *module_version = NULL;
+    JSObject *module_obj;
+
+    if (!gjs_parse_call_args(context, "importGIModule", "s?s", args,
+                             "moduleName", &module_name,
+                             "moduleVersion", &module_version))
+        goto out;
+
+    if (!gjs_import_gi_module(context, module_name, module_version, &module_obj))
+        goto out;
+
+    ret = JS_TRUE;
+    args.rval().setObject(*module_obj);
+
+ out:
+    g_free(module_name);
+    g_free(module_version);
+    return ret;
+}
+
+static JSBool
+eval_with_scope(JSContext *context,
+                unsigned   argc,
+                jsval     *vp)
+{
+    JSBool ret = JS_FALSE;
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JSObject *scope;
+    JSObject *script_obj;
+    guint8 *script;
+    gsize script_len;
+    char *filename = NULL;
+    jsval retval;
+
+    if (!gjs_parse_call_args(context, "evalWithScope", "oos", args,
+                             "scope", &scope,
+                             "script", &script_obj,
+                             "filename", &filename))
+        goto out;
+
+    gjs_byte_array_peek_data (context, script_obj, &script, &script_len);
+
+    if (!gjs_eval_with_scope(context, scope, (const char *) script, script_len, filename, &retval))
+        goto out;
+
+    ret = JS_TRUE;
+    args.rval().set(retval);
+
+ out:
+    g_free(filename);
+    return ret;
+}
+
+static JSBool
+get_builtin_search_path(JSContext *context,
+                        unsigned   argc,
+                        jsval     *vp)
+{
+    GjsContext *gjs_context = GJS_CONTEXT(JS_GetContextPrivate(context));
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    const char **context_search_path;
+    int context_search_path_length;
+    const char **global_search_path;
+    int global_search_path_length;
+    GArray *elems;
+    JSObject *search_path_obj;
+    int i;
+
+    context_search_path = gjs_context_get_search_path(gjs_context);
+    context_search_path_length = context_search_path ? g_strv_length((char **) context_search_path) : 0;
+    global_search_path = (const char **) gjs_get_search_path();
+    global_search_path_length = global_search_path ? g_strv_length((char **) global_search_path) : 0;
+
+    elems = g_array_sized_new(FALSE, FALSE, sizeof(jsval),
+                              context_search_path_length + global_search_path_length);
+
+    for (i = 0; i < context_search_path_length; i++) {
+        jsval element = STRING_TO_JSVAL(JS_NewStringCopyZ(context, context_search_path[i]));
+        g_array_append_val(elems, element);
+    }
+
+    for (i = 0; i < global_search_path_length; i++) {
+        jsval element = STRING_TO_JSVAL(JS_NewStringCopyZ(context, global_search_path[i]));
+        g_array_append_val(elems, element);
+    }
+
+    search_path_obj = JS_NewArrayObject(context, elems->len, (jsval *)elems->data);
+    g_array_free(elems, TRUE);
+
+    args.rval().setObject(*search_path_obj);
+    return JS_TRUE;
+}
+
+static JSFunctionSpec module_funcs[] = {
+    { "importGIModule", JSOP_WRAPPER (import_gi_module), 2, GJS_MODULE_PROP_FLAGS },
+    { "evalWithScope", JSOP_WRAPPER (eval_with_scope), 3, GJS_MODULE_PROP_FLAGS },
+    { "getBuiltinSearchPath", JSOP_WRAPPER (get_builtin_search_path), 0, GJS_MODULE_PROP_FLAGS },
+    { NULL },
+};
+
+JSBool
+gjs_js_define_importer_stuff(JSContext  *context,
+                             JSObject  **module_out)
+{
+    JSObject *module;
+
+    module = JS_NewObject(context, NULL, NULL, NULL);
+
+    if (!JS_DefineFunctions(context, module, &module_funcs[0]))
+        return JS_FALSE;
+
+    *module_out = module;
+    return JS_TRUE;
+}
diff --git a/gjs/gi.cpp b/modules/importer.h
similarity index 75%
rename from gjs/gi.cpp
rename to modules/importer.h
index 6ff4847..5fa25b0 100644
--- a/gjs/gi.cpp
+++ b/modules/importer.h
@@ -1,6 +1,6 @@
 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 /*
- * Copyright (c) 2008  litl, LLC
+ * Copyright 2013 Red Hat, Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -21,19 +21,18 @@
  * IN THE SOFTWARE.
  */
 
-#include "gi.h"
+#ifndef __GJS_MODULE_IMPORTER_H__
+#define __GJS_MODULE_IMPORTER_H__
 
-#include <util/misc.h>
+#include <config.h>
+#include <glib.h>
+#include "gjs/jsapi-util.h"
 
-#include <string.h>
+G_BEGIN_DECLS
 
-#include "gjs/native.h"
-#include "gjs/compat.h"
-#include "gi/repo.h"
+JSBool        gjs_js_define_importer_stuff   (JSContext      *context,
+                                              JSObject      **module_out);
 
-JSBool
-gjs_define_gi_stuff(JSContext      *context,
-                    JSObject      **module_out)
-{
-    return gjs_define_repo(context, module_out, "gi");
-}
+G_END_DECLS
+
+#endif  /* __GJS_MODULE_IMPORTER_H__ */
diff --git a/modules/modules.cpp b/modules/modules.cpp
index aae3569..0ccc432 100644
--- a/modules/modules.cpp
+++ b/modules/modules.cpp
@@ -32,6 +32,7 @@
 
 #include "system.h"
 #include "console.h"
+#include "importer.h"
 
 void
 gjs_register_static_modules (void)
@@ -40,5 +41,6 @@ gjs_register_static_modules (void)
     gjs_register_native_module("cairoNative", gjs_js_define_cairo_stuff);
 #endif
     gjs_register_native_module("system", gjs_js_define_system_stuff);
+    gjs_register_native_module("_importer", gjs_js_define_importer_stuff);
     gjs_register_native_module("console", gjs_define_console_stuff);
 }


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