diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 873e45b77..56fff81a2 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -15,6 +15,7 @@ */ package org.teavm.dependency; +import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -375,9 +376,23 @@ public class DependencyChecker implements DependencyInfo { } public void checkForMissingItems() { - if (missingClasses.isEmpty() && missingMethods.isEmpty() && missingFields.isEmpty()) { + if (!hasMissingItems()) { return; } + StringBuilder sb = new StringBuilder(); + try { + showMissingItems(sb); + } catch (IOException e) { + throw new AssertionError("StringBuilder should not throw IOException"); + } + throw new IllegalStateException(sb.toString()); + } + + public boolean hasMissingItems() { + return !missingClasses.isEmpty() || !missingMethods.isEmpty() || !missingFields.isEmpty(); + } + + public void showMissingItems(Appendable sb) throws IOException { List items = new ArrayList<>(); Map stackMap = new HashMap<>(); for (String cls : missingClasses.keySet()) { @@ -393,7 +408,6 @@ public class DependencyChecker implements DependencyInfo { items.add(field.toString()); } Collections.sort(items); - StringBuilder sb = new StringBuilder(); sb.append("Can't compile due to the following items missing:\n"); for (String item : items) { sb.append(" ").append(item).append("\n"); @@ -407,6 +421,5 @@ public class DependencyChecker implements DependencyInfo { } sb.append('\n'); } - throw new IllegalStateException(sb.toString()); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java index 473ff0164..0c890b4ea 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -112,6 +112,18 @@ public class JavascriptBuilder implements JavascriptBuilderHost { dependencyChecker.startListeners(); } + public boolean hasMissingItems() { + return dependencyChecker.hasMissingItems(); + } + + public void showMissingItems(Appendable target) throws IOException { + dependencyChecker.showMissingItems(target); + } + + public void checkForMissingItems() { + dependencyChecker.checkForMissingItems(); + } + public void build(Appendable writer, JavascriptBuildTarget target) throws RenderingException { AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, classSource); @@ -124,7 +136,9 @@ public class JavascriptBuilder implements JavascriptBuilderHost { dependencyChecker.linkMethod(new MethodReference("java.lang.String", new MethodDescriptor("", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), DependencyStack.ROOT).use(); executor.complete(); - dependencyChecker.checkForMissingItems(); + if (hasMissingItems()) { + return; + } ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(classSource); Decompiler decompiler = new Decompiler(classSet, classLoader, executor); devirtualize(classSet, dependencyChecker); diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index 26b69dd92..02fa7c6a1 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -91,6 +91,9 @@ public class JavaScriptBodyDependency implements DependencyListener { private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) { while (clsName != null) { ClassReader cls = classSource.get(clsName); + if (cls == null) { + return null; + } for (MethodReader method : cls.getMethods()) { if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) { return method; @@ -121,7 +124,7 @@ public class JavaScriptBodyDependency implements DependencyListener { } @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) { MethodDescriptor desc = MethodDescriptor.parse(method + params + "V"); - MethodReader reader = findMethod(dependencyChecker.getClassSource(), params, desc); + MethodReader reader = findMethod(dependencyChecker.getClassSource(), fqn, desc); MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc); MethodDependency methodDep = dependencyChecker.linkMethod(ref, caller.getStack()); if (!methodDep.isMissing()) { @@ -170,7 +173,7 @@ public class JavaScriptBodyDependency implements DependencyListener { if (subtype == null) { return false; } - if (isAssignableFrom(supertype, subtype.getParent())) { + if (subtype.getParent() != null && isAssignableFrom(supertype, subtype.getParent())) { return true; } for (String iface : subtype.getInterfaces()) { diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java index 330b2b66f..1675a3815 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptJUnitMojo.java @@ -245,12 +245,64 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo { builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()); builder.exportType("TestClass", cons.getClassName()); builder.build(innerWriter, new DirectoryBuildTarget(outputDir)); - innerWriter.append("\n"); - innerWriter.append("\nJUnitClient.run();"); - innerWriter.close(); + if (!builder.hasMissingItems()) { + innerWriter.append("\n"); + innerWriter.append("\nJUnitClient.run();"); + innerWriter.close(); + } else { + innerWriter.append("JUnitClient.reportError(\n"); + StringBuilder sb = new StringBuilder(); + builder.showMissingItems(sb); + escapeStringLiteral(sb.toString(), innerWriter); + innerWriter.append(");"); + getLog().warn("Error building test " + methodRef); + getLog().warn(sb); + } } } + private void escapeStringLiteral(String text, Writer writer) throws IOException { + int index = 0; + while (true) { + int next = text.indexOf('\n', index); + if (next < 0) { + break; + } + escapeString(text.substring(index, next + 1), writer); + writer.append(" +\n"); + index = next + 1; + } + escapeString(text.substring(index), writer); + } + + private void escapeString(String string, Writer writer) throws IOException { + writer.append('\"'); + for (int i = 0; i < string.length(); ++i) { + char c = string.charAt(i); + switch (c) { + case '"': + writer.append("\\\""); + break; + case '\\': + writer.append("\\\\"); + break; + case '\n': + writer.append("\\n"); + break; + case '\r': + writer.append("\\r"); + break; + case '\t': + writer.append("\\t"); + break; + default: + writer.append(c); + break; + } + } + writer.append('\"'); + } + private void findTestClasses(ClassLoader classLoader, File folder, String prefix) { Class testAnnot; try { diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java index bd5fe724f..e1493d2de 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java @@ -140,6 +140,7 @@ public class BuildJavascriptMojo extends AbstractMojo { .withValue(1, "java.lang.String"); targetDirectory.mkdirs(); builder.build(targetDirectory, targetFileName); + builder.checkForMissingItems(); log.info("JavaScript file successfully built"); if (!runtimeSuppressed) { resourceToFile("org/teavm/javascript/runtime.js", "runtime.js"); diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js index fa40f9804..d825dd71d 100644 --- a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js +++ b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js @@ -215,4 +215,11 @@ JUnitClient.run = function() { } window.parent.postMessage(JSON.stringify(message), "*"); }); +} +JUnitClient.reportError = function(error) { + var handler = window.addEventListener("message", function() { + window.removeEventListener("message", handler); + var message = { status : "exception", stack : error }; + window.parent.postMessage(JSON.stringify(message), "*"); + }); } \ No newline at end of file