From f63d0cd8d07a0d693f7ebfb66a8cace0c0a2064c Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Thu, 20 Feb 2014 11:58:44 +0400 Subject: [PATCH] Adds complete enum support --- .../classlib/impl/EnumDependencySupport.java | 68 +++++++++ .../teavm/classlib/impl/EnumTransformer.java | 38 +++++ .../org/teavm/classlib/impl/JCLPlugin.java | 31 +++++ .../java/lang/ClassNativeGenerator.java | 4 + .../java/lang/ObjectNativeGenerator.java | 9 +- .../teavm/classlib/java/lang/TCharacter.java | 15 ++ .../org/teavm/classlib/java/lang/TClass.java | 12 ++ .../lang/TClassCastException.java} | 24 ++-- .../org/teavm/classlib/java/lang/TDouble.java | 15 ++ .../org/teavm/classlib/java/lang/TEnum.java | 19 ++- .../org/teavm/classlib/java/lang/TLong.java | 16 +++ .../org/teavm/classlib/org/junit/Assert.java | 87 ------------ .../org/junit/AssertNativeGenerator.java | 42 ------ ...g.teavm.javascript.JavascriptBuilderPlugin | 1 + .../main/resources/META-INF/teavm.properties | 3 +- .../teavm/classlib/java/lang/EnumTest.java | 73 ++++++++++ .../java/org/teavm/javascript/Decompiler.java | 6 +- .../teavm/javascript/JavascriptBuilder.java | 5 + .../java/org/teavm/javascript/Renderer.java | 130 +++++++++++++----- .../teavm/javascript/ast/NodeModifier.java | 3 +- teavm-html4j/pom.xml | 4 +- 21 files changed, 419 insertions(+), 186 deletions(-) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java rename teavm-classlib/src/main/java/org/teavm/classlib/{org/junit/Test.java => java/lang/TClassCastException.java} (61%) delete mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java delete mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java create mode 100644 teavm-classlib/src/main/resources/META-INF/services/org.teavm.javascript.JavascriptBuilderPlugin create mode 100644 teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java new file mode 100644 index 000000000..5a505455d --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl; + +import org.teavm.dependency.*; +import org.teavm.model.ClassReader; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; +import org.teavm.model.ValueType; + +/** + * + * @author Alexey Andreev + */ +public class EnumDependencySupport implements DependencyListener { + private DependencyNode allEnums; + private volatile DependencyStack enumConstantsStack; + + @Override + public void started(DependencyChecker dependencyChecker) { + allEnums = dependencyChecker.createNode(); + } + + @Override + public void classAchieved(DependencyChecker dependencyChecker, String className) { + ClassReader cls = dependencyChecker.getClassSource().get(className); + if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) { + allEnums.propagate(className); + } + if (enumConstantsStack != null) { + MethodReader method = cls.getMethod(new MethodDescriptor("values", + ValueType.arrayOf(ValueType.object(cls.getName())))); + if (method != null) { + dependencyChecker.linkMethod(method.getReference(), enumConstantsStack).use(); + } + } + } + + @Override + public void methodAchieved(DependencyChecker dependencyChecker, MethodDependency method) { + if (method.getReference().getClassName().equals("java.lang.Class") && + method.getReference().getName().equals("getEnumConstantsImpl")) { + allEnums.connect(method.getResult().getArrayItem()); + method.getResult().propagate("[java.lang.Enum"); + enumConstantsStack = method.getStack(); + for (String cls : dependencyChecker.getAchievableClasses()) { + classAchieved(dependencyChecker, cls); + } + } + } + + @Override + public void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency field) { + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java new file mode 100644 index 000000000..ba5d33fb0 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumTransformer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl; + +import org.teavm.javascript.ni.PreserveOriginalName; +import org.teavm.model.*; + +/** + * + * @author Alexey Andreev + */ +public class EnumTransformer implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource) { + if (cls.getParent() != null && !cls.getParent().equals("java.lang.Enum")) { + return; + } + MethodHolder method = cls.getMethod(new MethodDescriptor("values", + ValueType.arrayOf(ValueType.object(cls.getName())))); + if (method == null) { + return; + } + method.getAnnotations().add(new AnnotationHolder(PreserveOriginalName.class.getName())); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java new file mode 100644 index 000000000..abe39dc91 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl; + +import org.teavm.javascript.JavascriptBuilderHost; +import org.teavm.javascript.JavascriptBuilderPlugin; + +/** + * + * @author Alexey Andreev + */ +public class JCLPlugin implements JavascriptBuilderPlugin { + @Override + public void install(JavascriptBuilderHost host) { + host.add(new EnumDependencySupport()); + host.add(new EnumTransformer()); + } +} 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 914840d67..8f1135758 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 @@ -74,6 +74,10 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "wrap": context.writeExpr(context.getArgument(0)); break; + case "getEnumConstantsImpl": + context.writeExpr(context.getArgument(0)); + context.getWriter().append(".$data.values()"); + break; } } 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 a7ca2e66b..252e5b26a 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 @@ -100,11 +100,12 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu } private void generateClone(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var copy = new ").append(context.getParameterName(0)).append(".constructor();").softNewLine(); - writer.append("for (var field in obj) {").softNewLine().indent(); - writer.append("if (!obj.hasOwnProperty(field)) {").softNewLine().indent(); + String obj = context.getParameterName(0); + writer.append("var copy = new ").append(obj).append(".constructor();").softNewLine(); + writer.append("for (var field in " + obj + ") {").softNewLine().indent(); + writer.append("if (!" + obj + ".hasOwnProperty(field)) {").softNewLine().indent(); writer.append("continue;").softNewLine().outdent().append("}").softNewLine(); - writer.append("copy[field] = obj[field];").softNewLine().outdent().append("}").softNewLine(); + writer.append("copy[field] = " + obj + "[field];").softNewLine().outdent().append("}").softNewLine(); writer.append("return copy;").softNewLine(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java index 35313ff34..50630cbc1 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java @@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang; import org.teavm.classlib.impl.unicode.UnicodeHelper; import org.teavm.dependency.PluggableDependency; import org.teavm.javascript.ni.GeneratedBy; +import org.teavm.javascript.ni.Rename; /** * @@ -41,6 +42,20 @@ public class TCharacter extends TObject { return new TCharacter(value); } + @Override + @Rename("toString") + public TString toString0() { + return new TString(new char[] { value }); + } + + @Override + public boolean equals(TObject other) { + if (this == other) { + return true; + } + return other instanceof TCharacter && ((TCharacter)other).value == value; + } + @GeneratedBy(CharacterNativeGenerator.class) public static native char toLowerCase(char ch); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index cbe325ca4..7c9be16fc 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -27,6 +27,7 @@ public class TClass extends TObject { TString name; boolean primitive; boolean array; + boolean isEnum; private TClass componentType; private boolean componentTypeDirty = true; @@ -52,6 +53,10 @@ public class TClass extends TObject { return array; } + public boolean isEnum() { + return isEnum; + } + public TClass getComponentType() { if (componentTypeDirty) { componentType = getComponentType0(); @@ -83,4 +88,11 @@ public class TClass extends TObject { @GeneratedBy(ClassNativeGenerator.class) @PluggableDependency(ClassNativeGenerator.class) public native TClass getSuperclass(); + + public T[] getEnumConstants() { + return isEnum ? getEnumConstantsImpl() : null; + } + + @InjectedBy(ClassNativeGenerator.class) + public native T[] getEnumConstantsImpl(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassCastException.java similarity index 61% rename from teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java rename to teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassCastException.java index dea4c7d4a..2eec560c0 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassCastException.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Alexey Andreev. + * Copyright 2014 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.classlib.org.junit; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +package org.teavm.classlib.java.lang; /** * - * @author Alexey Andreev + * @author Alexey Andreev */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Test { +public class TClassCastException extends TRuntimeException { + private static final long serialVersionUID = 5744987817019231273L; + + public TClassCastException() { + super(); + } + + public TClassCastException(TString message) { + super(message); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index ffa9c8bc1..a67c7a707 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.lang; import org.teavm.javascript.ni.GeneratedBy; +import org.teavm.javascript.ni.Rename; /** * @@ -52,6 +53,20 @@ public class TDouble extends TNumber { return new TDouble(d); } + @Override + @Rename("toString") + public TString toString0() { + return TString.wrap(new TStringBuilder().append(value).toString()); + } + + @Override + public boolean equals(TObject other) { + if (this == other) { + return true; + } + return other instanceof TDouble && ((TDouble)other).value == value; + } + @GeneratedBy(DoubleNativeGenerator.class) public static native boolean isNaN(double v); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java index 52e02f196..e04be9053 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java @@ -62,9 +62,7 @@ public abstract class TEnum> extends TObject implements TComp @SuppressWarnings("unchecked") public final TClass getDeclaringClass() { - TClass thisClass = TClass.wrap(getClass()); - TClass superClass = thisClass.getSuperclass(); - return (TClass)(superClass == TClass.wrap(TEnum.class) ? thisClass : superClass); + return (TClass)TClass.wrap(getClass()); } @Override @@ -76,4 +74,19 @@ public abstract class TEnum> extends TObject implements TComp } return TInteger.compare(ordinal, o.ordinal()); } + + public static > T valueOf(TClass enumType, TString name) { + // TODO: speed-up this method, use caching + T[] constants = enumType.getEnumConstants(); + if (constants == null) { + throw new TIllegalArgumentException(TString.wrap("Class does not represent enum: " + enumType.getName())); + } + for (T constant : constants) { + if (constant.name().equals(name)) { + return constant; + } + } + throw new TIllegalArgumentException(TString.wrap("Enum " + enumType.getName() + " does not have the " + name + + "constant")); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java index d1348a7fe..7ab1d4866 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java @@ -15,6 +15,8 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.javascript.ni.Rename; + /** * * @author Alexey Andreev @@ -49,4 +51,18 @@ public class TLong extends TNumber { public double doubleValue() { return value; } + + @Override + @Rename("toString") + public TString toString0() { + return TString.wrap(new TStringBuilder().append(value).toString()); + } + + @Override + public boolean equals(TObject other) { + if (this == other) { + return true; + } + return other instanceof TLong && ((TLong)other).value == value; + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java deleted file mode 100644 index 9f07b5e01..000000000 --- a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.teavm.classlib.org.junit; - -import org.teavm.javascript.ni.GeneratedBy; - -/** - * - * @author Alexey Andreev - */ -public class Assert { - public static void assertTrue(boolean condition) { - if (!condition) { - fail(); - } - } - - public static void assertFalse(boolean condition) { - if (condition) { - fail(); - } - } - - @GeneratedBy(AssertNativeGenerator.class) - public static native void fail(); - - public static void assertEquals(Object expected, Object actual) { - if (expected != null ? !expected.equals(actual) : actual != null) { - fail(); - } - } - - public static void assertNotEquals(Object expected, Object actual) { - if (expected != null ? expected.equals(actual) : actual == null) { - fail(); - } - } - - public static void assertEquals(long expected, long actual) { - if (expected != actual) { - fail(); - } - } - - public static void assertNotEquals(long expected, long actual) { - if (expected == actual) { - fail(); - } - } - - public static void assertNotNull(Object object) { - if (object == null) { - fail(); - } - } - - public static void assertNull(Object object) { - if (object != null) { - fail(); - } - } - - public static void assertSame(Object expected, Object actual) { - if (expected != actual) { - fail(); - } - } - - public static void assertNotSame(Object expected, Object actual) { - if (expected == actual) { - fail(); - } - } -} 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 deleted file mode 100644 index 2c5300782..000000000 --- a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2013 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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; -import org.teavm.model.MethodReference; - -/** - * - * @author Alexey Andreev - */ -public class AssertNativeGenerator implements Generator { - @Override - public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) - throws IOException { - switch (methodRef.getDescriptor().getName()) { - case "fail": - generateFail(writer); - break; - } - } - - private void generateFail(SourceWriter writer) throws IOException { - writer.append("throw new Error();").newLine(); - } -} diff --git a/teavm-classlib/src/main/resources/META-INF/services/org.teavm.javascript.JavascriptBuilderPlugin b/teavm-classlib/src/main/resources/META-INF/services/org.teavm.javascript.JavascriptBuilderPlugin new file mode 100644 index 000000000..a0af2a920 --- /dev/null +++ b/teavm-classlib/src/main/resources/META-INF/services/org.teavm.javascript.JavascriptBuilderPlugin @@ -0,0 +1 @@ +org.teavm.classlib.impl.JCLPlugin \ No newline at end of file diff --git a/teavm-classlib/src/main/resources/META-INF/teavm.properties b/teavm-classlib/src/main/resources/META-INF/teavm.properties index cf2fc8c31..4d80f6d1e 100644 --- a/teavm-classlib/src/main/resources/META-INF/teavm.properties +++ b/teavm-classlib/src/main/resources/META-INF/teavm.properties @@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. packagePrefix.java=org.teavm.classlib -classPrefix.java=T -packagePrefix.org.junit=org.teavm.classlib \ No newline at end of file +classPrefix.java=T \ No newline at end of file diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java new file mode 100644 index 000000000..4855eee1e --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/EnumTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class EnumTest { + private static enum Foo { + A, B, C + } + + private static enum Bar { + D, E + } + + @Test + public void sameConstantsAreEqual() { + assertEquals(Foo.A, Foo.A); + } + + @Test + public void differentConstansAreNotEqual() { + assertNotEquals(Foo.A, Foo.B); + } + + @Test + public void constantsOfDifferentEnumsAreNotEqual() { + assertNotEquals(Foo.A, Bar.D); + } + + @Test + public void declaringClassComputed() { + assertEquals(Foo.class, Foo.A.getDeclaringClass()); + } + + @Test + public void comparisonGivesZeroForSameConstants() { + assertEquals(0, Foo.A.compareTo(Foo.A)); + } + + @Test + public void comparisonGivesPositiveForLaterConstant() { + assertTrue(Foo.B.compareTo(Foo.A) > 0); + } + + @Test + public void comparisonGivesNegativeForEarlierConstant() { + assertTrue(Foo.A.compareTo(Foo.B) < 0); + } + + @Test + public void valueOfReturnsConstant() { + assertEquals("A", Enum.valueOf(Foo.class, "A").name()); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index 53392d15d..86a1e48a6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -231,9 +231,13 @@ public class Decompiler { Set result = EnumSet.noneOf(NodeModifier.class); if (modifiers.contains(ElementModifier.STATIC)) { result.add(NodeModifier.STATIC); - } else if (modifiers.contains(ElementModifier.INTERFACE)) { + } + if (modifiers.contains(ElementModifier.INTERFACE)) { result.add(NodeModifier.INTERFACE); } + if (modifiers.contains(ElementModifier.ENUM)) { + result.add(NodeModifier.ENUM); + } return result; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java index 0c890b4ea..5f47169a9 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -135,6 +135,11 @@ public class JavascriptBuilder implements JavascriptBuilderHost { ValueType.object("java.lang.Class"))), DependencyStack.ROOT).use(); dependencyChecker.linkMethod(new MethodReference("java.lang.String", new MethodDescriptor("", ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)), DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(new MethodReference("java.lang.String", new MethodDescriptor("getChars", + ValueType.INTEGER, ValueType.INTEGER, ValueType.arrayOf(ValueType.CHARACTER), ValueType.INTEGER, + ValueType.VOID)), DependencyStack.ROOT).use(); + dependencyChecker.linkMethod(new MethodReference("java.lang.Object", new MethodDescriptor("clone", + ValueType.object("java.lang.Object"))), DependencyStack.ROOT).use(); executor.complete(); if (hasMissingItems()) { return; 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 101e6d7dd..552b7f7e6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -77,6 +77,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { try { renderRuntimeCls(); renderRuntimeString(); + renderRuntimeUnwrapString(); renderRuntimeObjcls(); } catch (NamingException e) { throw new RenderingException("Error rendering runtime methods. See a cause for details", e); @@ -110,6 +111,11 @@ public class Renderer implements ExprVisitor, StatementVisitor { .append("=").ws().append("clsProto.$meta.item").ws().append("?").ws() .append("1").ws().append(":").ws().append("0;").softNewLine(); } + if (classSource.get(classClass).getField("isEnum") != null) { + writer.append("cls.").appendField(new FieldReference(classClass, "isEnum")).ws() + .append("=").ws().append("clsProto.$meta.enum").ws().append("?").ws() + .append("1").ws().append(":").ws().append("0;").softNewLine(); + } writer.append("clsProto.classObject").ws().append("=").ws().append("cls;").softNewLine(); writer.outdent().append("}").softNewLine(); writer.append("return cls;").softNewLine(); @@ -131,6 +137,25 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } + private void renderRuntimeUnwrapString() throws IOException { + String stringClass = "java.lang.String"; + MethodReference stringLen = new MethodReference(stringClass, new MethodDescriptor( + "length", ValueType.INTEGER)); + MethodReference getChars = new MethodReference(stringClass, new MethodDescriptor( + "getChars", ValueType.INTEGER, ValueType.INTEGER, ValueType.arrayOf(ValueType.CHARACTER), + ValueType.INTEGER, ValueType.VOID)); + writer.append("$rt_ustr = function(str) {").indent().softNewLine(); + writer.append("var result = \"\";").softNewLine(); + writer.append("var sz = ").appendMethodBody(stringLen).append("(str);").softNewLine(); + writer.append("var array = $rt_createCharArray(sz);"); + writer.appendMethodBody(getChars).append("(str, 0, sz, array, 0);"); + writer.append("for (var i = 0; i < sz; i = (i + 1) | 0) {").indent().softNewLine(); + writer.append("result += String.fromCharCode(array.data[i]);").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return result;").softNewLine(); + writer.outdent().append("}").newLine(); + } + private void renderRuntimeObjcls() throws IOException { writer.append("$rt_objcls = function() { return ").appendClass("java.lang.Object").append("; }").newLine(); } @@ -174,7 +199,9 @@ public class Renderer implements ExprVisitor, StatementVisitor { } writer.appendClass(cls.getName()).append(".$meta").ws().append("=").ws().append("{").ws(); writer.append("name").ws().append(":").ws().append("\"").append(cls.getName()).append("\",").ws(); - writer.append("primitive").ws().append(":").ws().append("false,").ws(); + if (cls.getModifiers().contains(NodeModifier.ENUM)) { + writer.append("enum").ws().append(":").ws().append("true,").ws(); + } writer.append("supertypes").ws().append(":").ws().append("["); boolean first = true; if (cls.getParentName() != null) { @@ -195,7 +222,10 @@ public class Renderer implements ExprVisitor, StatementVisitor { } writer.ws().append("};").softNewLine(); if (!cls.getModifiers().contains(NodeModifier.INTERFACE)) { - writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws().append("function()").ws() + writer.appendClass(cls.getName()).append(".$clinit").ws().append("=").ws() + .append("function()").ws().append("{").ws() + .appendClass(cls.getName()).append("_$clinit();").ws().append("}").newLine(); + writer.append("function ").appendClass(cls.getName()).append("_$clinit()").ws() .append("{").softNewLine().indent(); writer.appendClass(cls.getName()).append("_$clinit").ws().append("=").ws() .append("function(){};").newLine(); @@ -213,7 +243,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); for (MethodNode method : cls.getMethods()) { cls.getMethods(); - if (!method.getModifiers().contains(NodeModifier.STATIC)) { + if (!method.getModifiers().contains(NodeModifier.STATIC) || method.isOriginalNamePreserved()) { renderDeclaration(method); } } @@ -287,30 +317,10 @@ public class Renderer implements ExprVisitor, StatementVisitor { public void renderDeclaration(MethodNode method) throws RenderingException, IOException { try { - MethodReference ref = method.getReference(); - if (ref.getDescriptor().getName().equals("")) { - renderInitializer(method); - } - writer.appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) - .ws().append("=").ws().append("function("); - for (int i = 1; i <= ref.parameterCount(); ++i) { - if (i > 1) { - writer.append(", "); - } - writer.append(variableName(i)); - } - writer.append(")").ws().append("{").softNewLine().indent(); - writer.append("return ").appendMethodBody(ref).append("("); - writer.append("this"); - for (int i = 1; i <= ref.parameterCount(); ++i) { - writer.append(",").ws().append(variableName(i)); - } - writer.append(");").softNewLine(); - writer.outdent().append("}").newLine(); - if (method.isOriginalNamePreserved()) { - writer.appendClass(ref.getClassName()).append(".prototype.").append(ref.getName()).ws().append("=") - .ws().appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) - .append(';').newLine(); + if (method.getModifiers().contains(NodeModifier.STATIC)) { + renderStaticDeclaration(method); + } else { + renderVirtualDeclaration(method); } } catch (NamingException e) { throw new RenderingException("Error rendering method " + method.getReference() + ". " + @@ -318,6 +328,59 @@ public class Renderer implements ExprVisitor, StatementVisitor { } } + private void renderVirtualDeclaration(MethodNode method) throws NamingException, IOException { + MethodReference ref = method.getReference(); + if (ref.getDescriptor().getName().equals("")) { + renderInitializer(method); + } + writer.appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) + .ws().append("=").ws().append("function("); + for (int i = 1; i <= ref.parameterCount(); ++i) { + if (i > 1) { + writer.append(", "); + } + writer.append(variableName(i)); + } + writer.append(")").ws().append("{").softNewLine().indent(); + writer.append("return ").appendMethodBody(ref).append("("); + writer.append("this"); + for (int i = 1; i <= ref.parameterCount(); ++i) { + writer.append(",").ws().append(variableName(i)); + } + writer.append(");").softNewLine(); + writer.outdent().append("}").newLine(); + if (method.isOriginalNamePreserved()) { + writer.appendClass(ref.getClassName()).append(".prototype.").append(ref.getName()).ws().append("=") + .ws().appendClass(ref.getClassName()).append(".prototype.").appendMethod(ref) + .append(';').newLine(); + } + } + + private void renderStaticDeclaration(MethodNode method) throws NamingException, IOException { + MethodReference ref = method.getReference(); + if (ref.getDescriptor().getName().equals("")) { + renderInitializer(method); + } + writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function("); + for (int i = 0; i < ref.parameterCount(); ++i) { + if (i > 0) { + writer.append(", "); + } + writer.append(variableName(i + 1)); + } + writer.append(")").ws().append("{").softNewLine().indent(); + writer.append("return ").appendMethodBody(ref).append("("); + for (int i = 0; i < ref.parameterCount(); ++i) { + writer.append(",").ws().append(variableName(i + 1)); + } + writer.append(");").softNewLine(); + writer.outdent().append("}").newLine(); + if (method.isOriginalNamePreserved()) { + writer.appendClass(ref.getClassName()).append(".").append(ref.getName()).ws().append("=") + .ws().appendClass(ref.getClassName()).append(".").appendMethod(ref).append(';').newLine(); + } + } + public void renderBody(MethodNode method) throws IOException { MethodReference ref = method.getReference(); writer.appendMethodBody(ref).ws().append("=").ws().append("function("); @@ -1190,12 +1253,13 @@ public class Renderer implements ExprVisitor, StatementVisitor { InjectorHolder holder = injectorMap.get(ref); if (holder == null) { MethodHolder method = classSource.get(ref.getClassName()).getMethod(ref.getDescriptor()); - AnnotationHolder injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName()); - if (injectedByAnnot != null) { - ValueType type = injectedByAnnot.getValues().get("value").getJavaClass(); - holder = new InjectorHolder(instantiateInjector(((ValueType.Object)type).getClassName())); - } else { - holder = new InjectorHolder(null); + holder = new InjectorHolder(null); + if (method != null) { + AnnotationHolder injectedByAnnot = method.getAnnotations().get(InjectedBy.class.getName()); + if (injectedByAnnot != null) { + ValueType type = injectedByAnnot.getValues().get("value").getJavaClass(); + holder = new InjectorHolder(instantiateInjector(((ValueType.Object)type).getClassName())); + } } injectorMap.put(ref, holder); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java index d91d0204c..6ff273c75 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java @@ -21,5 +21,6 @@ package org.teavm.javascript.ast; */ public enum NodeModifier { STATIC, - INTERFACE + INTERFACE, + ENUM } diff --git a/teavm-html4j/pom.xml b/teavm-html4j/pom.xml index 6bd1e7bd0..064790534 100644 --- a/teavm-html4j/pom.xml +++ b/teavm-html4j/pom.xml @@ -84,9 +84,9 @@ true ${project.build.directory}/javascript-tck org.teavm.html4j.testing.KOTestAdapter - +