Title: ffi: Initial implementation by tianxiadys · Pull Request #57761 · nodejs/node · GitHub
Open Graph Title: ffi: Initial implementation by tianxiadys · Pull Request #57761 · nodejs/node
X Title: ffi: Initial implementation by tianxiadys · Pull Request #57761 · nodejs/node
Description: All the following texts were translated from Chinese by GPT-4. If any wording comes across as offensive, please forgive me, it was not my intention. Thanks to @bengl early work, this PR evolved from his efforts (#46905). How to use Currently, FFI does not support all the platforms that Node.js supports. To avoid compilation failures on unsupported platforms and to prevent confusion for developers, I have added a compile-time flag, which is disabled by default. Here is an example of the configure commands: ./configure.py --with-ffi Or you are on the Windows platform. ./vcbuild.bat ffi This is an experimental feature. You must run Node with the --experimental-ffi flag to use it. Additionally, it is a security-sensitive feature, so you also need to use the --allow-ffi flag. By default, all permissions are enabled unless the permission model is manually activated by using the flag --permission or any --allow-* flag. Here is a command-line example: node --experimental-ffi --allow-ffi demo.js This is a test code that can run on the Windows platform. const { dlopen, UnsafeCallback, UnsafeFnPointer, UnsafePointer, UnsafePointerView } = require('node:ffi'); let a1 = dlopen('user32', { EnumWindows: { result: 'i32', parameters: ['pointer', 'u64'] } }); let a2 = new UnsafeCallback({ result: 'i32', parameters: ['pointer', 'u64'] }, (hWnd, lParam) => { console.log('hWnd', hWnd, 'lParam', lParam); return 1n; }); a1.symbols.EnumWindows(a2.pointer, 3); console.log(a1); console.log(a2); let b1 = dlopen('kernel32', { MultiByteToWideChar: { result: 'i32', parameters: ['u32', 'u32', 'pointer', 'i32', 'pointer', 'i32'] }, HeapAlloc: { result: 'pointer', parameters: ['pointer', 'u32', 'usize'] }, GetProcessHeap: { result: 'pointer', parameters: [] } }); let b2 = Buffer.from('你好', 'utf-8'); let b3 = b1.symbols.GetProcessHeap(); let b4 = b1.symbols.HeapAlloc(b3, 0, 100); let b5 = b1.symbols.MultiByteToWideChar(65001, 0, b2, -1, b4, 50); let b6 = UnsafePointerView.getArrayBuffer(b4, 10, 0); console.log(b1); console.log(b2); console.log(b3); console.log(b4); console.log(b5); console.log(b6); About the "libffi/fixed" and automake dependency The core of this feature relies on calling the libffi library. Referencing libffi is challenging because it heavily depends on automake and has poor support for MSVC. To successfully integrate this library, I made some adjustments. First, I streamlined the library's files by removing parts we don't currently need. These can be added back in as needed. Next, I created a separate fixed folder where I wrote files: ffi.h, ffi.c, ffiasm.S, fficonfig.h and ffitarget.h. These files use #if macros to handle most of the work that would normally be done by automake. Upgrading libffi in the future is both necessary and entirely feasible. I only added files without modifying any of libffi's existing files. When updating to a newer version of libffi, you simply need to replace the corresponding files. Support Platform Here is the system and CPU support status: Windows Linux Mac Other System x86 NO YES ToDo x64 YES YES YES ToDo ARM32 YES YES ToDo ARM64 YES YES YES ToDo ARM-EC NO Other Arch ToDo ToDo Blank spaces in the table indicate the absence of such combinations. Windows does not support the fifth type of CPU, and Mac only supports x64 and ARM64. Windows x86 is the most unique platform because it has numerous calling conventions, which are an important part of a function's signature. However, our JS API does not have a field to specify the calling convention. Fortunately, this platform is no longer actively supported by Node.js, so we can reasonably abandon it. The hybrid architecture (Windows ARM-EC) is a crazy idea, and I’m not sure whether this technology is widely used. Undoubtedly, it presents significant challenges for FFI. Currently, it is not supported and likely won’t be in the future either. Support for other architectures and systems will be addressed in future PRs. About ffi double For @atgreen: It seems that a double type field is missing in ffi_raw. Was this intentionally designed? Although this issue can be resolved by forcibly casting the pointer type, I didn't do so because I suspect there might be some traps I'm unaware of waiting for me. About compatibility with Deno.js For @aapoalas: A portion of the content remains unimplemented, divided into three parts: the UnsafeCallback.threadSafe method the nonblocking option, and all methods of UnsafePointerView except static getArrayBuffer. threadSafe and nonblocking the threadSafe method should not be able to exist independently of nonblocking. The most complex callback scenarios I can think of are the Win32 window procedure callback and APC invocation. The former involves registering a callback method in RegisterWndClass, and later, during the message loop, the thread processes the window procedure by calling DispatchMessage. The latter involves registering a callback method in ReadFileEx, and subsequently, the APC callback is entered internally within the SleepEx method. In any case, the prerequisite for the current thread to enter a callback function is that the thread must call a certain method, and this method will internally locate the callback function pointer recorded somewhere, and then enter the callback function. A thread cannot arbitrarily enter a callback function at any location; perhaps only Linux's signal method has such magical capabilities. In summary, threadSafe seems to apply only to certain specific scenarios. For example, an independent thread outside of the Node.js event loop executes some kind of loop, and at a certain point in the loop, it sends a message to the Node.js main thread. Wouldn't such a requirement be more suitable for implementation in a third-party library rather than in the core code of Node.js or Deno.js? UnsafePointerView The functionality of UnsafePointerView is to read arbitrary memory addresses in JavaScript. However, after creating an ArrayBuffer via a pointer, most of the features of this class become redundant, as they can be replaced by TypedArray, DataView, or Buffer (Node.js only). Therefore, UnsafePointerView seems to merely act as a wrapper? Open issues I can't wait to share this module with everyone! The following tasks have not been completed yet, but perhaps they can be ignored in this PR and completed in future PRs: Documentation for the ffi module (nightly users can refer to Deno.js's documentation). Support only for Windows, Linux, Mac platforms, and certain CPU architectures. Auto-update scripts for libffi. Unit test code and benchmark test code The double type is not supported. Not fully compatible with Deno.js. Other details I haven't thought of yet.
Open Graph Description: All the following texts were translated from Chinese by GPT-4. If any wording comes across as offensive, please forgive me, it was not my intention. Thanks to @bengl early work, this PR evolved fro...
X Description: All the following texts were translated from Chinese by GPT-4. If any wording comes across as offensive, please forgive me, it was not my intention. Thanks to @bengl early work, this PR evolved fro...
Opengraph URL: https://github.com/nodejs/node/pull/57761
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:9f233e57-f04d-b182-d404-1cdcbdb8e5e7 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 91E8:37D99:23EF2E8:32F270E:6A4CE465 |
| html-safe-nonce | fade8dead8cf456df82a1f3a47a47f4f9a908c058d24ddecaadbc1ce70272b73 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MUU4OjM3RDk5OjIzRUYyRTg6MzJGMjcwRTo2QTRDRTQ2NSIsInZpc2l0b3JfaWQiOiI3NzM4OTEyNjA5OTM1NjE3MDEiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | f48081b4b194efe14d14a5cfe41ba58f1cc2799c8aa04c4337290a6d1c0c3dd1 |
| hovercard-subject-tag | pull_request:2441282989 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/nodejs/node/pull/57761/files |
| twitter:image | https://avatars.githubusercontent.com/u/29011385?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/29011385?s=400&v=4 |
| og:image:alt | All the following texts were translated from Chinese by GPT-4. If any wording comes across as offensive, please forgive me, it was not my intention. Thanks to @bengl early work, this PR evolved fro... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 299b43bca6e02ad35197ffeba30d2466846d5fb02ab96fbced5b5e6cec589fb8 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | c5a57f04eeb310f57c73fd6d751d957e2ca27ed2 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width