diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml index 3d9a13a75..711706dfe 100644 --- a/teavm-classlib/pom.xml +++ b/teavm-classlib/pom.xml @@ -16,5 +16,10 @@ 0.0.1-SNAPSHOT true + + junit + junit + 4.11 + \ No newline at end of file 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 1a64dc0d9..026202330 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 @@ -15,7 +15,7 @@ public class ObjectNativeGenerator implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { switch (methodRef.getDescriptor().getName()) { - case "init": + case "": generateInit(context, writer); break; case "getClass": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 17a7c068b..dfd3affd6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -2,17 +2,20 @@ package org.teavm.classlib.java.lang; import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.Rename; +import org.teavm.javascript.ni.Superclass; /** * * @author Alexey Andreev */ +@Superclass("") public class TObject { + @Rename("fakeInit") public TObject() { - init(); } @GeneratedBy(ObjectNativeGenerator.class) + @Rename("") private native void init(); @GeneratedBy(ObjectNativeGenerator.class) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectTests.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectTests.java new file mode 100644 index 000000000..08baad0be --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectTests.java @@ -0,0 +1,30 @@ +package org.teavm.classlib.java.lang; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +class TObjectTests { + @Test + public void objectCreated() { + Object a = new Object(); + assertNotNull(a); + } + + @Test + public void differentInstancesNotEqual() { + Object a = new Object(); + Object b = new Object(); + assertNotEquals(a, b); + } + + @Test + public void sameInstancesAreEqual() { + Object a = new Object(); + Object b = a; + assertEquals(a, b); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TAnnotation.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TAnnotation.java new file mode 100644 index 000000000..863f86765 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/annotation/TAnnotation.java @@ -0,0 +1,8 @@ +package org.teavm.classlib.java.lang.annotation; + +/** + * + * @author Alexey Andreev + */ +public @interface TAnnotation { +} 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 455fa93fc..cfa5a23eb 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -1,12 +1,15 @@ package org.teavm.classlibgen; +import java.io.IOException; +import java.io.InputStream; +import org.apache.commons.io.IOUtils; import org.teavm.codegen.DefaultAliasProvider; import org.teavm.codegen.DefaultNamingStrategy; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.Decompiler; import org.teavm.javascript.Renderer; import org.teavm.javascript.ast.ClassNode; -import org.teavm.model.ClassHolder; +import org.teavm.model.*; import org.teavm.model.resource.ClasspathClassHolderSource; /** @@ -14,16 +17,77 @@ import org.teavm.model.resource.ClasspathClassHolderSource; * @author Alexey Andreev */ public class ClasslibTestGenerator { - public static void main(String[] args) { - ClasspathClassHolderSource source = new ClasspathClassHolderSource(); - Decompiler decompiler = new Decompiler(source); - ClassHolder cls = source.getClassHolder("java.lang.Object"); - ClassNode clsNode = decompiler.decompile(cls); - DefaultAliasProvider aliasProvider = new DefaultAliasProvider(); - DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source); - SourceWriter writer = new SourceWriter(naming); - Renderer renderer = new Renderer(writer, source); - renderer.render(clsNode); + private static ClasspathClassHolderSource classSource; + private static Decompiler decompiler; + private static DefaultAliasProvider aliasProvider; + private static DefaultNamingStrategy naming; + private static SourceWriter writer; + private static Renderer renderer; + + public static void main(String[] args) throws IOException { + classSource = new ClasspathClassHolderSource(); + decompiler = new Decompiler(classSource); + aliasProvider = new DefaultAliasProvider(); + naming = new DefaultNamingStrategy(aliasProvider, classSource); + writer = new SourceWriter(naming); + renderer = new Renderer(writer, classSource); + decompileClass("java.lang.Object"); + decompileClass("java.lang.ObjectTests"); + decompileClass("java.lang.Class"); + decompileClass("java.lang.annotation.Annotation"); + decompileClass("org.junit.Assert"); + decompileClass("org.junit.Test"); + renderHead(); + ClassLoader classLoader = ClasslibTestGenerator.class.getClassLoader(); + try (InputStream input = classLoader.getResourceAsStream( + "org/teavm/classlib/junit-support.js")) { + System.out.println(IOUtils.toString(input)); + } + try (InputStream input = classLoader.getResourceAsStream( + "org/teavm/javascript/runtime.js")) { + System.out.println(IOUtils.toString(input)); + } + renderClassTest(classSource.getClassHolder("java.lang.ObjectTests")); System.out.println(writer); + renderFoot(); + } + + private static void decompileClass(String className) { + ClassHolder cls = classSource.getClassHolder(className); + ClassNode clsNode = decompiler.decompile(cls); + renderer.render(clsNode); + } + + private static void renderHead() { + System.out.println(""); + System.out.println(""); + System.out.println(" "); + System.out.println(" TeaVM JUnit tests"); + System.out.println(" "); + System.out.println(" TeaVM JUnit tests"); + System.out.println(" "); + System.out.println(" "); + System.out.println(" "); + System.out.println(" "); + System.out.println(""); + } + + private static void renderClassTest(ClassHolder cls) { + writer.append("testClass(\"" + cls.getName() + "\", function() {").newLine().indent(); + MethodReference cons = new MethodReference(cls.getName(), + new MethodDescriptor("", ValueType.VOID)); + for (MethodHolder method : cls.getMethods()) { + if (method.getAnnotations().get("org.junit.Test") != null) { + MethodReference ref = new MethodReference(cls.getName(), method.getDescriptor()); + writer.append("runTestCase(").appendClass(cls.getName()).append(".").appendMethod(cons) + .append("(), \"" + method.getName() + "\", \"").appendMethod(ref).append("\");").newLine(); + } + } + writer.outdent().append("})").newLine(); } } 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 new file mode 100644 index 000000000..64245d64f --- /dev/null +++ b/teavm-classlib/src/main/resources/org/teavm/classlib/junit-support.js @@ -0,0 +1,41 @@ +JUnitAssertionFailure = function() {} +JUnitAssertionFailure.prototype = new Error(); + +currentTestReportBody = null; + +runTestCase = function(instance, methodName, realMethodName) { + var row = document.createElement("tr"); + currentTestReportBody.appendChild(row); + var nameCell = document.createElement("td"); + row.appendChild(nameCell); + nameCell.appendChild(document.createTextNode(methodName)); + var statusCell = document.createElement("td"); + row.appendChild(statusCell); + var exceptionCell = document.createElement("td"); + row.appendChild(exceptionCell); + try { + instance[realMethodName](); + statusCell.appendChild(document.createTextNode("ok")); + } catch (e) { + if (e instanceof JUnitAssertionFailure) { + statusCell.appendChild(document.createTextNode("assertion failed")); + exceptionCell.appendChild(document.createTextNode(e.stack)); + } else { + statusCell.appendChild(document.createTextNode("unexpected exception")); + exceptionCell.appendChild(document.createTextNode(e.stack)); + } + } +} + +testClass = function(className, classTests) { + var table = document.createElement("table"); + document.body.appendChild(table); + var caption = document.createElement("caption"); + table.appendChild(caption); + caption.appendChild(document.createTextNode(className)); + var tbody = document.createElement("tbody"); + table.appendChild(tbody); + currentTestReportBody = tbody; + classTests(); + currentTestReportBody = null; +} \ No newline at end of file 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 0c45b38d4..848429315 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -153,7 +153,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { } renderWorkingMethod(method); int startParam = 0; - if (method.getModifiers().contains(ElementModifier.STATIC)) { + if (method.getModifiers().contains(NodeModifier.STATIC)) { startParam = 1; } writer.appendClass(ref.getClassName()).append('.'); @@ -186,7 +186,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { MethodReference ref = method.getReference(); writer.append("function ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append('('); int startParam = 0; - if (method.getModifiers().contains(ElementModifier.STATIC)) { + if (method.getModifiers().contains(NodeModifier.STATIC)) { startParam = 1; } for (int i = startParam; i <= ref.parameterCount(); ++i) { 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 ddc02fa91..171aacfb3 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -302,11 +302,11 @@ public class StatementGenerator implements InstructionVisitor { insn.getAlternative()); break; case NOT_NULL: - branch(Expr.binary(BinaryOperation.STRICT_EQUALS, Expr.var(insn.getOperand().getIndex()), + branch(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; case NULL: - branch(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, Expr.var(insn.getOperand().getIndex()), + branch(Expr.binary(BinaryOperation.STRICT_EQUALS, Expr.var(insn.getOperand().getIndex()), Expr.constant(null)), insn.getConsequent(), insn.getAlternative()); break; } @@ -489,7 +489,7 @@ public class StatementGenerator implements InstructionVisitor { public String findDeclaringClass(String className, MethodDescriptor method) { ClassHolder cls = classSource.getClassHolder(className); while (cls != null && cls.getMethod(method) == null) { - cls = classSource.getClassHolder(cls.getParent()); + cls = cls.getParent() != null ? classSource.getClassHolder(cls.getParent()) : null; } return cls != null ? cls.getName() : null; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java b/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java index dea50f7ab..0d806ccb2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java @@ -10,7 +10,7 @@ import java.lang.annotation.Target; * @author Alexey Andreev */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) public @interface Rename { String value(); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/Superclass.java b/teavm-core/src/main/java/org/teavm/javascript/ni/Superclass.java new file mode 100644 index 000000000..de5600d09 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/Superclass.java @@ -0,0 +1,16 @@ +package org.teavm.javascript.ni; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Superclass { + String value(); +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClassRefsRenamer.java b/teavm-core/src/main/java/org/teavm/model/resource/ClassRefsRenamer.java index 685ea2afa..cf3f70336 100644 --- a/teavm-core/src/main/java/org/teavm/model/resource/ClassRefsRenamer.java +++ b/teavm-core/src/main/java/org/teavm/model/resource/ClassRefsRenamer.java @@ -3,6 +3,7 @@ package org.teavm.model.resource; import java.util.Map; import org.teavm.codegen.Mapper; import org.teavm.javascript.ni.Rename; +import org.teavm.javascript.ni.Superclass; import org.teavm.model.*; import org.teavm.model.instructions.*; @@ -21,9 +22,15 @@ class ClassRefsRenamer implements InstructionVisitor { ClassHolder renamedCls = new ClassHolder(classNameMapper.map(cls.getName())); renamedCls.getModifiers().addAll(cls.getModifiers()); renamedCls.setLevel(cls.getLevel()); - if (cls.getParent() != null) { - renamedCls.setParent(classNameMapper.map(cls.getParent())); + String parent = cls.getParent(); + AnnotationHolder superclassAnnot = cls.getAnnotations().get(Superclass.class.getName()); + if (superclassAnnot != null) { + parent = superclassAnnot.getValues().get("value").getString(); + if (parent.isEmpty()) { + parent = null; + } } + renamedCls.setParent(parent != null ? classNameMapper.map(parent) : null); for (MethodHolder method : cls.getMethods()) { renamedCls.addMethod(rename(method)); } @@ -71,7 +78,8 @@ class ClassRefsRenamer implements InstructionVisitor { private void rename(AnnotationContainer source, AnnotationContainer target) { for (AnnotationHolder annot : source.all()) { - if (!annot.getType().equals(Rename.class.getName())) { + if (!annot.getType().equals(Rename.class.getName()) && + !annot.getType().equals(Superclass.class.getName())) { target.add(rename(annot)); } } diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java index 29f3bd4f3..5cb6d636e 100644 --- a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java +++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java @@ -81,7 +81,10 @@ public class ClasspathResourceMapper implements Mapper { String packageName = name.substring(0, index); ClassHolder classHolder = innerMapper.map(transformation.packagePrefix + "." + packageName + "." + transformation.classPrefix + className); - return renamer.rename(classHolder); + if (classHolder != null) { + classHolder = renamer.rename(classHolder); + } + return classHolder; } } return innerMapper.map(name);