Skip to content

Commit 20d92eb

Browse files
committed
esm: improve getFormatOfExtensionlessFile perf.
1 parent 7b624c3 commit 20d92eb

File tree

6 files changed

+70
-18
lines changed

6 files changed

+70
-18
lines changed

lib/internal/modules/esm/formats.js

Lines changed: 11 additions & 18 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2978,6 +2978,48 @@ static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
29782978
}
29792979
}
29802980

2981+
static void GetFormatOfExtensionlessFile(const FunctionCallbackInfo<Value>& args) {
2982+
CHECK_EQ(args.Length(), 1);
2983+
CHECK(args[0]->IsString());
2984+
2985+
Environment* env = Environment::GetCurrent(args);
2986+
node::Utf8Value input(args.GetIsolate(), args[0]);
2987+
2988+
THROW_IF_INSUFFICIENT_PERMISSIONS(
2989+
env, permission::PermissionScope::kFileSystemRead, input.ToStringView());
2990+
2991+
uv_fs_t req;
2992+
FS_SYNC_TRACE_BEGIN(open)
2993+
uv_file file = uv_fs_open(nullptr, &req, input.out(), O_RDONLY, 0, nullptr);
2994+
FS_SYNC_TRACE_END(open);
2995+
2996+
if (req.result < 0) {
2997+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2998+
}
2999+
3000+
auto cleanup = OnScopeLeave([&req, &file]() {
3001+
FS_SYNC_TRACE_BEGIN(close);
3002+
CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
3003+
FS_SYNC_TRACE_END(close);
3004+
uv_fs_req_cleanup(&req);
3005+
});
3006+
3007+
char buffer[4];
3008+
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
3009+
int err = uv_fs_read(nullptr, &req, file, &buf, 1, 0, nullptr);
3010+
3011+
if (err < 0) {
3012+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
3013+
}
3014+
3015+
// We do this by taking advantage of the fact that all Wasm files start with the header `0x00 0x61 0x73 0x6d`
3016+
if (buffer[0] == 0x00 && buffer[1] == 0x61 && buffer[2] == 0x73 && buffer[3] == 0x6d) {
3017+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_WASM);
3018+
}
3019+
3020+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
3021+
}
3022+
29813023
static bool FileURLToPath(
29823024
Environment* env,
29833025
const ada::url_aggregator& file_url,
@@ -3390,6 +3432,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
33903432
Local<ObjectTemplate> target) {
33913433
Isolate* isolate = isolate_data->isolate();
33923434

3435+
SetMethod(isolate, target, "getFormatOfExtensionlessFile", GetFormatOfExtensionlessFile);
33933436
SetMethod(isolate, target, "access", Access);
33943437
SetMethod(isolate, target, "accessSync", AccessSync);
33953438
SetMethod(isolate, target, "close", Close);
@@ -3518,6 +3561,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
35183561
StatWatcher::RegisterExternalReferences(registry);
35193562
BindingData::RegisterExternalReferences(registry);
35203563

3564+
registry->Register(GetFormatOfExtensionlessFile);
35213565
registry->Register(Close);
35223566
registry->Register(CloseSync);
35233567
registry->Register(ExistsSync);

typings/internalBinding/constants.d.ts

Lines changed: 2 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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)