I've been building a basic live-evaluation javascript development environment (I call it the WEPL.) over the past few days, and realized it'd be nice to be able to associate error messages to line numbers. Unfortunately, eval() doesn't provide a nice way to do this, that I can find.
The solution I've come up with so far is to transform the source before eval() so that it's a set of nested calls to a wrapper to eval() that records some information before eval, checks to see if the eval succeeds, and then uses that info to output more useful troubleshooting information to the user.
My question is, why might this be a bad idea? What problems do I need to solve to make sure this works well?
An example of the sort of transformation I mean, just to make this concrete.
This
if (cond) {
return foo + bar;
}
else {
return baz + quux;
}
becomes this
if (myEval('cond')) {
return myEval("myEval(\"foo\") + myEval(\"bar\")");
else {
return myEval("myEval(\"baz\") + myEval(\"quux\")");
}
Where I obviously didn't wrap the highest level, though I could've, and the programmatic version would.
This won't work if you want to accept even remotely complex scripts. A few potential problems:
var i = 1; // global scope
!function() {
var i = 2; // function scope
}();
alert(i); // 1
vs.
myEval('var i = 1;'); // global scope
myEval('!function() {
myEval(\'var i = 2;\'); // eval has global scope, always
}();');
myEval('alert(i);'); // 2
!function() {
var i = 1; // local to outer function
!function() { // inherits context from outer function
alert(i); // 1
}();
}();
vs.
myEval('!function() {
myEval(\'var i = 1;\'); // local to outer function
myEval(\'!function() { // eval has global scope; myEval inherits from wherever it was defined
myEval(\\\'alert(i);\\\'); // undefined
}();\');
}();');
var obj = {
n: 1,
f: function() {
return this.n; // this is the object f is called from
}
}
alert(obj.f()); // 1
vs.
myEval('var obj = {
n: myEval(\'1\'),
f: myEval(\'function() {
return myEval(\\\'this.n\\\'); // this is always the window in eval
}')
}');
myEval('alert(obj.f());'); // undefined
You need to escape every quote, and you need to escape escape signs as well. In code with lots of objects, closures, inner functions etc. this will result in escape signs becoming unmanageable:
!function() {
$(function() {
$('#foo').click(function() {
setTimeout(function() {
$.post('/', function(res) {
log(res);
});
}, 1000);
});
});
}();
(note that this is not a particularly contrived or complicated example, it only involves a delyed action with a callback firing on a certain event)
myEval('!function() {
myEval(\'$(myEval(\\\'function() {
myEval(\\\\\\\'$(\\\\\\\'#foo\\\\\\\').click(myEval(\\\\\\\\\\\\\\\'function() {
myEval(\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'setTimeout(myEval(\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'function() {
myEval(\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'$.post('/', myEval(\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'function(res) {
myEval(\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'log(res);\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\');
}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'));\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\');
}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'), 1000);\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\');
}\\\\\\\\\\\\\\\'));\\\\\\\');
}\\\'));\');
}();');
You can probably see what I am getting at.
I had to do something similar when working on an extension for IE. I ended up creating a global variable called 'lineNumber' and I transformed the code to be more like this:
lineNumber = 1; if (cond) {
lineNumber = 2; return foo + bar;
}
else {
lineNumber = 5; return baz + quux;
}
Of course, I made to make sure I used curly braces around all blocks and had to keep my coding simple enough to avoid confusing my poor lame parser -- but it got me through it.
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