I decided to create a userscript for YouTube live chat. Here is the code:
const toString = Function.prototype.toString
unsafeWindow.setTimeout = function (fn, t, ...args) {
unsafeWindow.console.log(fn, fn.toString(), toString.call(fn))
unsafeWindow.fns = (unsafeWindow.fns ?? []).concat(fn)
return setTimeout(fn, t, ...args)
}
Now look what the output looks like:
The output for some of the functions is predictable, but look at the other ones! When you do just console.log
it, you will see the function body, but if you call fn.toString()
, you will see function () { [native code] }
.
But why? The script is loaded before the page, so the YouTube's scripts couldn't replace the methods.
The toString() method returns a string as a string. The toString() method does not change the original string. The toString() method can be used to convert a string object into a string.
Native code is computer programming (code) that is compiled to run with a particular processor (such as an Intel x86-class processor) and its set of instructions. If the same program is run on a computer with a different processor, software can be provided so that the computer emulates the original processor.
We can use the toString() method on a function object to get the source code of a function. If we concatenate a string with a function, JavaScript will call the toString() method on the function internally and convert the function to a string.
javascript - Why does function.toString () output " [native code]", whereas logging to the console directly displays the function’s source code? - Stack Overflow Why does function.toString () output " [native code]", whereas logging to the console directly displays the function’s source code? I decided to create a userscript for YouTube live chat.
If the toString () method is called on a function created by the Function constructor, toString () returns the source code of a synthesized function declaration named "anonymous" using the provided parameters and function body. It's also possible to explicitly get the string representation of a function using the + operator: ({ a(){} }. a)
If the toString() method is called on a function created by the Function constructor, toString() returns the source code of a synthesized function declaration named "anonymous" using the provided parameters and function body.
The console.log() is a function in JavaScript which is used to print any kind of variables defined before in it or to just print any message that needs to be displayed to the user. Syntax: console.log(A); Parameters: It accepts a parameter which can be an array, an object or any message. Return value: It returns the value of the parameter given.
It is because those functions have been passed to Function.prototype.bind
.
> (function () { return 42; }).toString()
'function () { return 42; }'
> (function () { return 42; }).bind(this).toString()
'function () { [native code] }'
The bind
method transforms an arbitrary function object into a so-called bound function. Invoking a bound function has the same effect as invoking the original function, except that the this
parameter and a certain number of initial positional parameters (which may be zero) will have values fixed at the time of the creation of the bound function. Functionally, bind
is mostly equivalent to:
Function.prototype.bind = function (boundThis, ...boundArgs) {
return (...args) => this.call(boundThis, ...boundArgs, ...args);
};
Except that the above will, of course, produce a different value after string conversion. Bound functions are specified to have the same string conversion behaviour as native functions, in accordance with ECMA-262 11th Ed., §19.2.3.5 ¶2:
2. If func is a bound function exotic object or a built-in function object, then return an implementation-dependent String source code representation of func. The representation must have the syntax of a NativeFunction. […]
[…]
NativeFunction:
function PropertyName [~Yield, ~Await]opt ( FormalParameters [~Yield, ~Await] ) { [native code] }
When printing the function to the console directly (instead of the stringification), the implementation is not bound to any specification: it may present the function in the console any way it wishes. Chromium’s console, when asked to print a bound function, simply displays the source code of the original unbound function, as a matter of convenience.
Proving that this is indeed what happens in YouTube’s case is a bit of a nuisance, since YouTube’s JavaScript is obfuscated, but not exceedingly difficult. We can open YouTube’s main site, then enter the developer console and install our trap:
window.setTimeout = ((oldSetTimeout) => {
return function (...args) {
if (/native code/.test(String(args[0])))
debugger;
return oldSetTimeout.call(this, ...args);
};
})(window.setTimeout);
We should get a hit at the debugger
statement very quickly. I hit it in this function:
g.mh = function(a, b, c) {
if ("function" === typeof a)
c && (a = (0, g.D)(a, c));
else if (a && "function" == typeof a.handleEvent)
a = (0, g.D)(a.handleEvent, a);
else
throw Error("Invalid listener argument");
return 2147483647 < Number(b) ? -1 : g.C.setTimeout(a, b || 0)
}
The g.D
function looks particularly interesting: it seems to be invoked with the first argument a
, which is presumably a function. It looks like it might invoke bind
under the hood. When I ask the console to inspect it, I get this:
> String(g.D)
"function(a,b,c){return a.call.apply(a.bind,arguments)}"
So while the process is a bit convoluted, we can clearly see that this is indeed what happens.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With