Skip to content
Merged
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,6 @@ For information about the governance of the Node.js project, see
**Chengzhong Wu** <<[email protected]>> (he/him)
* [lemire](https://github.com/lemire) -
**Daniel Lemire** <<[email protected]>>
* [Linkgoron](https://github.com/Linkgoron) -
**Nitzan Uziely** <<[email protected]>>
* [LiviaMedeiros](https://github.com/LiviaMedeiros) -
**LiviaMedeiros** <<[email protected]>>
* [ljharb](https://github.com/ljharb) -
Expand Down Expand Up @@ -599,6 +597,8 @@ For information about the governance of the Node.js project, see
**Lance Ball** <<[email protected]>> (he/him)
* [Leko](https://github.com/Leko) -
**Shingo Inoue** <<[email protected]>> (he/him)
* [Linkgoron](https://github.com/Linkgoron) -
**Nitzan Uziely** <<[email protected]>>
* [lucamaraschi](https://github.com/lucamaraschi) -
**Luca Maraschi** <<[email protected]>> (he/him)
* [lundibundi](https://github.com/lundibundi) -
Expand Down
57 changes: 52 additions & 5 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
ArrayIsArray,
ArrayPrototypePop,
ArrayPrototypePush,
ArrayPrototypeReduce,
Error,
ErrorCaptureStackTrace,
FunctionPrototypeBind,
Expand All @@ -36,6 +37,8 @@ const {
ObjectSetPrototypeOf,
ObjectValues,
ReflectApply,
RegExp,
RegExpPrototypeSymbolReplace,
StringPrototypeToWellFormed,
} = primordials;

Expand Down Expand Up @@ -137,8 +140,7 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
// If the format is not an array, convert it to an array
const formatArray = ArrayIsArray(format) ? format : [format];

let left = '';
let right = '';
const codes = [];
for (const key of formatArray) {
if (key === 'none') continue;
const formatCodes = inspect.colors[key];
Expand All @@ -147,11 +149,56 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
validateOneOf(key, 'format', ObjectKeys(inspect.colors));
}
if (skipColorize) continue;
left += escapeStyleCode(formatCodes[0]);
right = `${escapeStyleCode(formatCodes[1])}${right}`;
ArrayPrototypePush(codes, formatCodes);
}

return skipColorize ? text : `${left}${text}${right}`;
if (skipColorize) {
return text;
}

// Build opening codes
let openCodes = '';
for (let i = 0; i < codes.length; i++) {
openCodes += escapeStyleCode(codes[i][0]);
}

// Process the text to handle nested styles
let processedText;
if (codes.length > 0) {
processedText = ArrayPrototypeReduce(
codes,
(text, code) => RegExpPrototypeSymbolReplace(
// Find the reset code
new RegExp(`\\u001b\\[${code[1]}m`, 'g'),
text,
(match, offset) => {
// Check if there's more content after this reset
if (offset + match.length < text.length) {
if (
code[0] === inspect.colors.dim[0] ||
code[0] === inspect.colors.bold[0]
) {
// Dim and bold are not mutually exclusive, so we need to reapply
return `${match}${escapeStyleCode(code[0])}`;
}
return `${escapeStyleCode(code[0])}`;
}
return match;
},
),
text,
);
} else {
processedText = text;
}

// Build closing codes in reverse order
let closeCodes = '';
for (let i = codes.length - 1; i >= 0; i--) {
closeCodes += escapeStyleCode(codes[i][1]);
}

return `${openCodes}${processedText}${closeCodes}`;
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/node_debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ using v8::Number;
using v8::Object;
using v8::Value;

thread_local std::unordered_map<std::string_view, int> v8_fast_api_call_counts;
thread_local std::unordered_map<FastStringKey, int, FastStringKey::Hash>
v8_fast_api_call_counts;

void TrackV8FastApiCall(std::string_view key) {
void TrackV8FastApiCall(FastStringKey key) {
v8_fast_api_call_counts[key]++;
}

int GetV8FastApiCallCount(std::string_view key) {
int GetV8FastApiCallCount(FastStringKey key) {
return v8_fast_api_call_counts[key];
}

Expand All @@ -40,7 +41,8 @@ void GetV8FastApiCallCount(const FunctionCallbackInfo<Value>& args) {
return;
}
Utf8Value utf8_key(env->isolate(), args[0]);
args.GetReturnValue().Set(GetV8FastApiCallCount(utf8_key.ToStringView()));
args.GetReturnValue().Set(GetV8FastApiCallCount(
FastStringKey::AllowDynamic(utf8_key.ToStringView())));
}

void SlowIsEven(const FunctionCallbackInfo<Value>& args) {
Expand Down
9 changes: 5 additions & 4 deletions src/node_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#ifdef DEBUG
#include <string_view>
#include "util.h"
#endif // DEBUG

namespace node {
namespace debug {

#ifdef DEBUG
void TrackV8FastApiCall(std::string_view key);
int GetV8FastApiCallCount(std::string_view key);
void TrackV8FastApiCall(FastStringKey key);
int GetV8FastApiCallCount(FastStringKey key);

#define TRACK_V8_FAST_API_CALL(key) node::debug::TrackV8FastApiCall(key)
#define TRACK_V8_FAST_API_CALL(key) \
node::debug::TrackV8FastApiCall(FastStringKey(key))
#else // !DEBUG
#define TRACK_V8_FAST_API_CALL(key)
#endif // DEBUG
Expand Down
9 changes: 8 additions & 1 deletion src/util-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,14 @@ constexpr bool FastStringKey::operator==(const FastStringKey& other) const {
return name_ == other.name_;
}

constexpr FastStringKey::FastStringKey(std::string_view name)
consteval FastStringKey::FastStringKey(std::string_view name)
: FastStringKey(name, 0) {}

constexpr FastStringKey FastStringKey::AllowDynamic(std::string_view name) {
return FastStringKey(name, 0);
}

constexpr FastStringKey::FastStringKey(std::string_view name, int dummy)
: name_(name), cached_hash_(HashImpl(name)) {}

constexpr std::string_view FastStringKey::as_string_view() const {
Expand Down
8 changes: 7 additions & 1 deletion src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,11 @@ class PersistentToLocal {
// computations.
class FastStringKey {
public:
constexpr explicit FastStringKey(std::string_view name);
// consteval ensures that the argument is a compile-time constant.
consteval explicit FastStringKey(std::string_view name);
// passing something that is not a compile-time constant needs explicit
// opt-in via this helper, as it defeats the purpose of FastStringKey.
static constexpr FastStringKey AllowDynamic(std::string_view name);

struct Hash {
constexpr size_t operator()(const FastStringKey& key) const;
Expand All @@ -832,6 +836,8 @@ class FastStringKey {
constexpr std::string_view as_string_view() const;

private:
constexpr explicit FastStringKey(std::string_view name, int dummy);

static constexpr size_t HashImpl(std::string_view str);

const std::string_view name_;
Expand Down
4 changes: 2 additions & 2 deletions test/addons/report-fatalerror/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const ARGS = [
tmpdir.refresh();
const args = ['--report-on-fatalerror', ...ARGS];
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
assert.notStrictEqual(child.status, 0, 'Process exited unexpectedly');
assert.notStrictEqual(child.status, 0);

const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
Expand All @@ -46,7 +46,7 @@ const ARGS = [
// Verify that --report-on-fatalerror is respected when not set.
const args = ARGS;
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
assert.notStrictEqual(child.status, 0, 'Process exited unexpectedly');
assert.notStrictEqual(child.status, 0);
const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 0);
}
12 changes: 12 additions & 0 deletions test/eslint.config_partial.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export default [
selector: "CallExpression:matches([callee.name='deepStrictEqual'], [callee.property.name='deepStrictEqual'])[arguments.2.type='Literal']",
message: 'Do not use a literal for the third argument of assert.deepStrictEqual()',
},
{
selector: "CallExpression:matches([callee.name='notDeepStrictEqual'], [callee.property.name='deepStrictEqual'])[arguments.2.type='Literal']",
message: 'Do not use a literal for the third argument of assert.notDeepStrictEqual()',
},
{
selector: "CallExpression:matches([callee.name='doesNotThrow'], [callee.property.name='doesNotThrow'])",
message: 'Do not use `assert.doesNotThrow()`. Write the code without the wrapper and add a comment instead.',
Expand All @@ -48,10 +52,18 @@ export default [
selector: "CallExpression:matches([callee.name='rejects'], [callee.property.name='rejects'])[arguments.length<2]",
message: '`assert.rejects()` must be invoked with at least two arguments.',
},
{
selector: "CallExpression[callee.property.name='notStrictEqual'][arguments.2.type='Literal']",
message: 'Do not use a literal for the third argument of assert.notStrictEqual()',
},
{
selector: "CallExpression[callee.property.name='strictEqual'][arguments.2.type='Literal']",
message: 'Do not use a literal for the third argument of assert.strictEqual()',
},
{
selector: "CallExpression[callee.name='assert'][arguments.1.type='Literal']:not([arguments.1.raw=/['\"`].*/])",
message: 'Do not use a non-string literal for the second argument of assert()',
},
{
selector: "CallExpression:matches([callee.name='throws'], [callee.property.name='throws'])[arguments.1.type='Literal']:not([arguments.1.regex])",
message: 'Use an object as second argument of `assert.throws()`.',
Expand Down
6 changes: 3 additions & 3 deletions test/js-native-api/test_object/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ assert.strictEqual(newObject.test_string, 'test string');
test_object.Wrap(wrapper);

assert(test_object.Unwrap(wrapper));
assert(wrapper.protoA);
assert.strictEqual(wrapper.protoA, true);
}

{
Expand All @@ -155,8 +155,8 @@ assert.strictEqual(newObject.test_string, 'test string');
Object.setPrototypeOf(wrapper, protoB);

assert(test_object.Unwrap(wrapper));
assert(wrapper.protoA, true);
assert(wrapper.protoB, true);
assert.strictEqual(wrapper.protoA, true);
assert.strictEqual(wrapper.protoB, true);
}

{
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-buffer-write-fast.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ testFastUtf8Write();

if (common.isDebug) {
const { getV8FastApiCallCount } = internalBinding('debug');
assert(getV8FastApiCallCount('buffer.writeString'), 4);
assert.strictEqual(getV8FastApiCallCount('buffer.writeString'), 4);
}
2 changes: 1 addition & 1 deletion test/parallel/test-net-listen-exclusive-random-ports.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ if (cluster.isPrimary) {
worker2.on('message', function(port2) {
assert.strictEqual(port2, port2 | 0,
`second worker could not listen on port ${port2}`);
assert.notStrictEqual(port1, port2, 'ports should not be equal');
assert.notStrictEqual(port1, port2);
worker1.kill();
worker2.kill();
});
Expand Down
6 changes: 3 additions & 3 deletions test/parallel/test-stream-toArray.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const assert = require('assert');
const ac = new AbortController();
let stream;
assert.rejects(async () => {
stream = Readable.from([1, 2, 3]).map(async (x) => {
stream = Readable.from([1, 2, 3, 4]).map(async (x) => {
if (x === 3) {
await new Promise(() => {}); // Explicitly do not pass signal here
}
Expand All @@ -69,8 +69,8 @@ const assert = require('assert');
}, {
name: 'AbortError',
}).then(common.mustCall(() => {
// Only stops toArray, does not destroy the stream
assert(stream.destroyed, false);
// Stops toArray *and* destroys the stream
assert.strictEqual(stream.destroyed, true);
}));
ac.abort();
}
Expand Down
77 changes: 77 additions & 0 deletions test/parallel/test-util-styletext.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,83 @@ assert.strictEqual(
'\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m',
);

assert.strictEqual(
util.styleText('red',
'A' + util.styleText('blue', 'B', { validateStream: false }) + 'C',
{ validateStream: false }),
'\u001b[31mA\u001b[34mB\u001b[31mC\u001b[39m'
);

assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red',
{ validateStream: false }
),
'\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[39m'
);

assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('red', 'red', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }),
{ validateStream: false }
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[31mred\x1b[31mred\x1b[34mblue\x1b[39m\x1b[39m'
);

assert.strictEqual(
util.styleText('red',
'A' + util.styleText(['bgRed', 'blue'], 'B', { validateStream: false }) +
'C', { validateStream: false }),
'\x1B[31mA\x1B[41m\x1B[34mB\x1B[31m\x1B[49mC\x1B[39m'
);

assert.strictEqual(
util.styleText('dim',
'dim' +
util.styleText('bold', 'bold', { validateStream: false }) +
'dim', { validateStream: false }),
'\x1B[2mdim\x1B[1mbold\x1B[22m\x1B[2mdim\x1B[22m'
);

assert.strictEqual(
util.styleText('blue',
'blue' +
util.styleText('red',
'red' +
util.styleText('green', 'green', { validateStream: false }) +
'red', { validateStream: false }) +
'blue', { validateStream: false }),
'\x1B[34mblue\x1B[31mred\x1B[32mgreen\x1B[31mred\x1B[34mblue\x1B[39m'
);

assert.strictEqual(
util.styleText(
'red',
'red' +
util.styleText(
'blue',
'blue' + util.styleText('red', 'red', {
validateStream: false,
}) + 'blue',
{
validateStream: false,
}
) + 'red', {
validateStream: false,
}
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[39m'
);

assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
util.styleText(
Expand Down
2 changes: 1 addition & 1 deletion test/report/test-report-fatalerror-oomerror-compact.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const REPORT_FIELDS = [
tmpdir.refresh();
const args = ['--report-on-fatalerror', '--report-compact', ...ARGS];
const child = spawnSync(process.execPath, args, { cwd: tmpdir.path });
assert.notStrictEqual(child.status, 0, 'Process exited unexpectedly');
assert.notStrictEqual(child.status, 0);

const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
Expand Down
2 changes: 1 addition & 1 deletion test/report/test-report-fatalerror-oomerror-directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const REPORT_FIELDS = [
const dir = '--report-directory=' + tmpdir.path;
const args = ['--report-on-fatalerror', dir, ...ARGS];
const child = spawnSync(process.execPath, args, { });
assert.notStrictEqual(child.status, 0, 'Process exited unexpectedly');
assert.notStrictEqual(child.status, 0);

const reports = helper.findReports(child.pid, tmpdir.path);
assert.strictEqual(reports.length, 1);
Expand Down
Loading
Loading