mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
In HtmlUnit, decode stack traces for all constructed exceptions
This commit is contained in:
parent
0c03379206
commit
45d0a13c9b
|
@ -154,7 +154,12 @@ public class TThrowable extends RuntimeException {
|
|||
}
|
||||
|
||||
public void printStackTrace(TPrintStream stream) {
|
||||
stream.println(TString.wrap(getClass().getName() + ": " + getMessage()));
|
||||
stream.print(TString.wrap(getClass().getName()));
|
||||
String message = getMessage();
|
||||
if (message != null) {
|
||||
stream.print(TString.wrap(": " + getMessage()));
|
||||
}
|
||||
stream.println();
|
||||
if (stackTrace != null) {
|
||||
for (TStackTraceElement element : stackTrace) {
|
||||
stream.print(TString.wrap(" at "));
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
|||
import org.teavm.debugging.information.SourceLocation;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.dependency.DependencyType;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.interop.PlatformMarkers;
|
||||
|
@ -200,6 +201,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
MethodDependency dep;
|
||||
|
||||
DependencyType stringType = dependencyAnalyzer.getType("java.lang.String");
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(Class.class.getName(), "getClass",
|
||||
ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)), null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("org.teavm.platform.PlatformClass"));
|
||||
|
@ -208,23 +211,23 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
|
||||
null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
dep.getVariable(0).propagate(stringType);
|
||||
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C"));
|
||||
dep.use();
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class,
|
||||
char[].class, int.class, void.class), null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
dep.getVariable(0).propagate(stringType);
|
||||
dep.getVariable(3).propagate(dependencyAnalyzer.getType("[C"));
|
||||
dep.use();
|
||||
|
||||
MethodDependency internDep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "intern",
|
||||
String.class), null);
|
||||
internDep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
internDep.getVariable(0).propagate(stringType);
|
||||
internDep.use();
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "length", int.class), null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
dep.getVariable(0).propagate(stringType);
|
||||
dep.use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class), null).use();
|
||||
|
@ -240,23 +243,39 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
dep.use();
|
||||
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoClassDefFoundError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchFieldError.class, "<init>",
|
||||
String.class, void.class), null);
|
||||
exceptionCons.use();
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchFieldError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(NoSuchMethodError.class, "<init>",
|
||||
String.class, void.class), null);
|
||||
exceptionCons.use();
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(NoSuchMethodError.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
|
||||
exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
RuntimeException.class, "<init>", String.class, void.class), null);
|
||||
exceptionCons.getVariable(0).propagate(dependencyAnalyzer.getType(RuntimeException.class.getName()));
|
||||
exceptionCons.getVariable(1).propagate(dependencyAnalyzer.getType("java.lang.String"));
|
||||
exceptionCons.getVariable(1).propagate(stringType);
|
||||
exceptionCons.use();
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
StackTraceElement.class, "<init>", String.class, String.class, String.class,
|
||||
int.class, void.class), null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
||||
dep.getVariable(1).propagate(stringType);
|
||||
dep.getVariable(2).propagate(stringType);
|
||||
dep.getVariable(3).propagate(stringType);
|
||||
dep.use();
|
||||
|
||||
dep = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
Throwable.class, "setStackTrace", StackTraceElement[].class, void.class), null);
|
||||
dep.getVariable(0).propagate(dependencyAnalyzer.getType(Throwable.class.getName()));
|
||||
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[Ljava/lang/StackTraceElement;"));
|
||||
dep.getVariable(1).getArrayItem().propagate(dependencyAnalyzer.getType(StackTraceElement.class.getName()));
|
||||
dep.use();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -51,6 +51,8 @@ public class RuntimeRenderer {
|
|||
renderRuntimeIntern();
|
||||
renderRuntimeThreads();
|
||||
renderRuntimeCreateException();
|
||||
renderCreateStackTraceElement();
|
||||
renderSetStackTrace();
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering runtime methods. See a cause for details", e);
|
||||
} catch (IOException e) {
|
||||
|
@ -170,4 +172,28 @@ public class RuntimeRenderer {
|
|||
writer.append("(message);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderCreateStackTraceElement() throws IOException {
|
||||
writer.append("function $rt_createStackElement(")
|
||||
.append("className,").ws()
|
||||
.append("methodName,").ws()
|
||||
.append("fileName,").ws()
|
||||
.append("lineNumber)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
writer.append(writer.getNaming().getNameForInit(new MethodReference(StackTraceElement.class,
|
||||
"<init>", String.class, String.class, String.class, int.class, void.class)));
|
||||
writer.append("(className,").ws()
|
||||
.append("methodName,").ws()
|
||||
.append("fileName,").ws()
|
||||
.append("lineNumber);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderSetStackTrace() throws IOException {
|
||||
writer.append("function $rt_setStack(e,").ws().append("stack)").ws().append("{").indent().softNewLine();
|
||||
writer.appendMethodBody(new MethodReference(Throwable.class, "setStackTrace", StackTraceElement[].class,
|
||||
void.class));
|
||||
writer.append("(e,").ws().append("stack);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,6 +228,16 @@ function $rt_exception(ex) {
|
|||
}
|
||||
err.$javaException = ex;
|
||||
ex.$jsException = err;
|
||||
if (typeof $rt_decodeStack === "function" && err.stack) {
|
||||
var stack = $rt_decodeStack(err.stack);
|
||||
var javaStack = $rt_createArray($rt_objcls(), stack.length);
|
||||
for (var i = 0; i < stack.length; ++i) {
|
||||
var element = stack[i];
|
||||
javaStack.data[i] = $rt_createStackElement($rt_str(element.className),
|
||||
$rt_str(element.methodName), $rt_str(element.fileName), element.lineNumber);
|
||||
}
|
||||
$rt_setStack(ex, javaStack);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -393,7 +403,7 @@ var $rt_stderrBuffer = "";
|
|||
var $rt_putStderr = typeof $rt_putStderrCustom === "function" ? $rt_putStderrCustom : function(ch) {
|
||||
if (ch === 0xA) {
|
||||
if (console) {
|
||||
console.info($rt_stderrBuffer);
|
||||
console.error($rt_stderrBuffer);
|
||||
}
|
||||
$rt_stderrBuffer = "";
|
||||
} else {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.junit;
|
|||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebConsole;
|
||||
import com.gargoylesoftware.htmlunit.WebWindow;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import java.io.File;
|
||||
|
@ -26,7 +27,10 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.BaseFunction;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.Context;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.Function;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.NativeArray;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject;
|
||||
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -59,17 +63,33 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
page.get().executeJavaScript(readFile(new File(run.getBaseDirectory(), run.getFileName())));
|
||||
HtmlPage pageRef = page.get();
|
||||
|
||||
pageRef.executeJavaScript(readFile(new File(run.getBaseDirectory(), run.getFileName())));
|
||||
boolean decodeStack = Boolean.parseBoolean(System.getProperty(TeaVMTestRunner.JS_DECODE_STACK, "true"));
|
||||
File debugFile = decodeStack ? new File(run.getBaseDirectory(), run.getFileName() + ".teavmdbg") : null;
|
||||
RhinoResultParser resultParser = new RhinoResultParser(debugFile);
|
||||
|
||||
AsyncResult asyncResult = new AsyncResult();
|
||||
Function function = (Function) page.get().executeJavaScript(readResource("teavm-htmlunit-adapter.js"))
|
||||
.getJavaScriptResult();
|
||||
Object[] args = new Object[] { new NativeJavaObject(function, asyncResult, AsyncResult.class) };
|
||||
page.get().executeJavaScriptFunctionIfPossible(function, function, args, page.get());
|
||||
Object[] args = new Object[] {
|
||||
decodeStack ? createStackDecoderFunction(resultParser) : null,
|
||||
new NativeJavaObject(function, asyncResult, AsyncResult.class)
|
||||
};
|
||||
pageRef.executeJavaScriptFunctionIfPossible(function, function, args, page.get());
|
||||
|
||||
boolean decodeStack = Boolean.parseBoolean(System.getProperty(TeaVMTestRunner.JS_DECODE_STACK, "true"));
|
||||
File debugFile = decodeStack ? new File(run.getBaseDirectory(), run.getFileName() + ".teavmdbg") : null;
|
||||
RhinoResultParser.parseResult((Scriptable) asyncResult.getResult(), run.getCallback(), debugFile);
|
||||
resultParser.parseResult((Scriptable) asyncResult.getResult(), run.getCallback());
|
||||
}
|
||||
|
||||
private Function createStackDecoderFunction(RhinoResultParser resultParser) {
|
||||
return new BaseFunction() {
|
||||
@Override
|
||||
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
|
||||
String stack = args[0].toString();
|
||||
return new NativeArray(resultParser.decodeStack(stack));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
|
@ -86,7 +106,57 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
}
|
||||
|
||||
private void init() {
|
||||
webClient.set(new WebClient(BrowserVersion.CHROME));
|
||||
WebClient client = new WebClient(BrowserVersion.CHROME);
|
||||
client.getWebConsole().setLogger(new WebConsole.Logger() {
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Object message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Object message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Object message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Object message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message) {
|
||||
System.err.println(message);
|
||||
}
|
||||
});
|
||||
webClient.set(client);
|
||||
}
|
||||
|
||||
private String readFile(File file) throws IOException {
|
||||
|
|
|
@ -36,11 +36,17 @@ import org.teavm.model.MethodReference;
|
|||
final class RhinoResultParser {
|
||||
private static Pattern pattern = Pattern.compile("(([A-Za-z_$]+)\\(\\))?@.+:([0-9]+)");
|
||||
private static Pattern lineSeparator = Pattern.compile("\\r\\n|\r|\n");
|
||||
private File debugFile;
|
||||
private DebugInformation debugInformation;
|
||||
private String[] script;
|
||||
|
||||
private RhinoResultParser() {
|
||||
RhinoResultParser(File debugFile) {
|
||||
debugInformation = debugFile != null ? getDebugInformation(debugFile) : null;
|
||||
script = getScript(new File(debugFile.getParentFile(),
|
||||
debugFile.getName().substring(0, debugFile.getName().length() - 9)));
|
||||
}
|
||||
|
||||
static void parseResult(Scriptable result, TestRunCallback callback, File debugFile) {
|
||||
void parseResult(Scriptable result, TestRunCallback callback) {
|
||||
if (result == null) {
|
||||
callback.complete();
|
||||
return;
|
||||
|
@ -65,10 +71,8 @@ final class RhinoResultParser {
|
|||
String stack = result.get("stack", result).toString();
|
||||
StackTraceElement[] decodedStack = null;
|
||||
if (debugInformation != null) {
|
||||
String[] script = getScript(new File(debugFile.getParentFile(),
|
||||
debugFile.getName().substring(0, debugFile.getName().length() - 9)));
|
||||
List<StackTraceElement> elements = new ArrayList<>();
|
||||
elements.addAll(Arrays.asList(decodeStack(stack, script, debugInformation)));
|
||||
elements.addAll(Arrays.asList(decodeStack(stack)));
|
||||
List<StackTraceElement> currentElements = Arrays.asList(Thread.currentThread().getStackTrace());
|
||||
elements.addAll(currentElements.subList(2, currentElements.size()));
|
||||
decodedStack = elements.toArray(new StackTraceElement[0]);
|
||||
|
@ -92,7 +96,7 @@ final class RhinoResultParser {
|
|||
}
|
||||
}
|
||||
|
||||
private static StackTraceElement[] decodeStack(String stack, String[] script, DebugInformation debugInformation) {
|
||||
StackTraceElement[] decodeStack(String stack) {
|
||||
List<StackTraceElement> elements = new ArrayList<>();
|
||||
for (String line : lineSeparator.split(stack)) {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
|
|
@ -21,7 +21,7 @@ final class TestEntryPoint {
|
|||
private TestEntryPoint() {
|
||||
}
|
||||
|
||||
public static void run() throws Throwable {
|
||||
public static void run() {
|
||||
before();
|
||||
try {
|
||||
launchTest();
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
function runMain(callback) {
|
||||
var $rt_decodeStack;
|
||||
|
||||
function runMain(stackDecoder, callback) {
|
||||
$rt_decodeStack = stackDecoder;
|
||||
main([], function(result) {
|
||||
var message = {};
|
||||
if (result instanceof Error) {
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
main([], function(result) {
|
||||
var message = {};
|
||||
if (result instanceof Error) {
|
||||
makeErrorMessage(message, result);
|
||||
} else {
|
||||
message.status = "ok";
|
||||
}
|
||||
window.parent.postMessage(JSON.stringify(message), "*");
|
||||
});
|
||||
|
||||
function makeErrorMessage(message, e) {
|
||||
message.status = "exception";
|
||||
var stack = "";
|
||||
if (e.$javaException && e.$javaException.constructor.$meta) {
|
||||
stack = e.$javaException.constructor.$meta.name + ": ";
|
||||
stack += e.$javaException.getMessage() || "";
|
||||
stack += "\n";
|
||||
}
|
||||
message.stack = stack + e.stack;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var runtimeSource = arguments[0];
|
||||
var testSource = arguments[1];
|
||||
var adapterSource = arguments[2];
|
||||
var seleniumCallback = arguments[arguments.length - 1];
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
var doc = iframe.contentDocument;
|
||||
|
||||
window.jsErrors = [];
|
||||
window.onerror = reportError;
|
||||
iframe.contentWindow.onerror = reportError;
|
||||
|
||||
loadScripts([ runtimeSource, testSource, adapterSource ]);
|
||||
window.addEventListener("message", handleMessage);
|
||||
|
||||
function handleMessage(event) {
|
||||
window.removeEventListener("message", handleMessage);
|
||||
document.body.removeChild(iframe);
|
||||
seleniumCallback(event.data)
|
||||
}
|
||||
|
||||
function loadScripts(scripts) {
|
||||
for (var i = 0; i < scripts.length; ++i) {
|
||||
var elem = doc.createElement("script");
|
||||
elem.type = "text/javascript";
|
||||
doc.head.appendChild(elem);
|
||||
elem.text = scripts[i];
|
||||
}
|
||||
}
|
||||
function reportError(error, url, line) {
|
||||
window.jsErrors.push(error + " at " + line)
|
||||
}
|
||||
function report(error) {
|
||||
window.jsErrors.push(error)
|
||||
}
|
||||
function globalEval(window, arg) {
|
||||
eval.apply(window, [arg])
|
||||
}
|
Loading…
Reference in New Issue
Block a user