svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.97k stars 515 forks source link

Consider adding Duktape.loadlib #89

Open creationix opened 9 years ago

creationix commented 9 years ago

Consider adding Duktape.loadlib or at least specifying how it should work since the implementation is not very portable. Here is how I wrote it for dukluv:

// Load a duktape C function from a shared library by path and name.
static duk_ret_t duv_loadlib(duk_context *ctx) {
  const char *name, *path;
  uv_lib_t lib;
  duk_c_function fn;

  // Check the args
  dschema_check(ctx, (const duv_schema_entry[]) {
    {"path", duk_is_string},
    {"name", duk_is_string},
    {NULL}
  });

  path = duk_get_string(ctx, 0);
  name = duk_get_string(ctx, 1);

  if (uv_dlopen(path, &lib)) {
    duk_error(ctx, DUK_ERR_ERROR, "Cannot load shared library %s", path);
    return 0;
  }
  if (uv_dlsym(&lib, name, (void**)&fn)) {
    duk_error(ctx, DUK_ERR_ERROR, "Unable to find %s in %s", name, path);
    return 0;
  }
  duk_push_c_function(ctx, fn, 0);
  return 1;
}

It takes a file path and a function name and assumes pushes the function on the stack (assuming it's duk_c_function).

I integrated this into my prototype require system as the loader for .so and .dll files.

  if (strcmp(ext, ".so") == 0 || strcmp(ext, ".dll") == 0) {
    const char* name = ext;
    while (name > id && name[-1] != '/' && name[-1] != '\\') { --name; }
    // Stack: [Duktape, this, id]
    duk_get_prop_string(ctx, -3, "loadlib");
    // Stack: [Duktape, this, id, loadlib]
    duk_insert(ctx, -2);
    // Stack: [Duktape, this, loadlib, id]
    duk_push_sprintf(ctx, "dukopen_%.*s", (int)(ext - name), name);
    // Stack: [Duktape, this, loadlib, id, name]
    duk_call(ctx, 2);
    // Stack: [Duktape, this, fn]
    duk_call(ctx, 0);
    // Stack: [Duktape, this, exports]
    duk_dup(ctx, -1);
    // Stack: [Duktape, this, exports, exports]
    duk_put_prop_string(ctx, -3, "exports");
    // Stack: [Duktape, this, exports]
    return 1;
  }

Note that is assumes the symbol is dukopen_ followed by the basename of the file without the extension.

svaarala commented 9 years ago

What problem would Duktape.loadlib solve in the core distributable? Note that some systems don't even have a file system so concepts like paths and filenames may be completely alien.

For these reasons I've been trying to push all such functionality into the module loading parts provided by the application.

creationix commented 9 years ago

Right, this is why I said to maybe only specify it's behavior. It's entirely possible to do in the embedding app like I've done.

svaarala commented 9 years ago

How would this relate to #60?

creationix commented 9 years ago

So the custom logic that uses loadlib is in the user-configurable part in Duktape.modLoad. Everything can be done in JavaScript to support loading binary addons except for the loadlib primitive. But since this is highly unportable I can see why core doesn't want it.

My idea was for the built-in modLoad to check if loadlib existed and use it for .so and .dll files. But now I see even that assumes a lot about platform capabilities.

svaarala commented 7 years ago

Module loading is now in extras; adding a DLL capable module loader as an extra is a backlog item so I'll leave this open for now.