Skip to content

Commit c617543

Browse files
ExE-Bossljharb
andcommitted
[Fix] ES5+: Use spec‑accurate IsCallable and IsConstructor impls
Co-authored-by: Jordan Harband <ljharb@gmail.com>
1 parent 752669e commit c617543

File tree

14 files changed

+289
-24
lines changed

14 files changed

+289
-24
lines changed

2015/IsCallable.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
var hasSymbols = require('has-symbols')();
4+
var callBound = require('../helpers/callBound.js');
5+
6+
var $toStr = callBound('%Object.prototype.toString%');
7+
38
// http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
49

5-
module.exports = require('is-callable');
10+
module.exports = function IsCallable(argument) {
11+
// some older engines say that typeof /abc/ === 'function',
12+
return typeof argument === 'function'
13+
&& (hasSymbols || $toStr(argument) !== '[object RegExp]');
14+
};

2015/IsConstructor.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
'use strict';
22

3+
var GetIntrinsic = require('../GetIntrinsic.js');
4+
5+
var $construct = GetIntrinsic('%Reflect.construct%', true);
6+
7+
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow');
8+
try {
9+
DefinePropertyOrThrow({}, '', { '[[Get]]': function () {} });
10+
} catch (e) {
11+
// Accessor properties aren't supported
12+
DefinePropertyOrThrow = null;
13+
}
14+
315
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
416

5-
module.exports = function IsConstructor(argument) {
6-
return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument`
7-
};
17+
if (DefinePropertyOrThrow && $construct) {
18+
var isConstructorMarker = {};
19+
var badArrayLike = {};
20+
DefinePropertyOrThrow(badArrayLike, 'length', {
21+
'[[Get]]': function () {
22+
throw isConstructorMarker;
23+
},
24+
'[[Enumerable]]': true
25+
});
26+
27+
module.exports = function IsConstructor(argument) {
28+
try {
29+
// `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
30+
$construct(argument, badArrayLike);
31+
} catch (err) {
32+
return err === isConstructorMarker;
33+
}
34+
};
35+
} else {
36+
module.exports = function IsConstructor(argument) {
37+
// unfortunately there's no way to truly check this without try/catch `new argument` in old environments
38+
return typeof argument === 'function' && !!argument.prototype;
39+
};
40+
}

2016/IsCallable.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
var hasSymbols = require('has-symbols')();
4+
var callBound = require('../helpers/callBound.js');
5+
6+
var $toStr = callBound('%Object.prototype.toString%');
7+
38
// http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
49

5-
module.exports = require('is-callable');
10+
module.exports = function IsCallable(argument) {
11+
// some older engines say that typeof /abc/ === 'function',
12+
return typeof argument === 'function'
13+
&& (hasSymbols || $toStr(argument) !== '[object RegExp]');
14+
};

2016/IsConstructor.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
'use strict';
22

3+
var GetIntrinsic = require('../GetIntrinsic.js');
4+
5+
var $construct = GetIntrinsic('%Reflect.construct%', true);
6+
7+
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow');
8+
try {
9+
DefinePropertyOrThrow({}, '', { '[[Get]]': function () {} });
10+
} catch (e) {
11+
// Accessor properties aren't supported
12+
DefinePropertyOrThrow = null;
13+
}
14+
315
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
416

5-
module.exports = function IsConstructor(argument) {
6-
return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument`
7-
};
17+
if (DefinePropertyOrThrow && $construct) {
18+
var isConstructorMarker = {};
19+
var badArrayLike = {};
20+
DefinePropertyOrThrow(badArrayLike, 'length', {
21+
'[[Get]]': function () {
22+
throw isConstructorMarker;
23+
},
24+
'[[Enumerable]]': true
25+
});
26+
27+
module.exports = function IsConstructor(argument) {
28+
try {
29+
// `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
30+
$construct(argument, badArrayLike);
31+
} catch (err) {
32+
return err === isConstructorMarker;
33+
}
34+
};
35+
} else {
36+
module.exports = function IsConstructor(argument) {
37+
// unfortunately there's no way to truly check this without try/catch `new argument` in old environments
38+
return typeof argument === 'function' && !!argument.prototype;
39+
};
40+
}

2017/IsCallable.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
var hasSymbols = require('has-symbols')();
4+
var callBound = require('../helpers/callBound.js');
5+
6+
var $toStr = callBound('%Object.prototype.toString%');
7+
38
// http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
49

5-
module.exports = require('is-callable');
10+
module.exports = function IsCallable(argument) {
11+
// some older engines say that typeof /abc/ === 'function',
12+
return typeof argument === 'function'
13+
&& (hasSymbols || $toStr(argument) !== '[object RegExp]');
14+
};

2017/IsConstructor.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
'use strict';
22

3+
var GetIntrinsic = require('../GetIntrinsic.js');
4+
5+
var $construct = GetIntrinsic('%Reflect.construct%', true);
6+
7+
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow');
8+
try {
9+
DefinePropertyOrThrow({}, '', { '[[Get]]': function () {} });
10+
} catch (e) {
11+
// Accessor properties aren't supported
12+
DefinePropertyOrThrow = null;
13+
}
14+
315
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
416

5-
module.exports = function IsConstructor(argument) {
6-
return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument`
7-
};
17+
if (DefinePropertyOrThrow && $construct) {
18+
var isConstructorMarker = {};
19+
var badArrayLike = {};
20+
DefinePropertyOrThrow(badArrayLike, 'length', {
21+
'[[Get]]': function () {
22+
throw isConstructorMarker;
23+
},
24+
'[[Enumerable]]': true
25+
});
26+
27+
module.exports = function IsConstructor(argument) {
28+
try {
29+
// `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
30+
$construct(argument, badArrayLike);
31+
} catch (err) {
32+
return err === isConstructorMarker;
33+
}
34+
};
35+
} else {
36+
module.exports = function IsConstructor(argument) {
37+
// unfortunately there's no way to truly check this without try/catch `new argument` in old environments
38+
return typeof argument === 'function' && !!argument.prototype;
39+
};
40+
}

2018/IsCallable.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
var hasSymbols = require('has-symbols')();
4+
var callBound = require('../helpers/callBound.js');
5+
6+
var $toStr = callBound('%Object.prototype.toString%');
7+
38
// http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
49

5-
module.exports = require('is-callable');
10+
module.exports = function IsCallable(argument) {
11+
// some older engines say that typeof /abc/ === 'function',
12+
return typeof argument === 'function'
13+
&& (hasSymbols || $toStr(argument) !== '[object RegExp]');
14+
};

2018/IsConstructor.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
'use strict';
22

3+
var GetIntrinsic = require('../GetIntrinsic.js');
4+
5+
var $construct = GetIntrinsic('%Reflect.construct%', true);
6+
7+
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow');
8+
try {
9+
DefinePropertyOrThrow({}, '', { '[[Get]]': function () {} });
10+
} catch (e) {
11+
// Accessor properties aren't supported
12+
DefinePropertyOrThrow = null;
13+
}
14+
315
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
416

5-
module.exports = function IsConstructor(argument) {
6-
return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument`
7-
};
17+
if (DefinePropertyOrThrow && $construct) {
18+
var isConstructorMarker = {};
19+
var badArrayLike = {};
20+
DefinePropertyOrThrow(badArrayLike, 'length', {
21+
'[[Get]]': function () {
22+
throw isConstructorMarker;
23+
},
24+
'[[Enumerable]]': true
25+
});
26+
27+
module.exports = function IsConstructor(argument) {
28+
try {
29+
// `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
30+
$construct(argument, badArrayLike);
31+
} catch (err) {
32+
return err === isConstructorMarker;
33+
}
34+
};
35+
} else {
36+
module.exports = function IsConstructor(argument) {
37+
// unfortunately there's no way to truly check this without try/catch `new argument` in old environments
38+
return typeof argument === 'function' && !!argument.prototype;
39+
};
40+
}

2019/IsCallable.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
var hasSymbols = require('has-symbols')();
4+
var callBound = require('../helpers/callBound.js');
5+
6+
var $toStr = callBound('%Object.prototype.toString%');
7+
38
// http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
49

5-
module.exports = require('is-callable');
10+
module.exports = function IsCallable(argument) {
11+
// some older engines say that typeof /abc/ === 'function',
12+
return typeof argument === 'function'
13+
&& (hasSymbols || $toStr(argument) !== '[object RegExp]');
14+
};

2019/IsConstructor.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
11
'use strict';
22

3+
var GetIntrinsic = require('../GetIntrinsic.js');
4+
5+
var $construct = GetIntrinsic('%Reflect.construct%', true);
6+
7+
var DefinePropertyOrThrow = require('./DefinePropertyOrThrow');
8+
try {
9+
DefinePropertyOrThrow({}, '', { '[[Get]]': function () {} });
10+
} catch (e) {
11+
// Accessor properties aren't supported
12+
DefinePropertyOrThrow = null;
13+
}
14+
315
// https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
416

5-
module.exports = function IsConstructor(argument) {
6-
return typeof argument === 'function' && !!argument.prototype; // unfortunately there's no way to truly check this without try/catch `new argument`
7-
};
17+
if (DefinePropertyOrThrow && $construct) {
18+
var isConstructorMarker = {};
19+
var badArrayLike = {};
20+
DefinePropertyOrThrow(badArrayLike, 'length', {
21+
'[[Get]]': function () {
22+
throw isConstructorMarker;
23+
},
24+
'[[Enumerable]]': true
25+
});
26+
27+
module.exports = function IsConstructor(argument) {
28+
try {
29+
// `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
30+
$construct(argument, badArrayLike);
31+
} catch (err) {
32+
return err === isConstructorMarker;
33+
}
34+
};
35+
} else {
36+
module.exports = function IsConstructor(argument) {
37+
// unfortunately there's no way to truly check this without try/catch `new argument` in old environments
38+
return typeof argument === 'function' && !!argument.prototype;
39+
};
40+
}

0 commit comments

Comments
 (0)