Bug 147377

Summary: Reimplement SbiDllMgr::Call using libffi
Product: LibreOffice Reporter: Mike Kaganski <mikekaganski>
Component: BASICAssignee: Not Assigned <libreoffice-bugs>
Status: NEW ---    
Severity: enhancement CC: 79045_79045, andreas.heinisch, aron.budea, himajin100000, mentoring, sokol
Priority: medium Keywords: difficultyInteresting, easyHack
Version: unspecified   
Hardware: All   
OS: All   
Whiteboard:
Crash report or crash signature: Regression By:
Bug Depends on:    
Bug Blocks: 143781    

Description Mike Kaganski 2022-02-11 15:15:16 UTC
Currently Basic's Declare statement [1] is implemented in basic/source/runtime/dllmgr*.cxx using home-grown marshaling code [2]. This poses several problems:

1. The code is platform-specific (requires separate maintenance per platform), with much duplication.
2. It has limitation to 20 arguments (see [3]).
3. Calls using non-default calling convention are not implemented (and would require much work) - so e.g. CDecl keyword is not implemented on Win32, where it makes sense.

There is libffi [4] that solves exactly this problem. Using it instead of home-grown code to call external DLL functions would allow to unify the code across platforms (and implement it where it isn't implemented); avoid any argument count limitations; define calling convention.

The library uses MIT license; it is already used in LO codebase (it is a requirement for Python), so it only needs to be linked to Library_sb to be usable there. Then the code could look similar to

>   std::vector<ffi_type*>arg_types;
>   std::vector<void*>arg_values;
>   std::vector<char> blob;
>   if (arguments)
>   {
>       arg_types.reserve(arguments->Count());
>       arg_values.reserve(arguments->Count());
>       blob.reserve(arguments->Count() * 8);
>
>       for (sal_uInt32 i = 1; i < arguments->Count(); ++i)
>       {
>           // Put argument values to blob, store pointers to them
>           // in arg_values, and their types in arg_types
>       }
>   }
>
>   ffi_type* ret_t = SbxDataType_to_ffi_type(result.GetType());
>   ffi_abi eAbi = GetAbi(); // e.g., FFI_DEFAULT_ABI or FFI_MS_CDECL
>
>   // Prepare the ffi_cif structure.
>   ffi_cif cif;
>   if (ffi_status status = ffi_prep_cif(&cif, eAbi, 2, ret_t, arg_types.data());
>       status != FFI_OK)
>   {
>       // Handle the ffi_status error.
>   }
>
>   // Invoke the function.
>   ffi_arg retval;
>   ffi_call(&cif, FFI_FN(proc.proc), &retval, arg_values.data());
>   // ...

(sketched after [5]).

[1] https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03090403.html
[2] https://opengrok.libreoffice.org/search?&defs=SbiDllMgr%3A%3ACall&project=core
[3] https://ask.libreoffice.org/t/passing-arrays-in-basic-works-with-excel-error-73-in-libreoffice/48267
[4] https://sourceware.org/libffi/
[5] https://linux.die.net/man/3/ffi_call
Comment 1 Roman Kuznetsov 2023-07-09 08:32:17 UTC
Set to NEW

It looks like EasyHack