Does anyone know of a way to eval a string so that if it (or a function it defines) generates an error, the line and column numbers shown in the stack trace will be offset by an amount specified in advance? Alternatively, suppose I want to break up a long source string into chunks and evaluate them separately, but still get stack traces that look as though the entire string was evaluated in one go. Is there any way to achieve this effect, except for using empty lines and columns? (I need a browser-based solution, preferably cross-browser, but I can settle for something that works on at least one of the major browsers.)
-
Probably sourcemaps are the way to go, but I have no idea whether they work with `eval`. – Bergi Apr 04 '16 at 12:40
-
I think a sourcemap would have to be generated by a tool that concatenates the chunks into a single string to be evaluated, but I specifically need to evaluate the chunks separately, because functions defined in a certain chunk may be used in the process of generating later chunks. – user3026691 Apr 04 '16 at 12:56
-
Why do you want to do this? – Apr 04 '16 at 14:08
-
I have a custom programming language that gets compiled into a subset of javascript. It has a lispy macro system, so it's possible to define a macro at any given point and use it in subsequent code, which means that it must be evaluated before anything else. To make matters worse, a macro can also use previously-defined normal functions, so they have to be evaluated before the macro. The problem is that source locations in stack traces become useless this way, since they overlap and I can't unambiguously map them back to the original source code. – user3026691 Apr 04 '16 at 14:21
-
if you don't need it to be sync, then turning the code into a data/blob URL and adding it as a script tag will allow error messages to show the right line and col numbers, unlike a raw `eval` – dandavis Apr 09 '16 at 22:44
-
@dandavis: The line/column numbers are still useless (at least when done dynamically), but it does associate the error with its unique base64 string. I could use this to look up the correct offsets in a dictionary. This can be made synchronous by looping until a flag is set by the onload callback. This is great! Post this as an answer and I will accept it. – user3026691 Apr 12 '16 at 16:24
-
@user3026691: i'm too swamped atm for a proper writeup, but glad to have helped. if you get something working consider posting a self-answer. – dandavis Apr 13 '16 at 02:12
2 Answers
I don't think is it possible because the underlying mechanism that is assumed working is actually deprecated. For security reasons browsers don't pass the error object to Javascript anymore.
However, since you are working with a custom programming language that gets compiled into Javascript, you know what the structure of the resulting script will be. You could also introduce statement counters in the resulting Javascript, so you can always know what the last thing executed was. Something like:
function(1); function(2);
function(3);
could be translated as:
var __column=0;
var __line=0;
function(1); __column+=12;
function(2); /*__column+=12;*/ __line++; __column=0;
function(3); /*__column+=12;*/ __line++; __column=0;
Where 12 is "function(n);".length
.Of course, the resulting code is ugly, but you could enable this behaviour with a debug flag or something.

- 1
- 1

- 6,293
- 3
- 30
- 46
-
It is possible to get the error object, but that doesn't really help when the provided line/column pairs aren't unique. (And they aren't, since I eval many chunks of code separately.) What I'm interested in is somehow telling the JS engine to generate a suitable stack trace to begin with. This is possible in NodeJS using its vm module, for instance, and I'm looking for a way to do the same on a browser. – user3026691 Apr 08 '16 at 16:20
-
As for the proposed solution, the last spot where execution took place isn't particularly useful on its own, so I'd have to maintain a stack of them wherever a function is entered/returned from. I'd also have to flatten things like a(b(c)) or even a+b+c using temporary variables. That's lot of boilerplate for every operation, which is undesirable for my main use case (game development). Even if I could put up with the performance hit during the development process, I'd have turn this feature off in the deployed product, so any errors that happen to users would be hard to debug. – user3026691 Apr 08 '16 at 16:20
The best solution I've found so far is to prepend a sourceURL directive to each string before it's eval'ed, giving it a marker in the form of a unique file name in the stack trace. Stack traces are then parsed (using the parser component stacktracejs) and corrected by looking up the line offsets associated with the markers.
var evalCounter = 0;
var lineCounter = 0;
var lineOffsetTable = {};
function myEval(code) {
lineOffsetTable[evalCounter] = lineCounter;
lineCounter += countLines(code);
return eval("//# sourceURL=" + (evalCounter++) + "\n" + code);
}
window.onerror = function(errorMsg, url, lineNumber, column, e) {
var stackFrames = ErrorStackParser.parse(e);
logStackTrace(stackFrames.map(function(f) {
if(f.fileName in lineOffsetTable)
f.lineNumber += lineOffsetTable[f.fileName];
return f;
}));
};
Unfortunately, this only works in Firefox at the moment. Chrome refuses to pass the error object to the onerror callback (a problem which only happens with eval'ed code, strangely enough) and IE ignores the sourceURL directive.

- 497
- 2
- 11