@@ -24,31 +24,21 @@ const {
24
24
ArrayPrototypeIndexOf,
25
25
ArrayPrototypeJoin,
26
26
ArrayPrototypePush,
27
- ArrayPrototypeShift,
28
27
ArrayPrototypeSlice,
29
28
Error,
30
- ErrorCaptureStackTrace,
31
- FunctionPrototypeBind,
32
29
NumberIsNaN,
33
30
ObjectAssign,
34
31
ObjectIs,
35
32
ObjectKeys,
36
33
ObjectPrototypeIsPrototypeOf,
37
34
ReflectApply,
38
35
RegExpPrototypeExec,
39
- RegExpPrototypeSymbolReplace,
40
- SafeMap,
41
36
String,
42
- StringPrototypeCharCodeAt,
43
- StringPrototypeIncludes,
44
37
StringPrototypeIndexOf,
45
- StringPrototypeReplace,
46
38
StringPrototypeSlice,
47
39
StringPrototypeSplit,
48
- StringPrototypeStartsWith,
49
40
} = primordials ;
50
41
51
- const { Buffer } = require ( 'buffer' ) ;
52
42
const {
53
43
codes : {
54
44
ERR_AMBIGUOUS_ARGUMENT ,
@@ -57,53 +47,27 @@ const {
57
47
ERR_INVALID_RETURN_VALUE ,
58
48
ERR_MISSING_ARGS ,
59
49
} ,
60
- isErrorStackTraceLimitWritable,
61
- overrideStackTrace,
62
50
} = require ( 'internal/errors' ) ;
63
51
const AssertionError = require ( 'internal/assert/assertion_error' ) ;
64
- const { openSync, closeSync, readSync } = require ( 'fs' ) ;
65
52
const { inspect } = require ( 'internal/util/inspect' ) ;
66
53
const { isPromise, isRegExp } = require ( 'internal/util/types' ) ;
67
- const { EOL } = require ( 'internal/constants' ) ;
68
- const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
69
54
const { isError, deprecate } = require ( 'internal/util' ) ;
55
+ const { innerOk } = require ( 'internal/assert/utils' ) ;
70
56
71
- const errorCache = new SafeMap ( ) ;
72
57
const CallTracker = require ( 'internal/assert/calltracker' ) ;
73
58
const {
74
59
validateFunction,
75
60
} = require ( 'internal/validators' ) ;
76
- const { fileURLToPath } = require ( 'internal/url' ) ;
77
61
78
62
let isDeepEqual ;
79
63
let isDeepStrictEqual ;
80
- let parseExpressionAt ;
81
- let findNodeAround ;
82
- let tokenizer ;
83
- let decoder ;
84
64
85
65
function lazyLoadComparison ( ) {
86
66
const comparison = require ( 'internal/util/comparisons' ) ;
87
67
isDeepEqual = comparison . isDeepEqual ;
88
68
isDeepStrictEqual = comparison . isDeepStrictEqual ;
89
69
}
90
70
91
- // Escape control characters but not \n and \t to keep the line breaks and
92
- // indentation intact.
93
- // eslint-disable-next-line no-control-regex
94
- const escapeSequencesRegExp = / [ \x00 - \x08 \x0b \x0c \x0e - \x1f ] / g;
95
- const meta = [
96
- '\\u0000' , '\\u0001' , '\\u0002' , '\\u0003' , '\\u0004' ,
97
- '\\u0005' , '\\u0006' , '\\u0007' , '\\b' , '' ,
98
- '' , '\\u000b' , '\\f' , '' , '\\u000e' ,
99
- '\\u000f' , '\\u0010' , '\\u0011' , '\\u0012' , '\\u0013' ,
100
- '\\u0014' , '\\u0015' , '\\u0016' , '\\u0017' , '\\u0018' ,
101
- '\\u0019' , '\\u001a' , '\\u001b' , '\\u001c' , '\\u001d' ,
102
- '\\u001e' , '\\u001f' ,
103
- ] ;
104
-
105
- const escapeFn = ( str ) => meta [ StringPrototypeCharCodeAt ( str , 0 ) ] ;
106
-
107
71
let warned = false ;
108
72
109
73
// The assert module provides functions that throw
@@ -178,237 +142,6 @@ assert.fail = fail;
178
142
// The AssertionError is defined in internal/error.
179
143
assert . AssertionError = AssertionError ;
180
144
181
- function findColumn ( fd , column , code ) {
182
- if ( code . length > column + 100 ) {
183
- try {
184
- return parseCode ( code , column ) ;
185
- } catch {
186
- // End recursion in case no code could be parsed. The expression should
187
- // have been found after 2500 characters, so stop trying.
188
- if ( code . length - column > 2500 ) {
189
- // eslint-disable-next-line no-throw-literal
190
- throw null ;
191
- }
192
- }
193
- }
194
- // Read up to 2500 bytes more than necessary in columns. That way we address
195
- // multi byte characters and read enough data to parse the code.
196
- const bytesToRead = column - code . length + 2500 ;
197
- const buffer = Buffer . allocUnsafe ( bytesToRead ) ;
198
- const bytesRead = readSync ( fd , buffer , 0 , bytesToRead ) ;
199
- code += decoder . write ( buffer . slice ( 0 , bytesRead ) ) ;
200
- // EOF: fast path.
201
- if ( bytesRead < bytesToRead ) {
202
- return parseCode ( code , column ) ;
203
- }
204
- // Read potentially missing code.
205
- return findColumn ( fd , column , code ) ;
206
- }
207
-
208
- function getCode ( fd , line , column ) {
209
- let bytesRead = 0 ;
210
- if ( line === 0 ) {
211
- // Special handle line number one. This is more efficient and simplifies the
212
- // rest of the algorithm. Read more than the regular column number in bytes
213
- // to prevent multiple reads in case multi byte characters are used.
214
- return findColumn ( fd , column , '' ) ;
215
- }
216
- let lines = 0 ;
217
- // Prevent blocking the event loop by limiting the maximum amount of
218
- // data that may be read.
219
- let maxReads = 32 ; // bytesPerRead * maxReads = 512 KiB
220
- const bytesPerRead = 16384 ;
221
- // Use a single buffer up front that is reused until the call site is found.
222
- let buffer = Buffer . allocUnsafe ( bytesPerRead ) ;
223
- while ( maxReads -- !== 0 ) {
224
- // Only allocate a new buffer in case the needed line is found. All data
225
- // before that can be discarded.
226
- buffer = lines < line ? buffer : Buffer . allocUnsafe ( bytesPerRead ) ;
227
- bytesRead = readSync ( fd , buffer , 0 , bytesPerRead ) ;
228
- // Read the buffer until the required code line is found.
229
- for ( let i = 0 ; i < bytesRead ; i ++ ) {
230
- if ( buffer [ i ] === 10 && ++ lines === line ) {
231
- // If the end of file is reached, directly parse the code and return.
232
- if ( bytesRead < bytesPerRead ) {
233
- return parseCode ( buffer . toString ( 'utf8' , i + 1 , bytesRead ) , column ) ;
234
- }
235
- // Check if the read code is sufficient or read more until the whole
236
- // expression is read. Make sure multi byte characters are preserved
237
- // properly by using the decoder.
238
- const code = decoder . write ( buffer . slice ( i + 1 , bytesRead ) ) ;
239
- return findColumn ( fd , column , code ) ;
240
- }
241
- }
242
- }
243
- }
244
-
245
- function parseCode ( code , offset ) {
246
- // Lazy load acorn.
247
- if ( parseExpressionAt === undefined ) {
248
- const Parser = require ( 'internal/deps/acorn/acorn/dist/acorn' ) . Parser ;
249
- ( { findNodeAround } = require ( 'internal/deps/acorn/acorn-walk/dist/walk' ) ) ;
250
-
251
- parseExpressionAt = FunctionPrototypeBind ( Parser . parseExpressionAt , Parser ) ;
252
- tokenizer = FunctionPrototypeBind ( Parser . tokenizer , Parser ) ;
253
- }
254
- let node ;
255
- let start ;
256
- // Parse the read code until the correct expression is found.
257
- for ( const token of tokenizer ( code , { ecmaVersion : 'latest' } ) ) {
258
- start = token . start ;
259
- if ( start > offset ) {
260
- // No matching expression found. This could happen if the assert
261
- // expression is bigger than the provided buffer.
262
- break ;
263
- }
264
- try {
265
- node = parseExpressionAt ( code , start , { ecmaVersion : 'latest' } ) ;
266
- // Find the CallExpression in the tree.
267
- node = findNodeAround ( node , offset , 'CallExpression' ) ;
268
- if ( node ?. node . end >= offset ) {
269
- return [
270
- node . node . start ,
271
- StringPrototypeReplace ( StringPrototypeSlice ( code ,
272
- node . node . start , node . node . end ) ,
273
- escapeSequencesRegExp , escapeFn ) ,
274
- ] ;
275
- }
276
- // eslint-disable-next-line no-unused-vars
277
- } catch ( err ) {
278
- continue ;
279
- }
280
- }
281
- // eslint-disable-next-line no-throw-literal
282
- throw null ;
283
- }
284
-
285
- function getErrMessage ( message , fn ) {
286
- const tmpLimit = Error . stackTraceLimit ;
287
- const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable ( ) ;
288
- // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
289
- // does to much work.
290
- if ( errorStackTraceLimitIsWritable ) Error . stackTraceLimit = 1 ;
291
- // We only need the stack trace. To minimize the overhead use an object
292
- // instead of an error.
293
- const err = { } ;
294
- ErrorCaptureStackTrace ( err , fn ) ;
295
- if ( errorStackTraceLimitIsWritable ) Error . stackTraceLimit = tmpLimit ;
296
-
297
- overrideStackTrace . set ( err , ( _ , stack ) => stack ) ;
298
- const call = err . stack [ 0 ] ;
299
-
300
- let filename = call . getFileName ( ) ;
301
- const line = call . getLineNumber ( ) - 1 ;
302
- let column = call . getColumnNumber ( ) - 1 ;
303
- let identifier ;
304
- let code ;
305
-
306
- if ( filename ) {
307
- identifier = `${ filename } ${ line } ${ column } ` ;
308
-
309
- // Skip Node.js modules!
310
- if ( StringPrototypeStartsWith ( filename , 'node:' ) &&
311
- BuiltinModule . exists ( StringPrototypeSlice ( filename , 5 ) ) ) {
312
- errorCache . set ( identifier , undefined ) ;
313
- return ;
314
- }
315
- } else {
316
- return message ;
317
- }
318
-
319
- if ( errorCache . has ( identifier ) ) {
320
- return errorCache . get ( identifier ) ;
321
- }
322
-
323
- let fd ;
324
- try {
325
- // Set the stack trace limit to zero. This makes sure unexpected token
326
- // errors are handled faster.
327
- if ( errorStackTraceLimitIsWritable ) Error . stackTraceLimit = 0 ;
328
-
329
- if ( filename ) {
330
- if ( decoder === undefined ) {
331
- const { StringDecoder } = require ( 'string_decoder' ) ;
332
- decoder = new StringDecoder ( 'utf8' ) ;
333
- }
334
-
335
- // ESM file prop is a file proto. Convert that to path.
336
- // This ensure opensync will not throw ENOENT for ESM files.
337
- const fileProtoPrefix = 'file://' ;
338
- if ( StringPrototypeStartsWith ( filename , fileProtoPrefix ) ) {
339
- filename = fileURLToPath ( filename ) ;
340
- }
341
-
342
- fd = openSync ( filename , 'r' , 0o666 ) ;
343
- // Reset column and message.
344
- ( { 0 : column , 1 : message } = getCode ( fd , line , column ) ) ;
345
- // Flush unfinished multi byte characters.
346
- decoder . end ( ) ;
347
- } else {
348
- for ( let i = 0 ; i < line ; i ++ ) {
349
- code = StringPrototypeSlice ( code ,
350
- StringPrototypeIndexOf ( code , '\n' ) + 1 ) ;
351
- }
352
- ( { 0 : column , 1 : message } = parseCode ( code , column ) ) ;
353
- }
354
- // Always normalize indentation, otherwise the message could look weird.
355
- if ( StringPrototypeIncludes ( message , '\n' ) ) {
356
- if ( EOL === '\r\n' ) {
357
- message = RegExpPrototypeSymbolReplace ( / \r \n / g, message , '\n' ) ;
358
- }
359
- const frames = StringPrototypeSplit ( message , '\n' ) ;
360
- message = ArrayPrototypeShift ( frames ) ;
361
- for ( const frame of frames ) {
362
- let pos = 0 ;
363
- while ( pos < column && ( frame [ pos ] === ' ' || frame [ pos ] === '\t' ) ) {
364
- pos ++ ;
365
- }
366
- message += `\n ${ StringPrototypeSlice ( frame , pos ) } ` ;
367
- }
368
- }
369
- message = `The expression evaluated to a falsy value:\n\n ${ message } \n` ;
370
- // Make sure to always set the cache! No matter if the message is
371
- // undefined or not
372
- errorCache . set ( identifier , message ) ;
373
-
374
- return message ;
375
- } catch {
376
- // Invalidate cache to prevent trying to read this part again.
377
- errorCache . set ( identifier , undefined ) ;
378
- } finally {
379
- // Reset limit.
380
- if ( errorStackTraceLimitIsWritable ) Error . stackTraceLimit = tmpLimit ;
381
- if ( fd !== undefined )
382
- closeSync ( fd ) ;
383
- }
384
- }
385
-
386
- function innerOk ( fn , argLen , value , message ) {
387
- if ( ! value ) {
388
- let generatedMessage = false ;
389
-
390
- if ( argLen === 0 ) {
391
- generatedMessage = true ;
392
- message = 'No value argument passed to `assert.ok()`' ;
393
- } else if ( message == null ) {
394
- generatedMessage = true ;
395
- message = getErrMessage ( message , fn ) ;
396
- } else if ( isError ( message ) ) {
397
- throw message ;
398
- }
399
-
400
- const err = new AssertionError ( {
401
- actual : value ,
402
- expected : true ,
403
- message,
404
- operator : '==' ,
405
- stackStartFn : fn ,
406
- } ) ;
407
- err . generatedMessage = generatedMessage ;
408
- throw err ;
409
- }
410
- }
411
-
412
145
/**
413
146
* Pure assertion tests whether a value is truthy, as determined
414
147
* by !!value.
0 commit comments