New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libcall feature is not supported in neovim #795

Closed
Shougo opened this Issue Jun 1, 2014 · 25 comments

Comments

Projects
None yet
4 participants
@Shougo
Copy link
Contributor

Shougo commented Jun 1, 2014

I tested neovim and found that libcall feature is not supported in neovim.
I need this feature to use vimproc plugin in neovim.

I edited "config/config.h" and added "FEAT_LIBCALL" line.
But many errors are occurred in make output...

Shougo/vimproc.vim#110

@justinmk

This comment has been minimized.

Copy link
Member

justinmk commented Jun 2, 2014

Not sure what make errors you are getting, but neovim will eventually re-implement mch_libcall() using libuv. So that's why that hasn't got any attention yet.

As an alternative, vimproc#system_bg() and vimproc#popen3() can be reimplemented using neovim job control. Job control ("async") is implemented and ready-to-use in neovim, but some small features are missing that might be needed by vimproc, such as exposing the pid (#557). This would allow users to use vimproc without the compiled part of vimproc. And plugins using vimproc would not have to change anything.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

We could perhaps call into LuaJIT and re-purpose the ffi for this in the long run.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

Another alternative is of course uv_dlopen/uv_dlsym/uv_dlclose, which abstracts over dlopen and LoadLibrary. It shouldn't be too hard to at least get basic libcall functionality back.

UPDATE: I notice that uv_dlopen/uv_dlsym/uv_dlclose are very, very thin wrappers over their platform-specific equivalents. So it's probably necessary to check which of that vim code still applies...

os_mswin.c:mch_libcall

    int
mch_libcall(
    char_u  *libname,
    char_u  *funcname,
    char_u  *argstring, /* NULL when using a argint */
    int     argint,
    char_u  **string_result,/* NULL when using number_result */
    int     *number_result)
{
    HINSTANCE       hinstLib;
    MYSTRPROCSTR    ProcAdd;
    MYINTPROCSTR    ProcAddI;
    char_u      *retval_str = NULL;
    int         retval_int = 0;
    size_t      len;

    BOOL fRunTimeLinkSuccess = FALSE;

    // Get a handle to the DLL module.
# ifdef WIN16
    hinstLib = LoadLibrary(libname);
# else
    hinstLib = vimLoadLib(libname);
# endif

    // If the handle is valid, try to get the function address.
    if (hinstLib != NULL)
    {
#ifdef HAVE_TRY_EXCEPT
    __try
    {
#endif
    if (argstring != NULL)
    {
        /* Call with string argument */
        ProcAdd = (MYSTRPROCSTR) GetProcAddress(hinstLib, funcname);
        if ((fRunTimeLinkSuccess = (ProcAdd != NULL)) != 0)
        {
        if (string_result == NULL)
            retval_int = ((MYSTRPROCINT)ProcAdd)(argstring);
        else
            retval_str = (ProcAdd)(argstring);
        }
    }
    else
    {
        /* Call with number argument */
        ProcAddI = (MYINTPROCSTR) GetProcAddress(hinstLib, funcname);
        if ((fRunTimeLinkSuccess = (ProcAddI != NULL)) != 0)
        {
        if (string_result == NULL)
            retval_int = ((MYINTPROCINT)ProcAddI)(argint);
        else
            retval_str = (ProcAddI)(argint);
        }
    }

    // Save the string before we free the library.
    // Assume that a "1" result is an illegal pointer.
    if (string_result == NULL)
        *number_result = retval_int;
    else if (retval_str != NULL
# ifdef WIN16
        && retval_str != (char_u *)1
        && retval_str != (char_u *)-1
        && !IsBadStringPtr(retval_str, INT_MAX)
        && (len = strlen(retval_str) + 1) > 0
# else
        && (len = check_str_len(retval_str)) > 0
# endif
        )
    {
        *string_result = lalloc((long_u)len, TRUE);
        if (*string_result != NULL)
        mch_memmove(*string_result, retval_str, len);
    }

#ifdef HAVE_TRY_EXCEPT
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        if (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW)
        RESETSTKOFLW();
        fRunTimeLinkSuccess = 0;
    }
#endif

    // Free the DLL module.
    (void)FreeLibrary(hinstLib);
    }

    if (!fRunTimeLinkSuccess)
    {
    EMSG2(_(e_libcall), funcname);
    return FAIL;
    }

    return OK;
}
#endif

os_unix.c:mch_libcall

/*
 * Call a DLL routine which takes either a string or int param
 * and returns an allocated string.
 */
    int
mch_libcall(libname, funcname, argstring, argint, string_result, number_result)
    char_u  *libname;
    char_u  *funcname;
    char_u  *argstring; /* NULL when using a argint */
    int     argint;
    char_u  **string_result;/* NULL when using number_result */
    int     *number_result;
{
# if defined(USE_DLOPEN)
    void    *hinstLib;
    char    *dlerr = NULL;
# else
    shl_t   hinstLib;
# endif
    STRPROCSTR  ProcAdd;
    INTPROCSTR  ProcAddI;
    char_u  *retval_str = NULL;
    int     retval_int = 0;
    int     success = FALSE;

    /*
     * Get a handle to the DLL module.
     */
# if defined(USE_DLOPEN)
    /* First clear any error, it's not cleared by the dlopen() call. */
    (void)dlerror();

    hinstLib = dlopen((char *)libname, RTLD_LAZY
#  ifdef RTLD_LOCAL
        | RTLD_LOCAL
#  endif
        );
    if (hinstLib == NULL)
    {
    /* "dlerr" must be used before dlclose() */
    dlerr = (char *)dlerror();
    if (dlerr != NULL)
        EMSG2(_("dlerror = \"%s\""), dlerr);
    }
# else
    hinstLib = shl_load((const char*)libname, BIND_IMMEDIATE|BIND_VERBOSE, 0L);
# endif

    /* If the handle is valid, try to get the function address. */
    if (hinstLib != NULL)
    {
# ifdef HAVE_SETJMP_H
    /*
     * Catch a crash when calling the library function.  For example when
     * using a number where a string pointer is expected.
     */
    mch_startjmp();
    if (SETJMP(lc_jump_env) != 0)
    {
        success = FALSE;
#  if defined(USE_DLOPEN)
        dlerr = NULL;
#  endif
        mch_didjmp();
    }
    else
# endif
    {
        retval_str = NULL;
        retval_int = 0;

        if (argstring != NULL)
        {
# if defined(USE_DLOPEN)
        ProcAdd = (STRPROCSTR)dlsym(hinstLib, (const char *)funcname);
        dlerr = (char *)dlerror();
# else
        if (shl_findsym(&hinstLib, (const char *)funcname,
                    TYPE_PROCEDURE, (void *)&ProcAdd) < 0)
            ProcAdd = NULL;
# endif
        if ((success = (ProcAdd != NULL
# if defined(USE_DLOPEN)
                && dlerr == NULL
# endif
                )))
        {
            if (string_result == NULL)
            retval_int = ((STRPROCINT)ProcAdd)(argstring);
            else
            retval_str = (ProcAdd)(argstring);
        }
        }
        else
        {
# if defined(USE_DLOPEN)
        ProcAddI = (INTPROCSTR)dlsym(hinstLib, (const char *)funcname);
        dlerr = (char *)dlerror();
# else
        if (shl_findsym(&hinstLib, (const char *)funcname,
                       TYPE_PROCEDURE, (void *)&ProcAddI) < 0)
            ProcAddI = NULL;
# endif
        if ((success = (ProcAddI != NULL
# if defined(USE_DLOPEN)
                && dlerr == NULL
# endif
                )))
        {
            if (string_result == NULL)
            retval_int = ((INTPROCINT)ProcAddI)(argint);
            else
            retval_str = (ProcAddI)(argint);
        }
        }

        /* Save the string before we free the library. */
        /* Assume that a "1" or "-1" result is an illegal pointer. */
        if (string_result == NULL)
        *number_result = retval_int;
        else if (retval_str != NULL
            && retval_str != (char_u *)1
            && retval_str != (char_u *)-1)
        *string_result = vim_strsave(retval_str);
    }

# ifdef HAVE_SETJMP_H
    mch_endjmp();
#  ifdef SIGHASARG
    if (lc_signal != 0)
    {
        int i;

        /* try to find the name of this signal */
        for (i = 0; signal_info[i].sig != -1; i++)
        if (lc_signal == signal_info[i].sig)
            break;
        EMSG2("E368: got SIG%s in libcall()", signal_info[i].name);
    }
#  endif
# endif

# if defined(USE_DLOPEN)
    /* "dlerr" must be used before dlclose() */
    if (dlerr != NULL)
        EMSG2(_("dlerror = \"%s\""), dlerr);

    /* Free the DLL module. */
    (void)dlclose(hinstLib);
# else
    (void)shl_unload(hinstLib);
# endif
    }

    if (!success)
    {
    EMSG2(_(e_libcall), funcname);
    return FAIL;
    }

    return OK;
}
#endif

The only usage of mch_libcall in the vim codebase is in eval.c:

    static void
libcall_common(argvars, rettv, type)
    typval_T    *argvars;
    typval_T    *rettv;
    int     type;
{
#ifdef FEAT_LIBCALL
    char_u      *string_in;
    char_u      **string_result;
    int         nr_result;
#endif

    rettv->v_type = type;
    if (type != VAR_NUMBER)
    rettv->vval.v_string = NULL;

    if (check_restricted() || check_secure())
    return;

#ifdef FEAT_LIBCALL
    /* The first two args must be strings, otherwise its meaningless */
    if (argvars[0].v_type == VAR_STRING && argvars[1].v_type == VAR_STRING)
    {
    string_in = NULL;
    if (argvars[2].v_type == VAR_STRING)
        string_in = argvars[2].vval.v_string;
    if (type == VAR_NUMBER)
        string_result = NULL;
    else
        string_result = &rettv->vval.v_string;
    if (mch_libcall(argvars[0].vval.v_string,
                 argvars[1].vval.v_string,
                 string_in,
                 argvars[2].vval.v_number,
                 string_result,
                 &nr_result) == OK
        && type == VAR_NUMBER)
        rettv->vval.v_number = nr_result;
    }
#endif
}
@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

Here's a first rudimentary reimplementation of mch_libcall that doesn't handle hard errors in the DLL/SO/DYLIB, because dragons live there:

/// Functions for using external native libraries

#include <stdint.h>
#include <uv.h>

#include "nvim/os/os.h"
#include "nvim/memory.h"
#include "nvim/message.h"

/// possible function prototypes
/// int -> int
/// int -> string
/// string -> string
/// string -> int
typedef void (*gen_fn)();
typedef const char *(*str_str_fn)(const char *str);
typedef int64_t (*str_int_fn)(const char *str);
typedef const char *(*int_str_fn)(int64_t i);
typedef int64_t (*int_int_fn)(int64_t i);

static char *os_save_string_result(const char *res) {
  // assume that a "1" or "-1" result is an illegal pointer.
  if (!res || (intptr_t) res == 1 || (intptr_t) res == -1) {
    return NULL;
  }

  return xstrdup(res);
}

/// os_libcall - call a function in a dynamic loadable library
///
/// an example of calling a function that takes a string and returns an int:
///
///   int64_t int_out = 0;
///   os_libcall("mylib.so", "somefn", "string-argument", 0, NULL, &int_out);
///
/// @param argv the input string, NULL when using `argi`
/// @param argi the input integer, not used when using `argv`
/// @param[out] string_out an allocated output string, caller must free if
///             not NULL. NULL when using `int_out`.
/// @param[out] int_out the output integer param
bool os_libcall(const char *libname,
                const char *funcname,
                const char *argv,
                int64_t argi,
                char **str_out,
                int64_t *int_out)
{
  if (!libname || !funcname) {
    return false;
  }

  uv_lib_t lib = {0};

  // open the dynamic loadable library
  if (uv_dlopen(libname, &lib)) {
      EMSG2(_("dlerror = \"%s\""), uv_dlerror(&lib));
      return false;
  }

  // find and load the requested function in the library
  gen_fn fn;
  if (uv_dlsym(&lib, funcname, (void **) &fn)) {
      EMSG2(_("dlerror = \"%s\""), uv_dlerror(&lib));
      uv_dlclose(&lib);
      return false;
  }

  // call the library and save the result
  // TODO(aktau): catch signals and use jmp (if available) to handle
  // exceptions. jmp's on UNIX seem to interact trickily with signals as
  // well.
  if (str_out) {
    str_str_fn sfn = (str_str_fn) fn;
    int_str_fn ifn = (int_str_fn) fn;
    *str_out = os_save_string_result(argv ? sfn(argv) : ifn(argi));
  } else {
    str_int_fn sfn = (str_int_fn) fn;
    int_int_fn ifn = (int_int_fn) fn;
    *int_out = argv ? sfn(argv) : ifn(argi);
  }

  // free the library
  uv_dlclose(&lib);

  return true;
}

So for now it only works if the DLL in question doesn't pull any funny business (exceptions, jumps...)

For error handling, we're going to have to do some wrangling, some useful things I have found for abstracting this to our target platforms:

UPDATE: I'm still undecided about using int64_t or native int as vim does.

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

Thanks. I get it.

As an alternative, vimproc#system_bg() and vimproc#popen3() can be reimplemented using neovim job control. Job control ("async") is implemented and ready-to-use in neovim, but some small features are missing that might be needed by vimproc, such as exposing the pid (#557). This would allow users to use vimproc without the compiled part of vimproc. And plugins using vimproc would not have to change anything.

I need libcall feature for vimproc. I know neovim has job control feature.
But vimproc is not only job control. Vimproc may use the job control feature for neovim.

Note: Of cource I also need if_lua feature in neovim for neocomplete, unite.vim, ..

@tarruda

This comment has been minimized.

Copy link
Member

tarruda commented Jun 2, 2014

@Shougo, vimproc description says 'Interactive command execution in Vim'. Am I right to assume that it opens processes connected to pseudo-terminals? If so, then job control can't help you, but maybe the msgpack-rpc API can(you can use a higher level language to open an interactive process and communicate with msgpack). I even posted a comment on reddit about this matter, it might interest you.

Enhancing job control to support opening processes connected to pseudo-terminals is something that we might add in the future.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

We'll need to implement libcall anyway for vim compatibility, at least in the near future. What we might consider is not implementing vim's extreme error recovery (exception handling, ...).

@tarruda

This comment has been minimized.

Copy link
Member

tarruda commented Jun 2, 2014

We'll need to implement libcall anyway for vim compatibility, at least in the near future. What we might consider is not implementing vim's extreme error recovery (exception handling, ...).

This is the first time I've seen someone complaining about libcall and most plugins that don't require other language bindings already seem to work, so I wonder if it's worth implementing this feature since the whole point is providing a way for vimscript communicate with native code, and that is already very well covered by the msgpack-rpc API

In fact, if we must reimplement this feature for compatibility purposes, I prefer to do it using the msgpack-rpc API, in the same way the python/ruby/perl.. ex commands will be implemented(this function can communicate with a luajit co-process that loads the library at runtime). This would be much better because there would be no chance of crashing the editor.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

In fact, if we must reimplement this feature for compatibility purposes, I prefer to do it using the msgpack-rpc API, in the same way the python/ruby/perl.. ex commands will be implemented(this function can communicate with a luajit co-process that loads the library at runtime). This would be much better because there would be no chance of crashing the editor.

Sure, this is more work (and slower) than the version I posted above, but it has the huge benefit of not needing a tricky exception/setjmp fence to prevent crashes, as well as not polluting the nvim address space.

Do you have any plan for loading these "on demand" coprocesses? Like a helper function to load an on demand job? For example, there's no reason to load libcall_coprocess if none of the VimL the user is running uses libcall, but after one call has happened, it will probably happen more, so it should be kept running.

UPDATE: Now that I think of it @tarruda, we would also need to deliver a luajit binary with neovim for that. I'm not so sure if package maintainers will like that. But maybe it doesn't matter so much.

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

This is the first time I've seen someone complaining about libcall and most plugins that don't require other language bindings already seem to work, so I wonder if it's worth implementing this feature since the whole point is providing a way for vimscript communicate with native code, and that is already very well covered by the msgpack-rpc API

Please don't remove the feature. I need this. I think compatibility is important than new APIs.

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

It is used feature widely.

https://github.com/search?q=libcall+vim&type=Code&ref=searchresults

If neovim does not support libcall, I don't want to use/support neovim.

This is the first time I've seen someone complaining about libcall and most plugins that don't require other language bindings already seem to work

Because, neovim is not tested by plugin authors.

@tarruda

This comment has been minimized.

Copy link
Member

tarruda commented Jun 2, 2014

Sure, this is more work (and slower) than the version I posted above

The slowness may not be noticeable: I think the main use case for this function is to perform expensive work in native code, that means most of the libcall execution time will be performed by the library and not by the translation. Sure there's some marshaling overhead for msgpack, but I bet most of the time is still going to be taken by the library and not by the data transfer.

Do you have any plan for loading these "on demand" coprocesses? Like a helper function to load an on demand job? For example, there's no reason to load libcall_coprocess if none of the VimL the user is running uses libcall, but after one call has happened, it will probably happen more, so it should be kept running.

I can use the same mechanism I was planning for the python ex commands:

  • has('python') simply tests if 'python' is available in PATH
  • python ex commands will load the python co-process the first time it's called

UPDATE: Now that I think of it @tarruda, we would also need to deliver a luajit binary with neovim for that. I'm not so sure if package maintainers will like that. But maybe it doesn't matter so much

Eventually the luajit library will be required for building neovim, and the lua binary is just a thin wrapper around the library. And if you think about it, the download/install of luajit is already performed, but @jszakmeister added the -DUSE_BUNDLED=OFF option for package maintainers and users that need system dependencies.

In this case package maintainers would simply list 'luajit' as a required dependency and 'python', 'ruby'... as optional dependencies.

Please don't remove the feature. I need this. I think compatibility is important than new APIs.

@Shougo for compatibility sake, I won't. The feature was already removed, but just like the python, ruby, lua ex commands, libcall will be reimplemented in a compatibility layer. In any case, I strongly encourage you to explore the msgpack API/job control features

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

In any case, I strongly encourage you to explore the msgpack API/job control features

No. If libcall feature is implemented, I will implement vimproc by job control features.
I need plugin compatibilities before creating new plugins.

I don't want to create new plugin for neovim at present.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

No. If libcall feature is implemented, I will implement vimproc by job control features.
I need plugin compatibilities before creating new plugins.

This is confusing. Once libcall is implemented, there would be little incentive for you to to use job control. Before libcall is implemented, job control is the only way to emulate some of that functionality.

I don't want to create new plugin for neovim at present.

Do you mean new code or new plugin? Because a new plugin wouldn't be necessary, possibly a has() check for neovim or job_control.

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

This is confusing. Once libcall is implemented, there would be little incentive for you to to use job control. Before libcall is implemented, job control is the only way to emulate some of that functionality.

Because, vimproc is not only job control features.
It cannot be replaced by job control.

Do you mean new code or new plugin? Because a new plugin wouldn't be necessary, possibly a has() check for neovim or job_control.

Yes, I want to add check. But it is incompatible(libcall).
I added "libcall" check in neovim.

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

OK. I explain vimproc features.

  1. Open file descriptors
  2. Open sockets
  3. Open pipe
  4. Open pty
  5. Get files
  6. Kill process

"3. Open pipe" and "4.Open pty" may be replaced. But others cannot.
I don't want to enable vimproc features partially. It should be full featues. And to replace them is too hard.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

Yes, I want to add check. But it is incompatible(libcall).
I added "libcall" check in neovim.

Don't worry, as we've said, vim compatbility is a goal, even though we'll never probably reach 100%, I think 99.5% is reachable.

I've tested my reimplementation of mch_libcall and it seems to work, by the way:

screen shot 2014-06-02 at 17 13 50

That's my local version of nvim. Is that <ff> supposed to be there?

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

That's my local version of nvim. Is that supposed to be there?

Yes. <ff> is used in vimproc. Can you test vimproc and vimshell in the neovim?

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

Yes. is used in vimproc. Can you test vimproc and vimshell in the neovim?

Wouldn't know how. But if @tarruda @justinmk think it's ok to ship an intermediary os_libcall implementation until we get pre-packaged LuaJIT working, we can create a PR for that and you can test it yourself.

@tarruda

This comment has been minimized.

Copy link
Member

tarruda commented Jun 2, 2014

Wouldn't know how. But if @tarruda @justinmk think it's ok to ship an intermediary os_libcall implementation until we get pre-packaged LuaJIT working, we can create a PR for that and you can test it yourself.

I don't have a problem with it. Unlike with language bindings, the code is really small and I don't think its usage will be widely spread now that we have the msgpack API.

@aktau

This comment has been minimized.

Copy link
Member

aktau commented Jun 2, 2014

@tarruda @justinmk I made a version that calls upon LuaJIT as well. I tested it, and it works. Pasted at the bottom.

All said and done, I think I prefer the libuv version because it's way shorter. It might serve as an example of LuaJIT usage though.

/// os_libcall - call a function in a dynamic loadable library
///
/// an example of calling a function that takes a string and returns an int:
///
///   int64_t int_out = 0;
///   os_libcall("mylib.so", "somefn", "string-argument", 0, NULL, &int_out);
///
/// implementation notes:
/// - http://www.lua.org/pil/25.html (loading and executing)
/// - http://www.lua.org/pil/24.2.2.html (querying elements)
///
/// @param argv the input string, NULL when using `argi`
/// @param argi the input integer, not used when using `argv`
/// @param[out] string_out an allocated output string, caller must free if
///             not NULL. NULL when using `int_out`.
/// @param[out] int_out the output integer param
bool os_libcall_lj(const char *libname,
                const char *funcname,
                const char *argv,
                int64_t argi,
                char **str_out,
                int64_t *int_out)
{
  if (!libname || !funcname) {
    return false;
  }

  // start a fresh luajit state
  lua_State *lua = luaL_newstate();
  if (!lua) {
    EMSG2(_("could not open LuaJIT state: %s"), "could not allocate");
    return false;
  }

  bool success = true;

  // the ffi is loaded by default (but require isn't)
  luaL_openlibs(lua);

  char cdecl[1024];
  int written = snprintf(cdecl, sizeof(cdecl), "%s %s(%s);",
    str_out ? "const char *" : "int",
    funcname,
    argv ? "const char *" : "int");

  if ((size_t) written >= sizeof(cdecl)) {
    EMSG2(_("libcall, not enough space for C decl: %s"), cdecl);
    success = false;
    goto cleanup;
  }

  const char src[] = LUA_SRC(
    local ffi = require('ffi');
    ffi.cdef(cdecl);
    local lib = ffi.load(libname);
    local val = lib[funcname](argv or argi)
    return (argv and ffi.string(val)) or tonumber(val)
  );

  // load the generated lua code, execute it and return the result
  // push the arguments on the stack

#define LUA_SET_GLOBAL_STR(L, val) \
  do { \
    lua_pushstring(lua, val); \
    lua_setglobal(lua, #val); \
  } while (0);

#define LUA_SET_GLOBAL_INT(L, val) \
  do { \
    lua_pushnumber(lua, (lua_Number) val); \
    lua_setglobal(lua, #val); \
  } while (0);

  LUA_SET_GLOBAL_STR(lua, libname);
  LUA_SET_GLOBAL_STR(lua, funcname);
  LUA_SET_GLOBAL_STR(lua, cdecl);
  if (argv) {
      LUA_SET_GLOBAL_STR(lua, argv);
  } else {
      LUA_SET_GLOBAL_INT(lua, argi);
  }

#undef LUA_SET_GLOBAL_STR
#undef LUA_SET_GLOBAL_INT

  int error = luaL_dostring(lua, src);
  if (!error && !lua_isnil(lua, -1)) {
      if (argv) {
          if (lua_type(lua, -1) == LUA_TSTRING) {
              *str_out = os_save_string_result(lua_tostring(lua, -1));
          } else {
              EMSG("libcall: library did not correctly return a string value");
              success = false;
              goto cleanup;
          }
      } else {
          if (lua_type(lua, -1) == LUA_TNUMBER) {
              *int_out = lua_tonumber(lua, -1);
          } else {
              EMSG("libcall: library did not correctly return an integer value");
              success = false;
              goto cleanup;
          }
      }

      lua_pop(lua, 1);
  }

cleanup:
  lua_close(lua);

  return success;
}

EDIT: there's another thing required for this, in CMakeLists.txt:

if(APPLE)
  set(CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000")
endif()

Which is required on OSX to be able to launch LuaJIT. I just dropped in the main CMakeLists.txt, @jszakmeister, got a better place for that one?

@justinmk

This comment has been minimized.

Copy link
Member

justinmk commented Jun 2, 2014

👍

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 2, 2014

I tested @aktau branch and it worked!

@justinmk

This comment has been minimized.

Copy link
Member

justinmk commented Jun 28, 2014

Closed by #801. Note this caveat about exceptions in the loaded library: https://github.com/neovim/neovim/pull/802/files?w=1#diff-086d79843f3ef23bcfc8765997d901c9R65

@justinmk justinmk closed this Jun 28, 2014

@justinmk justinmk added this to the first release milestone Jun 28, 2014

@Shougo

This comment has been minimized.

Copy link
Contributor

Shougo commented Jun 28, 2014

👍

@aktau aktau referenced this issue Jul 3, 2014

Merged

[WIP] July Newsletter #52

4 of 4 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment