Adds JavascriptBuilder - a facade for building whole JavaScript files

This commit is contained in:
konsoletyper 2013-12-16 17:48:38 +04:00
parent a736359a07
commit e5cb7a20d3
17 changed files with 629 additions and 398 deletions

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
@ -11,7 +12,8 @@ import org.teavm.model.MethodReference;
*/ */
public class ClassNativeGenerator implements Generator { public class ClassNativeGenerator implements Generator {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "isInstance": case "isInstance":
generateIsInstance(context, writer); 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(", ") writer.append("return $rt_isInstance(").append(context.getParameterName(1)).append(", ")
.append(context.getParameterName(0)).append(".$data);").softNewLine(); .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, ") writer.append("return $rt_isAssignable(").append(context.getParameterName(1)).append(".$data, ")
.append(context.getParameterName(0)).append(".$data;").softNewLine(); .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); String thisArg = context.getParameterName(0);
writer.append("var item = " + thisArg + ".$data.$meta.item;").softNewLine(); writer.append("var item = " + thisArg + ".$data.$meta.item;").softNewLine();
writer.append("return item != null ? $rt_cls(item) : null;").softNewLine(); writer.append("return item != null ? $rt_cls(item) : null;").softNewLine();

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
@ -11,7 +12,7 @@ import org.teavm.model.MethodReference;
*/ */
public class FloatNativeGenerator implements Generator { public class FloatNativeGenerator implements Generator {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "isNaN": case "isNaN":
generateIsNaN(context, writer); 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("?") writer.append("return (isNaN(").append(context.getParameterName(1)).append(")").ws().append("?")
.ws().append("1").ws().append(":").ws().append("0").ws().append(");").softNewLine(); .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("?") writer.append("return (isFinite(").append(context.getParameterName(1)).append(")").ws().append("?")
.ws().append("0").ws().append(":").ws().append("1").append(");").softNewLine(); .ws().append("0").ws().append(":").ws().append("1").append(");").softNewLine();
} }

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.DependencyPlugin;
@ -16,7 +17,7 @@ import org.teavm.model.ValueType;
*/ */
public class ObjectNativeGenerator implements Generator, DependencyPlugin { public class ObjectNativeGenerator implements Generator, DependencyPlugin {
@Override @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()) { switch (methodRef.getDescriptor().getName()) {
case "<init>": case "<init>":
generateInit(context, writer); 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(); 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); String thisArg = context.getParameterName(0);
writer.append("return $rt_cls(").append(thisArg).append(".$class);").softNewLine(); 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"); 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(); 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("var copy = new ").append(context.getParameterName(0)).append(".$class();").softNewLine();
writer.append("for (var field in obj) {").softNewLine().indent(); writer.append("for (var field in obj) {").softNewLine().indent();
writer.append("if (!obj.hasOwnProperty(field)) {").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()); 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(); writer.append("return ").append(context.getParameterName(1)).append(";").softNewLine();
} }

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
@ -11,7 +12,7 @@ import org.teavm.model.MethodReference;
*/ */
public class StringNativeGenerator implements Generator { public class StringNativeGenerator implements Generator {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "wrap": case "wrap":
generateWrap(context, writer); 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(); writer.append("return ").append(context.getParameterName(1)).softNewLine();
} }
} }

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyNode; import org.teavm.dependency.DependencyNode;
@ -15,7 +16,7 @@ import org.teavm.model.MethodReference;
*/ */
public class SystemNativeGenerator implements Generator, DependencyPlugin { public class SystemNativeGenerator implements Generator, DependencyPlugin {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "doArrayCopy": case "doArrayCopy":
generateArrayCopy(context, writer); 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 src = context.getParameterName(1);
String srcPos = context.getParameterName(2); String srcPos = context.getParameterName(2);
String dest = context.getParameterName(3); String dest = context.getParameterName(3);

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.java.lang.reflect; package org.teavm.classlib.java.lang.reflect;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyConsumer; import org.teavm.dependency.DependencyConsumer;
@ -24,7 +25,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
} }
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "getLength": case "getLength":
generateGetLength(context, writer); 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); String array = context.getParameterName(1);
writer.append("if (" + array + " === null || " + array + " .$class.$meta.item === undefined) {") writer.append("if (" + array + " === null || " + array + " .$class.$meta.item === undefined) {")
.softNewLine().indent(); .softNewLine().indent();

View File

@ -1,5 +1,6 @@
package org.teavm.classlib.org.junit; package org.teavm.classlib.org.junit;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext; import org.teavm.javascript.ni.GeneratorContext;
@ -11,7 +12,8 @@ import org.teavm.model.MethodReference;
*/ */
public class AssertNativeGenerator implements Generator { public class AssertNativeGenerator implements Generator {
@Override @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()) { switch (methodRef.getDescriptor().getName()) {
case "fail": case "fail":
generateFail(writer); 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(); writer.append("throw new Error();").newLine();
} }
} }

View File

@ -1,16 +1,14 @@
package org.teavm.classlibgen; package org.teavm.classlibgen;
import java.io.*; 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.apache.commons.io.IOUtils;
import org.teavm.codegen.*; import org.teavm.javascript.JavascriptBuilder;
import org.teavm.dependency.DependencyChecker;
import org.teavm.javascript.Decompiler;
import org.teavm.javascript.Renderer;
import org.teavm.javascript.ast.ClassNode;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.resource.ClasspathClassHolderSource; import org.teavm.model.resource.ClasspathClassHolderSource;
import org.teavm.optimization.ClassSetOptimizer;
/** /**
* *
@ -19,11 +17,6 @@ import org.teavm.optimization.ClassSetOptimizer;
public class ClasslibTestGenerator { public class ClasslibTestGenerator {
private static File outputDir; private static File outputDir;
private static ClasspathClassHolderSource classSource; 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<MethodReference> testMethods = new ArrayList<>(); private static List<MethodReference> testMethods = new ArrayList<>();
private static Map<String, List<MethodReference>> groupedMethods = new HashMap<>(); private static Map<String, List<MethodReference>> groupedMethods = new HashMap<>();
private static Map<MethodReference, String> fileNames = new HashMap<>(); private static Map<MethodReference, String> fileNames = new HashMap<>();
@ -90,46 +83,19 @@ public class ClasslibTestGenerator {
} }
private static void decompileClassesForTest(MethodReference methodRef, String targetName) throws IOException { private static void decompileClassesForTest(MethodReference methodRef, String targetName) throws IOException {
classSource = new ClasspathClassHolderSource(); JavascriptBuilder builder = new JavascriptBuilder();
decompiler = new Decompiler(classSource); builder.setMinifying(true);
aliasProvider = new DefaultAliasProvider(); @SuppressWarnings("resource")
naming = new DefaultNamingStrategy(aliasProvider, classSource); Writer innerWriter = new OutputStreamWriter(new FileOutputStream(new File(outputDir, targetName)), "UTF-8");
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);
MethodReference cons = new MethodReference(methodRef.getClassName(), MethodReference cons = new MethodReference(methodRef.getClassName(),
new MethodDescriptor("<init>", ValueType.VOID)); new MethodDescriptor("<init>", ValueType.VOID));
dependencyChecker.addEntryPoint(cons); builder.entryPoint("initInstance", cons);
dependencyChecker.addEntryPoint(methodRef); builder.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.Class", new MethodDescriptor("createNew", builder.exportType("TestClass", cons.getClassName());
ValueType.object("java.lang.Class")))); builder.build(innerWriter);
dependencyChecker.attachMethodGraph(new MethodReference("java.lang.String", new MethodDescriptor("<init>", innerWriter.append("\n");
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); innerWriter.append("\nJUnitClient.run();");
dependencyChecker.checkDependencies(); innerWriter.close();
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<String> classNames) {
List<ClassNode> clsNodes = decompiler.decompile(classNames);
for (ClassNode clsNode : clsNodes) {
renderer.render(clsNode);
}
} }
private static void findTests(ClassHolder cls) { private static void findTests(ClassHolder cls) {

View File

@ -182,12 +182,14 @@ JUnitServer.prototype.createFooter = function() {
} }
JUnitClient = {}; JUnitClient = {};
JUnitClient.run = function(runner) { JUnitClient.run = function() {
var handler = window.addEventListener("message", function() { var handler = window.addEventListener("message", function() {
window.removeEventListener("message", handler); window.removeEventListener("message", handler);
var message = {}; var message = {};
try { try {
runner(); var instance = new TestClass();
initInstance(instance);
runTest(instance);
message.status = "ok"; message.status = "ok";
} catch (e) { } catch (e) {
message.status = "exception"; message.status = "exception";

View File

@ -1,5 +1,6 @@
package org.teavm.codegen; package org.teavm.codegen;
import java.io.IOException;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -7,87 +8,98 @@ import org.teavm.model.MethodReference;
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class SourceWriter { public class SourceWriter implements Appendable {
private StringBuilder sb = new StringBuilder(); private Appendable innerWriter;
private int indentSize = 0; private int indentSize = 0;
private NamingStrategy naming; private NamingStrategy naming;
private boolean lineStart; private boolean lineStart;
private boolean minified; private boolean minified;
SourceWriter(NamingStrategy naming) { SourceWriter(NamingStrategy naming, Appendable innerWriter) {
this.naming = naming; this.naming = naming;
this.innerWriter = innerWriter;
} }
void setMinified(boolean minified) { void setMinified(boolean minified) {
this.minified = minified; this.minified = minified;
} }
public void clear() { public SourceWriter append(String value) throws IOException {
sb.setLength(0);
}
public SourceWriter append(String value) {
appendIndent(); appendIndent();
sb.append(value); innerWriter.append(value);
return this; return this;
} }
public SourceWriter append(Object value) { public SourceWriter append(Object value) throws IOException {
return append(String.valueOf(value)); return append(String.valueOf(value));
} }
public SourceWriter append(int value) { public SourceWriter append(int value) throws IOException {
return append(String.valueOf(value)); return append(String.valueOf(value));
} }
public SourceWriter append(char value) { @Override
return append(String.valueOf(value)); 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)); 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)); 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)); 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)); return append(naming.getFullNameFor(method));
} }
private void appendIndent() { private void appendIndent() throws IOException {
if (minified) { if (minified) {
return; return;
} }
if (lineStart) { if (lineStart) {
for (int i = 0; i < indentSize; ++i) { for (int i = 0; i < indentSize; ++i) {
sb.append(" "); innerWriter.append(" ");
} }
lineStart = false; lineStart = false;
} }
} }
public SourceWriter newLine() { public SourceWriter newLine() throws IOException{
sb.append('\n'); innerWriter.append('\n');
lineStart = true; lineStart = true;
return this; return this;
} }
public SourceWriter ws() { public SourceWriter ws() throws IOException{
if (!minified) { if (!minified) {
sb.append(' '); innerWriter.append(' ');
} }
return this; return this;
} }
public SourceWriter softNewLine() { public SourceWriter softNewLine() throws IOException{
if (!minified) { if (!minified) {
sb.append('\n'); innerWriter.append('\n');
lineStart = true; lineStart = true;
} }
return this; return this;
@ -106,9 +118,4 @@ public class SourceWriter {
public NamingStrategy getNaming() { public NamingStrategy getNaming() {
return naming; return naming;
} }
@Override
public String toString() {
return sb.toString();
}
} }

View File

@ -1,6 +1,5 @@
package org.teavm.codegen; package org.teavm.codegen;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
@ -21,8 +20,8 @@ public class SourceWriterBuilder {
this.minified = minified; this.minified = minified;
} }
public SourceWriter build() { public SourceWriter build(Appendable innerWriter) {
SourceWriter writer = new SourceWriter(naming); SourceWriter writer = new SourceWriter(naming, innerWriter);
writer.setMinified(minified); writer.setMinified(minified);
return writer; return writer;
} }

View File

@ -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<String, JavascriptEntryPoint> entryPoints = new HashMap<>();
private Map<String, String> 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("<init>",
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)));
dependencyChecker.checkDependencies();
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
ClassSetOptimizer optimizer = new ClassSetOptimizer();
optimizer.optimizeAll(classSet);
renderer.renderRuntime();
List<ClassNode> clsNodes = decompiler.decompile(classSet.getClassNames());
for (ClassNode clsNode : clsNodes) {
renderer.render(clsNode);
}
try {
for (Map.Entry<String, JavascriptEntryPoint> entry : entryPoints.entrySet()) {
sourceWriter.append(entry.getKey()).ws().append("=").ws().appendMethodBody(entry.getValue().reference)
.append(";").softNewLine();
}
for (Map.Entry<String, String> 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();
}
}
}

View File

@ -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;
}
}

View File

@ -1,9 +0,0 @@
package org.teavm.javascript;
/**
*
* @author Alexey Andreev
*/
public class JavascriptGenerator {
}

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.javascript; package org.teavm.javascript;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.codegen.NamingException; import org.teavm.codegen.NamingException;
@ -64,10 +65,12 @@ public class Renderer implements ExprVisitor, StatementVisitor {
renderRuntimeObjcls(); renderRuntimeObjcls();
} catch (NamingException e) { } catch (NamingException e) {
throw new RenderingException("Error rendering runtime methods. See a cause for details", 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("{") writer.append("$rt_cls").ws().append("=").ws().append("function(clsProto)").ws().append("{")
.indent().softNewLine(); .indent().softNewLine();
String classClass = "java.lang.Class"; String classClass = "java.lang.Class";
@ -98,7 +101,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
} }
private void renderRuntimeString() { private void renderRuntimeString() throws IOException {
String stringClass = "java.lang.String"; String stringClass = "java.lang.String";
MethodReference stringCons = new MethodReference(stringClass, new MethodDescriptor("<init>", MethodReference stringCons = new MethodReference(stringClass, new MethodDescriptor("<init>",
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)); ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID));
@ -113,7 +116,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
} }
private void renderRuntimeObjcls() { private void renderRuntimeObjcls() throws IOException {
writer.append("$rt_objcls = function() { return ").appendClass("java.lang.Object").append("; }").newLine(); 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) { } catch (NamingException e) {
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", 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; return null;
} }
private void renderInitializer(MethodNode method) { private void renderInitializer(MethodNode method) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function("); writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function(");
for (int i = 1; i <= ref.parameterCount(); ++i) { for (int i = 1; i <= ref.parameterCount(); ++i) {
@ -254,7 +259,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
} }
public void renderDeclaration(MethodNode method) throws RenderingException { public void renderDeclaration(MethodNode method) throws RenderingException, IOException {
try { try {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
if (ref.getDescriptor().getName().equals("<init>")) { if (ref.getDescriptor().getName().equals("<init>")) {
@ -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(); MethodReference ref = method.getReference();
writer.appendMethodBody(ref).ws().append("=").ws().append("function("); writer.appendMethodBody(ref).ws().append("=").ws().append("function(");
int startParam = 0; int startParam = 0;
@ -315,27 +320,35 @@ public class Renderer implements ExprVisitor, StatementVisitor {
private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
@Override @Override
public void visit(NativeMethodNode methodNode) { 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 @Override
public void visit(RegularMethodNode method) { public void visit(RegularMethodNode method) {
MethodReference ref = method.getReference(); try {
int variableCount = method.getVariableCount(); MethodReference ref = method.getReference();
boolean hasVars = variableCount > ref.parameterCount() + 1; int variableCount = method.getVariableCount();
if (hasVars) { boolean hasVars = variableCount > ref.parameterCount() + 1;
writer.append("var "); if (hasVars) {
boolean first = true; writer.append("var ");
for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { boolean first = true;
if (!first) { for (int i = ref.parameterCount() + 1; i < variableCount; ++i) {
writer.append(",").ws(); if (!first) {
writer.append(",").ws();
}
first = false;
writer.append(variableName(i));
} }
first = false; writer.append(";").softNewLine();
writer.append(variableName(i));
} }
writer.append(";").softNewLine(); method.getBody().acceptVisitor(Renderer.this);
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
} }
method.getBody().acceptVisitor(Renderer.this);
} }
@Override @Override
@ -345,13 +358,17 @@ public class Renderer implements ExprVisitor, StatementVisitor {
} }
@Override @Override
public void visit(AssignmentStatement statement) { public void visit(AssignmentStatement statement) throws RenderingException {
if (statement.getLeftValue() != null) { try {
statement.getLeftValue().acceptVisitor(this); if (statement.getLeftValue() != null) {
writer.ws().append("=").ws(); 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 @Override
@ -363,66 +380,82 @@ public class Renderer implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(ConditionalStatement statement) { public void visit(ConditionalStatement statement) {
writer.append("if").ws().append("("); try {
statement.getCondition().acceptVisitor(this); writer.append("if").ws().append("(");
writer.append(")").ws().append("{").softNewLine().indent(); statement.getCondition().acceptVisitor(this);
statement.getConsequent().acceptVisitor(this); writer.append(")").ws().append("{").softNewLine().indent();
if (statement.getAlternative() != null) { statement.getConsequent().acceptVisitor(this);
writer.outdent().append("}").ws().append("else").ws().append("{").indent().softNewLine(); if (statement.getAlternative() != null) {
statement.getAlternative().acceptVisitor(this); 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 @Override
public void visit(SwitchStatement statement) { public void visit(SwitchStatement statement) {
if (statement.getId() != null) { try {
writer.append(statement.getId()).append(": "); 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();
} }
writer.indent(); writer.append("switch").ws().append("(");
clause.getStatement().acceptVisitor(this); statement.getValue().acceptVisitor(this);
writer.outdent(); 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 @Override
public void visit(WhileStatement statement) { public void visit(WhileStatement statement) {
if (statement.getId() != null) { try {
writer.append(statement.getId()).append(":").ws(); 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 @Override
public void visit(BlockStatement statement) { public void visit(BlockStatement statement) {
writer.append(statement.getId()).append(":").ws().append("{").softNewLine().indent(); try {
for (Statement part : statement.getBody()) { writer.append(statement.getId()).append(":").ws().append("{").softNewLine().indent();
part.acceptVisitor(this); 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 @Override
@ -432,48 +465,68 @@ public class Renderer implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(BreakStatement statement) { 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 @Override
public void visit(ContinueStatement statement) { 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 @Override
public void visit(ReturnStatement statement) { public void visit(ReturnStatement statement) {
writer.append("return"); try {
if (statement.getResult() != null) { writer.append("return");
writer.append(' '); if (statement.getResult() != null) {
statement.getResult().acceptVisitor(this); writer.append(' ');
statement.getResult().acceptVisitor(this);
}
writer.append(";").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
} }
writer.append(";").softNewLine();
} }
@Override @Override
public void visit(ThrowStatement statement) { public void visit(ThrowStatement statement) {
writer.append("$rt_throw("); try {
statement.getException().acceptVisitor(this); writer.append("$rt_throw(");
writer.append(");").softNewLine(); statement.getException().acceptVisitor(this);
writer.append(");").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
public void visit(IncrementStatement statement) { public void visit(IncrementStatement statement) {
writer.append(variableName(statement.getVar())); try {
if (statement.getAmount() > 0) { writer.append(variableName(statement.getVar()));
if (statement.getAmount() == 1) { if (statement.getAmount() > 0) {
writer.append("++"); if (statement.getAmount() == 1) {
writer.append("++");
} else {
writer.ws().append("+=").ws().append(statement.getAmount());
}
} else { } else {
writer.ws().append("+=").ws().append(statement.getAmount()); if (statement.getAmount() == -1) {
} writer.append("--");
} else { } else {
if (statement.getAmount() == -1) { writer.ws().append("-=").ws().append(statement.getAmount());
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) { public String variableName(int index) {
@ -490,20 +543,28 @@ public class Renderer implements ExprVisitor, StatementVisitor {
} }
private void visitBinary(BinaryExpr expr, String op) { private void visitBinary(BinaryExpr expr, String op) {
writer.append('('); try {
expr.getFirstOperand().acceptVisitor(this); writer.append('(');
writer.ws().append(op).ws(); expr.getFirstOperand().acceptVisitor(this);
expr.getSecondOperand().acceptVisitor(this); writer.ws().append(op).ws();
writer.append(')'); expr.getSecondOperand().acceptVisitor(this);
writer.append(')');
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
private void visitBinaryFunction(BinaryExpr expr, String function) { private void visitBinaryFunction(BinaryExpr expr, String function) {
writer.append(function); try {
writer.append('('); writer.append(function);
expr.getFirstOperand().acceptVisitor(this); writer.append('(');
writer.append(",").ws(); expr.getFirstOperand().acceptVisitor(this);
expr.getSecondOperand().acceptVisitor(this); writer.append(",").ws();
writer.append(')'); expr.getSecondOperand().acceptVisitor(this);
writer.append(')');
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
@ -616,73 +677,85 @@ public class Renderer implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(UnaryExpr expr) { public void visit(UnaryExpr expr) {
switch (expr.getOperation()) { try {
case NOT: switch (expr.getOperation()) {
writer.append("(!"); case NOT:
expr.getOperand().acceptVisitor(this); writer.append("(!");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case NEGATE: break;
writer.append("(-"); case NEGATE:
expr.getOperand().acceptVisitor(this); writer.append("(-");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case LENGTH: break;
expr.getOperand().acceptVisitor(this); case LENGTH:
writer.append(".length"); expr.getOperand().acceptVisitor(this);
break; writer.append(".length");
case INT_TO_LONG: break;
writer.append("Long_fromInt("); case INT_TO_LONG:
expr.getOperand().acceptVisitor(this); writer.append("Long_fromInt(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case NUM_TO_LONG: break;
writer.append("Long_fromNumber("); case NUM_TO_LONG:
expr.getOperand().acceptVisitor(this); writer.append("Long_fromNumber(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case LONG_TO_NUM: break;
writer.append("Long_toNumber("); case LONG_TO_NUM:
expr.getOperand().acceptVisitor(this); writer.append("Long_toNumber(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case NEGATE_LONG: break;
writer.append("Long_neg("); case NEGATE_LONG:
expr.getOperand().acceptVisitor(this); writer.append("Long_neg(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case NOT_LONG: break;
writer.append("Long_not("); case NOT_LONG:
expr.getOperand().acceptVisitor(this); writer.append("Long_not(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case BYTE_TO_INT: break;
writer.append("$rt_byteToInt("); case BYTE_TO_INT:
expr.getOperand().acceptVisitor(this); writer.append("$rt_byteToInt(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
case SHORT_TO_INT: break;
writer.append("$rt_shortToInt("); case SHORT_TO_INT:
expr.getOperand().acceptVisitor(this); writer.append("$rt_shortToInt(");
writer.append(')'); expr.getOperand().acceptVisitor(this);
break; writer.append(')');
break;
}
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
} }
} }
@Override @Override
public void visit(ConditionalExpr expr) { public void visit(ConditionalExpr expr) {
writer.append('('); try {
expr.getCondition().acceptVisitor(this); writer.append('(');
writer.ws().append("?").ws(); expr.getCondition().acceptVisitor(this);
expr.getConsequent().acceptVisitor(this); writer.ws().append("?").ws();
writer.ws().append(":").ws(); expr.getConsequent().acceptVisitor(this);
expr.getAlternative().acceptVisitor(this); writer.ws().append(":").ws();
writer.append(')'); expr.getAlternative().acceptVisitor(this);
writer.append(')');
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
public void visit(ConstantExpr expr) { 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) { public String constantToString(Object cst) {
@ -798,173 +871,213 @@ public class Renderer implements ExprVisitor, StatementVisitor {
@Override @Override
public void visit(VariableExpr expr) { 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 @Override
public void visit(SubscriptExpr expr) { public void visit(SubscriptExpr expr) {
expr.getArray().acceptVisitor(this); try {
writer.append('['); expr.getArray().acceptVisitor(this);
expr.getIndex().acceptVisitor(this); writer.append('[');
writer.append(']'); expr.getIndex().acceptVisitor(this);
writer.append(']');
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
public void visit(UnwrapArrayExpr expr) { public void visit(UnwrapArrayExpr expr) {
expr.getArray().acceptVisitor(this); try {
writer.append(".data"); expr.getArray().acceptVisitor(this);
writer.append(".data");
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
public void visit(InvocationExpr expr) { public void visit(InvocationExpr expr) {
String className = naming.getNameFor(expr.getMethod().getClassName()); try {
String name = naming.getNameFor(expr.getMethod()); String className = naming.getNameFor(expr.getMethod().getClassName());
String fullName = naming.getFullNameFor(expr.getMethod()); String name = naming.getNameFor(expr.getMethod());
switch (expr.getType()) { String fullName = naming.getFullNameFor(expr.getMethod());
case STATIC: switch (expr.getType()) {
writer.append(fullName).append("("); case STATIC:
for (int i = 0; i < expr.getArguments().size(); ++i) { writer.append(fullName).append("(");
if (i > 0) { for (int i = 0; i < expr.getArguments().size(); ++i) {
writer.append(",").ws(); if (i > 0) {
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
} }
expr.getArguments().get(i).acceptVisitor(this); writer.append(')');
} break;
writer.append(')'); case SPECIAL:
break; writer.append(fullName).append("(");
case SPECIAL: expr.getArguments().get(0).acceptVisitor(this);
writer.append(fullName).append("("); for (int i = 1; i < expr.getArguments().size(); ++i) {
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(",").ws(); writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this);
} }
expr.getArguments().get(i).acceptVisitor(this); writer.append(")");
} break;
writer.append(')'); case DYNAMIC:
break; expr.getArguments().get(0).acceptVisitor(this);
case CONSTRUCTOR: writer.append(".").append(name).append("(");
writer.append(className).append(".").append(name).append("("); for (int i = 1; i < expr.getArguments().size(); ++i) {
for (int i = 0; i < expr.getArguments().size(); ++i) { if (i > 1) {
if (i > 0) { writer.append(",").ws();
writer.append(",").ws(); }
expr.getArguments().get(i).acceptVisitor(this);
} }
expr.getArguments().get(i).acceptVisitor(this); writer.append(')');
} break;
writer.append(')'); case CONSTRUCTOR:
break; 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 @Override
public void visit(QualificationExpr expr) { public void visit(QualificationExpr expr) {
expr.getQualified().acceptVisitor(this); try {
writer.append('.').appendField(expr.getField()); expr.getQualified().acceptVisitor(this);
writer.append('.').appendField(expr.getField());
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
} }
@Override @Override
public void visit(NewExpr expr) { 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 @Override
public void visit(NewArrayExpr expr) { public void visit(NewArrayExpr expr) {
ValueType type = expr.getType(); try {
while (type instanceof ValueType.Array) { ValueType type = expr.getType();
type = ((ValueType.Array)type).getItemType(); 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;
} }
} else { if (type instanceof ValueType.Primitive) {
writer.append("$rt_createArray(").append(typeToClsString(naming, expr.getType())).append(", "); switch (((ValueType.Primitive)type).getKind()) {
expr.getLength().acceptVisitor(this); case BOOLEAN:
writer.append(")"); 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 @Override
public void visit(NewMultiArrayExpr expr) { public void visit(NewMultiArrayExpr expr) {
writer.append("$rt_createMultiArray(").append(typeToClsString(naming, expr.getType())).append(",") try {
.ws().append("["); writer.append("$rt_createMultiArray(").append(typeToClsString(naming, expr.getType())).append(",")
boolean first = true; .ws().append("[");
for (Expr dimension : expr.getDimensions()) { boolean first = true;
if (!first) { for (Expr dimension : expr.getDimensions()) {
writer.append(",").ws(); if (!first) {
writer.append(",").ws();
}
first = false;
dimension.acceptVisitor(this);
} }
first = false; writer.append("])");
dimension.acceptVisitor(this); } catch (IOException e) {
throw new RenderingException("IO error occured", e);
} }
writer.append("])");
} }
@Override @Override
public void visit(InstanceOfExpr expr) { public void visit(InstanceOfExpr expr) {
if (expr.getType() instanceof ValueType.Object) { try {
String clsName = ((ValueType.Object)expr.getType()).getClassName(); if (expr.getType() instanceof ValueType.Object) {
ClassHolder cls = classSource.getClassHolder(clsName); String clsName = ((ValueType.Object)expr.getType()).getClassName();
if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) { ClassHolder cls = classSource.getClassHolder(clsName);
writer.append("("); if (!cls.getModifiers().contains(ElementModifier.INTERFACE)) {
expr.getExpr().acceptVisitor(this); writer.append("(");
writer.append(" instanceof ").appendClass(clsName).append(")"); expr.getExpr().acceptVisitor(this);
return; 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 @Override
public void visit(StaticClassExpr expr) { 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);
}
} }
} }

View File

@ -12,7 +12,7 @@ import org.teavm.model.instructions.InvocationType;
* *
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class StatementGenerator implements InstructionVisitor { class StatementGenerator implements InstructionVisitor {
private int lastSwitchId; private int lastSwitchId;
List<Statement> statements = new ArrayList<>(); List<Statement> statements = new ArrayList<>();
GraphIndexer indexer; GraphIndexer indexer;

View File

@ -1,5 +1,6 @@
package org.teavm.javascript.ni; package org.teavm.javascript.ni;
import java.io.IOException;
import org.teavm.codegen.SourceWriter; import org.teavm.codegen.SourceWriter;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -8,5 +9,5 @@ import org.teavm.model.MethodReference;
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public interface Generator { public interface Generator {
void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef); void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException;
} }