...

View Full Version : evaluating a function literal



Arielladog
06-26-2004, 01:13 AM
Hey guys,

I want to go back through a lot of functions and add a statistics timer to them and do some other things to keep up with performance, so I'm looping through a bunch of functions, changing the function to a string, and adding the stuff i want.

The real problem is making the string a function again.

The problem is I can go:

x = eval("new Function()");

but I cannot go:

x = eval("function(){}");

Is there a work around for this? Basically I just want to be able to name my functions (not have anonymous ones) for the purpose of figuring out for performance reasons where each call came from (when I call to find a functions caller, i don't want an anonymous function returned but something where i can extract its name out of)

Thanks,
aDog :cool:

SpiritualStorms
06-26-2004, 01:24 AM
I am probably not the right person for this, but i would think you might want to create a special function that basically keeps tracks of all your functions, and what they do, and all that by using the prototype property in your constructor. I would think this is really about the only thing you would be able to do.

Another way would be by going ahead, and storing all your function names in an array, and then basically manipulating the array in whatever manner concievable.

Arielladog
06-26-2004, 02:40 AM
Hey guys,

I think I've found a way around it:

x = eval("function(){}");
//x == undefined

eval("x = function(){}");
//x == function(){}

I guess a function literal doesn't return anything like the function constructor does. Maybe someone smarter can explain clearer or in more technical detail about what's going on.

aDog :cool:

SpiritualStorms
06-26-2004, 05:07 AM
You said you thought you had found a way around it. My question is, does it work? Does it give you a collection of functions in your document? To what degree do you get detailed information on each, and everyone of your functions? I ask because i will inevitably want to do something similar in the future. I am still in the sandbox stage of learning this programming language. I am one of those people that require a full explanation behind the reasoning, rather than just a quick snapshot explanation of how to do things. I am of the opinion that if you cant explain why certain things are done as they are, that chances are you will not be able to do your own coding. You will remain, in other words, in the copy, and paste, stage.

Willy Duitt
06-26-2004, 06:09 PM
Heh aDog;

Glad to see you out and about. :D
Sorry, but I do not have an answer to your question because quite frankly it is way beyond my limited understanding of javascript. :eek:


I am of the opinion that if you cant explain why certain things are done as they are, that chances are you will not be able to do your own coding. You will remain, in other words, in the copy, and paste, stage.

SpiritualStorms;

Trust me aDog is far from a cut & paster and I am sure that if you asked nicely he would break it down for you. But I am of the opinion that you are not yet advanced enough to understand what he is trying to do. :rolleyes:

Cheers;
.....Willy

Arielladog
06-26-2004, 08:09 PM
Hey Willy,

I do come here sometimes when I have a pressing question or am looking for a real explanation and not one I just make up :D And I def. wouldn't sell yourself short...there's nothing really special in the code I'm using (below) :o

Spiritual, JavaScript is a GREAT starting language, and I really love working with in. I just started working on a large project with it--it is a powerful language. Since you're learning, I figure this is a good time to plug my article:

http://www.sitepoint.com/article/470
http://www.sitepoint.com/article/473

FOr people who have OOP experience, it's poorly written but for those with basic javascript experience (you might need to get some more under your belt first), it's still poorly written but should help a lot :D

Ok, now that I've plugged myself, the actual code I'm working on:

Basically we have a bunch of objects like:

function myLayer(){}
myLayer.prototype.functionName = function(){}

What I'd like to do is analyze the performance of what's going on. I'd liek to keep track of how long a function takes, how many times a function is called, who called the function, etc... These sould be stored in our stats object.

THe big problems are I'm too lazy to insert code into the beginning and end of all of our functions (like to start a timer, end a timer, etc...) and that we made a bunch of anonymous functions like above, hard to extract a meaninful name from an anonymous function. Anyway, the code I'm using is:



stats = new Object();
stats.values = new Array();

stats.startUp = function (){
for(var i in window){
if(typeof window[i] == "function" && window[i].toString().toLowerCase().indexOf("native code") == -1){
stats.changeObject(window[i].prototype);
}
}
}

stats.changeObject = function (obj){
for(var i in obj){
if(typeof obj[i] == "function"){
eval("obj[i] = " + stats.changeFunction(obj[i],i));
}
}
}

stats.changeFunction = function(obj, name){
var startStuff = "";
startStuff += "var statNumber = stats.values.length;";
startStuff += "stats.values.push(new Object());";
startStuff += "stats.values[statNumber].traceStack = stats.traceStack(arguments.callee);";
startStuff += "stats.values[statNumber].object = this;";
startStuff += "var startTimer = new Date();";
var endStuff = "var endTimer = new Date();";
endStuff += "stats.values[statNumber].timer = endTimer - startTimer;";

var fString = obj.toString();
var fBody = fString.substring( fString.indexOf("{")+1, fString.lastIndexOf("}") );
var fArg = fString.substring( fString.indexOf("(")+1, fString.indexOf(")") );
var retString = "function " + name + "(";
retString += fArg;
retString += ") {\n";
retString += startStuff;
//fBody = fBody.split("return ").join(endStuff+";return ");
retString += fBody;
retString += endStuff;
retString += "}";

return retString;
}

stats.traceStack = function(obj){
if(obj == null) return "";
return arguments.callee(obj.caller) + " -> " + stats.getFunctionName(obj);
}

stats.getFunctionName = function(fObj){
var str = fObj.toString();
var fName = '';
fName = str.substring(str.indexOf("function")+"function".length,str.indexOf("("))
if(fName.length < 2) fName = "anonymous";
return fName;
}


The way you'd start up this process should be:

stats.startUp();

One thing I've noticed is that with so many objects, it slows down the code considerably, so instead of doing it for every object at the same time, I might end up just selecting an object or two to "watch"

The startUp function loops through to find all global functions that aren't built-in (no "native code"). These are the functions we're using as constructors for the user-defined objects.

Then, once it finds an appropriate constructor, it calls stats.changeObject on it's prototype. Then in changeObject, it loops through all the functions defined in the prototype object. Then, once it finds one, it calls this line (which is what I was having trouble with):

eval("obj[i] = " + stats.changeFunction(obj[i],i));

Before going on to that function, I'll go to describe two helper functions traceStack and getFunctionName. The latter takes a function object, converts it to a string, and gets it's name. So if it was given:

"function ryan(){}"

It'd hopefully extract "ryan" out of that. The other function trace's the calls to the function. First of inside each function, we have the arguments variable, and arguments.callee refers to that particular function.

function helpMe(){
//arguments.callee == helpMe
}

The next thing is functionName.caller refers to the function that called that particular function. Example:

function helpMe(){
helpMe2();
//arguments.callee == helpMe
//helpMe.caller == arguments.callee.caller == null (it's called from top-level)
}

function helpMe2(){
//arguments.callee == helpMe2
//helpMe2.caller == arguments.callee.caller == helpMe
// arguments.callee.caller.caller == null (it's what called helpMe)
}

So the traceStack function loops through the caller() to figure out what function called what function called what function .... called this function

Now, changeFunction() convers the function to a string. Adding in our stats lines where appropriate (at beginning and before our function ends--even if it ends prematurely with a return line). It also adds in the function name to name all our anonymous functions.




Anyway guys, this is a work in progress, so any comments (if any) would be helpful. Hopefully I'll add a function to make a new window and output the results in a easy-to-uderstand format (this could be the hardest part :D)

Thanks,
aDog :cool:

SpiritualStorms
06-27-2004, 12:13 AM
LOL... Did you just insult me?


SpiritualStorms;

Trust me aDog is far from a cut & paster and I am sure that if you asked nicely he would break it down for you. But I am of the opinion that you are not yet advanced enough to understand what he is trying to do.

Cheers;
.....Willy

Not sure how to take any of that.

SpiritualStorms
06-27-2004, 12:17 AM
From Willy:

Trust me aDog is far from a cut & paster

I never said he was a paste, and copy, sort of fellow. I was speaking generally.

Willy Duitt
06-27-2004, 12:43 AM
LOL... Did you just insult me?

Not at all.

However, being familar with aDog I knew that anything he was working on was more than likely over my head as well as yours. :D

aDog;

Thanks for the explanation but that gave me a headache. :eek:
Perhaps in a few more monthes I'll be able to wrap my head around it. And unfortunetely, our resident expert, Liorean is on walkabout for the summer but there are several other members here whom should be able to provide a real explanation.

.....Willy

glenngv
06-28-2004, 04:54 AM
Try changing this:

eval("obj[i] = " + stats.changeFunction(obj[i],i));

to:

obj[i] = stats.changeFunction(obj[i],i);


If that doesn't work, try this:


obj[i] = function(){
var statNumber = stats.values.length;
stats.values.push(new Object());
stats.values[statNumber].traceStack = stats.traceStack(arguments.callee);
stats.values[statNumber].object = this;
var startTimer = new Date();

var ret = obj[i]();

var endTimer = new Date();
stats.values[statNumber].timer = endTimer - startTimer;
return ret;
}

I'm not sure if that will work. Just experimenting...:)

Arielladog
06-28-2004, 06:15 AM
Hey Glenn,

THanks for the reply, unfortunatley, neither work :D

I think the problem with the first solution is that it just changes the function intro a string. So when I tried a simple example, Mozilla said "____ is not a function"

For the second on, the problem I got with Mozilla is "too much recursion" The problem with the solution is that the function doesn't "get evaluated" until runtime. At runtime, the function just points back to itself, so that'd explain the "too much recursion"

Thanks though!

Ryan

glenngv
06-28-2004, 06:50 AM
Try saving the function to variable.


var tempFunc=obj[i];
obj[i] = function(){
var statNumber = stats.values.length;
stats.values.push(new Object());
stats.values[statNumber].traceStack = stats.traceStack(arguments.callee);
stats.values[statNumber].object = this;
var startTimer = new Date();

var ret = tempFunc();

var endTimer = new Date();
stats.values[statNumber].timer = endTimer - startTimer;
return ret;
}

You may try this also but using the Function constructor is not efficient because it behaves just like eval.

obj[i] = new Function(stats.changeFunction(obj[i],i));

Arielladog
06-28-2004, 09:13 AM
Hey Glenn,

I think your solution will probably work. Iguess I was probably too caught up on converting the function to a string and doing it like that. Thanks for opening my eyes :thumbsup: Only problem I forsee is "this" and the arguments. So I'm gonna try something like:


var ret = tempFunc.apply(this,arguments);

I'll see how it goes tomorrow (don't feel like trying it now as its a little late ;) ), but that code should work and will def. speed things up a lot!

Thanks,
aDog :cool:

Arielladog
06-28-2004, 07:24 PM
ONe problem: then I can't easily name the function; thus, I can't easily extract the functionname when doing a trace stack :D

aDog :cool:

Arielladog
06-28-2004, 09:55 PM
The code works seemingly well for small stuff, but for larger stuff I've been running into a problem.

When I have it "turned on", it slows down the code immensely, and I don't really understand why.

I'd understand if startUp() took a while to run because it has to eval() stuff, but once it's evalled() that should be it, right?

What's really slow I think is the traceStack() function. Does anyone see a problem with it?

I've tried changing it to:


stats.traceStack = function(obj){
if(obj == null || obj == obj.caller || obj == arguments.callee) return "";
try{
return arguments.callee(obj.caller) + " -> " + stats.getFunctionName(obj);
}catch(e){
return " -> " + stats.getFunctionName(obj);
}

}


But it still seems to be really slow. Any ideas?


I could use glenn's solution and not have to eval() and just in loop give each function a "name" property. Then I could use that to get each function's name and thus use anonymouse functions. However, it really doesn't matter, as it's definately the traceStack function that's really slow. WHen I take it out, it works perfectly. Ideas???


Thanks,
aDog :cool:



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum