Skip to content

Commit 1e762dd

Browse files
anonrigtargos
authored andcommitted
esm: improve getFormatOfExtensionlessFile speed
PR-URL: #49965 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent f63a92b commit 1e762dd

File tree

6 files changed

+76
-18
lines changed

6 files changed

+76
-18
lines changed

lib/internal/modules/esm/formats.js

+11-18
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
'use strict';
22

3-
const {
4-
RegExpPrototypeExec,
5-
Uint8Array,
6-
} = primordials;
3+
const { RegExpPrototypeExec } = primordials;
74
const { getOptionValue } = require('internal/options');
8-
9-
const { closeSync, openSync, readSync } = require('fs');
5+
const { getValidatedPath } = require('internal/fs/utils');
6+
const pathModule = require('path');
7+
const fsBindings = internalBinding('fs');
8+
const { fs: fsConstants } = internalBinding('constants');
109

1110
const experimentalWasmModules = getOptionValue('--experimental-wasm-modules');
1211

@@ -47,20 +46,14 @@ function mimeToFormat(mime) {
4746
function getFormatOfExtensionlessFile(url) {
4847
if (!experimentalWasmModules) { return 'module'; }
4948

50-
const magic = new Uint8Array(4);
51-
let fd;
52-
try {
53-
// TODO(@anonrig): Optimize the following by having a single C++ call
54-
fd = openSync(url);
55-
readSync(fd, magic, 0, 4); // Only read the first four bytes
56-
if (magic[0] === 0x00 && magic[1] === 0x61 && magic[2] === 0x73 && magic[3] === 0x6d) {
49+
const path = pathModule.toNamespacedPath(getValidatedPath(url));
50+
51+
switch (fsBindings.getFormatOfExtensionlessFile(path)) {
52+
case fsConstants.EXTENSIONLESS_FORMAT_WASM:
5753
return 'wasm';
58-
}
59-
} finally {
60-
if (fd !== undefined) { closeSync(fd); }
54+
default:
55+
return 'module';
6156
}
62-
63-
return 'module';
6457
}
6558

6659
module.exports = {

src/node_constants.cc

+4
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,10 @@ void DefineSystemConstants(Local<Object> target) {
10581058
NODE_DEFINE_CONSTANT(target, UV_DIRENT_CHAR);
10591059
NODE_DEFINE_CONSTANT(target, UV_DIRENT_BLOCK);
10601060

1061+
// Define module specific constants
1062+
NODE_DEFINE_CONSTANT(target, EXTENSIONLESS_FORMAT_JAVASCRIPT);
1063+
NODE_DEFINE_CONSTANT(target, EXTENSIONLESS_FORMAT_WASM);
1064+
10611065
NODE_DEFINE_CONSTANT(target, S_IFMT);
10621066
NODE_DEFINE_CONSTANT(target, S_IFREG);
10631067
NODE_DEFINE_CONSTANT(target, S_IFDIR);

src/node_constants.h

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
#include "node.h"
2828
#include "v8.h"
2929

30+
#define EXTENSIONLESS_FORMAT_JAVASCRIPT (0)
31+
#define EXTENSIONLESS_FORMAT_WASM (1)
32+
3033
#if HAVE_OPENSSL
3134

3235
#ifndef RSA_PSS_SALTLEN_DIGEST

src/node_file.cc

+50
Original file line numberDiff line numberDiff line change
@@ -2832,6 +2832,51 @@ static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
28322832
}
28332833
}
28342834

2835+
static void GetFormatOfExtensionlessFile(
2836+
const FunctionCallbackInfo<Value>& args) {
2837+
CHECK_EQ(args.Length(), 1);
2838+
CHECK(args[0]->IsString());
2839+
2840+
Environment* env = Environment::GetCurrent(args);
2841+
node::Utf8Value input(args.GetIsolate(), args[0]);
2842+
2843+
THROW_IF_INSUFFICIENT_PERMISSIONS(
2844+
env, permission::PermissionScope::kFileSystemRead, input.ToStringView());
2845+
2846+
uv_fs_t req;
2847+
FS_SYNC_TRACE_BEGIN(open)
2848+
uv_file file = uv_fs_open(nullptr, &req, input.out(), O_RDONLY, 0, nullptr);
2849+
FS_SYNC_TRACE_END(open);
2850+
2851+
if (req.result < 0) {
2852+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2853+
}
2854+
2855+
auto cleanup = OnScopeLeave([&req, &file]() {
2856+
FS_SYNC_TRACE_BEGIN(close);
2857+
CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
2858+
FS_SYNC_TRACE_END(close);
2859+
uv_fs_req_cleanup(&req);
2860+
});
2861+
2862+
char buffer[4];
2863+
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
2864+
int err = uv_fs_read(nullptr, &req, file, &buf, 1, 0, nullptr);
2865+
2866+
if (err < 0) {
2867+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2868+
}
2869+
2870+
// We do this by taking advantage of the fact that all Wasm files start with
2871+
// the header `0x00 0x61 0x73 0x6d`
2872+
if (buffer[0] == 0x00 && buffer[1] == 0x61 && buffer[2] == 0x73 &&
2873+
buffer[3] == 0x6d) {
2874+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_WASM);
2875+
}
2876+
2877+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2878+
}
2879+
28352880
static bool FileURLToPath(
28362881
Environment* env,
28372882
const ada::url_aggregator& file_url,
@@ -3244,6 +3289,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
32443289
Local<ObjectTemplate> target) {
32453290
Isolate* isolate = isolate_data->isolate();
32463291

3292+
SetMethod(isolate,
3293+
target,
3294+
"getFormatOfExtensionlessFile",
3295+
GetFormatOfExtensionlessFile);
32473296
SetMethod(isolate, target, "access", Access);
32483297
SetMethod(isolate, target, "close", Close);
32493298
SetMethod(isolate, target, "existsSync", ExistsSync);
@@ -3364,6 +3413,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
33643413
StatWatcher::RegisterExternalReferences(registry);
33653414
BindingData::RegisterExternalReferences(registry);
33663415

3416+
registry->Register(GetFormatOfExtensionlessFile);
33673417
registry->Register(Close);
33683418
registry->Register(ExistsSync);
33693419
registry->Register(Open);

typings/internalBinding/constants.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ export interface ConstantsBinding {
186186
COPYFILE_FICLONE: 2;
187187
UV_FS_COPYFILE_FICLONE_FORCE: 4;
188188
COPYFILE_FICLONE_FORCE: 4;
189+
EXTENSIONLESS_FORMAT_JAVASCRIPT: 0;
190+
EXTENSIONLESS_FORMAT_WASM: 1;
189191
};
190192
crypto: {
191193
OPENSSL_VERSION_NUMBER: 269488319;

typings/internalBinding/fs.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ConstantsBinding } from './constants';
2+
13
declare namespace InternalFSBinding {
24
class FSReqCallback<ResultType = unknown> {
35
constructor(bigint?: boolean);
@@ -218,6 +220,8 @@ declare namespace InternalFSBinding {
218220
function writeString(fd: number, value: string, pos: unknown, encoding: unknown, req: FSReqCallback<number>): void;
219221
function writeString(fd: number, value: string, pos: unknown, encoding: unknown, req: undefined, ctx: FSSyncContext): number;
220222
function writeString(fd: number, value: string, pos: unknown, encoding: unknown, usePromises: typeof kUsePromises): Promise<number>;
223+
224+
function getFormatOfExtensionlessFile(url: string): ConstantsBinding['fs'];
221225
}
222226

223227
export interface FsBinding {
@@ -269,4 +273,6 @@ export interface FsBinding {
269273
writeBuffer: typeof InternalFSBinding.writeBuffer;
270274
writeBuffers: typeof InternalFSBinding.writeBuffers;
271275
writeString: typeof InternalFSBinding.writeString;
276+
277+
getFormatOfExtensionlessFile: typeof InternalFSBinding.getFormatOfExtensionlessFile;
272278
}

0 commit comments

Comments
 (0)