Skip to content

Commit 05be31d

Browse files
authored
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 47b2883 commit 05be31d

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
@@ -2797,6 +2797,51 @@ static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
27972797
}
27982798
}
27992799

2800+
static void GetFormatOfExtensionlessFile(
2801+
const FunctionCallbackInfo<Value>& args) {
2802+
CHECK_EQ(args.Length(), 1);
2803+
CHECK(args[0]->IsString());
2804+
2805+
Environment* env = Environment::GetCurrent(args);
2806+
node::Utf8Value input(args.GetIsolate(), args[0]);
2807+
2808+
THROW_IF_INSUFFICIENT_PERMISSIONS(
2809+
env, permission::PermissionScope::kFileSystemRead, input.ToStringView());
2810+
2811+
uv_fs_t req;
2812+
FS_SYNC_TRACE_BEGIN(open)
2813+
uv_file file = uv_fs_open(nullptr, &req, input.out(), O_RDONLY, 0, nullptr);
2814+
FS_SYNC_TRACE_END(open);
2815+
2816+
if (req.result < 0) {
2817+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2818+
}
2819+
2820+
auto cleanup = OnScopeLeave([&req, &file]() {
2821+
FS_SYNC_TRACE_BEGIN(close);
2822+
CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
2823+
FS_SYNC_TRACE_END(close);
2824+
uv_fs_req_cleanup(&req);
2825+
});
2826+
2827+
char buffer[4];
2828+
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
2829+
int err = uv_fs_read(nullptr, &req, file, &buf, 1, 0, nullptr);
2830+
2831+
if (err < 0) {
2832+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2833+
}
2834+
2835+
// We do this by taking advantage of the fact that all Wasm files start with
2836+
// the header `0x00 0x61 0x73 0x6d`
2837+
if (buffer[0] == 0x00 && buffer[1] == 0x61 && buffer[2] == 0x73 &&
2838+
buffer[3] == 0x6d) {
2839+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_WASM);
2840+
}
2841+
2842+
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
2843+
}
2844+
28002845
static bool FileURLToPath(
28012846
Environment* env,
28022847
const ada::url_aggregator& file_url,
@@ -3209,6 +3254,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
32093254
Local<ObjectTemplate> target) {
32103255
Isolate* isolate = isolate_data->isolate();
32113256

3257+
SetMethod(isolate,
3258+
target,
3259+
"getFormatOfExtensionlessFile",
3260+
GetFormatOfExtensionlessFile);
32123261
SetMethod(isolate, target, "access", Access);
32133262
SetMethod(isolate, target, "close", Close);
32143263
SetMethod(isolate, target, "existsSync", ExistsSync);
@@ -3329,6 +3378,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
33293378
StatWatcher::RegisterExternalReferences(registry);
33303379
BindingData::RegisterExternalReferences(registry);
33313380

3381+
registry->Register(GetFormatOfExtensionlessFile);
33323382
registry->Register(Close);
33333383
registry->Register(ExistsSync);
33343384
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)