Javascript log attribute access and function calls [duplicate] - javascript

Can I override the behavior of the Function object so that I can inject behavior prior t every function call, and then carry on as normal? Specifically, (though the general idea is intriguing in itself) can I log to the console every function call without having to insert console.log statements everywhere? And then the normal behavior goes on?
I do recognize that this will likely have significant performance problems; I have no intention of having this run typically, even in my development environment. But if it works it seems an elegant solution to get a 1000 meter view on the running code. And I suspect that the answer will show me something deeper about javascript.

The obvious answer is something like the following:
var origCall = Function.prototype.call;
Function.prototype.call = function (thisArg) {
console.log("calling a function");
var args = Array.prototype.slice.call(arguments, 1);
origCall.apply(thisArg, args);
};
But this actually immediately enters an infinite loop, because the very act of calling console.log executes a function call, which calls console.log, which executes a function call, which calls console.log, which...
Point being, I'm not sure this is possible.

Intercepting function calls
Many here have tried to override .call. Some have failed, some have succeeded.
I'm responding to this old question, as it has been brought up at my workplace, with this post being used as reference.
There are only two function-call related functions available for us to modify: .call and .apply. I will demonstrate a successful override of both.
TL;DR: What OP is asking is not possible. Some of the success-reports in the answers are due to the console calling .call internally right before evaluation, not because of the call we want to intercept.
Overriding Function.prototype.call
This appears to be the first idea people come up with. Some have been more successful than others, but here is an implementation that works:
// Store the original
var origCall = Function.prototype.call;
Function.prototype.call = function () {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, lets do it by ourselves.
console.log("Calling",
Function.prototype.toString.apply(this, []),
"with:",
Array.prototype.slice.apply(arguments, [1]).toString()
);
// A trace, for fun
console.trace.apply(console, []);
// The call. Apply is the only way we can pass all arguments, so don't touch that!
origCall.apply(this, arguments);
};
This successfully intercepts Function.prototype.call
Lets take it for a spin, shall we?
// Some tests
console.log("1"); // Does not show up
console.log.apply(console,["2"]); // Does not show up
console.log.call(console, "3"); // BINGO!
It is important that this is not run from a console. The various browsers have all sorts of console tools that call .call themselves a lot, including once for every input, which might confuse a user in the moment. Another mistake is to just console.log arguments, which goes through the console api for stringification, which in turn cause an infinite loop.
Overriding Function.prototype.apply as well
Well, what about apply then? They're the only magic calling functions we have, so lets try that as well. Here goes a version that catches both:
// Store apply and call
var origApply = Function.prototype.apply;
var origCall = Function.prototype.call;
// We need to be able to apply the original functions, so we need
// to restore the apply locally on both, including the apply itself.
origApply.apply = origApply;
origCall.apply = origApply;
// Some utility functions we want to work
Function.prototype.toString.apply = origApply;
Array.prototype.slice.apply = origApply;
console.trace.apply = origApply;
function logCall(t, a) {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, do it ourselves.
console.log("Calling",
Function.prototype.toString.apply(t, []),
"with:",
Array.prototype.slice.apply(a, [1]).toString()
);
console.trace.apply(console, []);
}
Function.prototype.call = function () {
logCall(this, arguments);
origCall.apply(this, arguments);
};
Function.prototype.apply = function () {
logCall(this, arguments);
origApply.apply(this, arguments);
}
... And lets try it out!
// Some tests
console.log("1"); // Passes by unseen
console.log.apply(console,["2"]); // Caught
console.log.call(console, "3"); // Caught
As you can see, the calling parenthesis go unnoticed.
Conclusion
Fortunately, calling parenthesis cannot be intercepted from JavaScript. But even if .call would intercept the parenthesis operator on function objects, how would we call the original without causing an infinite loop?
The only thing overriding .call/.apply does is to intercept explicit calls to those prototype functions. If the console is used with that hack in place, there will be lots and lots of spam. One must furthermore be very careful if it is used, as using the console API can quickly cause an infinite loop (console.log will use .call internally if one gives it an non-string).

I am getting SOME results and no page crashes with the following :
(function () {
var
origCall = Function.prototype.call,
log = document.getElementById ('call_log');
// Override call only if call_log element is present
log && (Function.prototype.call = function (self) {
var r = (typeof self === 'string' ? '"' + self + '"' : self) + '.' + this + ' (';
for (var i = 1; i < arguments.length; i++) r += (i > 1 ? ', ' : '') + arguments[i];
log.innerHTML += r + ')<br/>';
this.apply (self, Array.prototype.slice.apply (arguments, [1]));
});
}) ();
Only tested in Chrome version 9.xxx.
It is certainly not logging all function calls, but it is logging some!
I suspect only actual calls to 'call' intself are being processed

Only a quick test, but it seems to work for me.
It may not be useful this way, but I'm basically restoring the prototype whilst in my replacement's body and then "unrestoring" it before exiting.
This example simply logs all function calls - though there may be some fatal flaw I've yet to detect; doing this over a coffee break
implementation
callLog = [];
/* set up an override for the Function call prototype
* #param func the new function wrapper
*/
function registerOverride(func) {
oldCall = Function.prototype.call;
Function.prototype.call = func;
}
/* restore you to your regular programming
*/
function removeOverride() {
Function.prototype.call = oldCall;
}
/* a simple example override
* nb: if you use this from the node.js REPL you'll get a lot of buffer spam
* as every keypress is processed through a function
* Any useful logging would ideally compact these calls
*/
function myCall() {
// first restore the normal call functionality
Function.prototype.call = oldCall;
// gather the data we wish to log
var entry = {this:this, name:this.name, args:{}};
for (var key in arguments) {
if (arguments.hasOwnProperty(key)) {
entry.args[key] = arguments[key];
}
}
callLog.push(entry);
// call the original (I may be doing this part naughtily, not a js guru)
this(arguments);
// put our override back in power
Function.prototype.call = myCall;
}
usage
I've had some issues including calls to this in one big paste, so here's what I was typing into the REPL in order to test the above functions:
/* example usage
* (only tested through the node.js REPL)
*/
registerOverride(myCall);
console.log("hello, world!");
removeOverride(myCall);
console.log(callLog);

You can override Function.prototype.call, just make sure to only apply functions within your override.
window.callLog = [];
Function.prototype.call = function() {
Array.prototype.push.apply(window.callLog, [[this, arguments]]);
return this.apply(arguments[0], Array.prototype.slice.apply(arguments,[1]));
};

I found it easiest to instrument the file, using an automatic process. I built this little tool to make it easier for myself. Perhaps somebody else will find it useful. It's basically awk, but easier for a Javascript programmer to use.
// This tool reads a file and builds a buffer of say ten lines.
// When a line falls off the end of the buffer, it gets written to the output file.
// When a line is read from the input file, it gets written to the first line of the buffer.
// After each occurrence of a line being read from the input file and/or written to the output
// file, a routine is given control. The routine has the option of operating on the buffer.
// It can insert a line before or after a line that is there, based on the lines surrounding.
//
// The immediate case is that if I have a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// I want to change that to a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`compareNums: function (a, c, d) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// We are trying to figure out how a set of functions work, and I want each function to report
// its name when we enter it.
//
// To save time, options and the function that is called on each cycle appear at the beginning
// of this file. Ideally, they would be --something options on the command line.
const readline = require('readline');
//------------------------------------------------------------------------------------------------
// Here are the things that would properly be options on the command line. Put here for
// speed of building the tool.
const frameSize = 10;
const shouldReportFrame = false;
function reportFrame() {
for (i = frame.length - 1; i >= 0; i--) {
console.error(`${i}. ${frame[i]}`); // Using the error stream because the stdout stream may have been coopted.
}
}
function processFrame() {
// console.log(`******** ${frame[0]}`);
// if (frame[0].search('console.log(\`arguments.callee = \$\{arguments.callee.toString().substr(0,100)\}\`);') !== -1) {
// if (frame[0].search('arguments.callee') !== -1) {
// if (frame[0].search(/console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/) !== -1) {
var matchArray = frame[0].match(/([ \t]*)console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/);
if (matchArray) {
// console.log('******** Matched');
frame.splice(1, 0, `${matchArray[1]}console.log('${frame[1]}');`);
}
}
//------------------------------------------------------------------------------------------------
var i;
var frame = [];
const rl = readline.createInterface({
input: process.stdin
});
rl.on('line', line => {
if (frame.length > frameSize - 1) {
for (i = frame.length - 1; i > frameSize - 2; i--) {
process.stdout.write(`${frame[i]}\n`);
}
}
frame.splice(frameSize - 1, frame.length - frameSize + 1);
frame.splice(0, 0, line);
if (shouldReportFrame) reportFrame();
processFrame();
// process.stdout.write(`${line}\n`); // readline gives us the line with the newline stripped off
});
rl.on('close', () => {
for (i = frame.length - 1; i > -1; i--) {
process.stdout.write(`${frame[i]}\n`);
}
});
// Notes
//
// We are not going to control the writing to the output stream. In particular, we are not
// going to listen for drain events. Nodejs' buffering may get overwhelmed.
//

Related

How to let callee know who is the caller [duplicate]

function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
Is there a way to find out the call stack?
Note that this solution is deprecated and should no longer be used according to MDN documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
function Hello()
{
alert("caller is " + Hello.caller);
}
Note that this feature is non-standard, from Function.caller:
Non-standard
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.
The following is the old answer from 2008, which is no longer supported in modern Javascript:
function Hello()
{
alert("caller is " + arguments.callee.caller.toString());
}
StackTrace
You can find the entire stack trace using browser specific code. The good thing is someone already made it; here is the project code on GitHub.
But not all the news is good:
It is really slow to get the stack trace so be careful (read this for more).
You will need to define function names for the stack trace to be legible. Because if you have code like this:
var Klass = function kls() {
this.Hello = function() { alert(printStackTrace().join('\n\n')); };
}
new Klass().Hello();
Google Chrome will alert ... kls.Hello ( ... but most browsers will expect a function name just after the keyword function and will treat it as an anonymous function. An not even Chrome will be able to use the Klass name if you don't give the name kls to the function.
And by the way, you can pass to the function printStackTrace the option {guess: true} but I didn't find any real improvement by doing that.
Not all browsers give you the same information. That is, parameters, code column, etc.
Caller Function Name
By the way, if you only want the name of the caller function (in most browsers, but not IE) you can use:
arguments.callee.caller.name
But note that this name will be the one after the function keyword. I found no way (even on Google Chrome) to get more than that without getting the code of the whole function.
Caller Function Code
And summarizing the rest of the best answers (by Pablo Cabrera, nourdine, and Greg Hewgill). The only cross-browser and really safe thing you can use is:
arguments.callee.caller.toString();
Which will show the code of the caller function. Sadly, that is not enough for me, and that is why I give you tips for the StackTrace and the caller function Name (although they are not cross-browser).
I usually use (new Error()).stack in Chrome.
The nice thing is that this also gives you the line numbers where the caller called the function. The downside is that it limits the length of the stack to 10, which is why I came to this page in the first place.
(I'm using this to collect callstacks in a low-level constructor during execution, to view and debug later, so setting a breakpoint isn't of use since it will be hit thousands of times)
I know you mentioned "in Javascript", but if the purpose is debugging, I think it's easier to just use your browser's developer tools. This is how it looks in Chrome:
Just drop the debugger where you want to investigate the stack.
In both ES6 and Strict mode, use the following to get the Caller function
console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])
Please note that, the above line will throw an exception if there is no caller or no previous stack. Use accordingly.
To get callee (the current function name), use:
console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
If you are not going to run it in IE < 11 then console.trace() would suit.
function main() {
Hello();
}
function Hello() {
console.trace()
}
main()
// Hello # VM261:9
// main # VM261:4
You can get the full stacktrace:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
Until caller is null.
Note: it cause an infinite loop on recursive functions.
To recap (and make it clearer) ...
this code:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
is equivalent to this:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
Clearly the first bit is more portable, since you can change the name of the function, say from "Hello" to "Ciao", and still get the whole thing to work.
In the latter, in case you decide to refactor the name of the invoked function (Hello), you would have to change all its occurrences :(
I would do this:
function Hello() {
console.trace();
}
You can use Function.Caller to get the calling function. The old method using argument.caller is considered obsolete.
The following code illustrates its use:
function Hello() { return Hello.caller;}
Hello2 = function NamedFunc() { return NamedFunc.caller; };
function main()
{
Hello(); //both return main()
Hello2();
}
Notes about obsolete argument.caller: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller
Be aware Function.caller is non-standard: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
Looks like this is quite a solved question but I recently found out that callee is not allowed in 'strict mode' so for my own use I wrote a class that will get the path from where it is called. It's part of a small helper lib and if you want to use the code standalone change the offset used to return the stack trace of the caller (use 1 instead of 2)
function ScriptPath() {
var scriptPath = '';
try {
//Throw an error to generate a stack trace
throw new Error();
}
catch(e) {
//Split the stack trace into each line
var stackLines = e.stack.split('\n');
var callerIndex = 0;
//Now walk though each line until we find a path reference
for(var i in stackLines){
if(!stackLines[i].match(/http[s]?:\/\//)) continue;
//We skipped all the lines with out an http so we now have a script reference
//This one is the class constructor, the next is the getScriptPath() call
//The one after that is the user code requesting the path info (so offset by 2)
callerIndex = Number(i) + 2;
break;
}
//Now parse the string for each section we want to return
pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/);
}
this.fullPath = function() {
return pathParts[1];
};
this.path = function() {
return pathParts[2];
};
this.file = function() {
return pathParts[3];
};
this.fileNoExt = function() {
var parts = this.file().split('.');
parts.length = parts.length != 1 ? parts.length - 1 : 1;
return parts.join('.');
};
}
heystewart's answer and JiarongWu's answer both mentioned that the Error object has access to the stack.
Here's an example:
function main() {
Hello();
}
function Hello() {
try {
throw new Error();
} catch (err) {
let stack = err.stack;
// N.B. stack === "Error\n at Hello ...\n at main ... \n...."
let m = stack.match(/.*?Hello.*?\n(.*?)\n/);
if (m) {
let caller_name = m[1];
console.log("Caller is:", caller_name);
}
}
}
main();
Different browsers shows the stack in different string formats:
Safari : Caller is: main#https://stacksnippets.net/js:14:8
Firefox : Caller is: main#https://stacksnippets.net/js:14:3
Chrome : Caller is: at main (https://stacksnippets.net/js:14:3)
IE Edge : Caller is: at main (https://stacksnippets.net/js:14:3)
IE : Caller is: at main (https://stacksnippets.net/js:14:3)
Most browsers will set the stack with var stack = (new Error()).stack. In Internet Explorer the stack will be undefined - you have to throw a real exception to retrieve the stack.
Conclusion: It's possible to determine "main" is the caller to "Hello" using the stack in the Error object. In fact it will work in cases where the callee / caller approach doesn't work. It will also show you context, i.e. source file and line number. However effort is required to make the solution cross platform.
function Hello() {
alert(Hello.caller);
}
It's safer to use *arguments.callee.caller since arguments.caller is deprecated...
2018 Update
caller is forbidden in strict mode. Here is an alternative using the (non-standard) Error stack.
The following function seems to do the job in Firefox 52 and Chrome 61-71 though its implementation makes a lot of assumptions about the logging format of the two browsers and should be used with caution, given that it throws an exception and possibly executes two regex matchings before being done.
'use strict';
const fnNameMatcher = /([^(]+)#|at ([^(]+) \(/;
function fnName(str) {
const regexResult = fnNameMatcher.exec(str);
return regexResult[1] || regexResult[2];
}
function log(...messages) {
const logLines = (new Error().stack).split('\n');
const callerName = fnName(logLines[1]);
if (callerName !== null) {
if (callerName !== 'log') {
console.log(callerName, 'called log with:', ...messages);
} else {
console.log(fnName(logLines[2]), 'called log with:', ...messages);
}
} else {
console.log(...messages);
}
}
function foo() {
log('hi', 'there');
}
(function main() {
foo();
}());
Try accessing this:
arguments.callee.caller.name
Just console log your error stack. You can then know how are you being called
const hello = () => {
console.log(new Error('I was called').stack)
}
const sello = () => {
hello()
}
sello()
I wanted to add my fiddle here for this:
http://jsfiddle.net/bladnman/EhUm3/
I tested this is chrome, safari and IE (10 and 8). Works fine. There is only 1 function that matters, so if you get scared by the big fiddle, read below.
Note:
There is a fair amount of my own "boilerplate" in this fiddle. You can remove all of that and use split's if you like. It's just an ultra-safe" set of functions I've come to rely on.
There is also a "JSFiddle" template in there that I use for many fiddles to simply quick fiddling.
If you just want the function name and not the code, and want a browser-independent solution, use the following:
var callerFunction = arguments.callee.caller.toString().match(/function ([^\(]+)/)[1];
Note that the above will return an error if there is no caller function as there is no [1] element in the array. To work around, use the below:
var callerFunction = (arguments.callee.caller.toString().match(/function ([^\(]+)/) === null) ? 'Document Object Model': arguments.callee.caller.toString().match(/function ([^\(]+)/)[1], arguments.callee.toString().match(/function ([^\(]+)/)[1]);
Here, everything but the functionname is stripped from caller.toString(), with RegExp.
<!DOCTYPE html>
<meta charset="UTF-8">
<title>Show the callers name</title><!-- This validates as html5! -->
<script>
main();
function main() { Hello(); }
function Hello(){
var name = Hello.caller.toString().replace(/\s\([^#]+$|^[^\s]+\s/g,'');
name = name.replace(/\s/g,'');
if ( typeof window[name] !== 'function' )
alert ("sorry, the type of "+name+" is "+ typeof window[name]);
else
alert ("The name of the "+typeof window[name]+" that called is "+name);
}
</script>
Just want to let you know that on PhoneGap/Android the name doesnt seem to be working. But arguments.callee.caller.toString() will do the trick.
here is a function to get full stacktrace:
function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
stack += '\n' + f.name;
f = f.caller;
}
return stack;
}
Note you can't use Function.caller in Node.js, use caller-id package instead. For example:
var callerId = require('caller-id');
function foo() {
bar();
}
function bar() {
var caller = callerId.getData();
/*
caller = {
typeName: 'Object',
functionName: 'foo',
filePath: '/path/of/this/file.js',
lineNumber: 5,
topLevelFlag: true,
nativeFlag: false,
evalFlag: false
}
*/
}
Works great for me, and you can chose how much you want to go back in the functions:
function getCaller(functionBack= 0) {
const back = functionBack * 2;
const stack = new Error().stack.split('at ');
const stackIndex = stack[3 + back].includes('C:') ? (3 + back) : (4 + back);
const isAsync = stack[stackIndex].includes('async');
let result;
if (isAsync)
result = stack[stackIndex].split(' ')[1].split(' ')[0];
else
result = stack[stackIndex].split(' ')[0];
return result;
}
I could use these in 2021 and get the stack which starts from the caller function :
1. console.trace();
2. console.log((new Error).stack)
// do the same as #2 just with better view
3. console.log((new Error).stack.split("\n"))
Try the following code:
function getStackTrace(){
var f = arguments.callee;
var ret = [];
var item = {};
var iter = 0;
while ( f = f.caller ){
// Initialize
item = {
name: f.name || null,
args: [], // Empty array = no arguments passed
callback: f
};
// Function arguments
if ( f.arguments ){
for ( iter = 0; iter<f.arguments.length; iter++ ){
item.args[iter] = f.arguments[iter];
}
} else {
item.args = null; // null = argument listing not supported
}
ret.push( item );
}
return ret;
}
Worked for me in Firefox-21 and Chromium-25.
Another way around this problem is to simply pass the name of the calling function as a parameter.
For example:
function reformatString(string, callerName) {
if (callerName === "uid") {
string = string.toUpperCase();
}
return string;
}
Now, you could call the function like this:
function uid(){
var myString = "apples";
reformatString(myString, function.name);
}
My example uses a hard coded check of the function name, but you could easily use a switch statement or some other logic to do what you want there.
As far as I know, we have 2 way for this from given sources like this-
arguments.caller
function whoCalled()
{
if (arguments.caller == null)
console.log('I was called from the global scope.');
else
console.log(arguments.caller + ' called me!');
}
Function.caller
function myFunc()
{
if (myFunc.caller == null) {
return 'The function was called from the top!';
}
else
{
return 'This function\'s caller was ' + myFunc.caller;
}
}
Think u have your answer :).
Why all of the solutions above look like a rocket science. Meanwhile, it should not be more complicated than this snippet. All credits to this guy
How do you find out the caller function in JavaScript?
var stackTrace = function() {
var calls = [];
var caller = arguments.callee.caller;
for (var k = 0; k < 10; k++) {
if (caller) {
calls.push(caller);
caller = caller.caller;
}
}
return calls;
};
// when I call this inside specific method I see list of references to source method, obviously, I can add toString() to each call to see only function's content
// [function(), function(data), function(res), function(l), function(a, c), x(a, b, c, d), function(c, e)]
I think the following code piece may be helpful:
window.fnPureLog = function(sStatement, anyVariable) {
if (arguments.length < 1) {
throw new Error('Arguments sStatement and anyVariable are expected');
}
if (typeof sStatement !== 'string') {
throw new Error('The type of sStatement is not match, please use string');
}
var oCallStackTrack = new Error();
console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}
Execute the code:
window.fnPureLog = function(sStatement, anyVariable) {
if (arguments.length < 1) {
throw new Error('Arguments sStatement and anyVariable are expected');
}
if (typeof sStatement !== 'string') {
throw new Error('The type of sStatement is not match, please use string');
}
var oCallStackTrack = new Error();
console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}
function fnBsnCallStack1() {
fnPureLog('Stock Count', 100)
}
function fnBsnCallStack2() {
fnBsnCallStack1()
}
fnBsnCallStack2();
The log looks like this:
Call Stack:
at window.fnPureLog (<anonymous>:8:27)
at fnBsnCallStack1 (<anonymous>:13:5)
at fnBsnCallStack2 (<anonymous>:17:5)
at <anonymous>:20:1
Stock Count: 100

Extend $.mobile.changePage to accept more options?

I would like to extend $.mobile.changePage to accept more options such as adding a callback function for when the page finishes loading as well as more options for the AJAX call like contentType. Is there a way to do this without changing the source code? If not, I am willing to change the source code for educational purposes, but could not find it in the jQuery Mobile GitHub: https://github.com/jquery/jquery-mobile . Thanks for any helps or guidance.
One of the more exciting parts of JavaScript is that ability to redefine any function using a technique which is commonly referred to as Monkey Patching. (as an aside ES5 provides a new freeze method which allows developers to prevent such modifications.)
Here's an example of a JavaScript MonkeyPatch which allows us to modify the behaviour of a function without editing it's source:
// A namespace object.
var Example = {};
// Sums two values.
Example.sum = function (a, b) {
return a + b;
}
// Usage:
var result = Example.sum(1, 2);
Say we wanted to add logging to the sum method, we could just add a console.log line to the function, but we can also monkey patch it:
// Store a reference to the current 'Example.sum' function.
var originalSum = Example.sum;
// Now redeclare Example.sum...
Example.sum = function (a, b) {
// Call the originalSum function first...
var result = originalSum(a, b);
// Now add some logging...
console.log("Example.sum(" + a + ", " + b + ") yields " + result);
return result;
};
Now when Example.sum is called, not only will we get the result as before, but a console message will also be written. With this in mind, you can monkey patch the $.mobile.changePage method in the same way:
var originalChangePage = $.mobile.changePage;
// Redefine `changePage` so it accepts a 'complete' function in the options
// object which will be invoked when the page change is complete.
$.mobile.changePage = function (to, options) {
if (typeof options.complete === "function") {
$(body).one("pagechange", function (event) {
options.complete(event);
});
}
originalChangePage(to, options);
};

Force missing parameters in JavaScript

When you call a function in JavaScript and you miss to pass some parameter, nothing happens.
This makes the code harder to debug, so I would like to change that behavior.
I've seen
How best to determine if an argument is not sent to the JavaScript function
but I want a solution with a constant number of typed lines of code; not typing extra code for each function.
I've thought about automatically prefixing the code of all functions with that code, by modifying the constructor of the ("first-class") Function object.
Inspired by
Changing constructor in JavaScript
I've first tested whether I can change the constructor of the Function object, like this:
function Function2 () {
this.color = "white";
}
Function.prototype = new Function2();
f = new Function();
alert(f.color);
But it alerts "undefined" instead of "white", so it is not working, so I've don't further explored this technique.
Do you know any solution for this problem at any level? Hacking the guts of JavaScript would be OK but any other practical tip on how to find missing arguments would be OK as well.
If a function of yours requires certain arguments to be passed, you should check for those arguments specifically as part of the validation of the function.
Extending the Function object is not the best idea because many libraries rely on the behavior of defaulting arguments that are not passed (such as jQuery not passing anything to it's scoped undefined variable).
Two approaches I tend to use:
1) an argument is required for the function to work
var foo = function (requiredParam) {
if (typeof requiredParam === 'undefined') {
throw new Error('You must pass requiredParam to function Foo!');
}
// solve world hunger here
};
2) an argument not passed but can be defaulted to something (uses jQuery)
var foo = function (argumentObject) {
argumentObject = $.extend({
someArgument1: 'defaultValue1',
someArgument2: 'defaultValue2'
}, argumentObject || {});
// save the world from alien invaders here
};
As others have said, there are many reasons not to do this, but I know of a couple of ways, so I'll tell you how! For science!
This is the first, stolen from Gaby, give him an upvote! Here's a rough overview of how it works:
//example function
function thing(a, b, c) {
}
var functionPool = {} // create a variable to hold the original versions of the functions
for( var func in window ) // scan all items in window scope
{
if (typeof(window[func]) === 'function') // if item is a function
{
functionPool[func] = window[func]; // store the original to our global pool
(function(){ // create an closure to maintain function name
var functionName = func;
window[functionName] = function(){ // overwrite the function with our own version
var args = [].splice.call(arguments,0); // convert arguments to array
// do the logging before callling the method
if(functionPool[functionName].length > args.length)
throw "Not enough arguments for function " + functionName + " expected " + functionPool[functionName].length + " got " + args.length;
// call the original method but in the window scope, and return the results
return functionPool[functionName].apply(window, args );
// additional logging could take place here if we stored the return value ..
}
})();
}
}
thing(1,2 ,3); //fine
thing(1,2); //throws error
The second way:
Now there is another way to do this that I can't remember the details exactly, basically you overrride Function.prototype.call. But as it says in this question, this involves an infinite loop. So you need an untainted Function object to call, this is done by a trick of turning the variables into a string and then using eval to call the function in an untainted context! There's a really great snippet out the showing you how from the early days of the web, but alas I can't find it at the moment. There's a hack that's required to pass the variables properly and I think you may actually lose context, so it's pretty fragile.
Still, as stated, don't try and force javascript to do something against its nature, either trust your fellow programmers or supply defaults, as per all the other answers.
You can imitate something like Python’s decorators. This does require extra typing per function, though not extra lines.
function force(inner) {
return function() {
if (arguments.length === inner.length) {
return inner.apply(this, arguments);
} else {
throw "expected " + inner.length +
" arguments, got " + arguments.length;
}
}
}
var myFunc = force(function(foo, bar, baz) {
// ...
});
In general this sounds like a bad idea, because you’re basically messing with the language. Do you really forget to pass arguments that often?
You could use the decorator pattern. The following decorator allows you to specify minimum and maximum number of arguments that need to be passed and an optional error handler.
/* Wrap the function *f*, so that *error_callback* is called when the number
of passed arguments is not with range *nmin* to *nmax*. *error_callback*
may be ommited to make the wrapper just throw an error message.
The wrapped function is returned. */
function require_arguments(f, nmin, nmax, error_callback) {
if (!error_callback) {
error_callback = function(n, nmin, nmax) {
throw 'Expected arguments from ' + nmin + ' to ' + nmax + ' (' +
n + ' passed).';
}
}
function wrapper() {
var n_args = arguments.length;
console.log(n_args, nmin, nmax);
console.log((nmin <= 0) && (0 <= nmax));
if ((nmin <= n_args) && (n_args <= nmax)) {
return f.apply(this, arguments);
}
return error_callback(n_args, nmin, nmax);
}
for (e in f) {
wrapper[e] = f[e];
}
return wrapper;
}
var foo = require_arguments(function(a, b, c) {
/* .. */
}, 1, 3);
foo(1);
foo(1, 2);
foo(1, 2, 3);
foo(1, 2, 3, 4); // uncaught exception: Expected arguments from 1 to 3 (4 passed).
foo(); // uncaught exception: Expected arguments from 1 to 3 (0 passed).

Javascript Scope Issues for inner-function

Pretty sure this has been asked already, but I don't know what to search for. Anyway,
var livemarks = [];
var livemarkIds = PlacesUtils.annotations.getItemsWithAnnotation("livemark/feedURI", {});
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
}
});
}
alert(livemarks.length);
I am trying to play a bit with a Firefox addon that's no longer maintained by its creator, just to learn a bit. I recently got an error saying getFeedURI is going to be deprecated and I want to change his old function.
EDIT:
From a function defined in a function (inner function), I am unable to access a var defined in the parent. Why?
E.g. I cannot access var livemarks from inside getLivemark(), or other similar internal functions.
I was checking (scroll down completely): this and his code works fine. So what's wrong with my code? I just wanted to avoid the recursion, if possible.
I suspect the PlacesUtils.livemarks.getLivemark function does its work asynchronously, so your callback is called after you alert the length. Put your alert inside the callback and you should see the correct length (eventually). Here's one way:
var expecting = livemarkIds.length;
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
// ***New stuff***
if (livemarks.length === expecting) {
// Now you have them all, now you can do the next thing
doSomethingWithTheLiveMarks(livemarks);
}
}
});
}
Note that there I put livemarkIds.length into expecting, just in case you do other things with livemarkIds while the function is running. If you aren't, you can just use that directly.
Re your comment below:
However, the system works like this: I get the livemarks in an array. This code is in a class (and method) actually, so another class initializes this one and will call the function getFeeds(), which will return that array of livemarks.
If PlacesUtils.livemarks.getLivemark is asynchronous, it's impossible for getFeeds to return the array as a return value. E.g., it cannot be used like this:
a = b;
c = 42;
feeds = getFeeds(feedIds);
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
The good news is it's really easy to fix: Have getFeeds accept a callback function that it calls when it has the feeds. The code above changes to look like this:
a = b;
c = 42;
feeds = getFeeds(feedIds, function(feeds) {
if (feeds.length === 0) {
// Do something
}
else {
// Do something else
}
});
As you can see, it's a pretty straightforward change. Assuming the loop above is all of getFeeds, then getFeeds ends up looking something like this:
function getFeeds(livemarkIds, callback) {
var livemarks = [];
for (var i = 0; i < livemarkIds.length; i++){
PlacesUtils.livemarks.getLivemark( {id : livemarkIds[i]}, function(result, livemark){
if (result == Components.results.NS_OK){
livemarks.push(livemark);
if (livemarks.length === livemarkIds.length) {
// Done, trigger the callback
callback(livemarks);
}
}
});
}
}
And the pattern continues: If the code calling getFeeds is being called by something else that's expecting a return value from the async stuff, instead of returning that value, you have that code accept a callback, and call the callback from the getFeeds callback. And so on.
Once you get used to it, it's very easy to do. Getting used to it can be tricky. :-)

Can I override the Javascript Function object to log all function calls?

Can I override the behavior of the Function object so that I can inject behavior prior t every function call, and then carry on as normal? Specifically, (though the general idea is intriguing in itself) can I log to the console every function call without having to insert console.log statements everywhere? And then the normal behavior goes on?
I do recognize that this will likely have significant performance problems; I have no intention of having this run typically, even in my development environment. But if it works it seems an elegant solution to get a 1000 meter view on the running code. And I suspect that the answer will show me something deeper about javascript.
The obvious answer is something like the following:
var origCall = Function.prototype.call;
Function.prototype.call = function (thisArg) {
console.log("calling a function");
var args = Array.prototype.slice.call(arguments, 1);
origCall.apply(thisArg, args);
};
But this actually immediately enters an infinite loop, because the very act of calling console.log executes a function call, which calls console.log, which executes a function call, which calls console.log, which...
Point being, I'm not sure this is possible.
Intercepting function calls
Many here have tried to override .call. Some have failed, some have succeeded.
I'm responding to this old question, as it has been brought up at my workplace, with this post being used as reference.
There are only two function-call related functions available for us to modify: .call and .apply. I will demonstrate a successful override of both.
TL;DR: What OP is asking is not possible. Some of the success-reports in the answers are due to the console calling .call internally right before evaluation, not because of the call we want to intercept.
Overriding Function.prototype.call
This appears to be the first idea people come up with. Some have been more successful than others, but here is an implementation that works:
// Store the original
var origCall = Function.prototype.call;
Function.prototype.call = function () {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, lets do it by ourselves.
console.log("Calling",
Function.prototype.toString.apply(this, []),
"with:",
Array.prototype.slice.apply(arguments, [1]).toString()
);
// A trace, for fun
console.trace.apply(console, []);
// The call. Apply is the only way we can pass all arguments, so don't touch that!
origCall.apply(this, arguments);
};
This successfully intercepts Function.prototype.call
Lets take it for a spin, shall we?
// Some tests
console.log("1"); // Does not show up
console.log.apply(console,["2"]); // Does not show up
console.log.call(console, "3"); // BINGO!
It is important that this is not run from a console. The various browsers have all sorts of console tools that call .call themselves a lot, including once for every input, which might confuse a user in the moment. Another mistake is to just console.log arguments, which goes through the console api for stringification, which in turn cause an infinite loop.
Overriding Function.prototype.apply as well
Well, what about apply then? They're the only magic calling functions we have, so lets try that as well. Here goes a version that catches both:
// Store apply and call
var origApply = Function.prototype.apply;
var origCall = Function.prototype.call;
// We need to be able to apply the original functions, so we need
// to restore the apply locally on both, including the apply itself.
origApply.apply = origApply;
origCall.apply = origApply;
// Some utility functions we want to work
Function.prototype.toString.apply = origApply;
Array.prototype.slice.apply = origApply;
console.trace.apply = origApply;
function logCall(t, a) {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, do it ourselves.
console.log("Calling",
Function.prototype.toString.apply(t, []),
"with:",
Array.prototype.slice.apply(a, [1]).toString()
);
console.trace.apply(console, []);
}
Function.prototype.call = function () {
logCall(this, arguments);
origCall.apply(this, arguments);
};
Function.prototype.apply = function () {
logCall(this, arguments);
origApply.apply(this, arguments);
}
... And lets try it out!
// Some tests
console.log("1"); // Passes by unseen
console.log.apply(console,["2"]); // Caught
console.log.call(console, "3"); // Caught
As you can see, the calling parenthesis go unnoticed.
Conclusion
Fortunately, calling parenthesis cannot be intercepted from JavaScript. But even if .call would intercept the parenthesis operator on function objects, how would we call the original without causing an infinite loop?
The only thing overriding .call/.apply does is to intercept explicit calls to those prototype functions. If the console is used with that hack in place, there will be lots and lots of spam. One must furthermore be very careful if it is used, as using the console API can quickly cause an infinite loop (console.log will use .call internally if one gives it an non-string).
I am getting SOME results and no page crashes with the following :
(function () {
var
origCall = Function.prototype.call,
log = document.getElementById ('call_log');
// Override call only if call_log element is present
log && (Function.prototype.call = function (self) {
var r = (typeof self === 'string' ? '"' + self + '"' : self) + '.' + this + ' (';
for (var i = 1; i < arguments.length; i++) r += (i > 1 ? ', ' : '') + arguments[i];
log.innerHTML += r + ')<br/>';
this.apply (self, Array.prototype.slice.apply (arguments, [1]));
});
}) ();
Only tested in Chrome version 9.xxx.
It is certainly not logging all function calls, but it is logging some!
I suspect only actual calls to 'call' intself are being processed
Only a quick test, but it seems to work for me.
It may not be useful this way, but I'm basically restoring the prototype whilst in my replacement's body and then "unrestoring" it before exiting.
This example simply logs all function calls - though there may be some fatal flaw I've yet to detect; doing this over a coffee break
implementation
callLog = [];
/* set up an override for the Function call prototype
* #param func the new function wrapper
*/
function registerOverride(func) {
oldCall = Function.prototype.call;
Function.prototype.call = func;
}
/* restore you to your regular programming
*/
function removeOverride() {
Function.prototype.call = oldCall;
}
/* a simple example override
* nb: if you use this from the node.js REPL you'll get a lot of buffer spam
* as every keypress is processed through a function
* Any useful logging would ideally compact these calls
*/
function myCall() {
// first restore the normal call functionality
Function.prototype.call = oldCall;
// gather the data we wish to log
var entry = {this:this, name:this.name, args:{}};
for (var key in arguments) {
if (arguments.hasOwnProperty(key)) {
entry.args[key] = arguments[key];
}
}
callLog.push(entry);
// call the original (I may be doing this part naughtily, not a js guru)
this(arguments);
// put our override back in power
Function.prototype.call = myCall;
}
usage
I've had some issues including calls to this in one big paste, so here's what I was typing into the REPL in order to test the above functions:
/* example usage
* (only tested through the node.js REPL)
*/
registerOverride(myCall);
console.log("hello, world!");
removeOverride(myCall);
console.log(callLog);
You can override Function.prototype.call, just make sure to only apply functions within your override.
window.callLog = [];
Function.prototype.call = function() {
Array.prototype.push.apply(window.callLog, [[this, arguments]]);
return this.apply(arguments[0], Array.prototype.slice.apply(arguments,[1]));
};
I found it easiest to instrument the file, using an automatic process. I built this little tool to make it easier for myself. Perhaps somebody else will find it useful. It's basically awk, but easier for a Javascript programmer to use.
// This tool reads a file and builds a buffer of say ten lines.
// When a line falls off the end of the buffer, it gets written to the output file.
// When a line is read from the input file, it gets written to the first line of the buffer.
// After each occurrence of a line being read from the input file and/or written to the output
// file, a routine is given control. The routine has the option of operating on the buffer.
// It can insert a line before or after a line that is there, based on the lines surrounding.
//
// The immediate case is that if I have a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// I want to change that to a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`compareNums: function (a, c, d) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// We are trying to figure out how a set of functions work, and I want each function to report
// its name when we enter it.
//
// To save time, options and the function that is called on each cycle appear at the beginning
// of this file. Ideally, they would be --something options on the command line.
const readline = require('readline');
//------------------------------------------------------------------------------------------------
// Here are the things that would properly be options on the command line. Put here for
// speed of building the tool.
const frameSize = 10;
const shouldReportFrame = false;
function reportFrame() {
for (i = frame.length - 1; i >= 0; i--) {
console.error(`${i}. ${frame[i]}`); // Using the error stream because the stdout stream may have been coopted.
}
}
function processFrame() {
// console.log(`******** ${frame[0]}`);
// if (frame[0].search('console.log(\`arguments.callee = \$\{arguments.callee.toString().substr(0,100)\}\`);') !== -1) {
// if (frame[0].search('arguments.callee') !== -1) {
// if (frame[0].search(/console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/) !== -1) {
var matchArray = frame[0].match(/([ \t]*)console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/);
if (matchArray) {
// console.log('******** Matched');
frame.splice(1, 0, `${matchArray[1]}console.log('${frame[1]}');`);
}
}
//------------------------------------------------------------------------------------------------
var i;
var frame = [];
const rl = readline.createInterface({
input: process.stdin
});
rl.on('line', line => {
if (frame.length > frameSize - 1) {
for (i = frame.length - 1; i > frameSize - 2; i--) {
process.stdout.write(`${frame[i]}\n`);
}
}
frame.splice(frameSize - 1, frame.length - frameSize + 1);
frame.splice(0, 0, line);
if (shouldReportFrame) reportFrame();
processFrame();
// process.stdout.write(`${line}\n`); // readline gives us the line with the newline stripped off
});
rl.on('close', () => {
for (i = frame.length - 1; i > -1; i--) {
process.stdout.write(`${frame[i]}\n`);
}
});
// Notes
//
// We are not going to control the writing to the output stream. In particular, we are not
// going to listen for drain events. Nodejs' buffering may get overwhelmed.
//

Categories

Resources