Getting a scala stacktrace - scala.js

When my scala-js code throws an error, I'd like to send a sensible stacktrace back to my server to put in the logs. By "sensible stacktrace" I mean something that gives the Scala methods, filenames, and line numbers rather than the transpiled javascript code.
I've made good progress by getting the source map and using the Javascript source-map library (https://github.com/mozilla/source-map) to translate each element of the stacktrace from javascript to the corresponding Scala code.
My issue: I need the column number of the javascript code that threw the error but don't see how to obtain it. Printing a StackTraceElement gives a result similar to
oat.browser.views.query.QueryRunView$.renderParamsTable$1(https://localhost:9443/assets/browser-fastopt.js:34787:188)
I need the "188" at the end of the line but don't see how to get it other than calling toString and parsing the result. Looking at the StackTraceElement code, the column number is a private variable with nothing in the API to access it.
Is there another approach to this that I'm completely overlooking? Anything built into scala-js that converts a javascript stacktrace to a Scala stacktrace?

I subsequently found the StackTraceJS library which does what I needed. I combined a ScalaJS facade for it with a facade for JSNlog to come up with a package that meets my needs pretty well. See jsnlog-facade. It logs to the browser console and/or the server, with Scala stack traces. Demo code included.

There is nothing in the public API to access the column number because this is a Java API, and Scala.js cannot add public members to Java APIs.
To work around this issue in the case of StackTraceElement, we export getColumnNumber(): Int to JavaScript. You can therefore use the following code to retrieve the column number:
def columnNumberOfStackTraceElement(ste: StackTraceElement): Int =
ste.asInstanceOf[js.Dynamic].getColumnNumber().asInstanceOf[Int]
Note that this "feature" is undocumented, and might change without notice in a future major version of Scala.js. If it disappears, it will be replaced by something reliable. In the meantime, the above should get you going.

Related

Scala.js stacktraces

I'm frustrated by unintelligible stacktraces when my Scala.js code throws an exception. I thought I had a solution using a Javascript library (see Getting a scala stacktrace) but it breaks too often.
How do you extract meaning (where the program broke; how it got there -- in terms of the Scala code) from a stacktrace like the following. Or am I doing something wrong to even get an untranslated stacktrace?
Take a look at this code I wrote a while back in my youi framework: https://github.com/outr/youi/tree/e66dc36a12780fa8941152d07de9c3a52d28fc10/app/js/src/main/scala/io/youi/app/sourceMap
It is used to reverse JS stack traces to Scala stack traces. In youi I send the errors to the server so I can monitor browser errors that occur with the complete traceback.
Brief Overview
source-map.js
You need source-map.js to parse the js.map file that Scala.js
generated when it compiled your code. See:
https://github.com/mozilla/source-map
Load the js.map file via Ajax
The SourceMapConsumer needs a js.Object (JSON) of the js.map file. See https://github.com/outr/youi/blob/e66dc36a12780fa8941152d07de9c3a52d28fc10/app/js/src/main/scala/io/youi/app/sourceMap/ErrorTrace.scala#L58 for an example of loading via youi's Ajax features.
Process the Throwable
The trace represents line and columns in the JS file and you can pass
that information to SourceMapConsumer to get the original Scala line
numbers back (see SourceMapConsumer.originalPositionFor). See
ErrorTrace.toCause
(https://github.com/outr/youi/blob/e66dc36a12780fa8941152d07de9c3a52d28fc10/app/js/src/main/scala/io/youi/app/sourceMap/ErrorTrace.scala#L98)
for an example iterating over the Throwable's trace elements.
Handling Errors
Now that you have the capacity to process JavaScript errors and
convert them back to Scala traces, you need to actually receive the
errors. If you want to globally handle uncaught errors set a function
to window.onerror to capture errors. As of this writing, the
function signature in Scala.js isn't ideal for handling all
information, so in youi I use js.Dynamic to set it to what I need
(see:
https://github.com/outr/youi/blob/e66dc36a12780fa8941152d07de9c3a52d28fc10/app/js/src/main/scala/io/youi/app/ClientApplication.scala#L35).
Also, notice that in ErrorTrace it supports multiple incoming types
of errors (ErrorEvent, Throwable, and a more generic scenario).
This is because in JavaScript the errors come in different ways based
on what's happening. This is a fairly complex topic, and why I
created this functionality in youi to simplify things.
Not nearly as brief an overview as I would have liked, but this isn't a simple problem to solve. The source-map GitHub project (https://github.com/mozilla/source-map) has decent documentation and is what I used originally to write my solution (with some added trial and error). If the information I've provided is incomplete I'd recommend reading more there as it should provide the majority of information, and probably better explained.

Java / Eclipse - macro to specify current method name?

I'm using eclipse and want to have a "macro" that the preprocessor will replace with the name of the current method before compiling it.
I have an error reporting function, that is called as: reportthis(String errormessage) - different functions throughout the application have try/catch blocks, that call reportthis(...) from the catch block upon errors.
I'd like to be able to specify something like reportthis(MACRO_CURRENT_METHOD_NAME + ":" + e.ToString()); - where MACRO_CURRENT_METHOD_NAME will be preprocessed by eclipse before compilation and result in the name of the method where the catch {} block calls reportthis().
So if the catch{} block happens in main(), the macro should return the string "main" (or "main()", etc.).
Is this possible? how do i go about achieving my goal?
Thank you!
Edit
I wish to get this done by preprocessors in Eclipse - are those impossible? isn't it possible to perhaps write a plugin for eclipse to replace all occurrences of "MACRO_CURRENT_METHOD_NAME" with the current function name?
I've not found an automated way of doing this, so have manually added a string literal that indicates the name of the caller at each invocation of the logging code.
Nokia's S40 platform is also based on Java-ME, and I know some Nokia S40 developers have made good use of Jarrut, which is available on Sourceforge, to produce stack traces by modifying the program to track the stack. You could leverage this functionality to get the calling function name in your logging code, but you may need to modify Jarrut a bit to make that work.
Java does not support Macros.
But what you can do to determine the current method is something like
final StackTraceElement aTop = Thread.currentThread ().getStackTrace ()[1];
System.out.println (aTop.getMethodName ());
By using the element at index [1] you get the calling method, because the element at [0] is Thread.getStackTrace().
If you wrap this code in an additional method, you must adopt the array index e.g. to 2, depending on the number of wrapping methods you are using.
There is no preprocessor in java, and no macro language either.
While there are situations where either could be useful, if I understand your problem its entirely pointless, since the stack trace of the exception will already contain class and method of the place where the excetion occured.
Instead of passing a String to your "reportthis()", make a signature that just takes the exception and prints it (or just write e.printStackTrace()).

Does Scala have "macro" definitions ready to use -- like LINE, FILE?

When you get a stack trace from exception you get files and line numbers. I need something like this for my reporting, so I could get to the cause very fast.
I am looking in particular for LINE and FILE macro. Is there anything like this in Scala?
There is no such macro neither in Scala nor in Java. Of course the line number information is stored in the bytecode (also for debugging purposes) but there is no API to obtain it.
Stack traces with class names and line numbers are generated via native Throwable.fillInStackTrace(). Logging libraries might also use Thread.getStackTrace().
In both cases it boils down to parse stack trace and find our current location. Note that generating stack trace is time-consuming and should be avoided.
The sourcecode library is based on macros and provides metadata at compile-time.
Example (taken from their page):
object Main extends App {
def log(message: String)(implicit line: sourcecode.Line, file: sourcecode.File) =
println(s"${file.value}:${line.value} $message")
log("foo")
}
This will print:
/Users/jhoffmann/Development/sourcecode/src/main/scala/Main.scala:5 foo
You can use the implicits anywhere in your code.
The is a project to provide a macro capability in Scala.
Perhaps you could approach the project team to discuss what you need

Why am I getting NullPointerException in the CompilationUnit instances returned from ASTParser.createASTs()

I am working on an Eclipse JDT plugin that requires parsing large numbers of source files,
so I am hoping to use the batch method ASTParser.createASTs(). The parsing executes without errors, but within the CompilationUnit instances it produces, many of the org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding instances have had their scope field set to null. This setting to null is occurring in the CompilationUnitDeclaration.cleanUp() methods, which are invoked on a worker thread that is unrelated to my plugin's code (i.e., my plugin's classes do not appear on the cleanUp() method call stack).
My parsing code looks like this (all rawSources are within the same project):
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setStatementsRecovery(true);
parser.setBindingsRecovery(true);
parser.setIgnoreMethodBodies(false);
parser.setProject(project);
parser.createASTs(rawSources.values().toArray(new ICompilationUnit[0]), new String[0], this, deltaAnalyzer.progressMonitor);
Alternatively, I can execute the parsing this way, and no such problems occur:
for (ICompilationUnit source : rawSources.values())
{
parser.setResolveBindings(true);
parser.setStatementsRecovery(true);
parser.setBindingsRecovery(true);
parser.setIgnoreMethodBodies(false);
parser.setProject(project);
parser.setSource(source);
CompilationUnit ast = (CompilationUnit)parser.createAST(deltaAnalyzer.progressMonitor);
parsedSources.add(deltaAnalyzer.createParsedSource(source, ast));
}
This issue occurs in both Helios and Indigo (the very latest release build). I filed a bug in Eclipse Bugzilla, but if anyone knows of a way to work around this--or if I am using the API wrong--I would greatly appreciate your help.
Byron
Without knowing exactly what your exception is, I can still offer 2 suggestions:
Have a look at org.eclipse.jdt.ui.SharedASTProvider. If you are not making any changes to ASTs, this class may provide a more robust way of getting the ASTs.
Play around with some of the settings that you are using. Do you really need bindingsRecovery set to true? What about statementRecovery? Setting these to false may help you.

GWT works great in Hosted Mode, and Compiles without error -- but its JS files are buggy

I have no errors show up in either the compilation or the hosted mode process, but the JS that GWT creates contain errors that disallow the proper rendering of the website. How can this happen? Is this a problem with the compiler?
FireBug is giving me nothing, no errors at all.
But I don't know where to go from here or what more information to give you all, since I can't effectively debug JS like this. More fundamentally, I just don't understand why GWT is giving me JS that doesn't work.
EDIT: I didn't really know what Pretty and Detailed meant until now. Thanks for pointing me to this. What I get now is http://i.imgur.com/qUyNb.png.
I'm not sure where to go from here.
EDIT 2: Here is the final image I will post (I promise!): http://i.imgur.com/ZVQVW.png. This is the pretty output. The error reads: "Uncaught com.google.gwt.core.client.JavaScriptException (TypeError): Cannot call method 'isString' of null (anonymous function)."
The resolution to this issue was the realization that isString was not a JNSI method, but instead a method I wrote in a try / catch block. This was the code that tripped me up:
try{something that will create a NullPointerException}
catch(NullPointerException npe){npe.printStackTrace()}
#Luismahou's link above said the following about error-catching in GWT:
Exceptions: try, catch, finally and user-defined exceptions are supported as normal, although Throwable.getStackTrace() is not meaningfully supported in production mode.
Note: Several fundamental exceptions implicitly produced by the Java VM, most notably NullPointerException, StackOverflowError, and OutOfMemoryError, do not occur in production mode as such. Instead, a JavaScriptException is produced for any implicitly generated exceptions. This is because the nature of the underlying JavaScript exception cannot be reliably mapped onto the appropriate Java exception type.
I think what happened is that my try block threw a NullPointerException, which was represented as a JavaScriptException and was uncaught by the catch block. Lesson learned: don't catch NullPointerExceptions, StackOverflowErrors, and OutOfMemoryErrors in GWT.