From c1891a1908213c839044789952c378510bb2feea Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 3 Mar 2020 12:17:39 +0300 Subject: [PATCH] Further work on decreasing generated code size --- .../teavm/classlib/impl/ObfuscationHacks.java | 2 +- .../org/teavm/classlib/java/lang/TClass.java | 7 +- .../org/teavm/classlib/java/lang/TEnum.java | 4 +- .../dependency/DependencyGraphBuilder.java | 96 ++++++++++++++++++- .../teavm/html4j/test/KnockoutTCKTest.java | 2 - 5 files changed, 100 insertions(+), 11 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ObfuscationHacks.java b/classlib/src/main/java/org/teavm/classlib/impl/ObfuscationHacks.java index 2fec8937b..4b00ea8ca 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ObfuscationHacks.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ObfuscationHacks.java @@ -25,7 +25,7 @@ import org.teavm.model.util.ProgramUtils; public class ObfuscationHacks implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { - if (cls.getName().equals("java.lang.Object")) { + if (cls.getName().equals("java.lang.Object") || cls.getName().equals("java.lang.Class")) { if (context.isObfuscated() && !context.isStrict()) { processObjectClass(cls); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index 2406224b0..c236f2b49 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -82,8 +82,11 @@ public class TClass extends TObject implements TAnnotatedElement, TType { @Override public String toString() { - return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) - + getName(); + return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); + } + + private String obfuscatedToString() { + return "javaClass@" + identity(); } public PlatformClass getPlatformClass() { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java index 600257182..5f0fd84f5 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TEnum.java @@ -65,7 +65,7 @@ public abstract class TEnum> extends TObject implements TComp public final int compareTo(E o) { if (o.getDeclaringClass() != getDeclaringClass()) { throw new TIllegalArgumentException("Can't compare " - + getDeclaringClass().getName() + " to " + o.getDeclaringClass().getName()); + + getDeclaringClass() + " to " + o.getDeclaringClass()); } return TInteger.compare(ordinal, o.ordinal()); } @@ -81,6 +81,6 @@ public abstract class TEnum> extends TObject implements TComp return constant; } } - throw new TIllegalArgumentException("Enum does not have the " + name + "constant"); + throw new TIllegalArgumentException("Enum " + enumType + " does not have the " + name + "constant"); } } diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index d1a46a42c..2bd7ac8e0 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -326,10 +326,10 @@ class DependencyGraphBuilder { @Override protected void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method, List arguments) { - if (method.getDescriptor().equals(GET_CLASS)) { - invokeGetClass(receiver, instance); + if (handleSpecialMethod(receiver, instance, method)) { return; } + CallLocation callLocation = getCallLocation(); if (instance == null) { dependencyAnalyzer.linkClass(method.getClassName()).initClass(callLocation); @@ -366,8 +366,7 @@ class DependencyGraphBuilder { @Override protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method, List arguments) { - if (method.getDescriptor().equals(GET_CLASS)) { - invokeGetClass(receiver, instance); + if (handleSpecialMethod(receiver, instance, method)) { return; } @@ -387,6 +386,26 @@ class DependencyGraphBuilder { }); } + private boolean handleSpecialMethod(VariableReader receiver, VariableReader instance, MethodReference method) { + if (method.getDescriptor().equals(GET_CLASS)) { + invokeGetClass(receiver, instance); + return true; + } else if (method.getClassName().equals("java.lang.Class")) { + switch (method.getName()) { + case "getComponentType": + invokeGetComponentType(receiver, instance, method); + return true; + case "getSuperclass": + invokeGetSuperclass(receiver, instance, method); + return true; + case "getInterfaces": + invokeGetInterfaces(receiver, instance, method); + return true; + } + } + return false; + } + private void invokeGetClass(VariableReader receiver, VariableReader instance) { MethodDependency getClassDep = dependencyAnalyzer.linkMethod("java.lang.Object", GET_CLASS); getClassDep.addLocation(getCallLocation()); @@ -402,6 +421,75 @@ class DependencyGraphBuilder { getClassDep.use(); } + private void invokeGetComponentType(VariableReader receiver, VariableReader instance, + MethodReference methodReference) { + MethodDependency methodDep = dependencyAnalyzer.linkMethod(methodReference); + methodDep.use(); + + DependencyNode instanceNode = getNode(instance); + DependencyNode receiverNode = getNode(receiver); + receiverNode.propagate(dependencyAnalyzer.classType); + instanceNode.getClassValueNode().addConsumer(t -> { + if (!t.getName().startsWith("[")) { + return; + } + String typeName = t.getName().substring(1); + if (typeName.charAt(0) == 'L') { + typeName = ((ValueType.Object) ValueType.parse(typeName)).getClassName(); + } + receiverNode.getClassValueNode().propagate(dependencyAnalyzer.getType(typeName)); + + methodDep.getVariable(0).propagate(t); + }); + } + + private void invokeGetSuperclass(VariableReader receiver, VariableReader instance, + MethodReference methodReference) { + MethodDependency methodDep = dependencyAnalyzer.linkMethod(methodReference); + methodDep.use(); + + DependencyNode instanceNode = getNode(instance); + DependencyNode receiverNode = getNode(receiver); + receiverNode.propagate(dependencyAnalyzer.classType); + instanceNode.getClassValueNode().addConsumer(type -> { + String className = type.getName(); + if (className.startsWith("[")) { + return; + } + + ClassReader cls = dependencyAnalyzer.getClassSource().get(className); + if (cls != null && cls.getParent() != null) { + receiverNode.getClassValueNode().propagate(dependencyAnalyzer.getType(cls.getParent())); + } + methodDep.getVariable(0).propagate(type); + }); + } + + private void invokeGetInterfaces(VariableReader receiver, VariableReader instance, + MethodReference methodReference) { + MethodDependency methodDep = dependencyAnalyzer.linkMethod(methodReference); + methodDep.use(); + + DependencyNode instanceNode = getNode(instance); + DependencyNode receiverNode = getNode(receiver); + receiverNode.propagate(dependencyAnalyzer.classType); + instanceNode.getClassValueNode().addConsumer(type -> { + String className = type.getName(); + if (className.startsWith("[")) { + return; + } + + ClassReader cls = dependencyAnalyzer.getClassSource().get(className); + if (cls != null) { + for (String iface : cls.getInterfaces()) { + receiverNode.getClassValueNode().propagate(dependencyAnalyzer.getType(iface)); + } + } + + methodDep.getVariable(0).propagate(type); + }); + } + @Override public void nullCheck(VariableReader receiver, VariableReader value) { super.nullCheck(receiver, value); diff --git a/html4j/src/test/java/org/teavm/html4j/test/KnockoutTCKTest.java b/html4j/src/test/java/org/teavm/html4j/test/KnockoutTCKTest.java index b37b4b074..cad0eeeec 100644 --- a/html4j/src/test/java/org/teavm/html4j/test/KnockoutTCKTest.java +++ b/html4j/src/test/java/org/teavm/html4j/test/KnockoutTCKTest.java @@ -21,7 +21,6 @@ import net.java.html.json.tests.KnockoutTest; import net.java.html.json.tests.MinesTest; import net.java.html.json.tests.OperationsTest; import net.java.html.json.tests.WebSocketTest; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.junit.SkipJVM; @@ -323,7 +322,6 @@ public class KnockoutTCKTest { } @Test - @Ignore public void deserializeWrongEnum() throws Exception { jsonTest.deserializeWrongEnum(); }