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);