diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java index 4837f169a..761cf083e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayList.java @@ -86,6 +86,7 @@ public class TArrayList extends TAbstractList implements TCloneable, TSeri array[i] = array[i - 1]; } array[index] = element; + ++size; ++modCount; } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/util/ArrayListTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/util/ArrayListTest.java new file mode 100644 index 000000000..57ca456ad --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/util/ArrayListTest.java @@ -0,0 +1,88 @@ +/* + * 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.util; + +import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.List; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class ArrayListTest { + @Test + public void elementsAdded() { + List list = new ArrayList<>(); + list.add(2); + list.add(3); + list.add(4); + assertEquals(3, list.size()); + assertEquals(Integer.valueOf(4), list.get(2)); + } + + @Test + public void capacityIncreased() { + List list = new ArrayList<>(); + for (int i = 0; i < 150; ++i) { + list.add(i); + } + assertEquals(150, list.size()); + assertEquals(Integer.valueOf(101), list.get(101)); + } + + @Test + public void elementsInserted() { + List list = fillFromZeroToNine(); + list.add(5, -1); + assertEquals(11, list.size()); + assertEquals(Integer.valueOf(-1), list.get(5)); + assertEquals(Integer.valueOf(5), list.get(6)); + assertEquals(Integer.valueOf(9), list.get(10)); + } + + @Test + public void elementsRemoved() { + List list = fillFromZeroToNine(); + list.remove(5); + assertEquals(9, list.size()); + assertEquals(Integer.valueOf(6), list.get(5)); + assertEquals(Integer.valueOf(9), list.get(8)); + } + + @Test(expected = ConcurrentModificationException.class) + public void concurrentModificationsRestricted() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; ++i) { + list.add(i); + } + for (Integer item : list) { + if (item.equals(5)) { + list.remove(5); + } + } + } + + private List fillFromZeroToNine() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; ++i) { + list.add(i); + } + return list; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 1340ae7a2..b08c5d9ef 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -208,16 +208,25 @@ public class DependencyChecker implements DependencyInfo { private MethodReader findMethodReader(MethodReference methodRef) { String clsName = methodRef.getClassName(); MethodDescriptor desc = methodRef.getDescriptor(); - while (clsName != null) { - ClassReader cls = classSource.get(clsName); - if (cls == null) { - return null; + ClassReader cls = classSource.get(clsName); + if (cls == null) { + return null; + } + MethodReader reader = cls.getMethod(desc); + if (reader != null) { + return reader; + } + if (cls.getParent() != null) { + reader = methodReaderCache.map(new MethodReference(cls.getParent(), desc)); + if (reader != null) { + return reader; } - MethodReader method = cls.getMethod(desc); - if (method != null) { - return method; + } + for (String ifaceName : cls.getInterfaces()) { + reader = methodReaderCache.map(new MethodReference(ifaceName, desc)); + if (reader != null) { + return reader; } - clsName = cls.getParent(); } return null; } @@ -347,31 +356,6 @@ public class DependencyChecker implements DependencyInfo { plugin.methodAchieved(this, methodDep); } - public ListableClassHolderSource cutUnachievableClasses(ClassHolderSource classSource) { - MutableClassHolderSource cutClasses = new MutableClassHolderSource(); - for (String className : achievableClasses.keySet()) { - ClassHolder classHolder = classSource.get(className); - cutClasses.putClassHolder(classHolder); - for (MethodHolder method : classHolder.getMethods().toArray(new MethodHolder[0])) { - MethodReference methodRef = new MethodReference(className, method.getDescriptor()); - MethodDependency methodDep = getMethod(methodRef); - if (methodDep == null) { - classHolder.removeMethod(method); - } else if (!methodDep.isUsed()) { - method.getModifiers().add(ElementModifier.ABSTRACT); - method.setProgram(null); - } - } - for (FieldHolder field : classHolder.getFields().toArray(new FieldHolder[0])) { - FieldReference fieldRef = new FieldReference(className, field.getName()); - if (!fieldCache.getCachedPreimages().contains(fieldRef)) { - classHolder.removeField(field); - } - } - } - return cutClasses; - } - @Override public MethodDependency getMethod(MethodReference methodRef) { return methodCache.getKnown(methodRef); diff --git a/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java b/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java index 06b8e185d..9c820aa33 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/FieldDependency.java @@ -48,6 +48,7 @@ public class FieldDependency implements FieldDependencyInfo { return field; } + @Override public FieldReference getReference() { return reference; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java b/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java index 965f5cadc..e6decab32 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java +++ b/teavm-core/src/main/java/org/teavm/dependency/FieldDependencyInfo.java @@ -15,10 +15,14 @@ */ package org.teavm.dependency; +import org.teavm.model.FieldReference; + /** * * @author Alexey Andreev */ public interface FieldDependencyInfo { ValueDependencyInfo getValue(); + + FieldReference getReference(); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/Linker.java b/teavm-core/src/main/java/org/teavm/dependency/Linker.java new file mode 100644 index 000000000..583eb29f2 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/Linker.java @@ -0,0 +1,88 @@ +/* + * 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.dependency; + +import org.teavm.model.*; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.PutFieldInstruction; + +/** + * + * @author Alexey Andreev + */ +public class Linker { + private DependencyInfo dependency; + + public Linker(DependencyInfo dependency) { + this.dependency = dependency; + } + + public ListableClassHolderSource link(ClassHolderSource classes) { + MutableClassHolderSource cutClasses = new MutableClassHolderSource(); + for (String className : dependency.getAchievableClasses()) { + ClassHolder classHolder = classes.get(className); + cutClasses.putClassHolder(classHolder); + for (MethodHolder method : classHolder.getMethods().toArray(new MethodHolder[0])) { + MethodReference methodRef = new MethodReference(className, method.getDescriptor()); + MethodDependencyInfo methodDep = dependency.getMethod(methodRef); + if (methodDep == null) { + classHolder.removeMethod(method); + } else if (!methodDep.isUsed()) { + method.getModifiers().add(ElementModifier.ABSTRACT); + method.setProgram(null); + } else if (method.getProgram() != null) { + link(method); + } + } + for (FieldHolder field : classHolder.getFields().toArray(new FieldHolder[0])) { + FieldReference fieldRef = new FieldReference(className, field.getName()); + if (dependency.getField(fieldRef) == null) { + classHolder.removeField(field); + } + } + } + return cutClasses; + } + + public void link(MethodHolder cls) { + Program program = cls.getProgram(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Instruction insn : block.getInstructions()) { + if (insn instanceof InvokeInstruction) { + InvokeInstruction invoke = (InvokeInstruction)insn; + MethodDependencyInfo linkedMethod = dependency.getMethod(invoke.getMethod()); + if (linkedMethod != null) { + invoke.setMethod(linkedMethod.getReference()); + } + } else if (insn instanceof GetFieldInstruction) { + GetFieldInstruction getField = (GetFieldInstruction)insn; + FieldDependencyInfo linkedField = dependency.getField(getField.getField()); + if (linkedField != null) { + getField.setField(linkedField.getReference()); + } + } else if (insn instanceof PutFieldInstruction) { + PutFieldInstruction getField = (PutFieldInstruction)insn; + FieldDependencyInfo linkedField = dependency.getField(getField.getField()); + if (linkedField != null) { + getField.setField(linkedField.getReference()); + } + } + } + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java index c5f94b01a..3f174855f 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -73,6 +73,7 @@ public class MethodDependency implements MethodDependencyInfo { return stack; } + @Override public MethodReference getReference() { return reference; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java index 2c3f26e46..95556e6e8 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependencyInfo.java @@ -15,6 +15,8 @@ */ package org.teavm.dependency; +import org.teavm.model.MethodReference; + /** * * @author Alexey Andreev @@ -30,5 +32,7 @@ public interface MethodDependencyInfo { DependencyNode getResult(); + MethodReference getReference(); + boolean isUsed(); } 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 5f47169a9..a0fd24f46 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -19,10 +19,7 @@ import java.io.*; import java.util.*; import org.teavm.codegen.*; import org.teavm.common.FiniteExecutor; -import org.teavm.dependency.DependencyChecker; -import org.teavm.dependency.DependencyInfo; -import org.teavm.dependency.DependencyListener; -import org.teavm.dependency.DependencyStack; +import org.teavm.dependency.*; import org.teavm.javascript.ast.ClassNode; import org.teavm.model.*; import org.teavm.model.util.ListingBuilder; @@ -144,7 +141,8 @@ public class JavascriptBuilder implements JavascriptBuilderHost { if (hasMissingItems()) { return; } - ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(classSource); + Linker linker = new Linker(dependencyChecker); + ListableClassHolderSource classSet = linker.link(classSource); Decompiler decompiler = new Decompiler(classSet, classLoader, executor); devirtualize(classSet, dependencyChecker); executor.complete(); 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 49726648e..943fe0e03 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -517,10 +517,6 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(InvokeInstruction insn) { - MethodReference method = findDeclaringClass(insn.getMethod()); - if (method == null) { - throw new IllegalArgumentException("Method not found: " + insn.getMethod()); - } Expr[] exprArgs = new Expr[insn.getMethod().getParameterTypes().length]; for (int i = 0; i < insn.getArguments().size(); ++i) { exprArgs[i] = Expr.var(insn.getArguments().get(i).getIndex()); @@ -530,10 +526,11 @@ class StatementGenerator implements InstructionVisitor { if (insn.getType() == InvocationType.VIRTUAL) { invocationExpr = Expr.invoke(insn.getMethod(), Expr.var(insn.getInstance().getIndex()), exprArgs); } else { - invocationExpr = Expr.invokeSpecial(method, Expr.var(insn.getInstance().getIndex()), exprArgs); + invocationExpr = Expr.invokeSpecial(insn.getMethod(), + Expr.var(insn.getInstance().getIndex()), exprArgs); } } else { - invocationExpr = Expr.invokeStatic(method, exprArgs); + invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs); } if (insn.getReceiver() != null) { assign(invocationExpr, insn.getReceiver().getIndex()); @@ -542,14 +539,6 @@ class StatementGenerator implements InstructionVisitor { } } - public MethodReference findDeclaringClass(MethodReference method) { - ClassHolder cls = classSource.get(method.getClassName()); - while (cls != null && cls.getMethod(method.getDescriptor()) == null) { - cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null; - } - return cls != null ? new MethodReference(cls.getName(), method.getDescriptor()) : null; - } - @Override public void visit(IsInstanceInstruction insn) { assign(Expr.instanceOf(Expr.var(insn.getValue().getIndex()), insn.getType()),