Skip to content

Commit ebb334b

Browse files
y1d7ngtargos
authored andcommitted
assert: fix CallTracker wraps the function causes the length to be lost
PR-URL: #42909 Fixes: #40484 Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 129b05e commit ebb334b

File tree

2 files changed

+67
-15
lines changed

2 files changed

+67
-15
lines changed

lib/internal/assert/calltracker.js

+18-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
ArrayPrototypePush,
55
Error,
66
FunctionPrototype,
7+
Proxy,
78
ReflectApply,
89
SafeSet,
910
} = primordials;
@@ -46,20 +47,23 @@ class CallTracker {
4647
const callChecks = this.#callChecks;
4748
callChecks.add(context);
4849

49-
return function() {
50-
context.actual++;
51-
if (context.actual === context.exact) {
52-
// Once function has reached its call count remove it from
53-
// callChecks set to prevent memory leaks.
54-
callChecks.delete(context);
55-
}
56-
// If function has been called more than expected times, add back into
57-
// callchecks.
58-
if (context.actual === context.exact + 1) {
59-
callChecks.add(context);
60-
}
61-
return ReflectApply(fn, this, arguments);
62-
};
50+
return new Proxy(fn, {
51+
__proto__: null,
52+
apply(fn, thisArg, argList) {
53+
context.actual++;
54+
if (context.actual === context.exact) {
55+
// Once function has reached its call count remove it from
56+
// callChecks set to prevent memory leaks.
57+
callChecks.delete(context);
58+
}
59+
// If function has been called more than expected times, add back into
60+
// callchecks.
61+
if (context.actual === context.exact + 1) {
62+
callChecks.add(context);
63+
}
64+
return ReflectApply(fn, thisArg, argList);
65+
},
66+
});
6367
}
6468

6569
report() {

test/parallel/test-assert-calltracker-calls.js

+49-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
require('../common');
2+
const common = require('../common');
33
const assert = require('assert');
44

55
// This test ensures that assert.CallTracker.calls() works as intended.
@@ -78,3 +78,51 @@ assert.throws(
7878
callsNoop();
7979
tracker.verify();
8080
}
81+
82+
{
83+
function func() {}
84+
const tracker = new assert.CallTracker();
85+
const callsfunc = tracker.calls(func);
86+
assert.strictEqual(callsfunc.length, 0);
87+
}
88+
89+
{
90+
function func(a, b, c = 2) {}
91+
const tracker = new assert.CallTracker();
92+
const callsfunc = tracker.calls(func);
93+
assert.strictEqual(callsfunc.length, 2);
94+
}
95+
96+
{
97+
function func(a, b, c = 2) {}
98+
delete func.length;
99+
const tracker = new assert.CallTracker();
100+
const callsfunc = tracker.calls(func);
101+
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), false);
102+
}
103+
104+
{
105+
const ArrayIteratorPrototype = Reflect.getPrototypeOf(
106+
Array.prototype.values()
107+
);
108+
const { next } = ArrayIteratorPrototype;
109+
ArrayIteratorPrototype.next = common.mustNotCall(
110+
'%ArrayIteratorPrototype%.next'
111+
);
112+
Object.prototype.get = common.mustNotCall('%Object.prototype%.get');
113+
114+
const customPropertyValue = Symbol();
115+
function func(a, b, c = 2) {
116+
return a + b + c;
117+
}
118+
func.customProperty = customPropertyValue;
119+
Object.defineProperty(func, 'length', { get: common.mustNotCall() });
120+
const tracker = new assert.CallTracker();
121+
const callsfunc = tracker.calls(func);
122+
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), true);
123+
assert.strictEqual(callsfunc.customProperty, customPropertyValue);
124+
assert.strictEqual(callsfunc(1, 2, 3), 6);
125+
126+
ArrayIteratorPrototype.next = next;
127+
delete Object.prototype.get;
128+
}

0 commit comments

Comments
 (0)