Skip to content

Commit

Permalink
Merge branch 'nodejs:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
cloorc authored Oct 18, 2024
2 parents 685eef7 + 7ae193d commit e5ddc77
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 86 deletions.
2 changes: 1 addition & 1 deletion lib/internal/process/execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function tryGetCwd() {

let evalIndex = 0;
function getEvalModuleUrl() {
return pathToFileURL(`${process.cwd()}/[eval${++evalIndex}]`).href;
return `${pathToFileURL(process.cwd())}/[eval${++evalIndex}]`;
}

/**
Expand Down
94 changes: 57 additions & 37 deletions lib/internal/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -1498,44 +1498,75 @@ function fileURLToPath(path, options = kEmptyObject) {
return (windows ?? isWindows) ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
}

// The following characters are percent-encoded when converting from file path
// to URL:
// - %: The percent character is the only character not encoded by the
// `pathname` setter.
// - \: Backslash is encoded on non-windows platforms since it's a valid
// character but the `pathname` setters replaces it by a forward slash.
// - LF: The newline character is stripped out by the `pathname` setter.
// (See whatwg/url#419)
// - CR: The carriage return character is also stripped out by the `pathname`
// setter.
// - TAB: The tab character is also stripped out by the `pathname` setter.
// RFC1738 defines the following chars as "unsafe" for URLs
// @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues
const percentRegEx = /%/g;
const backslashRegEx = /\\/g;
const newlineRegEx = /\n/g;
const carriageReturnRegEx = /\r/g;
const tabRegEx = /\t/g;
const questionRegex = /\?/g;
const quoteRegEx = /"/g;
const hashRegex = /#/g;
const spaceRegEx = / /g;
const questionMarkRegex = /\?/g;
const openSquareBracketRegEx = /\[/g;
const backslashRegEx = /\\/g;
const closeSquareBracketRegEx = /]/g;
const caretRegEx = /\^/g;
const verticalBarRegEx = /\|/g;
const tildeRegEx = /~/g;

function encodePathChars(filepath, options = kEmptyObject) {
const windows = options?.windows;
if (StringPrototypeIndexOf(filepath, '%') !== -1)
if (StringPrototypeIncludes(filepath, '%')) {
filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25');
// In posix, backslash is a valid character in paths:
if (!(windows ?? isWindows) && StringPrototypeIndexOf(filepath, '\\') !== -1)
filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C');
if (StringPrototypeIndexOf(filepath, '\n') !== -1)
}

if (StringPrototypeIncludes(filepath, '\t')) {
filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09');
}
if (StringPrototypeIncludes(filepath, '\n')) {
filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A');
if (StringPrototypeIndexOf(filepath, '\r') !== -1)
}
if (StringPrototypeIncludes(filepath, '\r')) {
filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D');
if (StringPrototypeIndexOf(filepath, '\t') !== -1)
filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09');
}
if (StringPrototypeIncludes(filepath, ' ')) {
filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20');
}
if (StringPrototypeIncludes(filepath, '"')) {
filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22');
}
if (StringPrototypeIncludes(filepath, '#')) {
filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23');
}
if (StringPrototypeIncludes(filepath, '?')) {
filepath = RegExpPrototypeSymbolReplace(questionMarkRegex, filepath, '%3F');
}
if (StringPrototypeIncludes(filepath, '[')) {
filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B');
}
// Back-slashes must be special-cased on Windows, where they are treated as path separator.
if (!options.windows && StringPrototypeIncludes(filepath, '\\')) {
filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C');
}
if (StringPrototypeIncludes(filepath, ']')) {
filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D');
}
if (StringPrototypeIncludes(filepath, '^')) {
filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E');
}
if (StringPrototypeIncludes(filepath, '|')) {
filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C');
}
if (StringPrototypeIncludes(filepath, '~')) {
filepath = RegExpPrototypeSymbolReplace(tildeRegEx, filepath, '%7E');
}

return filepath;
}

function pathToFileURL(filepath, options = kEmptyObject) {
const windows = options?.windows;
if ((windows ?? isWindows) && StringPrototypeStartsWith(filepath, '\\\\')) {
const windows = options?.windows ?? isWindows;
if (windows && StringPrototypeStartsWith(filepath, '\\\\')) {
const outURL = new URL('file://');
// UNC path format: \\server\share\resource
// Handle extended UNC path and standard UNC path
Expand Down Expand Up @@ -1566,20 +1597,9 @@ function pathToFileURL(filepath, options = kEmptyObject) {
);
return outURL;
}
let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath);

// Call encodePathChars first to avoid encoding % again for ? and #.
resolved = encodePathChars(resolved, { windows });
const resolved = windows ? path.win32.resolve(filepath) : path.posix.resolve(filepath);

// Question and hash character should be included in pathname.
// Therefore, encoding is required to eliminate parsing them in different states.
// This is done as an optimization to not creating a URL instance and
// later triggering pathname setter, which impacts performance
if (StringPrototypeIndexOf(resolved, '?') !== -1)
resolved = RegExpPrototypeSymbolReplace(questionRegex, resolved, '%3F');
if (StringPrototypeIndexOf(resolved, '#') !== -1)
resolved = RegExpPrototypeSymbolReplace(hashRegex, resolved, '%23');
return new URL(`file://${resolved}`);
return new URL(`file://${encodePathChars(resolved, { windows })}`);
}

function toPathIfFileURL(fileURLOrPath) {
Expand Down
115 changes: 75 additions & 40 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3127,6 +3127,55 @@ static void GetFormatOfExtensionlessFile(
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
}

#ifdef _WIN32
std::wstring ConvertToWideString(const std::string& str) {
int size_needed = MultiByteToWideChar(
CP_UTF8, 0, &str[0], static_cast<int>(str.size()), nullptr, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8,
0,
&str[0],
static_cast<int>(str.size()),
&wstrTo[0],
size_needed);
return wstrTo;
}

#define BufferValueToPath(str) \
std::filesystem::path(ConvertToWideString(str.ToString()))

std::string ConvertWideToUTF8(const std::wstring& wstr) {
if (wstr.empty()) return std::string();

int size_needed = WideCharToMultiByte(CP_UTF8,
0,
&wstr[0],
static_cast<int>(wstr.size()),
nullptr,
0,
nullptr,
nullptr);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8,
0,
&wstr[0],
static_cast<int>(wstr.size()),
&strTo[0],
size_needed,
nullptr,
nullptr);
return strTo;
}

#define PathToString(path) ConvertWideToUTF8(path.wstring());

#else // _WIN32

#define BufferValueToPath(str) std::filesystem::path(str.ToStringView());
#define PathToString(path) path.native();

#endif // _WIN32

static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Expand All @@ -3139,15 +3188,15 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, src.ToStringView());

auto src_path = std::filesystem::path(src.ToU8StringView());
auto src_path = BufferValueToPath(src);

BufferValue dest(isolate, args[1]);
CHECK_NOT_NULL(*dest);
ToNamespacedPath(env, &dest);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());

auto dest_path = std::filesystem::path(dest.ToU8StringView());
auto dest_path = BufferValueToPath(dest);
bool dereference = args[2]->IsTrue();
bool recursive = args[3]->IsTrue();

Expand Down Expand Up @@ -3176,47 +3225,41 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
(src_status.type() == std::filesystem::file_type::directory) ||
(dereference && src_status.type() == std::filesystem::file_type::symlink);

auto src_path_str = PathToString(src_path);
auto dest_path_str = PathToString(dest_path);

if (!error_code) {
// Check if src and dest are identical.
if (std::filesystem::equivalent(src_path, dest_path)) {
std::u8string message =
u8"src and dest cannot be the same " + dest_path.u8string();
return THROW_ERR_FS_CP_EINVAL(
env, reinterpret_cast<const char*>(message.c_str()));
std::string message = "src and dest cannot be the same %s";
return THROW_ERR_FS_CP_EINVAL(env, message.c_str(), dest_path_str);
}

const bool dest_is_dir =
dest_status.type() == std::filesystem::file_type::directory;

if (src_is_dir && !dest_is_dir) {
std::u8string message = u8"Cannot overwrite non-directory " +
src_path.u8string() + u8" with directory " +
dest_path.u8string();
std::string message =
"Cannot overwrite non-directory %s with directory %s";
return THROW_ERR_FS_CP_DIR_TO_NON_DIR(
env, reinterpret_cast<const char*>(message.c_str()));
env, message.c_str(), src_path_str, dest_path_str);
}

if (!src_is_dir && dest_is_dir) {
std::u8string message = u8"Cannot overwrite directory " +
dest_path.u8string() + u8" with non-directory " +
src_path.u8string();
std::string message =
"Cannot overwrite directory %s with non-directory %s";
return THROW_ERR_FS_CP_NON_DIR_TO_DIR(
env, reinterpret_cast<const char*>(message.c_str()));
env, message.c_str(), dest_path_str, src_path_str);
}
}

std::u8string dest_path_str = dest_path.u8string();
std::u8string src_path_str = src_path.u8string();
if (!src_path_str.ends_with(std::filesystem::path::preferred_separator)) {
src_path_str += std::filesystem::path::preferred_separator;
}
// Check if dest_path is a subdirectory of src_path.
if (src_is_dir && dest_path_str.starts_with(src_path_str)) {
std::u8string message = u8"Cannot copy " + src_path.u8string() +
u8" to a subdirectory of self " +
dest_path.u8string();
std::string message = "Cannot copy %s to a subdirectory of self %s";
return THROW_ERR_FS_CP_EINVAL(
env, reinterpret_cast<const char*>(message.c_str()));
env, message.c_str(), src_path_str, dest_path_str);
}

auto dest_parent = dest_path.parent_path();
Expand All @@ -3227,11 +3270,9 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
dest_parent.parent_path() != dest_parent) {
if (std::filesystem::equivalent(
src_path, dest_path.parent_path(), error_code)) {
std::u8string message = u8"Cannot copy " + src_path.u8string() +
u8" to a subdirectory of self " +
dest_path.u8string();
std::string message = "Cannot copy %s to a subdirectory of self %s";
return THROW_ERR_FS_CP_EINVAL(
env, reinterpret_cast<const char*>(message.c_str()));
env, message.c_str(), src_path_str, dest_path_str);
}

// If equivalent fails, it's highly likely that dest_parent does not exist
Expand All @@ -3243,29 +3284,23 @@ static void CpSyncCheckPaths(const FunctionCallbackInfo<Value>& args) {
}

if (src_is_dir && !recursive) {
std::u8string message =
u8"Recursive option not enabled, cannot copy a directory: " +
src_path.u8string();
return THROW_ERR_FS_EISDIR(env,
reinterpret_cast<const char*>(message.c_str()));
std::string message =
"Recursive option not enabled, cannot copy a directory: %s";
return THROW_ERR_FS_EISDIR(env, message.c_str(), src_path_str);
}

switch (src_status.type()) {
case std::filesystem::file_type::socket: {
std::u8string message = u8"Cannot copy a socket file: " + dest_path_str;
return THROW_ERR_FS_CP_SOCKET(
env, reinterpret_cast<const char*>(message.c_str()));
std::string message = "Cannot copy a socket file: %s";
return THROW_ERR_FS_CP_SOCKET(env, message.c_str(), dest_path_str);
}
case std::filesystem::file_type::fifo: {
std::u8string message = u8"Cannot copy a FIFO pipe: " + dest_path_str;
return THROW_ERR_FS_CP_FIFO_PIPE(
env, reinterpret_cast<const char*>(message.c_str()));
std::string message = "Cannot copy a FIFO pipe: %s";
return THROW_ERR_FS_CP_FIFO_PIPE(env, message.c_str(), dest_path_str);
}
case std::filesystem::file_type::unknown: {
std::u8string message =
u8"Cannot copy an unknown file type: " + dest_path_str;
return THROW_ERR_FS_CP_UNKNOWN(
env, reinterpret_cast<const char*>(message.c_str()));
std::string message = "Cannot copy an unknown file type: %s";
return THROW_ERR_FS_CP_UNKNOWN(env, message.c_str(), dest_path_str);
}
default:
break;
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/wpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Last update:

- common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common
- compression: https://github.com/web-platform-tests/wpt/tree/da8d6860b2/compression
- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console
- console: https://github.com/web-platform-tests/wpt/tree/e48251b778/console
- dom/abort: https://github.com/web-platform-tests/wpt/tree/0143fe244b/dom/abort
- dom/events: https://github.com/web-platform-tests/wpt/tree/0a811c5161/dom/events
- encoding: https://github.com/web-platform-tests/wpt/tree/1ac8deee08/encoding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,25 @@
console.count();
console.countReset();
console.count();
console.countReset();

console.count(undefined);
console.countReset(undefined);
console.count(undefined);
console.countReset(undefined);

console.count("default");
console.countReset("default");
console.count("default");
console.countReset("default");

console.count({toString() {return "default"}});
console.countReset({toString() {return "default"}});
console.count({toString() {return "default"}});
console.countReset({toString() {return "default"}});

console.count("a label");
console.countReset();
console.countReset("a label");
console.count("a label");

console.countReset("b"); // should produce a warning
Expand Down
8 changes: 8 additions & 0 deletions test/fixtures/wpt/console/console-log-large-array.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// META: global=window,dedicatedworker,shadowrealm
"use strict";
// https://console.spec.whatwg.org/

test(() => {
console.log(new Array(10000000).fill("x"));
console.log(new Uint8Array(10000000));
}, "Logging large arrays works");
10 changes: 10 additions & 0 deletions test/fixtures/wpt/console/console-log-symbol.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// META: global=window,dedicatedworker,shadowrealm
"use strict";
// https://console.spec.whatwg.org/

test(() => {
console.log(Symbol());
console.log(Symbol("abc"));
console.log(Symbol.for("def"));
console.log(Symbol.isConcatSpreadable);
}, "Logging a symbol doesn't throw");
2 changes: 1 addition & 1 deletion test/fixtures/wpt/versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"path": "compression"
},
"console": {
"commit": "767ae354642bee1e4d90b28df4480475b9260e14",
"commit": "e48251b77834f9689e9df3f49b93b3387dee72d6",
"path": "console"
},
"dom/abort": {
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-fs-cp.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tmpdir.refresh();

let dirc = 0;
function nextdir(dirname) {
return tmpdir.resolve(dirname || `copy_${++dirc}`);
return tmpdir.resolve(dirname || `copy_%${++dirc}`);
}

// Synchronous implementation of copy.
Expand Down
Loading

0 comments on commit e5ddc77

Please sign in to comment.