[gjs/wip/ptomato/jasper-imports: 5/6] bootstrap: Add a JS implementation of the imports system
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/ptomato/jasper-imports: 5/6] bootstrap: Add a JS implementation of the imports system
- Date: Tue, 24 Jan 2017 08:09:15 +0000 (UTC)
commit b47c7b5f594c98cb6dc60b52ffaef646351f3824
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 | 3 -
gi/repo.cpp | 380 +------------
gi/repo.h | 4 -
gjs/bootstrap.cpp | 53 ++-
gjs/context.cpp | 34 +-
gjs/context.h | 2 +
gjs/gi.h | 3 -
gjs/importer.cpp | 1152 --------------------------------------
gjs/importer.h | 50 --
gjs/jsapi-util.h | 7 -
modules/bootstrap.js | 143 +++++-
modules/importer.cpp | 127 +++++
gjs/gi.cpp => modules/importer.h | 25 +-
modules/modules.cpp | 2 +
15 files changed, 358 insertions(+), 1634 deletions(-)
---
diff --git a/Makefile-modules.am b/Makefile-modules.am
index 3165d86..238a32f 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_CPPFLAGS)
libconsole_la_LIBADD = $(JS_NATIVE_MODULE_LIBADD) $(READLINE_LIBS)
libconsole_la_SOURCES = modules/console.h modules/console.cpp
+
+libimporter_la_CPPFLAGS = $(JS_NATIVE_MODULE_CPPFLAGS)
+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 f781f35..dea173c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,9 +80,7 @@ libgjs_la_SOURCES = \
gjs/byteArray.cpp \
gjs/context.cpp \
gjs/context-private.h \
- gjs/importer.cpp \
gjs/gi.h \
- gjs/gi.cpp \
gjs/coverage-internal.h \
gjs/coverage.cpp \
gjs/jsapi-constructor-proxy.cpp \
@@ -154,7 +152,6 @@ libgjs_la_SOURCES += $(libgjs_private_source_files)
# These used to be public headers for external modules
libgjs_la_SOURCES += \
gjs/byteArray.h \
- gjs/importer.h \
gjs/jsapi-util.h \
gjs/jsapi-wrapper.h \
gjs/runtime.h \
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 65726ef..f0ca12d 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -45,276 +45,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 bool lookup_override_function(JSContext *, JS::HandleId,
- JS::MutableHandleValue);
-
-static bool
-get_version_for_ns (JSContext *context,
- JS::HandleObject repo_obj,
- JS::HandleId ns_id,
- char **version)
-{
- JS::RootedObject versions(context);
- JS::RootedValue version_val(context);
- JS::RootedId versions_name(context,
- gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS));
-
- if (!gjs_object_require_property_value(context, repo_obj,
- "GI repository object", versions_name,
- &versions))
- return false;
-
- if (!gjs_object_require_property_value(context, versions, NULL, ns_id, version)) {
- /* Property not actually required, so clear an exception */
- JS_ClearPendingException(context);
- *version = NULL;
- }
-
- return true;
-}
-
-static bool
-resolve_namespace_object(JSContext *context,
- JS::HandleObject repo_obj,
- JS::HandleId ns_id,
- const char *ns_name)
-{
- char *version;
-
- JSAutoRequest ar(context);
-
- if (!get_version_for_ns(context, repo_obj, ns_id, &version))
- return false;
-
- /* Defines a property on "obj" (the javascript repo object)
- * with the given namespace name, pointing to that namespace
- * in the repo.
- */
- JS::RootedObject gi_namespace(context);
- if (!gjs_import_gi_module(context, ns_name, version, &gi_namespace)) {
- g_free(version);
- return false;
- }
-
- g_free(version);
-
- /* 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, gi_namespace,
- GJS_MODULE_PROP_FLAGS))
- g_error("no memory to define ns property");
-
- JS::RootedValue override(context);
- if (!lookup_override_function(context, ns_id, &override))
- return false;
-
- JS::RootedValue result(context);
- if (!override.isUndefined() &&
- !JS_CallFunctionValue (context, gi_namespace, /* thisp */
- override, /* callee */
- JS::HandleValueArray::empty(), &result))
- return false;
-
- gjs_debug(GJS_DEBUG_GNAMESPACE,
- "Defined namespace '%s' %p in GIRepository %p", ns_name,
- gi_namespace.get(), repo_obj.get());
-
- gjs_schedule_gc_if_needed(context);
- return true;
-}
-
-/*
- * 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 bool
-repo_new_resolve(JSContext *context,
- JS::HandleObject obj,
- JS::HandleId id,
- JS::MutableHandleObject objp)
-{
- Repo *priv;
- char *name;
- bool ret = true;
-
- if (!gjs_get_string_id(context, id, &name))
- return 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.get(), priv);
-
- if (priv == NULL) /* we are the prototype, or have the wrong class */
- goto out;
-
- if (!resolve_namespace_object(context, obj, id, name)) {
- ret = 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 |
- JSCLASS_IMPLEMENTS_BARRIERS,
- 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
-};
-
-JSPropertySpec gjs_repo_proto_props[] = {
- JS_PS_END
-};
-
-JSFunctionSpec gjs_repo_proto_funcs[] = {
- JS_FS_END
-};
-
-static JSObject*
-repo_new(JSContext *context)
-{
- Repo *priv;
- JSObject *versions;
- JSObject *private_ns;
- bool found;
-
- JS::RootedObject global(context, 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
- */
- JS::NullPtr(),
- &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);
- }
-
- JS::RootedObject repo(context,
- JS_NewObject(context, &gjs_repo_class, JS::NullPtr(), 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.get(), priv);
-
- versions = JS_NewObject(context, NULL, JS::NullPtr(), global);
- JS::RootedId versions_name(context,
- gjs_context_get_const_string(context, GJS_STRING_GI_VERSIONS));
- JS_DefinePropertyById(context, repo,
- versions_name,
- JS::ObjectValue(*versions),
- NULL, NULL,
- JSPROP_PERMANENT);
-
- private_ns = JS_NewObject(context, NULL, JS::NullPtr(), global);
- JS::RootedId private_ns_name(context,
- gjs_context_get_const_string(context, GJS_STRING_PRIVATE_NS_MARKER));
- JS_DefinePropertyById(context, repo,
- private_ns_name,
- JS::ObjectValue(*private_ns),
- NULL, NULL, JSPROP_PERMANENT);
-
- /* FIXME - hack to make namespaces load, since
- * gobject-introspection does not yet search a path properly.
- */
- {
- JS::RootedValue value(context);
- JS_GetProperty(context, repo, "GLib", &value);
- }
-
- return repo;
-}
-
-bool
-gjs_define_repo(JSContext *cx,
- JS::MutableHandleObject repo,
- const char *name)
-{
- repo.set(repo_new(cx));
- return true;
-}
-
static bool
gjs_define_constant(JSContext *context,
JS::HandleObject in_object,
@@ -555,114 +285,24 @@ gjs_lookup_namespace_object(JSContext *context,
return gjs_lookup_namespace_object_by_name(context, ns_name);
}
-/* Check if an exception's 'name' property is equal to compare_name. Ignores
- * all errors that might arise. Requires request. */
-static bool
-error_has_name(JSContext *cx,
- JS::HandleValue thrown_value,
- JSString *compare_name)
-{
- if (!thrown_value.isObject())
- return false;
-
- JS::AutoSaveExceptionState saved_exc(cx);
- JS::RootedId name_id(cx, gjs_context_get_const_string(cx, GJS_STRING_NAME));
- JS::RootedObject exc(cx, &thrown_value.toObject());
- JS::RootedValue exc_name(cx);
- bool retval = false;
-
- if (!JS_GetPropertyById(cx, exc, name_id, &exc_name))
- goto out;
-
- int32_t cmp_result;
- if (!JS_CompareStrings(cx, exc_name.toString(), compare_name, &cmp_result))
- goto out;
-
- if (cmp_result == 0)
- retval = true;
-
-out:
- saved_exc.restore();
- return retval;
-}
-
-static bool
-lookup_override_function(JSContext *cx,
- JS::HandleId ns_name,
- JS::MutableHandleValue function)
-{
- JSAutoRequest ar(cx);
- JS::AutoSaveExceptionState saved_exc(cx);
-
- JS::RootedValue importer(cx, gjs_get_global_slot(cx, GJS_GLOBAL_SLOT_IMPORTS));
- g_assert(importer.isObject());
-
- JS::RootedId overrides_name(cx,
- gjs_context_get_const_string(cx, GJS_STRING_GI_OVERRIDES));
- JS::RootedId object_init_name(cx,
- gjs_context_get_const_string(cx, GJS_STRING_GOBJECT_INIT));
- JS::RootedObject overridespkg(cx), module(cx);
- JS::RootedObject importer_obj(cx, &importer.toObject());
-
- if (!gjs_object_require_property_value(cx, importer_obj, "importer",
- overrides_name, &overridespkg))
- goto fail;
-
- if (!gjs_object_require_property_value(cx, overridespkg, "GI repository object",
- ns_name, &module)) {
- JS::RootedValue exc(cx);
- JS_GetPendingException(cx, &exc);
-
- /* If the exception was an ImportError (i.e., module not found) then
- * we simply didn't have an override, don't throw an exception */
- if (error_has_name(cx, exc, JS_InternString(cx, "ImportError"))) {
- saved_exc.restore();
- return true;
- }
-
- goto fail;
- }
-
- if (!gjs_object_require_property(cx, module, "override module",
- object_init_name, function) ||
- !function.isObjectOrNull()) {
- gjs_throw(cx, "Unexpected value for _init in overrides module");
- goto fail;
- }
- return true;
-
- fail:
- saved_exc.drop();
- return false;
-}
-
JSObject*
-gjs_lookup_namespace_object_by_name(JSContext *context,
- JS::HandleId ns_name)
+gjs_lookup_namespace_object_by_name(JSContext *context,
+ JS::HandleId ns_name)
{
- JSAutoRequest ar(context);
+ g_autofree char *name = NULL;
- JS::RootedValue importer(context,
- gjs_get_global_slot(context, GJS_GLOBAL_SLOT_IMPORTS));
- g_assert(importer.isObject());
-
- JS::RootedId gi_name(context,
- gjs_context_get_const_string(context, GJS_STRING_GI_MODULE));
- JS::RootedObject repo(context), importer_obj(context, &importer.toObject());
+ JSAutoRequest ar(context);
- if (!gjs_object_require_property_value(context, importer_obj, "importer",
- gi_name, &repo)) {
- gjs_log_exception(context);
- gjs_throw(context, "No gi property in importer");
+ if (!gjs_get_string_id(context, ns_name, &name))
return NULL;
- }
- JS::RootedObject retval(context);
- if (!gjs_object_require_property_value(context, repo, "GI repository object",
- ns_name, &retval))
+ g_autofree char *script = g_strdup_printf("imports.gi.%s;", name);
+ JS::RootedValue ns_val(context);
+ if (!gjs_eval_with_scope(context, JS::NullPtr(), script, -1, "<internal>",
+ &ns_val))
return NULL;
- return retval;
+ return &ns_val.toObject();
}
const char*
diff --git a/gi/repo.h b/gi/repo.h
index a3a38fc..46be4b3 100644
--- a/gi/repo.h
+++ b/gi/repo.h
@@ -34,10 +34,6 @@
G_BEGIN_DECLS
-bool gjs_define_repo(JSContext *cx,
- JS::MutableHandleObject repo,
- 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/gjs/bootstrap.cpp b/gjs/bootstrap.cpp
index 8c6f4f5..372f487 100644
--- a/gjs/bootstrap.cpp
+++ b/gjs/bootstrap.cpp
@@ -27,15 +27,64 @@
#include "bootstrap.h"
#include "gjs/jsapi-util.h"
+#include "gjs/jsapi-util-args.h"
#include "gjs/jsapi-wrapper.h"
+#include "native.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 bool
+import_native_module(JSContext *cx,
+ unsigned argc,
+ jsval *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ g_autofree char *module_name = NULL;
+
+ if (!gjs_parse_call_args(cx, "importNativeModule", args, "s",
+ "moduleName", &module_name))
+ return false;
+
+ JS::RootedObject module_obj(cx);
+ if (!gjs_import_native_module(cx, module_name, &module_obj))
+ return false;
+
+ args.rval().setObjectOrNull(module_obj);
+ return true;
+}
+
+static JSFunctionSpec environment_funcs[] = {
+ JS_FS("importNativeModule", import_native_module, 1, GJS_MODULE_PROP_FLAGS),
+ JS_FS_END,
+};
+
+static bool
+define_bootstrap_environment(JSContext *cx,
+ JS::MutableHandleObject environment)
+{
+ environment.set(JS_NewObject(cx, NULL, JS::NullPtr(), JS::NullPtr()));
+
+ if (!environment)
+ return false;
+
+ return JS_DefineFunctions(cx, environment, &environment_funcs[0]);
+}
#define BOOTSTRAP_FILE "resource:///org/gnome/gjs/modules/bootstrap.js"
bool
gjs_run_bootstrap(JSContext *cx)
{
- JS::RootedObject environment(cx,
- JS_NewObject(cx, NULL, JS::NullPtr(), JS::NullPtr()));
+ JS::RootedObject environment(cx);
+ define_bootstrap_environment(cx, &environment);
+ if (!environment)
+ return false;
g_autoptr(GFile) file = g_file_new_for_uri(BOOTSTRAP_FILE);
g_autofree char *script = NULL;
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 6838d69..6496005 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-constructor-proxy.h"
#include "jsapi-private.h"
#include "jsapi-util.h"
@@ -82,10 +81,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", "__modulePath__"
};
@@ -360,7 +357,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();
}
@@ -493,23 +489,8 @@ gjs_context_constructed(GObject *object)
gjs_define_constructor_proxy_factory(js_context->context);
- /* 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");
/* FIXME: We should define the Promise object before any imports, in case
* the imports want to use it. Currently that's not possible as it needs to
@@ -517,9 +498,6 @@ gjs_context_constructed(GObject *object)
if(!gjs_define_promise_object(js_context->context, global))
g_error("Failed to define global Promise object");
- if (!gjs_run_bootstrap(js_context->context))
- g_error("Failed to bootstrap GJS context");
-
JS_EndRequest(js_context->context);
g_mutex_lock (&contexts_lock);
@@ -888,6 +866,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;
+}
+
const char * const *
gjs_get_search_path(void)
{
diff --git a/gjs/context.h b/gjs/context.h
index c1abff0..44997d6 100644
--- a/gjs/context.h
+++ b/gjs/context.h
@@ -78,6 +78,8 @@ void gjs_context_gc (GjsContext *context);
void gjs_dumpstack (void);
+const char **gjs_context_get_search_path(GjsContext *context);
+
const char * const *gjs_get_search_path(void);
G_END_DECLS
diff --git a/gjs/gi.h b/gjs/gi.h
index 22d1b70..3445687 100644
--- a/gjs/gi.h
+++ b/gjs/gi.h
@@ -30,9 +30,6 @@
G_BEGIN_DECLS
-bool gjs_define_gi_stuff(JSContext *cx,
- JS::MutableHandleObject module);
-
bool gjs_define_private_gi_stuff(JSContext *cx,
JS::MutableHandleObject module);
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index c26e40f..b9429a5 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -45,7 +45,6 @@ enum {
};
typedef enum {
- GJS_GLOBAL_SLOT_IMPORTS,
GJS_GLOBAL_SLOT_KEEP_ALIVE,
GJS_GLOBAL_SLOT_BYTE_ARRAY_PROTOTYPE,
GJS_GLOBAL_SLOT_LAST,
@@ -441,15 +440,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..f15e02f
--- /dev/null
+++ b/modules/importer.cpp
@@ -0,0 +1,127 @@
+/* -*- 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 "gi/ns.h"
+#include "gjs/byteArray.h"
+#include "gjs/context.h"
+#include "gjs/jsapi-util-args.h"
+#include "importer.h"
+
+static bool
+import_gi_module(JSContext *cx,
+ unsigned argc,
+ jsval *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ g_autofree char *module_name = NULL;
+ g_autofree char *module_version = NULL;
+
+ if (!gjs_parse_call_args(cx, "importGIModule", args, "s?s",
+ "moduleName", &module_name,
+ "moduleVersion", &module_version))
+ return false;
+
+ JS::RootedObject module_obj(cx);
+ if (!gjs_import_gi_module(cx, module_name, module_version, &module_obj))
+ return false;
+
+ args.rval().setObject(*module_obj);
+ return true;
+}
+
+static bool
+eval_with_scope(JSContext *cx,
+ unsigned argc,
+ jsval *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedObject scope(cx);
+ JS::RootedObject script_obj(cx);
+ guint8 *script;
+ size_t script_len;
+ g_autofree char *filename = NULL;
+
+ if (!gjs_parse_call_args(cx, "evalWithScope", args, "oos",
+ "scope", &scope,
+ "script", &script_obj,
+ "filename", &filename))
+ return false;
+
+ gjs_byte_array_peek_data(cx, script_obj, &script, &script_len);
+
+ return gjs_eval_with_scope(cx, scope, (const char *) script, script_len,
+ filename, args.rval());
+}
+
+static bool
+get_builtin_search_path(JSContext *cx,
+ unsigned argc,
+ jsval *vp)
+{
+ GjsContext *gjs_context = GJS_CONTEXT(JS_GetContextPrivate(cx));
+ 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;
+ JS::AutoValueVector elems(cx);
+ 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.reserve(context_search_path_length + global_search_path_length);
+
+ JS::RootedValue element(cx);
+ for (i = 0; i < context_search_path_length; i++) {
+ element.setString(JS_NewStringCopyZ(cx, context_search_path[i]));
+ elems.infallibleAppend(element);
+ }
+
+ for (i = 0; i < global_search_path_length; i++) {
+ element.setString(JS_NewStringCopyZ(cx, global_search_path[i]));
+ elems.infallibleAppend(element);
+ }
+
+ args.rval().setObject(*JS_NewArrayObject(cx, elems));
+ return true;
+}
+
+static JSFunctionSpec module_funcs[] = {
+ JS_FS("importGIModule", import_gi_module, 2, GJS_MODULE_PROP_FLAGS),
+ JS_FS("evalWithScope", eval_with_scope, 3, GJS_MODULE_PROP_FLAGS),
+ JS_FS("getBuiltinSearchPath", get_builtin_search_path, 0, GJS_MODULE_PROP_FLAGS),
+ JS_FS_END,
+};
+
+bool
+gjs_js_define_importer_stuff(JSContext *cx,
+ JS::MutableHandleObject module)
+{
+ module.set(JS_NewObject(cx, NULL, JS::NullPtr(), JS::NullPtr()));
+ return JS_DefineFunctions(cx, module, &module_funcs[0]);
+}
diff --git a/gjs/gi.cpp b/modules/importer.h
similarity index 76%
rename from gjs/gi.cpp
rename to modules/importer.h
index b82618d..b126e59 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/jsapi-wrapper.h"
-#include "gi/repo.h"
+bool gjs_js_define_importer_stuff(JSContext *context,
+ JS::MutableHandleObject module_out);
-bool
-gjs_define_gi_stuff(JSContext *cx,
- JS::MutableHandleObject module)
-{
- return gjs_define_repo(cx, module, "gi");
-}
+G_END_DECLS
+
+#endif /* GJS_MODULE_IMPORTER_H */
diff --git a/modules/modules.cpp b/modules/modules.cpp
index 555171d..4223599 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]