Javascript remove last item in stack trace string - javascript

I am creating a Javascript logger, in which, on error messages, I am also adding the stack trace like this:
function logMessage(logMessage)
{
let stackTrace = new Error().stack;
logMessage.stackTrace = stackTrace;
...
}
This gives me the stack trace, but it also obviously adds the method of logMessage itself as the last item on the stack...
How can I remove the last trace so I'll only see the trace up until the point that the logMessage was called, but without the logMessage itself?

The way to do it is really simple since the stack we are getting is a string divided by \n, in this format:
ERROR \n
at ... \n
at ... \n
so all we need to do is:
let stackTrace = new Error().stack; //get the stack trace string
let arr = stackTrace.split("\n"); //create an array with all lines
arr.splice(1,1); //remove the second line (first line after "ERROR")
stackTrace = arr.join("\n"); //join array back to a string

Stack trace returns a multiline string starting with the error message and then all lines starting with at {function name/position};
You can simply change the multiline string and bypassing the first occurence of at {function name/position} with split, filter and join
stackTrace.split('\n').filter(function(line, index) { return index !== 1 }).join('\n');
See snippet example
function deepFunctionStack() {
return new Error().stack;
}
function layer1Function() {
return deepFunctionStack();
}
function topLayerFunction() {
return layer1Function();
}
var originalStack = topLayerFunction();
var formattedStack = originalStack.split('\n').filter(function(line, index) { return index !== 1 }).join('\n');
document.write('Original Stack:');
document.write('<pre>' + originalStack + '</pre>');
document.write('Formatted Stack:');
document.write('<pre>' + formattedStack + '</pre>');

Here is an additional version which handles the differences between Firefox, Chrome, & IE stack traces. It will remove the initial "ERROR" line from Chrome and IE. It is also significantly faster than other versions listed here.
// stack: string - call stack string
// levels: int - number of levels to remove from the top of the call stack
function trimCallStack(stack, levels) {
if (stack) {
const newLineChar = "\n"; // Each line deliminated by '\n'
const isFirefoxCallStack = stack.indexOf("#") > -1; // If stacktrace contains '#' it is FireFox
// remove an additional line if browser is NOT FireFox (i.e. Chrome or IE) since those start stack trace with the error name
// remove N additional lines specified by `levels`
let iterations = (isFirefoxCallStack ? 0 : 1) + (levels ?? 0);
let start = 0;
while(iterations-- && start !== -1) {
start = stack.indexOf(newLineChar, start + 1);
}
stack = start !== -1 ? stack.substring(start + 1) : ""; // start === -1 if removing more lines than exist, so return "" in that case
}
return stack || "";
}
Examples of stack traces from FireFox and Chrome/IE:
Chrome/IE stacktrace:
Error: fail
at test (<anonymous>:2:8)
at test1 (<anonymous>:5:5)
at test2 (<anonymous>:8:5)
at test3 (<anonymous>:11:5)
at <anonymous>:1:5
Firefox stacktrace:
test#debugger eval code:2:8
test1#debugger eval code:5:5
test2#debugger eval code:8:5
test3#debugger eval code:11:5
#debugger eval code:1:5
After using the provided function:
Chrome/IE stacktrace:
at test (<anonymous>:2:8)
at test1 (<anonymous>:5:5)
at test2 (<anonymous>:8:5)
at test3 (<anonymous>:11:5)
at <anonymous>:1:5
Firefox stacktrace:
test#debugger eval code:2:8
test1#debugger eval code:5:5
test2#debugger eval code:8:5
test3#debugger eval code:11:5
#debugger eval code:1:5

Related

How to find line number in function which generated error in typescript?

I want know on executing code if some error is occured between try then need to know the linenumer in that function.
I have tried Tracer It gives me Function name,stack trace but line number given by it is wrong because it gives me line number from where trace logger was called not from where errro occured.
I have tried below code which can give me linenumber
debugLine(error:Error) {
if (error.stack!=undefined) {
let frame = error.stack.split("\n")[1];
let lineNumber = frame.split(":")[1];
let functionName = frame.split(" ")[5];
return functionName + ":" + lineNumber + " " + error.message;
}
}
But if stack trace is complex then it does not guarantee the correct linenumber. So which is the accurate and fast way to get the linenumber ?

JS: getting error line number inside `new Function(...)`

In my application I parse some user input and then run it as Javascipt code using (new Function(...))(). If the input is incorrect, this throws an exception. What I need is a way to get the line number where the exception happened in the parsed string that had been provided to new Function(). Is it possible?
For this we need to write a logic to capture the stacktrace from the error object and find out where exactly the anonymous function has indicated the error has been thrown.
The line number where the error is thrown in Chrome is indicated as <anonymous>:5:17, where as in Firefox it is Function:5:17
try{
(new Function(`var hello = 10;
const world = 20;
let foo = 'bar';
xyz; //simulating error here
`))();
}catch(err){
let line = err.stack.split("\n").find(e => e.includes("<anonymous>:") || e.includes("Function:"));
let lineIndex = (line.includes("<anonymous>:") && line.indexOf("<anonymous>:") + "<anonymous>:".length) || (line.includes("Function:") && line.indexOf("Function:") + "Function:".length);
console.log(+line.substring(lineIndex, lineIndex + 1) - 2);
}

Unable to Get Output From While Loop in Javascript

I'm working on my final project of the Winter 2017 quarter to demonstrate how to use Regular Expressions in both C# and JavaScript code behind pages. I've got the C# version of my demonstration program done, but the JavaScript version is making me pull what little hair I have left on my head out (no small achievement since I got a fresh buzz cut this morning!). The problem involves not getting any output after applying a Regular Expression in a While loop to get each instance of the expression and printing it out.
On my HTML page I have an input textarea, seven radio buttons, an output textarea, and two buttons underneath (one button is to move the output text to the input area to perform multiple iterations of applying expressions, and the other button to clear all textareas for starting from scratch). Each radio button links to a function that applies a regular expression to the text in the input area. Five of my seven functions work; the sixth is the one I can't figure out, and the seventh is essentially the same but with a slightly different RegEx pattern, so if I fix the sixth function, the seventh function will be a snap.
(I tried to insert/upload a JPG of the front end, but the photo upload doesn't seem to be working. Hopefully you get the drift of what I've set up.)
Here are my problem children from my JS code behind:
// RegEx_Demo_JS.js - code behind for RegEx_Demo_JS
var inputString; // Global variable for the input from the input text box.
var pattern; // Global variable for the regular expression.
var result; // Global variable for the result of applying the regular expression to the user input.
// Initializes a new instance of the StringBuilder class
// and appends the given value if supplied
function StringBuilder()
{
var strings = [];
this.append = function (string)
{
string = verify(string);
if (string.length > 0) strings[strings.length] = string;
}
this.appendLine = function (string)
{
string = verify(string);
if (this.isEmpty())
{
if (string.length > 0) strings[strings.length] = string;
else return;
}
else strings[strings.length] = string.length > 0 ? "\r\n" + string : "\r\n";
}
this.clear = function () { strings = []; };
this.isEmpty = function () { return strings.length == 0; };
this.toString = function () { return strings.join(""); };
var verify = function (string)
{
if (!defined(string)) return "";
if (getType(string) != getType(new String())) return String(string);
return string;
}
var defined = function (el)
{
// Changed per Ryan O'Hara's comment:
return el != null && typeof(el) != "undefined";
}
var getType = function (instance)
{
if (!defined(instance.constructor)) throw Error("Unexpected object type");
var type = String(instance.constructor).match(/function\s+(\w+)/);
return defined(type) ? type[1] : "undefined";
}
}
Within the code of the second radio button (which will be the seventh and last function to complete), I tested the ScriptBuilder with data in a local variable, and it ran successfully and produced output into the output textarea. But I get no output from this next function that invokes a While loop:
function RegEx_Match_TheOnly_AllInstances()
{
inputString = document.getElementById("txtUserInput").value;
pattern = /(\s+the\s+)/ig; // Using an Flag (/i) to select either lowercase or uppercase version. Finds first occurrence either as a standalone word or inside a word.
//result = pattern.exec(inputString); // Finds the first index location
var arrResult; // Array for the results of the search.
var sb = getStringBuilder(); // Variable to hold iterations of the result and the text
while ((arrResult = pattern.exec(inputString)) !==null)
{
sb.appendLine = "Match: " + arrResult[0] ;
}
document.getElementById("txtRegExOutput").value = sb.toString();
/* Original code from C# version:
// string pattern = #"\s+(?i)the\s+"; // Same as above, but using Option construct for case insensitive search.
string pattern = #"(^|\s+)(?i)the(\W|\s+)";
MatchCollection matches = Regex.Matches(userTextInput, pattern);
StringBuilder outputString = new StringBuilder();
foreach (Match match in matches)
{
string outputRegExs = "Match: " + "\"" + match.Value + "\"" + " at index [" + match.Index + ","
+ (match.Index + match.Length) + "]" + "\n";
outputString.Append(outputRegExs);
}
txtRegExOutput.Text = outputString.ToString();
*/
} // End RegEx_Match_The_AllInstances
I left the commented code in to show what I had used in the C# code behind version to illustrate what I'm trying to accomplish.
The test input/string I used for this function is:
Don’t go there. If you want to be the Man, you have to beat The Man.
That should return two hits. Ideally, I want it to show the word that it found and the index where it found the word, but at this point I'd be happy to just get some output showing every instance it found, and then build on that with the index and possibly the lastIndex.
So, is my problem in my While loop, the way I'm applying the StringBuilder, or a combination of the two? I know the StringBuilder code works, at least when not being used in a loop and using some test data from the site I found that code. And the code for simply finding the first instance of "the" as a standalone or inside another word does work and returns output, but that doesn't use a loop.
I've looked through Stack Overflow and several other JavaScript websites for inspiration, but nothing I've tried so far has worked. I appreciate any help anyone can provide! (If you need me to post any other code, please advise and I'll be happy to oblige.)

IE8 fails on return line of titleCase function

This script that I found on SO seems to be giving IE8 problems. What could the issue with it be?
function titleCase (str, glue){
glue = (glue) ? glue : ['of', 'for', 'and'];
return str.replace(/(\w)(\w*)/g, function(_, i, r){
var j = i.toUpperCase() + (r !== null ? r : "");
return (glue.indexOf(j.toLowerCase())<0)?j:j.toLowerCase();
});
}
It says about the line return (glue.indexOf(j.toLowerCase())<0)?j:j.toLowerCase();
Error: Object doesn't support this property or method
Also, it seems to take issue with line 14 of jquery. Which is the very first line that isn't a comment (function( window, undefined ) {. But that is when I have it "showing all script errors", so maybe that's just a warning and not really something that's causing it actual problems.

How to generate call-graphs for given javascript? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I have seen "https://stackoverflow.com/questions/1385335/how-to-generate-function-call-graphs-for-javascript", and tried it. It works well, if you want to get an abstract syntax tree.
Unfortunately Closure Compiler only seems to offer --print_tree, --print_ast and --print_pass_graph. None of them are useful for me.
I want to see a chart of which function calls which other functions.
code2flow does exactly this. Full disclosure, I started this project
To run
$ code2flow source1.js source2.js -o out.gv
Then, open out.gv with graphviz
Edit: For now, this project is unmaintained. I would suggest trying out a different solution before using code2flow.
If you filter the output of closure --print_tree you get what you want.
For example take the following file:
var fib = function(n) {
if (n < 2) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
};
console.log(fib(fib(5)));
Filter the output of closure --print_tree
NAME fib 1
FUNCTION 1
CALL 5
NAME fib 5
SUB 5
NAME a 5
NUMBER 1.0 5
CALL 5
NAME fib 5
SUB 5
NAME a 5
NUMBER 2.0 5
EXPR_RESULT 9
CALL 9
GETPROP 9
NAME console 9
STRING log 9
CALL 9
CALL 9
NAME fib 9
CALL 9
CALL 9
NAME fib 9
NUMBER 5.0 9
And you can see all the call statements.
I wrote the following scripts to do this.
./call_tree
#! /usr/bin/env sh
function make_tree() {
closure --print_tree $1 | grep $1
}
function parse_tree() {
gawk -f parse_tree.awk
}
if [[ "$1" = "--tree" ]]; then
make_tree $2
else
make_tree $1 | parse_tree
fi
parse_tree.awk
BEGIN {
lines_c = 0
indent_width = 4
indent_offset = 0
string_offset = ""
calling = 0
call_indent = 0
}
{
sub(/\[source_file.*$/, "")
sub(/\[free_call.*$/, "")
}
/SCRIPT/ {
indent_offset = calculate_indent($0)
root_indent = indent_offset - 1
}
/FUNCTION/ {
pl = get_previous_line()
if (calculate_indent(pl) < calculate_indent($0))
print pl
print
}
{
lines_v[lines_c] = $0
lines_c += 1
}
{
indent = calculate_indent($0)
if (indent <= call_indent) {
calling = 0
}
if (calling) {
print
}
}
/CALL/ {
calling = 1
call_indent = calculate_indent($0)
print
}
/EXPR/{
line_indent = calculate_indent($0)
if (line_indent == root_indent) {
if ($0 !~ /(FUNCTION)/) {
print
}
}
}
function calculate_indent(line) {
match(line, /^ */)
return int(RLENGTH / indent_width) - indent_offset
}
function get_previous_line() {
return lines_v[lines_c - 1]
}
I finally managed this using UglifyJS2 and Dot/GraphViz, in a sort of combination of the above answer and the answers to the linked question.
The missing part, for me, was how to filter the parsed AST. It turns out that UglifyJS has the TreeWalker object, which basically applys a function to each node of the AST. This is the code I have so far:
//to be run using nodejs
var UglifyJS = require('uglify-js')
var fs = require('fs');
var util = require('util');
var file = 'path/to/file...';
//read in the code
var code = fs.readFileSync(file, "utf8");
//parse it to AST
var toplevel = UglifyJS.parse(code);
//open the output DOT file
var out = fs.openSync('path/to/output/file...', 'w');
//output the start of a directed graph in DOT notation
fs.writeSync(out, 'digraph test{\n');
//use a tree walker to examine each node
var walker = new UglifyJS.TreeWalker(function(node){
//check for function calls
if (node instanceof UglifyJS.AST_Call) {
if(node.expression.name !== undefined)
{
//find where the calling function is defined
var p = walker.find_parent(UglifyJS.AST_Defun);
if(p !== undefined)
{
//filter out unneccessary stuff, eg calls to external libraries or constructors
if(node.expression.name == "$" || node.expression.name == "Number" || node.expression.name =="Date")
{
//NOTE: $ is from jquery, and causes problems if it's in the DOT file.
//It's also very frequent, so even replacing it with a safe string
//results in a very cluttered graph
}
else
{
fs.writeSync(out, p.name.name);
fs.writeSync(out, " -> ");
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
}
}
else
{
//it's a top level function
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
}
}
}
if(node instanceof UglifyJS.AST_Defun)
{
//defined but not called
fs.writeSync(out, node.name.name);
fs.writeSync(out, "\n");
}
});
//analyse the AST
toplevel.walk(walker);
//finally, write out the closing bracket
fs.writeSync(out, '}');
I run it with node, and then put the output through
dot -Tpng -o graph_name.png dot_file_name.dot
Notes:
It gives a pretty basic graph - only black and white and no formatting.
It doesn't catch ajax at all, and presumably not stuff like eval or with either, as others have mentioned.
Also, as it stands it includes in the graph: functions called by other functions (and consequently functions that call other functions), functions that are called independantly, AND functions that are defined but not called.
As a result of all this, it may miss things that are relevant, or include things that are not. It's a start though, and appears to accomplish what I was after, and what led me to this question in the first place.
https://github.com/mishoo/UglifyJS
gives access to an ast in javascript.
ast.coffee
util = require 'util'
jsp = require('uglify-js').parser
orig_code = """
var a = function (x) {
return x * x;
};
function b (x) {
return a(x)
}
console.log(a(5));
console.log(b(5));
"""
ast = jsp.parse(orig_code)
console.log util.inspect ast, true, null, true

Categories