Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions benchmark/ffi/add-f64.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common.js');
const ffi = require('node:ffi');
const { libraryPath, ensureFixtureLibrary } = require('./common.js');

const bench = common.createBenchmark(main, {
n: [1e7],
}, {
flags: ['--experimental-ffi'],
});

ensureFixtureLibrary();

const { lib, functions } = ffi.dlopen(libraryPath, {
add_f64: { result: 'f64', parameters: ['f64', 'f64'] },
});

const add = functions.add_f64;

function main({ n }) {
bench.start();
for (let i = 0; i < n; ++i)
add(1.5, 2.5);
bench.end(n);

lib.close();
}
28 changes: 28 additions & 0 deletions benchmark/ffi/add-i32.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common.js');
const ffi = require('node:ffi');
const { libraryPath, ensureFixtureLibrary } = require('./common.js');

const bench = common.createBenchmark(main, {
n: [1e7],
}, {
flags: ['--experimental-ffi'],
});

ensureFixtureLibrary();

const { lib, functions } = ffi.dlopen(libraryPath, {
add_i32: { result: 'i32', parameters: ['i32', 'i32'] },
});

const add = functions.add_i32;

function main({ n }) {
bench.start();
for (let i = 0; i < n; ++i)
add(20, 22);
bench.end(n);

lib.close();
}
24 changes: 24 additions & 0 deletions benchmark/ffi/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const common = require('../common.js');
const fs = require('node:fs');
const path = require('node:path');

// Cannot use test/ffi/ffi-test-common.js because it requires test/common
// (the test harness module). Construct the path directly.
const libraryPath = path.join(__dirname, '..', '..', 'test', 'ffi',
'fixture_library', 'build', common.buildType,
process.platform === 'win32' ? 'ffi_test_library.dll' :
process.platform === 'darwin' ? 'ffi_test_library.dylib' :
'ffi_test_library.so');

function ensureFixtureLibrary() {
if (!fs.existsSync(libraryPath)) {
throw new Error(
`Missing FFI fixture library: ${libraryPath}. ` +
'Build it with `tools/test.py test/ffi/test-ffi-calls.js` first.',
);
}
}

module.exports = { libraryPath, ensureFixtureLibrary };
28 changes: 28 additions & 0 deletions benchmark/ffi/many-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common.js');
const ffi = require('node:ffi');
const { libraryPath, ensureFixtureLibrary } = require('./common.js');

const bench = common.createBenchmark(main, {
n: [1e7],
}, {
flags: ['--experimental-ffi'],
});

ensureFixtureLibrary();

const { lib, functions } = ffi.dlopen(libraryPath, {
sum_6_i32: { result: 'i32', parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32'] },
});

const fn = functions.sum_6_i32;

function main({ n }) {
bench.start();
for (let i = 0; i < n; ++i)
fn(1, 2, 3, 4, 5, 6);
bench.end(n);

lib.close();
}
28 changes: 28 additions & 0 deletions benchmark/ffi/pointer-bigint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common.js');
const ffi = require('node:ffi');
const { libraryPath, ensureFixtureLibrary } = require('./common.js');

const bench = common.createBenchmark(main, {
n: [1e7],
}, {
flags: ['--experimental-ffi'],
});

ensureFixtureLibrary();

const { lib, functions } = ffi.dlopen(libraryPath, {
pointer_to_usize: { result: 'u64', parameters: ['pointer'] },
});

const fn = functions.pointer_to_usize;

function main({ n }) {
bench.start();
for (let i = 0; i < n; ++i)
fn(0xdeadbeefn);
bench.end(n);

lib.close();
}
33 changes: 33 additions & 0 deletions benchmark/ffi/sum-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

const common = require('../common.js');
const ffi = require('node:ffi');
const { libraryPath, ensureFixtureLibrary } = require('./common.js');

const bench = common.createBenchmark(main, {
size: [64, 1024, 16384],
n: [1e6],
}, {
flags: ['--experimental-ffi'],
});

ensureFixtureLibrary();

const { lib, functions } = ffi.dlopen(libraryPath, {
sum_buffer: { result: 'u64', parameters: ['pointer', 'u64'] },
});

function main({ n, size }) {
const buf = Buffer.alloc(size, 0x42);
const ptr = ffi.getRawPointer(buf);
const len = BigInt(size);

const sum = functions.sum_buffer;

bench.start();
for (let i = 0; i < n; ++i)
sum(ptr, len);
bench.end(n);

lib.close();
}
23 changes: 23 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,27 @@ added:

Enable experimental support for the network inspection with Chrome DevTools.

### `--experimental-package-map=<path>`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

Enable experimental package map resolution. The `path` argument specifies the
location of a JSON configuration file that defines package resolution mappings.

```bash
node --experimental-package-map=./package-map.json app.js
```

When enabled, bare specifier resolution consults the package map for resolution.
This allows explicit control over which packages can import which dependencies.

See [Package maps][] for details on the configuration file format and
resolution algorithm.

### `--experimental-print-required-tla`

<!-- YAML
Expand Down Expand Up @@ -3702,6 +3723,7 @@ one is included in the list below.
* `--experimental-json-modules`
* `--experimental-loader`
* `--experimental-modules`
* `--experimental-package-map`
* `--experimental-print-required-tla`
* `--experimental-quic`
* `--experimental-require-module`
Expand Down Expand Up @@ -4298,6 +4320,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
[Navigator API]: globals.md#navigator
[Node.js issue tracker]: https://github.com/nodejs/node/issues
[OSSL_PROVIDER-legacy]: https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html
[Package maps]: packages.md#package-maps
[Permission Model]: permissions.md#permission-model
[REPL]: repl.md
[ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage
Expand Down
72 changes: 72 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2514,6 +2514,77 @@ A given value is out of the accepted range.
The `package.json` [`"imports"`][] field does not define the given internal
package specifier mapping.

<a id="ERR_PACKAGE_MAP_EXTERNAL_FILE"></a>

### `ERR_PACKAGE_MAP_EXTERNAL_FILE`

<!-- YAML
added: REPLACEME
-->

A module attempted to resolve a bare specifier using the [package map][], but
the importing file is not located within any package defined in the map.

```console
$ node --experimental-package-map=./package-map.json /tmp/script.js
Error [ERR_PACKAGE_MAP_EXTERNAL_FILE]: Cannot resolve "dep-a" from "/tmp/script.js": file is not within any package defined in /path/to/package-map.json
```

To fix this error, ensure the importing file is inside one of the package
directories listed in the package map, or add a new package entry whose `path`
covers the importing file.

<a id="ERR_PACKAGE_MAP_INVALID"></a>

### `ERR_PACKAGE_MAP_INVALID`

<!-- YAML
added: REPLACEME
-->

The [package map][] configuration file is invalid. This can occur when:

* The file does not exist at the specified path.
* The file contains invalid JSON.
* The file is missing the required `packages` object.
* A package entry is missing the required `path` field.
* Two package entries have the same `path` value.

```console
$ node --experimental-package-map=./missing.json app.js
Error [ERR_PACKAGE_MAP_INVALID]: Invalid package map at "./missing.json": file not found
```

<a id="ERR_PACKAGE_MAP_KEY_NOT_FOUND"></a>

### `ERR_PACKAGE_MAP_KEY_NOT_FOUND`

<!-- YAML
added: REPLACEME
-->

A package's `dependencies` object in the [package map][] references a package
key that is not defined in the `packages` object.

```json
{
"packages": {
"app": {
"path": "./app",
Comment thread
arcanis marked this conversation as resolved.
"dependencies": {
"foo": "nonexistent"
}
}
}
}
```

In this example, `"nonexistent"` is referenced as a dependency target but not
defined in `packages`, which will throw this error.

To fix this error, ensure all package keys referenced in `dependencies` values
are defined in the `packages` object.

<a id="ERR_PACKAGE_PATH_NOT_EXPORTED"></a>

### `ERR_PACKAGE_PATH_NOT_EXPORTED`
Expand Down Expand Up @@ -4501,6 +4572,7 @@ An error occurred trying to allocate memory. This should never happen.
[domains]: domain.md
[event emitter-based]: events.md#class-eventemitter
[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
[package map]: packages.md#package-maps
[relative URL]: https://url.spec.whatwg.org/#relative-url-string
[self-reference a package using its name]: packages.md#self-referencing-a-package-using-its-name
[special scheme]: https://url.spec.whatwg.org/#special-scheme
Expand Down
8 changes: 8 additions & 0 deletions doc/api/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,12 @@ The default loader has the following properties
* Fails on unknown extensions for `file:` loading
(supports only `.cjs`, `.js`, and `.mjs`)
When the [`--experimental-package-map`][] flag is enabled, bare specifier
resolution first consults the package map configuration. If the importing
module is within a mapped package and the specifier matches a declared
dependency, the package map resolution takes precedence. See [Package maps][]
for details.
### Resolution algorithm
The algorithm to load an ES module specifier is given through the
Expand Down Expand Up @@ -1306,12 +1312,14 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
[Loading ECMAScript modules using `require()`]: modules.md#loading-ecmascript-modules-using-require
[Module customization hooks]: module.md#customization-hooks
[Node.js Module Resolution And Loading Algorithm]: #resolution-algorithm-specification
[Package maps]: packages.md#package-maps
[Source Phase Imports]: https://github.com/tc39/proposal-source-phase-imports
[Terminology]: #terminology
[URL]: https://url.spec.whatwg.org/
[WebAssembly JS String Builtins Proposal]: https://github.com/WebAssembly/js-string-builtins
[`"exports"`]: packages.md#exports
[`"type"`]: packages.md#type
[`--experimental-package-map`]: cli.md#--experimental-package-mappath
[`--input-type`]: cli.md#--input-typetype
[`data:` URLs]: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data
[`export`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
Expand Down
Loading
Loading