~aperezdc/kiln

cd4ac1512a7e7155e08b57752a984d6a08c32796 — Adrian Perez de Castro 4 years ago aa0864f
loader: Support manually adding modules

Allow adding a KilnModule to a KilnLoader given the name of the module
and the callback function which implements opening the module. This
allows programs using the library to add their own modules which bypass
the file system lookup. In this case internally the GModule associated
with the module will be NULL.
3 files changed, 104 insertions(+), 16 deletions(-)

M libkn/kiln-loader.c
M libkn/kiln-loader.h
M shell/main.c
M libkn/kiln-loader.c => libkn/kiln-loader.c +63 -15
@@ 14,6 14,7 @@ struct _KilnModule
    char              *name;
    GModule           *module;
    KilnModuleOpenFunc open_in_context;
    void              *userdata;
};

G_DEFINE_BOXED_TYPE (KilnModule, kiln_module, kiln_module_ref, kiln_module_unref);


@@ 22,24 23,28 @@ G_DEFINE_BOXED_TYPE (KilnModule, kiln_module, kiln_module_ref, kiln_module_unref
static KilnModule*
kiln_module_new (const char        *name,
                 GModule           *module,
                 KilnModuleOpenFunc open_in_context)
                 KilnModuleOpenFunc open_in_context,
                 void              *userdata)
{
    g_assert (name);
    g_assert (module);
    g_assert (open_in_context);

    KilnModule *self = g_rc_box_new0 (KilnModule);
    self->name = g_strdup (name);
    self->module = module;
    self->open_in_context = open_in_context;
    self->userdata = userdata;

    if (self->module) {
        /*
         * Ideally we would use GTypeModule, and allow plug-ins to register
         * types which can be loaded and unloaded; but it's simpler to keep
         * modules around and allow them to register types normally, without
         * having to worry about the GModule being unloaded.
         */
        g_module_make_resident (self->module);
    }

    /*
     * Ideally we would use GTypeModule, and allow plug-ins to register
     * types which can be loaded and unloaded; but it's simpler to keep
     * modules around and allow them to register types normally, without
     * having to worry about the GModule being unloaded.
     */
    g_module_make_resident (self->module);
    return self;
}



@@ 133,7 138,7 @@ kiln_module_open (const KilnModule *self,
    g_return_val_if_fail (self, NULL);
    g_return_val_if_fail (JSC_IS_CONTEXT (context), NULL);

    return (*self->open_in_context) (context, error);
    return (*self->open_in_context) (context, error, self->userdata);
}




@@ 384,6 389,10 @@ kiln_loader_find_module (KilnLoader *self,
    g_return_val_if_fail (KILN_IS_LOADER (self), NULL);
    g_return_val_if_fail (module_name, NULL);

    KilnModule *module = g_hash_table_lookup (self->modules, module_name);
    if (module)
        return module;

    /*
     * TODO: Validate that the module name only contains characters
     *       in the [A-Za-z0-9_] set.


@@ 398,10 407,6 @@ kiln_loader_find_module (KilnLoader *self,
        return NULL;
    }

    KilnModule *module = g_hash_table_lookup (self->modules, module_name);
    if (module)
        return module;

    for (GList *iter = g_list_first (self->module_paths); iter; iter = g_list_next (iter)) {
        g_autofree char *module_path = g_module_build_path (iter->data,
                                                            module_name);


@@ 423,7 428,7 @@ kiln_loader_find_module (KilnLoader *self,
            continue;
        }

        module = kiln_module_new (module_name, gmodule, fn);
        module = kiln_module_new (module_name, gmodule, fn, NULL);
        g_hash_table_insert (self->modules,
                             g_strdup (module_name),
                             module);


@@ 516,3 521,46 @@ kiln_loader_open_module (KilnLoader *self,
    jsc_context_set_value (self->context, module_name, ns_value);
    return TRUE;
}

/**
 * kiln_loader_add_module:
 * @loader: A #KilnLoader
 * @module_name: Name of the module to add and optionally open.
 * @callback: Module initialization function.
 * @userdata: User data passed to the callback.
 * @error: (nullable): Location where to store an error, if any.
 *
 * Register a module with the loader, which will use the provided @callback
 * function to initialize the module in a #JSCContext when loaded.
 *
 * If the module cannot be added because another module with the same
 * name already exists, @NULL is returned and @error will be filled
 * in, if provided.
 *
 * Returns: (transfer none) (nullable): A #KilnModule
 */
KilnModule*
kiln_loader_add_module (KilnLoader        *self,
                        const char        *module_name,
                        KilnModuleOpenFunc callback,
                        void              *userdata,
                        GError           **error)
{
    g_return_val_if_fail (KILN_IS_LOADER (self), NULL);
    g_return_val_if_fail (module_name, NULL);
    g_return_val_if_fail (callback, NULL);

    KilnModule *module = g_hash_table_lookup (self->modules, module_name);
    if (module) {
        g_set_error (error,
                     KILN_LOADER_ERROR,
                     KILN_LOADER_ERROR_NS_EXISTS,
                     "Module '%s' already exists",
                     module_name);
        return NULL;
    }

    module = kiln_module_new (module_name, NULL, callback, userdata);
    g_hash_table_insert (self->modules, g_strdup (module_name), module);
    return module;
}

M libkn/kiln-loader.h => libkn/kiln-loader.h +7 -1
@@ 39,7 39,7 @@ typedef enum {
    KILN_LOADER_ERROR_NS_EXISTS,
} KilnLoaderError;

typedef JSCValue* (*KilnModuleOpenFunc) (JSCContext*, GError**);
typedef JSCValue* (*KilnModuleOpenFunc) (JSCContext*, GError**, void*);

#define KILN_TYPE_LOADER  (kiln_loader_get_type ())
#define KILN_LOADER_ERROR (kiln_loader_error_quark ())


@@ 75,4 75,10 @@ gboolean    kiln_loader_open_module  (KilnLoader *self,
                                      const char *module_name,
                                      GError    **error);

KilnModule* kiln_loader_add_module   (KilnLoader        *self,
                                      const char        *module_name,
                                      KilnModuleOpenFunc callback,
                                      void              *userdata,
                                      GError           **error);

G_END_DECLS

M shell/main.c => shell/main.c +34 -0
@@ 112,6 112,32 @@ open_builtin_modules (KilnLoader *loader,
    return true;
}

static void
js_print (GPtrArray *values)
{
    for (unsigned i = 0; i < values->len; ++i) {
        if (i > 0)
            g_print (" \t");
        JSCValue *value = g_ptr_array_index (values, i);
        g_autofree char *value_string = jsc_value_to_string (value);
        g_print ("%s", value_string);
    }
    g_print ("\n");
}

static JSCValue*
open_print_module (JSCContext *context,
                   GError   **error G_GNUC_UNUSED,
                   void      *userdata G_GNUC_UNUSED)
{
    return jsc_value_new_function_variadic (context,
                                            "print",
                                            G_CALLBACK (js_print),
                                            NULL,  /* userdata */
                                            NULL,  /* destroy_notify */
                                            G_TYPE_NONE);
}

int
main (int argc, char *argv[])
{


@@ 124,6 150,14 @@ main (int argc, char *argv[])
    g_autoptr(JSCContext) js_context = jsc_context_new ();

    g_autoptr(KilnLoader) kn_loader = kiln_loader_new (js_context);

    if (!kiln_loader_add_module (kn_loader, "print", open_print_module, NULL, &error) ||
        !kiln_loader_open_module (kn_loader, "print", &error))
    {
        g_printerr ("%s: %s\n", argv[0], error->message);
        return EXIT_FAILURE;
    }

    for (unsigned i = 0; opt_module_paths && opt_module_paths[i]; ++i) {
        /* TODO: Check whether the path is actually a directory. */
        kiln_loader_append_path (kn_loader, opt_module_paths[i]);