Skip to content

Commit aed9474

Browse files
committed
fix callbackify function length should keep old behavior on old browser engine
1 parent 87caee5 commit aed9474

File tree

5 files changed

+62
-29
lines changed

5 files changed

+62
-29
lines changed
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
module.exports = function isFunctionLengthConfigurable(arg) {
3+
return Object.getOwnPropertyDescriptor(function () { }, 'length').configurable;
4+
}

support/nodeJSVersion.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
module.exports = function nodeJSVersion(arg) {
3+
4+
if (!process || !process.version || !process.version.match(/^v(\d+)\.(\d+)/)) {
5+
return false;
6+
}
7+
return parseInt(process.version.split('.')[0].slice(1), 10);
8+
}

test/browser/callbackify.js

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
var test = require('tape');
44
var callbackify = require('../../').callbackify;
55

6+
var isFunctionLengthConfigurable = require('../../support/isFunctionLengthConfigurable');
7+
68
if (typeof Promise === 'undefined') {
79
console.log('no global Promise found, skipping callbackify tests');
810
return;
@@ -169,6 +171,11 @@ test('util.callbackify non-function inputs throw', function (t) {
169171
});
170172

171173
test('util.callbackify resulting function should have one more argument', function (t) {
174+
175+
if (!isFunctionLengthConfigurable()) {
176+
console.log("skipping this test as function.length is not configurable in the current javascript engine");
177+
return;
178+
}
172179
// Test that resulting function should have one more argument
173180
[
174181
function() { },

test/node/callbackify.js

+33-26
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,39 @@ var common = require('./common');
77
var assert = require('assert');
88
var callbackify = require('../../').callbackify;
99
var execFile = require('child_process').execFile;
10+
var nodeJSVersion = require('../../support/nodeJSVersion');
11+
var isFunctionLengthConfigurable = require("../../support/isFunctionLengthConfigurable");
12+
13+
(function callbackify_resulting_function_should_have_one_more_argument() {
14+
15+
if (!isFunctionLengthConfigurable()) {
16+
console.log("skipping this test as function.length is not configurable in the current javascript engine");
17+
return;
18+
}
19+
var nodeVersion = nodeJSVersion();
20+
21+
console.log("Testing callbackify resulting function should have one more argument")
22+
var original_callbackify = require('util').callbackify;
23+
// Test that resulting function should have one more argument
24+
[
25+
function () { },
26+
function (a) { },
27+
function (a, b) { }
28+
].forEach(function (fct) {
29+
30+
var node_callbackified = original_callbackify(fct);
31+
var browser_callbackified = callbackify(fct);
32+
33+
if (nodeVersion >= 12 && node_callbackified.length !== fct.length + 1) {
34+
// this behavior is only true with node 12 and above, where the bug was fixed
35+
throw new Error("callbackified function should have one more argument");
36+
}
37+
if (browser_callbackified.length !== node_callbackified.length) {
38+
console.log("browser_callbackified=", browser_callbackified.length, "node_callbackified=", node_callbackified.length)
39+
throw new Error("callbackified function should have one more argument, like in node");
40+
}
41+
});
42+
})();
1043

1144
if (typeof Promise === 'undefined') {
1245
console.log('no global Promise found, skipping callbackify tests');
@@ -193,29 +226,3 @@ if (false) {
193226
if (require('is-async-supported')()) {
194227
require('./callbackify-async');
195228
}
196-
197-
(function callbackify_resulting_function_should_have_one_more_argument() {
198-
199-
var nodeJSVersion = parseInt(process.version.substring(1,3),10);
200-
201-
console.log("Testing callbackify resulting function should have one more argument")
202-
var original_callbackify = require('util').callbackify;
203-
// Test that resulting function should have one more argument
204-
[
205-
function(){ },
206-
function(a){ },
207-
function(a, b) { }
208-
].forEach(function (fct) {
209-
210-
var node_callbackified = original_callbackify(fct);
211-
var browser_callbackified = callbackify(fct);
212-
213-
if (nodeJSVersion >= 12 && node_callbackified.length !== fct.length + 1) {
214-
// this behavior is only true with node 12 and above, where the bug was fixed
215-
throw new Error("callbackified function should have one more argument");
216-
}
217-
if (browser_callbackified.length !== node_callbackified.length) {
218-
throw new Error("callbackified function should have one more argument, like in node");
219-
}
220-
});
221-
})();

util.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -703,13 +703,20 @@ function callbackify(original) {
703703
// In true node style we process the callback on `nextTick` with all the
704704
// implications (stack, `uncaughtException`, `async_hooks`)
705705
original.apply(this, args)
706-
.then(function(ret) { process.nextTick(cb.bind(null, null, ret)) },
707-
function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) });
706+
.then(function (ret) { process.nextTick(cb.bind(null, null, ret)) },
707+
function (rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) });
708708
}
709709

710710
Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
711711
const desc = getOwnPropertyDescriptors(original);
712-
desc.length.value += 1;
712+
var isFunctionLengthConfigurable = Object.getOwnPropertyDescriptor(callbackified, 'length').configurable;
713+
714+
// versions of NodeJS <12.0 have a bug where the callback's `length` is not adjusted as in recent NodeJS version
715+
// even though functionLength is configurable.
716+
if (isFunctionLengthConfigurable) {
717+
desc.length.value += 1;
718+
}
719+
713720
Object.defineProperties(callbackified, desc);
714721
return callbackified;
715722
}

0 commit comments

Comments
 (0)