From e5cb7a20d3e90fa4240fb1ca7d9fb90962d3b319 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 16 Dec 2013 17:48:38 +0400 Subject: [PATCH] Adds JavascriptBuilder - a facade for building whole JavaScript files --- .../java/lang/ClassNativeGenerator.java | 10 +- .../java/lang/FloatNativeGenerator.java | 7 +- .../java/lang/ObjectNativeGenerator.java | 13 +- .../java/lang/StringNativeGenerator.java | 5 +- .../java/lang/SystemNativeGenerator.java | 5 +- .../lang/reflect/ArrayNativeGenerator.java | 5 +- .../org/junit/AssertNativeGenerator.java | 6 +- .../classlibgen/ClasslibTestGenerator.java | 66 +- .../org/teavm/classlib/junit-support.js | 6 +- .../java/org/teavm/codegen/SourceWriter.java | 67 +- .../teavm/codegen/SourceWriterBuilder.java | 5 +- .../teavm/javascript/JavascriptBuilder.java | 111 +++ .../javascript/JavascriptEntryPoint.java | 32 + .../teavm/javascript/JavascriptGenerator.java | 9 - .../java/org/teavm/javascript/Renderer.java | 675 ++++++++++-------- .../teavm/javascript/StatementGenerator.java | 2 +- .../org/teavm/javascript/ni/Generator.java | 3 +- 17 files changed, 629 insertions(+), 398 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java delete mode 100644 teavm-core/src/main/java/org/teavm/javascript/JavascriptGenerator.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index cdd12a13a..7bfdcf085 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; @@ -11,7 +12,8 @@ import org.teavm.model.MethodReference; */ public class ClassNativeGenerator implements Generator { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { switch (methodRef.getName()) { case "isInstance": generateIsInstance(context, writer); @@ -25,17 +27,17 @@ public class ClassNativeGenerator implements Generator { } } - private void generateIsInstance(GeneratorContext context, SourceWriter writer) { + private void generateIsInstance(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return $rt_isInstance(").append(context.getParameterName(1)).append(", ") .append(context.getParameterName(0)).append(".$data);").softNewLine(); } - private void generateIsAssignableFrom(GeneratorContext context, SourceWriter writer) { + private void generateIsAssignableFrom(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return $rt_isAssignable(").append(context.getParameterName(1)).append(".$data, ") .append(context.getParameterName(0)).append(".$data;").softNewLine(); } - private void generateGetComponentType(GeneratorContext context, SourceWriter writer) { + private void generateGetComponentType(GeneratorContext context, SourceWriter writer) throws IOException { String thisArg = context.getParameterName(0); writer.append("var item = " + thisArg + ".$data.$meta.item;").softNewLine(); writer.append("return item != null ? $rt_cls(item) : null;").softNewLine(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/FloatNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/FloatNativeGenerator.java index 72f9da08e..82006a882 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/FloatNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/FloatNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; @@ -11,7 +12,7 @@ import org.teavm.model.MethodReference; */ public class FloatNativeGenerator implements Generator { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "isNaN": generateIsNaN(context, writer); @@ -22,12 +23,12 @@ public class FloatNativeGenerator implements Generator { } } - private void generateIsNaN(GeneratorContext context, SourceWriter writer) { + private void generateIsNaN(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return (isNaN(").append(context.getParameterName(1)).append(")").ws().append("?") .ws().append("1").ws().append(":").ws().append("0").ws().append(");").softNewLine(); } - private void generateIsInfinite(GeneratorContext context, SourceWriter writer) { + private void generateIsInfinite(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return (isFinite(").append(context.getParameterName(1)).append(")").ws().append("?") .ws().append("0").ws().append(":").ws().append("1").append(");").softNewLine(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 3bf139735..b2d809e1d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyPlugin; @@ -16,7 +17,7 @@ import org.teavm.model.ValueType; */ public class ObjectNativeGenerator implements Generator, DependencyPlugin { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getDescriptor().getName()) { case "": generateInit(context, writer); @@ -51,11 +52,11 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { } } - private void generateInit(GeneratorContext context, SourceWriter writer) { + private void generateInit(GeneratorContext context, SourceWriter writer) throws IOException { writer.append(context.getParameterName(0)).append(".$id = $rt_nextId();").softNewLine(); } - private void generateGetClass(GeneratorContext context, SourceWriter writer) { + private void generateGetClass(GeneratorContext context, SourceWriter writer) throws IOException { String thisArg = context.getParameterName(0); writer.append("return $rt_cls(").append(thisArg).append(".$class);").softNewLine(); } @@ -68,11 +69,11 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { checker.attachMethodGraph(method).getResultNode().propagate("java.lang.Class"); } - private void generateHashCode(GeneratorContext context, SourceWriter writer) { + private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return ").append(context.getParameterName(0)).append(".$id;").softNewLine(); } - private void generateClone(GeneratorContext context, SourceWriter writer) { + private void generateClone(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("var copy = new ").append(context.getParameterName(0)).append(".$class();").softNewLine(); writer.append("for (var field in obj) {").softNewLine().indent(); writer.append("if (!obj.hasOwnProperty(field)) {").softNewLine().indent(); @@ -86,7 +87,7 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { graph.getVariableNode(0).connect(graph.getResultNode()); } - private void generateWrap(GeneratorContext context, SourceWriter writer) { + private void generateWrap(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java index c997fb5e2..2dd0c0135 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/StringNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; @@ -11,7 +12,7 @@ import org.teavm.model.MethodReference; */ public class StringNativeGenerator implements Generator { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "wrap": generateWrap(context, writer); @@ -19,7 +20,7 @@ public class StringNativeGenerator implements Generator { } } - private void generateWrap(GeneratorContext context, SourceWriter writer) { + private void generateWrap(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return ").append(context.getParameterName(1)).softNewLine(); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java index fadbea995..16f25672f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/SystemNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyNode; @@ -15,7 +16,7 @@ import org.teavm.model.MethodReference; */ public class SystemNativeGenerator implements Generator, DependencyPlugin { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "doArrayCopy": generateArrayCopy(context, writer); @@ -32,7 +33,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin { } } - private void generateArrayCopy(GeneratorContext context, SourceWriter writer) { + private void generateArrayCopy(GeneratorContext context, SourceWriter writer) throws IOException { String src = context.getParameterName(1); String srcPos = context.getParameterName(2); String dest = context.getParameterName(3); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 50f863705..a2a9bbb92 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.java.lang.reflect; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyConsumer; @@ -24,7 +25,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { } @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "getLength": generateGetLength(context, writer); @@ -32,7 +33,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { } } - private void generateGetLength(GeneratorContext context, SourceWriter writer) { + private void generateGetLength(GeneratorContext context, SourceWriter writer) throws IOException { String array = context.getParameterName(1); writer.append("if (" + array + " === null || " + array + " .$class.$meta.item === undefined) {") .softNewLine().indent(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java index d1b5e586f..931d23233 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java @@ -1,5 +1,6 @@ package org.teavm.classlib.org.junit; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; @@ -11,7 +12,8 @@ import org.teavm.model.MethodReference; */ public class AssertNativeGenerator implements Generator { @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { switch (methodRef.getDescriptor().getName()) { case "fail": generateFail(writer); @@ -19,7 +21,7 @@ public class AssertNativeGenerator implements Generator { } } - private void generateFail(SourceWriter writer) { + private void generateFail(SourceWriter writer) throws IOException { writer.append("throw new Error();").newLine(); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java index 0cef8d25b..70f09e1b8 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -1,16 +1,14 @@ package org.teavm.classlibgen; import java.io.*; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.commons.io.IOUtils; -import org.teavm.codegen.*; -import org.teavm.dependency.DependencyChecker; -import org.teavm.javascript.Decompiler; -import org.teavm.javascript.Renderer; -import org.teavm.javascript.ast.ClassNode; +import org.teavm.javascript.JavascriptBuilder; import org.teavm.model.*; import org.teavm.model.resource.ClasspathClassHolderSource; -import org.teavm.optimization.ClassSetOptimizer; /** * @@ -19,11 +17,6 @@ import org.teavm.optimization.ClassSetOptimizer; public class ClasslibTestGenerator { private static File outputDir; private static ClasspathClassHolderSource classSource; - private static Decompiler decompiler; - private static AliasProvider aliasProvider; - private static DefaultNamingStrategy naming; - private static SourceWriter writer; - private static Renderer renderer; private static List testMethods = new ArrayList<>(); private static Map> groupedMethods = new HashMap<>(); private static Map fileNames = new HashMap<>(); @@ -90,46 +83,19 @@ public class ClasslibTestGenerator { } private static void decompileClassesForTest(MethodReference methodRef, String targetName) throws IOException { - classSource = new ClasspathClassHolderSource(); - decompiler = new Decompiler(classSource); - aliasProvider = new DefaultAliasProvider(); - naming = new DefaultNamingStrategy(aliasProvider, classSource); - naming.setMinifying(false); - SourceWriterBuilder builder = new SourceWriterBuilder(naming); - builder.setMinified(false); - writer = builder.build(); - renderer = new Renderer(writer, classSource); - renderer.renderRuntime(); - DependencyChecker dependencyChecker = new DependencyChecker(classSource); + JavascriptBuilder builder = new JavascriptBuilder(); + builder.setMinifying(true); + @SuppressWarnings("resource") + Writer innerWriter = new OutputStreamWriter(new FileOutputStream(new File(outputDir, targetName)), "UTF-8"); MethodReference cons = new MethodReference(methodRef.getClassName(), new MethodDescriptor("", ValueType.VOID)); - dependencyChecker.addEntryPoint(cons); - dependencyChecker.addEntryPoint(methodRef); - dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew", - ValueType.object("java.lang.Class")))); - dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("", - ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); - dependencyChecker.checkDependencies(); - ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); - ClassSetOptimizer optimizer = new ClassSetOptimizer(); - optimizer.optimizeAll(classSet); - renderer.renderRuntime(); - decompileClasses(classSet.getClassNames()); - writer.append("JUnitClient.run(function() {").softNewLine().indent(); - writer.append("var testObj = ").appendClass(methodRef.getClassName()).append(".") - .appendMethod(cons).append("();").softNewLine(); - writer.append("testObj.").appendMethod(methodRef).append("();").softNewLine(); - writer.outdent().append("});").newLine(); - try (Writer out = new OutputStreamWriter(new FileOutputStream(new File(outputDir, targetName)), "UTF-8")) { - out.write(writer.toString()); - } - } - - private static void decompileClasses(Collection classNames) { - List clsNodes = decompiler.decompile(classNames); - for (ClassNode clsNode : clsNodes) { - renderer.render(clsNode); - } + builder.entryPoint("initInstance", cons); + builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()); + builder.exportType("TestClass", cons.getClassName()); + builder.build(innerWriter); + innerWriter.append("\n"); + innerWriter.append("\nJUnitClient.run();"); + innerWriter.close(); } private static void findTests(ClassHolder cls) { diff --git a/teavm-classlib/src/main/resources/org/teavm/classlib/junit-support.js b/teavm-classlib/src/main/resources/org/teavm/classlib/junit-support.js index c716310d0..0377aa9f5 100644 --- a/teavm-classlib/src/main/resources/org/teavm/classlib/junit-support.js +++ b/teavm-classlib/src/main/resources/org/teavm/classlib/junit-support.js @@ -182,12 +182,14 @@ JUnitServer.prototype.createFooter = function() { } JUnitClient = {}; -JUnitClient.run = function(runner) { +JUnitClient.run = function() { var handler = window.addEventListener("message", function() { window.removeEventListener("message", handler); var message = {}; try { - runner(); + var instance = new TestClass(); + initInstance(instance); + runTest(instance); message.status = "ok"; } catch (e) { message.status = "exception"; diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java index ed0417985..6c0e2c14b 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java @@ -1,5 +1,6 @@ package org.teavm.codegen; +import java.io.IOException; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; @@ -7,87 +8,98 @@ import org.teavm.model.MethodReference; * * @author Alexey Andreev */ -public class SourceWriter { - private StringBuilder sb = new StringBuilder(); +public class SourceWriter implements Appendable { + private Appendable innerWriter; private int indentSize = 0; private NamingStrategy naming; private boolean lineStart; private boolean minified; - SourceWriter(NamingStrategy naming) { + SourceWriter(NamingStrategy naming, Appendable innerWriter) { this.naming = naming; + this.innerWriter = innerWriter; } void setMinified(boolean minified) { this.minified = minified; } - public void clear() { - sb.setLength(0); - } - - public SourceWriter append(String value) { + public SourceWriter append(String value) throws IOException { appendIndent(); - sb.append(value); + innerWriter.append(value); return this; } - public SourceWriter append(Object value) { + public SourceWriter append(Object value) throws IOException { return append(String.valueOf(value)); } - public SourceWriter append(int value) { + public SourceWriter append(int value) throws IOException { return append(String.valueOf(value)); } - public SourceWriter append(char value) { - return append(String.valueOf(value)); + @Override + public SourceWriter append(char value) throws IOException { + innerWriter.append(value); + return this; } - public SourceWriter appendClass(String cls) throws NamingException { + @Override + public SourceWriter append(CharSequence csq) throws IOException { + innerWriter.append(csq); + return this; + } + + @Override + public SourceWriter append(CharSequence csq, int start, int end) throws IOException { + innerWriter.append(csq, start, end); + return this; + } + + public SourceWriter appendClass(String cls) throws NamingException, IOException { return append(naming.getNameFor(cls)); } - public SourceWriter appendField(FieldReference field) throws NamingException { + public SourceWriter appendField(FieldReference field) throws NamingException, IOException { return append(naming.getNameFor(field)); } - public SourceWriter appendMethod(MethodReference method) throws NamingException { + public SourceWriter appendMethod(MethodReference method) throws NamingException, IOException { return append(naming.getNameFor(method)); } - public SourceWriter appendMethodBody(MethodReference method) throws NamingException { + public SourceWriter appendMethodBody(MethodReference method) throws NamingException, IOException { return append(naming.getFullNameFor(method)); } - private void appendIndent() { + private void appendIndent() throws IOException { if (minified) { return; } if (lineStart) { for (int i = 0; i < indentSize; ++i) { - sb.append(" "); + innerWriter.append(" "); } lineStart = false; } } - public SourceWriter newLine() { - sb.append('\n'); + public SourceWriter newLine() throws IOException{ + innerWriter.append('\n'); lineStart = true; return this; } - public SourceWriter ws() { + public SourceWriter ws() throws IOException{ if (!minified) { - sb.append(' '); + innerWriter.append(' '); } return this; } - public SourceWriter softNewLine() { + public SourceWriter softNewLine() throws IOException{ if (!minified) { - sb.append('\n'); + innerWriter.append('\n'); lineStart = true; } return this; @@ -106,9 +118,4 @@ public class SourceWriter { public NamingStrategy getNaming() { return naming; } - - @Override - public String toString() { - return sb.toString(); - } } diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java index eef0d805a..5bd54d93e 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriterBuilder.java @@ -1,6 +1,5 @@ package org.teavm.codegen; - /** * * @author Alexey Andreev @@ -21,8 +20,8 @@ public class SourceWriterBuilder { this.minified = minified; } - public SourceWriter build() { - SourceWriter writer = new SourceWriter(naming); + public SourceWriter build(Appendable innerWriter) { + SourceWriter writer = new SourceWriter(naming, innerWriter); writer.setMinified(minified); return writer; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java new file mode 100644 index 000000000..c81a3964b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -0,0 +1,111 @@ +package org.teavm.javascript; + +import java.io.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.teavm.codegen.*; +import org.teavm.dependency.DependencyChecker; +import org.teavm.javascript.ast.ClassNode; +import org.teavm.model.*; +import org.teavm.model.resource.ClasspathClassHolderSource; +import org.teavm.optimization.ClassSetOptimizer; + +/** + * + * @author Alexey Andreev + */ +public class JavascriptBuilder { + private ClassHolderSource classSource; + private DependencyChecker dependencyChecker; + private boolean minifying = true; + private Map entryPoints = new HashMap<>(); + private Map exportedClasses = new HashMap<>(); + + public JavascriptBuilder(ClassHolderSource classSource) { + this.classSource = classSource; + dependencyChecker = new DependencyChecker(classSource); + } + + public JavascriptBuilder() { + this(new ClasspathClassHolderSource()); + } + + public boolean isMinifying() { + return minifying; + } + + public void setMinifying(boolean minifying) { + this.minifying = minifying; + } + + public JavascriptEntryPoint entryPoint(String name, MethodReference ref) { + if (entryPoints.containsKey(name)) { + throw new IllegalArgumentException("Entry point with public name `" + name + "' already defined " + + "for method " + ref); + } + JavascriptEntryPoint entryPoint = new JavascriptEntryPoint(name, ref, + dependencyChecker.attachMethodGraph(ref)); + entryPoints.put(name, entryPoint); + return entryPoint; + } + + public void exportType(String name, String className) { + if (exportedClasses.containsKey(name)) { + throw new IllegalArgumentException("Class with public name `" + name + "' already defined for class " + + className); + } + exportedClasses.put(name, className); + } + + public ClassHolderSource getClassSource() { + return classSource; + } + + public void build(Appendable writer) throws RenderingException { + Decompiler decompiler = new Decompiler(classSource); + AliasProvider aliasProvider = minifying ? new MinifyingAliasProvider() : new DefaultAliasProvider(); + DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, classSource); + naming.setMinifying(minifying); + SourceWriterBuilder builder = new SourceWriterBuilder(naming); + builder.setMinified(minifying); + SourceWriter sourceWriter = builder.build(writer); + Renderer renderer = new Renderer(sourceWriter, classSource); + renderer.renderRuntime(); + dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew", + ValueType.object("java.lang.Class")))); + dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("", + ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); + dependencyChecker.checkDependencies(); + ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); + ClassSetOptimizer optimizer = new ClassSetOptimizer(); + optimizer.optimizeAll(classSet); + renderer.renderRuntime(); + List clsNodes = decompiler.decompile(classSet.getClassNames()); + for (ClassNode clsNode : clsNodes) { + renderer.render(clsNode); + } + try { + for (Map.Entry entry : entryPoints.entrySet()) { + sourceWriter.append(entry.getKey()).ws().append("=").ws().appendMethodBody(entry.getValue().reference) + .append(";").softNewLine(); + } + for (Map.Entry entry : exportedClasses.entrySet()) { + sourceWriter.append(entry.getKey()).ws().append("=").ws().appendClass(entry.getValue()).append(";") + .softNewLine(); + } + } catch (IOException e) { + throw new RenderingException("IO Error occured", e); + } + } + + public void build(File file) throws RenderingException { + try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { + build(writer); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Platform does not support UTF-8", e); + } catch (IOException e) { + throw new RenderingException(); + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java new file mode 100644 index 000000000..7b931a0f2 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptEntryPoint.java @@ -0,0 +1,32 @@ +package org.teavm.javascript; + +import org.teavm.dependency.MethodGraph; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class JavascriptEntryPoint { + private String publicName; + MethodReference reference; + private MethodGraph graph; + + JavascriptEntryPoint(String publicName, MethodReference reference, MethodGraph graph) { + this.publicName = publicName; + this.reference = reference; + this.graph = graph; + } + + String getPublicName() { + return publicName; + } + + public JavascriptEntryPoint withValue(int argument, String type) { + if (argument > reference.parameterCount()) { + throw new IllegalArgumentException("Illegal argument #" + argument + " of " + reference.parameterCount()); + } + graph.getVariableNode(argument).propagate(type); + return this; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptGenerator.java deleted file mode 100644 index c6e7151ed..000000000 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptGenerator.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.teavm.javascript; - -/** - * - * @author Alexey Andreev - */ -public class JavascriptGenerator { - -} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index f0505a5b0..68937b44b 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -15,6 +15,7 @@ */ package org.teavm.javascript; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.teavm.codegen.NamingException; @@ -64,10 +65,12 @@ public class Renderer implements ExprVisitor, StatementVisitor { renderRuntimeObjcls(); } catch (NamingException e) { throw new RenderingException("Error rendering runtime methods. See a cause for details", e); + } catch (IOException e) { + throw new RenderingException("IO error", e); } } - private void renderRuntimeCls() { + private void renderRuntimeCls() throws IOException { writer.append("$rt_cls").ws().append("=").ws().append("function(clsProto)").ws().append("{") .indent().softNewLine(); String classClass = "java.lang.Class"; @@ -98,7 +101,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } - private void renderRuntimeString() { + private void renderRuntimeString() throws IOException { String stringClass = "java.lang.String"; MethodReference stringCons = new MethodReference(stringClass, new MethodDescriptor("", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)); @@ -113,7 +116,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } - private void renderRuntimeObjcls() { + private void renderRuntimeObjcls() throws IOException { writer.append("$rt_objcls = function() { return ").appendClass("java.lang.Object").append("; }").newLine(); } @@ -202,6 +205,8 @@ public class Renderer implements ExprVisitor, StatementVisitor { } } catch (NamingException e) { throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } } @@ -230,7 +235,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { return null; } - private void renderInitializer(MethodNode method) { + private void renderInitializer(MethodNode method) throws IOException { MethodReference ref = method.getReference(); writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function("); for (int i = 1; i <= ref.parameterCount(); ++i) { @@ -254,7 +259,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } - public void renderDeclaration(MethodNode method) throws RenderingException { + public void renderDeclaration(MethodNode method) throws RenderingException, IOException { try { MethodReference ref = method.getReference(); if (ref.getDescriptor().getName().equals("")) { @@ -294,7 +299,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { } } - public void renderBody(MethodNode method) { + public void renderBody(MethodNode method) throws IOException { MethodReference ref = method.getReference(); writer.appendMethodBody(ref).ws().append("=").ws().append("function("); int startParam = 0; @@ -315,27 +320,35 @@ public class Renderer implements ExprVisitor, StatementVisitor { private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { @Override public void visit(NativeMethodNode methodNode) { - methodNode.getGenerator().generate(this, writer, methodNode.getReference()); + try { + methodNode.getGenerator().generate(this, writer, methodNode.getReference()); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(RegularMethodNode method) { - MethodReference ref = method.getReference(); - int variableCount = method.getVariableCount(); - boolean hasVars = variableCount > ref.parameterCount() + 1; - if (hasVars) { - writer.append("var "); - boolean first = true; - for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { - if (!first) { - writer.append(",").ws(); + try { + MethodReference ref = method.getReference(); + int variableCount = method.getVariableCount(); + boolean hasVars = variableCount > ref.parameterCount() + 1; + if (hasVars) { + writer.append("var "); + boolean first = true; + for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { + if (!first) { + writer.append(",").ws(); + } + first = false; + writer.append(variableName(i)); } - first = false; - writer.append(variableName(i)); + writer.append(";").softNewLine(); } - writer.append(";").softNewLine(); + method.getBody().acceptVisitor(Renderer.this); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - method.getBody().acceptVisitor(Renderer.this); } @Override @@ -345,13 +358,17 @@ public class Renderer implements ExprVisitor, StatementVisitor { } @Override - public void visit(AssignmentStatement statement) { - if (statement.getLeftValue() != null) { - statement.getLeftValue().acceptVisitor(this); - writer.ws().append("=").ws(); + public void visit(AssignmentStatement statement) throws RenderingException { + try { + if (statement.getLeftValue() != null) { + statement.getLeftValue().acceptVisitor(this); + writer.ws().append("=").ws(); + } + statement.getRightValue().acceptVisitor(this); + writer.append(";").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - statement.getRightValue().acceptVisitor(this); - writer.append(";").softNewLine(); } @Override @@ -363,66 +380,82 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(ConditionalStatement statement) { - writer.append("if").ws().append("("); - statement.getCondition().acceptVisitor(this); - writer.append(")").ws().append("{").softNewLine().indent(); - statement.getConsequent().acceptVisitor(this); - if (statement.getAlternative() != null) { - writer.outdent().append("}").ws().append("else").ws().append("{").indent().softNewLine(); - statement.getAlternative().acceptVisitor(this); + try { + writer.append("if").ws().append("("); + statement.getCondition().acceptVisitor(this); + writer.append(")").ws().append("{").softNewLine().indent(); + statement.getConsequent().acceptVisitor(this); + if (statement.getAlternative() != null) { + writer.outdent().append("}").ws().append("else").ws().append("{").indent().softNewLine(); + statement.getAlternative().acceptVisitor(this); + } + writer.outdent().append("}").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.outdent().append("}").softNewLine(); } @Override public void visit(SwitchStatement statement) { - if (statement.getId() != null) { - writer.append(statement.getId()).append(": "); - } - writer.append("switch").ws().append("("); - statement.getValue().acceptVisitor(this); - writer.append(")").ws().append("{").softNewLine().indent(); - for (SwitchClause clause : statement.getClauses()) { - for (int condition : clause.getConditions()) { - writer.append("case ").append(condition).append(":").softNewLine(); + try { + if (statement.getId() != null) { + writer.append(statement.getId()).append(": "); } - writer.indent(); - clause.getStatement().acceptVisitor(this); - writer.outdent(); + writer.append("switch").ws().append("("); + statement.getValue().acceptVisitor(this); + writer.append(")").ws().append("{").softNewLine().indent(); + for (SwitchClause clause : statement.getClauses()) { + for (int condition : clause.getConditions()) { + writer.append("case ").append(condition).append(":").softNewLine(); + } + writer.indent(); + clause.getStatement().acceptVisitor(this); + writer.outdent(); + } + if (statement.getDefaultClause() != null) { + writer.append("default:").softNewLine().indent(); + statement.getDefaultClause().acceptVisitor(this); + writer.outdent(); + } + writer.outdent().append("}").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - if (statement.getDefaultClause() != null) { - writer.append("default:").softNewLine().indent(); - statement.getDefaultClause().acceptVisitor(this); - writer.outdent(); - } - writer.outdent().append("}").softNewLine(); } @Override public void visit(WhileStatement statement) { - if (statement.getId() != null) { - writer.append(statement.getId()).append(":").ws(); + try { + if (statement.getId() != null) { + writer.append(statement.getId()).append(":").ws(); + } + writer.append("while").ws().append("("); + if (statement.getCondition() != null) { + statement.getCondition().acceptVisitor(this); + } else { + writer.append("true"); + } + writer.append(")").ws().append("{").softNewLine().indent(); + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + writer.outdent().append("}").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.append("while").ws().append("("); - if (statement.getCondition() != null) { - statement.getCondition().acceptVisitor(this); - } else { - writer.append("true"); - } - writer.append(")").ws().append("{").softNewLine().indent(); - for (Statement part : statement.getBody()) { - part.acceptVisitor(this); - } - writer.outdent().append("}").softNewLine(); } @Override public void visit(BlockStatement statement) { - writer.append(statement.getId()).append(":").ws().append("{").softNewLine().indent(); - for (Statement part : statement.getBody()) { - part.acceptVisitor(this); + try { + writer.append(statement.getId()).append(":").ws().append("{").softNewLine().indent(); + for (Statement part : statement.getBody()) { + part.acceptVisitor(this); + } + writer.outdent().append("}").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.outdent().append("}").softNewLine(); } @Override @@ -432,48 +465,68 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(BreakStatement statement) { - writer.append("break ").append(statement.getTarget().getId()).append(";").softNewLine(); + try { + writer.append("break ").append(statement.getTarget().getId()).append(";").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(ContinueStatement statement) { - writer.append("continue ").append(statement.getTarget().getId()).append(";").softNewLine(); + try { + writer.append("continue ").append(statement.getTarget().getId()).append(";").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(ReturnStatement statement) { - writer.append("return"); - if (statement.getResult() != null) { - writer.append(' '); - statement.getResult().acceptVisitor(this); + try { + writer.append("return"); + if (statement.getResult() != null) { + writer.append(' '); + statement.getResult().acceptVisitor(this); + } + writer.append(";").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.append(";").softNewLine(); } @Override public void visit(ThrowStatement statement) { - writer.append("$rt_throw("); - statement.getException().acceptVisitor(this); - writer.append(");").softNewLine(); + try { + writer.append("$rt_throw("); + statement.getException().acceptVisitor(this); + writer.append(");").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(IncrementStatement statement) { - writer.append(variableName(statement.getVar())); - if (statement.getAmount() > 0) { - if (statement.getAmount() == 1) { - writer.append("++"); + try { + writer.append(variableName(statement.getVar())); + if (statement.getAmount() > 0) { + if (statement.getAmount() == 1) { + writer.append("++"); + } else { + writer.ws().append("+=").ws().append(statement.getAmount()); + } } else { - writer.ws().append("+=").ws().append(statement.getAmount()); - } - } else { - if (statement.getAmount() == -1) { - writer.append("--"); - } else { - writer.ws().append("-=").ws().append(statement.getAmount()); + if (statement.getAmount() == -1) { + writer.append("--"); + } else { + writer.ws().append("-=").ws().append(statement.getAmount()); + } } + writer.append(";").softNewLine(); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.append(";").softNewLine(); } public String variableName(int index) { @@ -490,20 +543,28 @@ public class Renderer implements ExprVisitor, StatementVisitor { } private void visitBinary(BinaryExpr expr, String op) { - writer.append('('); - expr.getFirstOperand().acceptVisitor(this); - writer.ws().append(op).ws(); - expr.getSecondOperand().acceptVisitor(this); - writer.append(')'); + try { + writer.append('('); + expr.getFirstOperand().acceptVisitor(this); + writer.ws().append(op).ws(); + expr.getSecondOperand().acceptVisitor(this); + writer.append(')'); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } private void visitBinaryFunction(BinaryExpr expr, String function) { - writer.append(function); - writer.append('('); - expr.getFirstOperand().acceptVisitor(this); - writer.append(",").ws(); - expr.getSecondOperand().acceptVisitor(this); - writer.append(')'); + try { + writer.append(function); + writer.append('('); + expr.getFirstOperand().acceptVisitor(this); + writer.append(",").ws(); + expr.getSecondOperand().acceptVisitor(this); + writer.append(')'); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override @@ -616,73 +677,85 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(UnaryExpr expr) { - switch (expr.getOperation()) { - case NOT: - writer.append("(!"); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case NEGATE: - writer.append("(-"); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case LENGTH: - expr.getOperand().acceptVisitor(this); - writer.append(".length"); - break; - case INT_TO_LONG: - writer.append("Long_fromInt("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case NUM_TO_LONG: - writer.append("Long_fromNumber("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case LONG_TO_NUM: - writer.append("Long_toNumber("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case NEGATE_LONG: - writer.append("Long_neg("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case NOT_LONG: - writer.append("Long_not("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case BYTE_TO_INT: - writer.append("$rt_byteToInt("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; - case SHORT_TO_INT: - writer.append("$rt_shortToInt("); - expr.getOperand().acceptVisitor(this); - writer.append(')'); - break; + try { + switch (expr.getOperation()) { + case NOT: + writer.append("(!"); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case NEGATE: + writer.append("(-"); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case LENGTH: + expr.getOperand().acceptVisitor(this); + writer.append(".length"); + break; + case INT_TO_LONG: + writer.append("Long_fromInt("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case NUM_TO_LONG: + writer.append("Long_fromNumber("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case LONG_TO_NUM: + writer.append("Long_toNumber("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case NEGATE_LONG: + writer.append("Long_neg("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case NOT_LONG: + writer.append("Long_not("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case BYTE_TO_INT: + writer.append("$rt_byteToInt("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + case SHORT_TO_INT: + writer.append("$rt_shortToInt("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; + } + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } } @Override public void visit(ConditionalExpr expr) { - writer.append('('); - expr.getCondition().acceptVisitor(this); - writer.ws().append("?").ws(); - expr.getConsequent().acceptVisitor(this); - writer.ws().append(":").ws(); - expr.getAlternative().acceptVisitor(this); - writer.append(')'); + try { + writer.append('('); + expr.getCondition().acceptVisitor(this); + writer.ws().append("?").ws(); + expr.getConsequent().acceptVisitor(this); + writer.ws().append(":").ws(); + expr.getAlternative().acceptVisitor(this); + writer.append(')'); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(ConstantExpr expr) { - writer.append(constantToString(expr.getValue())); + try { + writer.append(constantToString(expr.getValue())); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } public String constantToString(Object cst) { @@ -798,173 +871,213 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(VariableExpr expr) { - writer.append(variableName(expr.getIndex())); + try { + writer.append(variableName(expr.getIndex())); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(SubscriptExpr expr) { - expr.getArray().acceptVisitor(this); - writer.append('['); - expr.getIndex().acceptVisitor(this); - writer.append(']'); + try { + expr.getArray().acceptVisitor(this); + writer.append('['); + expr.getIndex().acceptVisitor(this); + writer.append(']'); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(UnwrapArrayExpr expr) { - expr.getArray().acceptVisitor(this); - writer.append(".data"); + try { + expr.getArray().acceptVisitor(this); + writer.append(".data"); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(InvocationExpr expr) { - String className = naming.getNameFor(expr.getMethod().getClassName()); - String name = naming.getNameFor(expr.getMethod()); - String fullName = naming.getFullNameFor(expr.getMethod()); - switch (expr.getType()) { - case STATIC: - writer.append(fullName).append("("); - for (int i = 0; i < expr.getArguments().size(); ++i) { - if (i > 0) { - writer.append(",").ws(); + try { + String className = naming.getNameFor(expr.getMethod().getClassName()); + String name = naming.getNameFor(expr.getMethod()); + String fullName = naming.getFullNameFor(expr.getMethod()); + switch (expr.getType()) { + case STATIC: + writer.append(fullName).append("("); + for (int i = 0; i < expr.getArguments().size(); ++i) { + if (i > 0) { + writer.append(",").ws(); + } + expr.getArguments().get(i).acceptVisitor(this); } - expr.getArguments().get(i).acceptVisitor(this); - } - writer.append(')'); - break; - case SPECIAL: - writer.append(fullName).append("("); - expr.getArguments().get(0).acceptVisitor(this); - for (int i = 1; i < expr.getArguments().size(); ++i) { - writer.append(",").ws(); - expr.getArguments().get(i).acceptVisitor(this); - } - writer.append(")"); - break; - case DYNAMIC: - expr.getArguments().get(0).acceptVisitor(this); - writer.append(".").append(name).append("("); - for (int i = 1; i < expr.getArguments().size(); ++i) { - if (i > 1) { + writer.append(')'); + break; + case SPECIAL: + writer.append(fullName).append("("); + expr.getArguments().get(0).acceptVisitor(this); + for (int i = 1; i < expr.getArguments().size(); ++i) { writer.append(",").ws(); + expr.getArguments().get(i).acceptVisitor(this); } - expr.getArguments().get(i).acceptVisitor(this); - } - writer.append(')'); - break; - case CONSTRUCTOR: - writer.append(className).append(".").append(name).append("("); - for (int i = 0; i < expr.getArguments().size(); ++i) { - if (i > 0) { - writer.append(",").ws(); + writer.append(")"); + break; + case DYNAMIC: + expr.getArguments().get(0).acceptVisitor(this); + writer.append(".").append(name).append("("); + for (int i = 1; i < expr.getArguments().size(); ++i) { + if (i > 1) { + writer.append(",").ws(); + } + expr.getArguments().get(i).acceptVisitor(this); } - expr.getArguments().get(i).acceptVisitor(this); - } - writer.append(')'); - break; + writer.append(')'); + break; + case CONSTRUCTOR: + writer.append(className).append(".").append(name).append("("); + for (int i = 0; i < expr.getArguments().size(); ++i) { + if (i > 0) { + writer.append(",").ws(); + } + expr.getArguments().get(i).acceptVisitor(this); + } + writer.append(')'); + break; + } + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } } @Override public void visit(QualificationExpr expr) { - expr.getQualified().acceptVisitor(this); - writer.append('.').appendField(expr.getField()); + try { + expr.getQualified().acceptVisitor(this); + writer.append('.').appendField(expr.getField()); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(NewExpr expr) { - writer.append("new ").append(naming.getNameFor(expr.getConstructedClass())).append("()"); + try { + writer.append("new ").append(naming.getNameFor(expr.getConstructedClass())).append("()"); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } @Override public void visit(NewArrayExpr expr) { - ValueType type = expr.getType(); - while (type instanceof ValueType.Array) { - type = ((ValueType.Array)type).getItemType(); - } - if (type instanceof ValueType.Primitive) { - switch (((ValueType.Primitive)type).getKind()) { - case BOOLEAN: - writer.append("$rt_createBooleanArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case BYTE: - writer.append("$rt_createByteArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case SHORT: - writer.append("$rt_createShortArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case INTEGER: - writer.append("$rt_createIntArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case LONG: - writer.append("$rt_createLongArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case FLOAT: - writer.append("$rt_createFloatArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case DOUBLE: - writer.append("$rt_createDoubleArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; - case CHARACTER: - writer.append("$rt_createCharArray("); - expr.getLength().acceptVisitor(this); - writer.append(")"); - break; + try { + ValueType type = expr.getType(); + while (type instanceof ValueType.Array) { + type = ((ValueType.Array)type).getItemType(); } - } else { - writer.append("$rt_createArray(").append(typeToClsString(naming, expr.getType())).append(", "); - expr.getLength().acceptVisitor(this); - writer.append(")"); + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + writer.append("$rt_createBooleanArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case BYTE: + writer.append("$rt_createByteArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case SHORT: + writer.append("$rt_createShortArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case INTEGER: + writer.append("$rt_createIntArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case LONG: + writer.append("$rt_createLongArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case FLOAT: + writer.append("$rt_createFloatArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case DOUBLE: + writer.append("$rt_createDoubleArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + case CHARACTER: + writer.append("$rt_createCharArray("); + expr.getLength().acceptVisitor(this); + writer.append(")"); + break; + } + } else { + writer.append("$rt_createArray(").append(typeToClsString(naming, expr.getType())).append(", "); + expr.getLength().acceptVisitor(this); + writer.append(")"); + } + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } } @Override public void visit(NewMultiArrayExpr expr) { - writer.append("$rt_createMultiArray(").append(typeToClsString(naming, expr.getType())).append(",") - .ws().append("["); - boolean first = true; - for (Expr dimension : expr.getDimensions()) { - if (!first) { - writer.append(",").ws(); + try { + writer.append("$rt_createMultiArray(").append(typeToClsString(naming, expr.getType())).append(",") + .ws().append("["); + boolean first = true; + for (Expr dimension : expr.getDimensions()) { + if (!first) { + writer.append(",").ws(); + } + first = false; + dimension.acceptVisitor(this); } - first = false; - dimension.acceptVisitor(this); + writer.append("])"); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.append("])"); } @Override public void visit(InstanceOfExpr expr) { - if (expr.getType() instanceof ValueType.Object) { - String clsName = ((ValueType.Object)expr.getType()).getClassName(); - ClassHolder cls = classSource.getClassHolder(clsName); - if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) { - writer.append("("); - expr.getExpr().acceptVisitor(this); - writer.append(" instanceof ").appendClass(clsName).append(")"); - return; + try { + if (expr.getType() instanceof ValueType.Object) { + String clsName = ((ValueType.Object)expr.getType()).getClassName(); + ClassHolder cls = classSource.getClassHolder(clsName); + if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) { + writer.append("("); + expr.getExpr().acceptVisitor(this); + writer.append(" instanceof ").appendClass(clsName).append(")"); + return; + } } + writer.append("$rt_isInstance("); + expr.getExpr().acceptVisitor(this); + writer.append(",").ws().append(typeToClsString(naming, expr.getType())).append(")"); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); } - writer.append("$rt_isInstance("); - expr.getExpr().acceptVisitor(this); - writer.append(",").ws().append(typeToClsString(naming, expr.getType())).append(")"); } @Override public void visit(StaticClassExpr expr) { - writer.append(typeToClsString(naming, expr.getType())); + try { + writer.append(typeToClsString(naming, expr.getType())); + } catch (IOException e) { + throw new RenderingException("IO error occured", e); + } } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index 0056b26dd..86c0045be 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -12,7 +12,7 @@ import org.teavm.model.instructions.InvocationType; * * @author Alexey Andreev */ -public class StatementGenerator implements InstructionVisitor { +class StatementGenerator implements InstructionVisitor { private int lastSwitchId; List statements = new ArrayList<>(); GraphIndexer indexer; diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java b/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java index 329a397ca..d4e0bba25 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java @@ -1,5 +1,6 @@ package org.teavm.javascript.ni; +import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.model.MethodReference; @@ -8,5 +9,5 @@ import org.teavm.model.MethodReference; * @author Alexey Andreev */ public interface Generator { - void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef); + void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException; }