Skip to content

Commit e4fc5ee

Browse files
committed
assert: refactor to use more primordials
1 parent 43ddf46 commit e4fc5ee

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

lib/assert.js

+30-21
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@ const {
3535
ObjectKeys,
3636
ObjectPrototypeIsPrototypeOf,
3737
ReflectApply,
38-
RegExpPrototypeTest,
38+
RegExpPrototypeExec,
3939
SafeMap,
4040
String,
4141
StringPrototypeCharCodeAt,
4242
StringPrototypeIncludes,
4343
StringPrototypeIndexOf,
44-
StringPrototypeReplace,
4544
StringPrototypeSlice,
4645
StringPrototypeSplit,
4746
StringPrototypeStartsWith,
47+
SafeRegExpPrototypeSymbolReplace,
48+
SafeRegExpPrototypeTest,
49+
makeSafeStringObject,
4850
} = primordials;
4951

5052
const { Buffer } = require('buffer');
@@ -283,9 +285,11 @@ function parseCode(code, offset) {
283285

284286
return [
285287
node.node.start,
286-
StringPrototypeReplace(StringPrototypeSlice(code,
287-
node.node.start, node.node.end),
288-
escapeSequencesRegExp, escapeFn),
288+
SafeRegExpPrototypeSymbolReplace(
289+
escapeSequencesRegExp,
290+
StringPrototypeSlice(code, node.node.start, node.node.end),
291+
escapeFn
292+
),
289293
];
290294
}
291295

@@ -357,17 +361,21 @@ function getErrMessage(message, fn) {
357361
}
358362
// Always normalize indentation, otherwise the message could look weird.
359363
if (StringPrototypeIncludes(message, '\n')) {
360-
if (EOL === '\r\n') {
361-
message = StringPrototypeReplace(message, /\r\n/g, '\n');
362-
}
363-
const frames = StringPrototypeSplit(message, '\n');
364-
message = ArrayPrototypeShift(frames);
365-
for (const frame of frames) {
366-
let pos = 0;
367-
while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
364+
const splitter = EOL === '\r\n' ? /\r\n/g : /\n/g;
365+
const splitterLength = EOL === '\r\n' ? 2 : 1;
366+
const str = message;
367+
let previousIndex = RegExpPrototypeExec(splitter, str).index;
368+
message = StringPrototypeSlice(message, 0, previousIndex);
369+
while (previousIndex != null) {
370+
const lineEndIndex = RegExpPrototypeExec(splitter, str)?.index;
371+
previousIndex += splitterLength;
372+
let pos = previousIndex;
373+
while (pos < previousIndex + column &&
374+
(str[pos] === ' ' || str[pos] === '\t')) {
368375
pos++;
369376
}
370-
message += `\n ${StringPrototypeSlice(frame, pos)}`;
377+
message += `\n ${StringPrototypeSlice(str, pos, lineEndIndex)}`;
378+
previousIndex = lineEndIndex;
371379
}
372380
}
373381
message = `The expression evaluated to a falsy value:\n\n ${message}\n`;
@@ -619,7 +627,7 @@ class Comparison {
619627
if (actual !== undefined &&
620628
typeof actual[key] === 'string' &&
621629
isRegExp(obj[key]) &&
622-
RegExpPrototypeTest(obj[key], actual[key])) {
630+
SafeRegExpPrototypeTest(obj[key], actual[key])) {
623631
this[key] = actual[key];
624632
} else {
625633
this[key] = obj[key];
@@ -665,7 +673,7 @@ function expectedException(actual, expected, message, fn) {
665673
// Handle regular expressions.
666674
if (isRegExp(expected)) {
667675
const str = String(actual);
668-
if (RegExpPrototypeTest(expected, str))
676+
if (SafeRegExpPrototypeTest(expected, str))
669677
return;
670678

671679
if (!message) {
@@ -700,7 +708,7 @@ function expectedException(actual, expected, message, fn) {
700708
for (const key of keys) {
701709
if (typeof actual[key] === 'string' &&
702710
isRegExp(expected[key]) &&
703-
RegExpPrototypeTest(expected[key], actual[key])) {
711+
SafeRegExpPrototypeTest(expected[key], actual[key])) {
704712
continue;
705713
}
706714
compareExceptionKey(actual, expected, key, message, keys, fn);
@@ -864,7 +872,7 @@ function hasMatchingError(actual, expected) {
864872
if (typeof expected !== 'function') {
865873
if (isRegExp(expected)) {
866874
const str = String(actual);
867-
return RegExpPrototypeTest(expected, str);
875+
return SafeRegExpPrototypeTest(expected, str);
868876
}
869877
throw new ERR_INVALID_ARG_TYPE(
870878
'expected', ['Function', 'RegExp'], expected
@@ -978,10 +986,11 @@ assert.ifError = function ifError(err) {
978986
// This will remove any duplicated frames from the error frames taken
979987
// from within `ifError` and add the original error frames to the newly
980988
// created ones.
981-
const tmp2 = StringPrototypeSplit(origStack, '\n');
989+
const LINE_RETURN = makeSafeStringObject('\n');
990+
const tmp2 = StringPrototypeSplit(origStack, LINE_RETURN);
982991
ArrayPrototypeShift(tmp2);
983992
// Filter all frames existing in err.stack.
984-
let tmp1 = StringPrototypeSplit(newErr.stack, '\n');
993+
let tmp1 = StringPrototypeSplit(newErr.stack, LINE_RETURN);
985994
for (const errFrame of tmp2) {
986995
// Find the first occurrence of the frame.
987996
const pos = ArrayPrototypeIndexOf(tmp1, errFrame);
@@ -1007,7 +1016,7 @@ function internalMatch(string, regexp, message, fn) {
10071016
}
10081017
const match = fn.name === 'match';
10091018
if (typeof string !== 'string' ||
1010-
RegExpPrototypeTest(regexp, string) !== match) {
1019+
SafeRegExpPrototypeTest(regexp, string) !== match) {
10111020
if (message instanceof Error) {
10121021
throw message;
10131022
}

lib/internal/assert/assertion_error.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
StringPrototypeRepeat,
1616
StringPrototypeSlice,
1717
StringPrototypeSplit,
18+
makeSafeStringObject,
1819
} = primordials;
1920

2021
const { inspect } = require('internal/util/inspect');
@@ -80,14 +81,16 @@ function inspectValue(val) {
8081
);
8182
}
8283

84+
const SAFE_LINE_RETURN = makeSafeStringObject('\n');
8385
function createErrDiff(actual, expected, operator) {
8486
let other = '';
8587
let res = '';
8688
let end = '';
8789
let skipped = false;
8890
const actualInspected = inspectValue(actual);
89-
const actualLines = StringPrototypeSplit(actualInspected, '\n');
90-
const expectedLines = StringPrototypeSplit(inspectValue(expected), '\n');
91+
const actualLines = StringPrototypeSplit(actualInspected, SAFE_LINE_RETURN);
92+
const expectedLines = StringPrototypeSplit(inspectValue(expected),
93+
SAFE_LINE_RETURN);
9194

9295
let i = 0;
9396
let indicator = '';
@@ -164,7 +167,7 @@ function createErrDiff(actual, expected, operator) {
164167
// E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() })
165168
if (maxLines === 0) {
166169
// We have to get the result again. The lines were all removed before.
167-
const actualLines = StringPrototypeSplit(actualInspected, '\n');
170+
const actualLines = StringPrototypeSplit(actualInspected, SAFE_LINE_RETURN);
168171

169172
// Only remove lines in case it makes sense to collapse those.
170173
// TODO: Accept env to always show the full error.
@@ -315,7 +318,7 @@ function createErrDiff(actual, expected, operator) {
315318
}
316319

317320
function addEllipsis(string) {
318-
const lines = StringPrototypeSplit(string, '\n', 11);
321+
const lines = StringPrototypeSplit(string, SAFE_LINE_RETURN, 11);
319322
if (lines.length > 10) {
320323
lines.length = 10;
321324
return `${ArrayPrototypeJoin(lines, '\n')}\n...`;
@@ -380,7 +383,8 @@ class AssertionError extends Error {
380383
// In case the objects are equal but the operator requires unequal, show
381384
// the first object and say A equals B
382385
let base = kReadableOperator[operator];
383-
const res = StringPrototypeSplit(inspectValue(actual), '\n');
386+
const res = StringPrototypeSplit(inspectValue(actual),
387+
SAFE_LINE_RETURN);
384388

385389
// In case "actual" is an object or a function, it should not be
386390
// reference equal.

0 commit comments

Comments
 (0)