19

I would like to know if there is a way to differentiate a JavaScript script function (function(){}) from a JavaScript native function (like Math.cos).
I already know the func.toString().indexOf('[native code]') != -1 trick but I was wondering if there is another way to detect it.

context:
I need to create a No-op forwarding ES6 Proxy that can handle native functions on an object but it fails with TypeError: Illegal invocation (see Illegal invocation error using ES6 Proxy and node.js).

To workaround this I .bind() all my functions in the get handler of my Proxy but if I could detect native function effectively, I will only need to .bind() these native functions.

more details: https://github.com/FranckFreiburger/module-invalidate/blob/master/index.js#L106

note:

(function() {}).toString() -> "function () {}"
(function() {}).prototype  -> {}

(require('os').cpus).toString() -> "function getCPUs() { [native code] }"
(require('os').cpus).prototype  -> getCPUs {}

(Math.cos).toString() -> "function cos() { [native code] }"
(Math.cos).prototype  -> undefined

(Promise.resolve().then).toString() -> "function then() { [native code] }"
(Promise.resolve().then).prototype  -> undefined

edit:
For the moment, the best solution is to test !('prototype' in fun) but it will not work with require('os').cpus ...

Community
  • 1
  • 1
Franck Freiburger
  • 26,310
  • 20
  • 70
  • 95

2 Answers2

16

You could try to use a Function constructor with the toString value of the function. If it does not throw an error, then you get a custom function, otherwise you have a native function.

function isNativeFn(fn) {
    try {
        void new Function(fn.toString());    
    } catch (e) {
        return true;
    }
    return false;
}

function customFn() { var foo; }

console.log(isNativeFn(Math.cos));          // true
console.log(isNativeFn(customFn));          // false
console.log(isNativeFn(customFn.bind({}))); // true, because bind 
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
8

My summary on this topic: don't use it, it doesn't work. You can not certainly detect wether a function is native, because Function#bind() also creates "native" functions.

function isSupposedlyNative(fn){
    return (/\{\s*\[native code\]\s*\}/).test(fn);
}

function foo(){ }
var whatever = {};

console.log("Math.cos():", isSupposedlyNative( Math.cos ));
console.log("foo():", isSupposedlyNative( foo ));
console.log("foo.bind():", isSupposedlyNative( foo.bind(whatever) ));

And since the Version by John-David Dalton, which Tareq linked to in this comment, does basically the same that code doesn't work either. I've checked it.

And the approach from Nina works on a similar principle, because again it is the [native code] part in the function body wich throws the error when trying to parse it into a new function.

The only secure way to determine wether the function you're dealing with is the native function, is to hold a reference to the native function and compare your function against that reference, but I guess this is no option for your use case.

Thomas
  • 11,958
  • 1
  • 14
  • 23
  • I agree. Additionally, nowadays we have the Proxy API that makes it even harder to detect whether a function is monkey patched. The only "safe" way to do this check is to hold a reference of the “clean” native function and, later on, compare your potentially monkey patched function with it (which can be impractical). I added some more context here: https://stackoverflow.com/a/73204287/4836602 – Matteo Mazzarolo Aug 02 '22 at 08:32