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 f27377f16..4689e6438 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 @@ -560,7 +560,7 @@ public class TClass extends TObject implements TAnnotatedElement { @SuppressWarnings("unchecked") public T cast(TObject obj) { if (obj != null && !isAssignableFrom((TClass) (Object) obj.getClass())) { - throw new TClassCastException(obj.getClass().getName() + " is not subtype of " + name); + throw new TClassCastException(obj.getClass().getName() + " is not subtype of " + getName()); } return (T) obj; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index e54364c60..376469c35 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -185,7 +185,7 @@ public class TObject { } } - boolean isEmptyMonitor() { + final boolean isEmptyMonitor() { Monitor monitor = this.monitor; if (monitor == null) { return true; @@ -246,7 +246,7 @@ public class TObject { return getClass().getName() + "@" + TInteger.toHexString(identity()); } - int identity() { + final int identity() { if (PlatformDetector.isLowLevel()) { Monitor monitor = this.monitor; if (monitor == null) { diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 99d1a800c..de79e69dd 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -27,7 +27,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -100,6 +99,7 @@ import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; +import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.InvocationType; @@ -115,6 +115,8 @@ import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.model.transformation.ClassPatch; import org.teavm.model.util.AsyncMethodFinder; import org.teavm.runtime.Allocator; +import org.teavm.runtime.CallSite; +import org.teavm.runtime.CallSiteLocation; import org.teavm.runtime.EventQueue; import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.Fiber; @@ -266,6 +268,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } } + dependencyAnalyzer.linkClass(CallSite.class.getName()); + dependencyAnalyzer.linkClass(CallSiteLocation.class.getName()); + dependencyAnalyzer.addDependencyListener(new ExceptionHandlingDependencyListener()); } @@ -535,7 +540,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { - Set virtualMethods = new LinkedHashSet<>(); + VirtualTableBuilder builder = new VirtualTableBuilder(classes); + builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); + builder.setMethodCalledVirtually(controller::isVirtual); + return builder.build(); + } + + private Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { + Set virtualMethods = new HashSet<>(); for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); @@ -560,7 +572,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } } - return new VirtualTableProvider(classes, virtualMethods, controller::isVirtual); + return virtualMethods; } private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) { @@ -631,6 +643,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateAllFile(ListableClassHolderSource classes, List types, BuildTarget buildTarget) throws IOException { BufferedCodeWriter writer = new BufferedCodeWriter(false); + writer.println("#define _XOPEN_SOURCE"); + writer.println("#define __USE_XOPEN"); + writer.println("#define _GNU_SOURCE"); + IncludeManager includes = new SimpleIncludeManager(writer); includes.init("all.c"); includes.includePath("runtime.c"); diff --git a/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java b/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java index d24dee91f..73f061733 100644 --- a/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java +++ b/core/src/main/java/org/teavm/backend/c/analyze/InteropDependencyListener.java @@ -24,6 +24,8 @@ import org.teavm.model.AnnotationReader; import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReader; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; public class InteropDependencyListener extends AbstractDependencyListener { @Override @@ -31,11 +33,16 @@ public class InteropDependencyListener extends AbstractDependencyListener { if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), className, false)) { ClassReader cls = agent.getClassSource().get(className); if (cls != null) { - for (FieldReader field : cls.getFields()) { - if (!field.hasModifier(ElementModifier.STATIC)) { - agent.linkField(field.getReference()); - } - } + reachFields(agent, cls); + } + } + } + + private void reachFields(DependencyAgent agent, ClassReader cls) { + for (FieldReader field : cls.getFields()) { + if (!field.hasModifier(ElementModifier.STATIC)) { + agent.linkField(field.getReference()); + reachType(agent, field.getType()); } } } @@ -51,8 +58,22 @@ public class InteropDependencyListener extends AbstractDependencyListener { return; } - if (method.getReference().getReturnType().isObject("java.lang.String")) { + MethodReference reference = method.getReference(); + if (reference.getReturnType().isObject("java.lang.String")) { method.getResult().propagate(agent.getType("java.lang.String")); } + + for (int i = 0; i < reference.parameterCount(); ++i) { + reachType(agent, reference.parameterType(i)); + } + } + + private void reachType(DependencyAgent agent, ValueType type) { + if (type instanceof ValueType.Object) { + String fieldClassName = ((ValueType.Object) type).getClassName(); + if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), fieldClassName, false)) { + agent.linkClass(fieldClassName); + } + } } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index ee83423a1..18a320f11 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -18,7 +18,6 @@ package org.teavm.backend.c.generate; import java.io.IOException; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -436,7 +435,9 @@ public class ClassGenerator { } else if (type instanceof ValueType.Array) { className = "java.lang.Object"; } - String structName = className != null + ClassReader cls = className != null ? context.getClassSource().get(className) : null; + + String structName = className != null && (cls == null || !cls.hasModifier(ElementModifier.INTERFACE)) ? context.getNames().forClassClass(className) : "TeaVM_Class"; if (className != null) { @@ -444,7 +445,6 @@ public class ClassGenerator { } String name = context.getNames().forClassInstance(type); - ClassReader cls = className != null ? context.getClassSource().get(className) : null; String enumConstants; if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { enumConstants = writeEnumConstants(cls, name); @@ -459,29 +459,15 @@ public class ClassGenerator { codeWriter.print("alignas(8) ").print(structName).print(" ").print(name).println(" = {").indent(); if (className != null) { - codeWriter.println(".parent = {").indent(); - generateRuntimeClassInitializer(type, enumConstants); - codeWriter.outdent().println("},"); - VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className); - if (virtualTable != null) { - List entries = new ArrayList<>(virtualTable.getEntries().values()); - for (int i = 0; i < entries.size(); ++i) { - VirtualTableEntry entry = entries.get(i); - String methodName = context.getNames().forVirtualMethod( - new MethodReference(className, entry.getMethod())); - String implName = entry.getImplementor() != null - ? "&" + context.getNames().forMethod(entry.getImplementor()) - : "NULL"; - if (entry.getImplementor() != null) { - includes.includeClass(entry.getImplementor().getClassName()); - } - codeWriter.print(".").print(methodName).print(" = ").print(implName); - if (i < entries.size() - 1) { - codeWriter.print(","); - } - codeWriter.println(); - } + if (cls.hasModifier(ElementModifier.INTERFACE)) { + generateRuntimeClassInitializer(type, enumConstants); + } else if (virtualTable != null) { + generateVirtualTableContent(virtualTable, virtualTable, type, enumConstants); + } else { + codeWriter.println(".parent = {").indent(); + generateRuntimeClassInitializer(type, enumConstants); + codeWriter.outdent().println("}"); } } else { generateRuntimeClassInitializer(type, enumConstants); @@ -490,6 +476,34 @@ public class ClassGenerator { codeWriter.outdent().println("};"); } + private void generateVirtualTableContent(VirtualTable current, VirtualTable original, ValueType type, + String enumConstants) { + codeWriter.println(".parent = {").indent(); + if (current.getParent() == null) { + generateRuntimeClassInitializer(type, enumConstants); + } else { + generateVirtualTableContent(current.getParent(), original, type, enumConstants); + } + codeWriter.outdent().print("}"); + + for (MethodDescriptor method : current.getMethods()) { + if (method == null) { + continue; + } + VirtualTableEntry entry = original.getEntry(method); + if (entry == null) { + continue; + } + + codeWriter.println(","); + String methodName = context.getNames().forVirtualMethod(method); + String implName = "&" + context.getNames().forMethod(entry.getImplementor()); + includes.includeClass(entry.getImplementor().getClassName()); + codeWriter.print(".").print(methodName).print(" = ").print(implName); + } + codeWriter.println(); + } + private String writeEnumConstants(ClassReader cls, String baseName) { List fields = cls.getFields().stream() .filter(f -> f.hasModifier(ElementModifier.ENUM)) @@ -633,17 +647,30 @@ public class ClassGenerator { String name = context.getNames().forClassClass(className); headerWriter.print("typedef struct ").print(name).println(" {").indent(); - headerWriter.println("TeaVM_Class parent;"); VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className); if (virtualTable != null) { - for (VirtualTableEntry entry : virtualTable.getEntries().values()) { - String methodName = context.getNames().forVirtualMethod( - new MethodReference(className, entry.getMethod())); - headerWriter.printType(entry.getMethod().getResultType()) - .print(" (*").print(methodName).print(")("); - codeGenerator.generateMethodParameters(headerWriter, entry.getMethod(), false, false); - headerWriter.println(");"); + String parentName = "TeaVM_Class"; + int index = 0; + if (virtualTable.getParent() != null) { + headerIncludes.includeClass(virtualTable.getParent().getClassName()); + parentName = context.getNames().forClassClass(virtualTable.getParent().getClassName()); + index = virtualTable.getParent().size(); + } + + headerWriter.println(parentName + " parent;"); + int padIndex = 0; + for (MethodDescriptor method : virtualTable.getMethods()) { + if (method != null) { + String methodName = context.getNames().forVirtualMethod(method); + headerWriter.printType(method.getResultType()) + .print(" (*").print(methodName).print(")("); + codeGenerator.generateMethodParameters(headerWriter, method, false, false); + headerWriter.print(")"); + } else { + headerWriter.print("void (*pad" + padIndex++ + ")()"); + } + headerWriter.println("; // " + index++); } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 1dd02b3c6..86fe439f0 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -422,87 +422,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { pushLocation(expr.getLocation()); switch (expr.getType()) { - case CONSTRUCTOR: { - String receiver = allocTemporaryVariable(CVariableType.PTR); - writer.print("(" + receiver + " = "); - allocObject(expr.getMethod().getClassName()); - writer.print(", "); - - MethodReader method = context.getClassSource().resolve(expr.getMethod()); - MethodReference reference = expr.getMethod(); - if (method != null) { - reference = method.getReference(); - } - - includes.includeClass(reference.getClassName()); - writer.print(names.forMethod(reference)); - - writer.print("(" + receiver); - for (Expr arg : expr.getArguments()) { - writer.print(", "); - arg.acceptVisitor(this); - } - writer.print("), " + receiver + ")"); - - freeTemporaryVariable(CVariableType.PTR); - + case CONSTRUCTOR: + generateCallToConstructor(expr.getMethod(), expr.getArguments()); break; - } + case SPECIAL: - case STATIC: { - MethodReader method = context.getClassSource().resolve(expr.getMethod()); - if (method != null && isWrappedNativeCall(method)) { - generateWrappedNativeCall(method, expr); - } else { - MethodReference reference = expr.getMethod(); - if (method != null) { - reference = method.getReference(); - } - includes.includeClass(reference.getClassName()); - writer.print(names.forMethod(reference)); - - writer.print("("); - if (!expr.getArguments().isEmpty()) { - expr.getArguments().get(0).acceptVisitor(this); - for (int i = 1; i < expr.getArguments().size(); ++i) { - writer.print(", "); - expr.getArguments().get(i).acceptVisitor(this); - } - } - writer.print(")"); - } - + case STATIC: + generateDirectCall(expr.getMethod(), expr.getArguments()); break; - } + case DYNAMIC: { - VirtualTable vtable = context.getVirtualTableProvider().lookup(expr.getMethod().getClassName()); - if (vtable == null || !vtable.getEntries().containsKey(expr.getMethod().getDescriptor())) { - writer.print("("); - for (Expr arg : expr.getArguments()) { - arg.acceptVisitor(this); - writer.print(", "); - } - printDefaultValue(expr.getMethod().getReturnType()); - writer.print(")"); - } else { - String receiver = allocTemporaryVariable(CVariableType.PTR); - writer.print("((").print(receiver).print(" = "); - expr.getArguments().get(0).acceptVisitor(this); - - includes.includeClass(expr.getMethod().getClassName()); - writer.print("), TEAVM_METHOD(") - .print(receiver).print(", ") - .print(names.forClassClass(expr.getMethod().getClassName())).print(", ") - .print(names.forVirtualMethod(expr.getMethod())) - .print(")(").print(receiver); - for (int i = 1; i < expr.getArguments().size(); ++i) { - writer.print(", "); - expr.getArguments().get(i).acceptVisitor(this); - } - writer.print("))"); - - freeTemporaryVariable(CVariableType.PTR); - } + generateVirtualCall(expr.getMethod(), expr.getArguments()); break; } } @@ -510,7 +440,100 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { popLocation(expr.getLocation()); } - private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) { + private void generateCallToConstructor(MethodReference reference, List arguments) { + String receiver = allocTemporaryVariable(CVariableType.PTR); + writer.print("(" + receiver + " = "); + allocObject(reference.getClassName()); + writer.print(", "); + + MethodReader method = context.getClassSource().resolve(reference); + if (method != null) { + reference = method.getReference(); + } + + includes.includeClass(reference.getClassName()); + writer.print(names.forMethod(reference)); + + writer.print("(" + receiver); + for (Expr arg : arguments) { + writer.print(", "); + arg.acceptVisitor(this); + } + writer.print("), " + receiver + ")"); + + freeTemporaryVariable(CVariableType.PTR); + } + + private void generateDirectCall(MethodReference reference, List arguments) { + MethodReader method = context.getClassSource().resolve(reference); + if (method != null && isWrappedNativeCall(method)) { + generateWrappedNativeCall(method, arguments); + } else { + if (method == null || method.hasModifier(ElementModifier.ABSTRACT)) { + generateNoMethodCall(reference, arguments); + return; + } + + reference = method.getReference(); + includes.includeClass(reference.getClassName()); + writer.print(names.forMethod(reference)); + + writer.print("("); + if (!arguments.isEmpty()) { + arguments.get(0).acceptVisitor(this); + for (int i = 1; i < arguments.size(); ++i) { + writer.print(", "); + arguments.get(i).acceptVisitor(this); + } + } + writer.print(")"); + } + } + + private void generateVirtualCall(MethodReference reference, List arguments) { + VirtualTable vtable = context.getVirtualTableProvider().lookup(reference.getClassName()); + String vtableClass = null; + if (vtable != null) { + VirtualTable containingVt = vtable.findMethodContainer(reference.getDescriptor()); + if (containingVt != null) { + vtableClass = containingVt.getClassName(); + } + } + if (vtableClass == null) { + generateNoMethodCall(reference, arguments); + return; + } + + String receiver = allocTemporaryVariable(CVariableType.PTR); + writer.print("((").print(receiver).print(" = "); + arguments.get(0).acceptVisitor(this); + + includes.includeClass(vtableClass); + writer.print("), TEAVM_METHOD(") + .print(receiver).print(", ") + .print(names.forClassClass(vtableClass)).print(", ") + .print(names.forVirtualMethod(reference.getDescriptor())) + .print(")(").print(receiver); + for (int i = 1; i < arguments.size(); ++i) { + writer.print(", "); + arguments.get(i).acceptVisitor(this); + } + writer.print("))"); + + freeTemporaryVariable(CVariableType.PTR); + } + + private void generateNoMethodCall(MethodReference reference, List arguments) { + writer.print("("); + for (Expr arg : arguments) { + arg.acceptVisitor(this); + writer.print(", "); + } + printDefaultValue(reference.getReturnType()); + writer.print(")"); + } + + private void generateWrappedNativeCall(MethodReader method, List arguments) { List temporaries = new ArrayList<>(); List stringTemporaries = new ArrayList<>(); String resultTmp = null; @@ -518,13 +541,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { resultTmp = allocTemporaryVariable(typeToCType(method.getResultType())); } - for (int i = 0; i < expr.getArguments().size(); ++i) { + for (int i = 0; i < arguments.size(); ++i) { temporaries.add(allocTemporaryVariable(parameterTypeForCall(method, i))); } boolean stringResult = method.getResultType().isObject(String.class); writer.print("("); - for (int i = 0; i < expr.getArguments().size(); ++i) { + for (int i = 0; i < arguments.size(); ++i) { String tmp = temporaries.get(i); writer.print(tmp + " = "); ValueType type = method.hasModifier(ElementModifier.STATIC) @@ -532,23 +555,23 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { : i == 0 ? ValueType.object(method.getOwnerName()) : method.parameterType(i - 1); if (type.isObject(String.class)) { writer.print("teavm_stringToC("); - expr.getArguments().get(i).acceptVisitor(this); + arguments.get(i).acceptVisitor(this); writer.print(")"); stringTemporaries.add(tmp); } else if (isPrimitiveArray(type)) { writer.print("TEAVM_ARRAY_DATAN("); - expr.getArguments().get(i).acceptVisitor(this); + arguments.get(i).acceptVisitor(this); writer.print(", ").printStrictType(((ValueType.Array) type).getItemType()).print(")"); } else if (isPrimitiveBuffer(type)) { writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD("); String typeName = ((ValueType.Object) type).getClassName(); - expr.getArguments().get(i).acceptVisitor(this); + arguments.get(i).acceptVisitor(this); includes.includeClass(typeName); writer.print(", ").print(names.forClass(typeName)).print(", ") .print(names.forMemberField(new FieldReference(typeName, "array"))).print(")"); writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")"); } else { - expr.getArguments().get(i).acceptVisitor(this); + arguments.get(i).acceptVisitor(this); } writer.print(", "); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 0c9b60639..4f640dc01 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -239,6 +239,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { dep.use(); dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters")); + dependencyAnalyzer.linkMethod(new MethodReference(String.class, "hashCode", int.class)) + .propagate(0, "java.lang.String") + .use(); + dependencyAnalyzer.linkMethod(new MethodReference(String.class, "equals", Object.class, boolean.class)) + .propagate(0, "java.lang.String") + .propagate(1, "java.lang.String") + .use(); dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class)); MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference( diff --git a/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java index 2424ce06c..8662ee494 100644 --- a/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java +++ b/core/src/main/java/org/teavm/backend/lowlevel/generate/LowLevelNameProvider.java @@ -26,6 +26,7 @@ import org.teavm.interop.Import; import org.teavm.model.AnnotationReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -34,11 +35,11 @@ public abstract class LowLevelNameProvider { private ClassReaderSource classSource; protected Set occupiedTopLevelNames = new HashSet<>(); - protected Map> occupiedVtableNames = new HashMap<>(); + protected Set occupiedVtableNames = new HashSet<>(); protected Map> occupiedClassNames = new HashMap<>(); protected Map methodNames = new HashMap<>(); - protected Map virtualMethodNames = new HashMap<>(); + protected Map virtualMethodNames = new HashMap<>(); protected Map staticFieldNames = new HashMap<>(); protected Map memberFieldNames = new HashMap<>(); @@ -50,7 +51,6 @@ public abstract class LowLevelNameProvider { protected Map classInstanceNames = new HashMap<>(); protected Map supertypeNames = new HashMap<>(); - public LowLevelNameProvider(ClassReaderSource classSource) { this.classSource = classSource; } @@ -62,11 +62,10 @@ public abstract class LowLevelNameProvider { }); } - public String forVirtualMethod(MethodReference method) { + public String forVirtualMethod(MethodDescriptor method) { return virtualMethodNames.computeIfAbsent(method, k -> { - Set occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(), - c -> new HashSet<>(Arrays.asList("parent"))); - return pickUnoccupied("virt_" + k.getName(), occupied); + Set occupied = occupiedVtableNames; + return pickUnoccupied("virt_" + sanitize(k.getName()), occupied); }); } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index be771a14a..7d81dbd0b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -123,6 +123,7 @@ import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; +import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.InvocationType; @@ -776,6 +777,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { + VirtualTableBuilder builder = new VirtualTableBuilder(classes); + builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); + builder.setMethodCalledVirtually(controller::isVirtual); + return builder.build(); + } + + private Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { Set virtualMethods = new HashSet<>(); for (String className : classes.getClassNames()) { @@ -801,7 +809,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } - return new VirtualTableProvider(classes, virtualMethods, controller::isVirtual); + return virtualMethods; } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index e0461dedc..5d50cf5a3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -163,7 +163,7 @@ public class WasmClassGenerator { ClassBinaryData itemBinaryData = binaryDataMap.get(itemType); VirtualTable vtable = vtableProvider.lookup("java.lang.Object"); - int vtableSize = vtable != null ? vtable.getEntries().size() : 0; + int vtableSize = vtable != null ? vtable.size() : 0; DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize); DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue(); @@ -250,7 +250,7 @@ public class WasmClassGenerator { int flags = 0; VirtualTable vtable = vtableProvider.lookup(name); - int vtableSize = vtable != null ? vtable.getEntries().size() : 0; + int vtableSize = vtable != null ? vtable.size() : 0; DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize); DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue(); @@ -376,19 +376,28 @@ public class WasmClassGenerator { private void fillVirtualTable(VirtualTable vtable, DataValue array) { int index = 0; - for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) { - int methodIndex; - if (vtableEntry.getImplementor() == null) { - methodIndex = -1; - } else { - methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> { - int result = functionTable.size(); - functionTable.add(names.forMethod(implementor)); - return result; - }); - } + List tables = new ArrayList<>(); + while (vtable != null) { + tables.add(vtable); + vtable = vtable.getParent(); + } + for (int i = tables.size() - 1; i >= 0; --i) { + vtable = tables.get(i); + for (MethodDescriptor method : vtable.getMethods()) { + int methodIndex = -1; + if (method != null) { + VirtualTableEntry entry = vtable.getEntry(method); + if (entry != null) { + methodIndex = functions.computeIfAbsent(entry.getImplementor(), implementor -> { + int result = functionTable.size(); + functionTable.add(names.forMethod(implementor)); + return result; + }); + } + } - array.setInt(index++, methodIndex); + array.setInt(index++, methodIndex); + } } } diff --git a/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java b/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java index 51153b943..8df10e176 100644 --- a/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/AbstractInstructionAnalyzer.java @@ -160,7 +160,7 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader { fieldDep.getValue().connect(receiverNode); } } - initClass(field.getClassName()); + touchField(instance, fieldDep, field); } @Override @@ -174,7 +174,17 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader { valueNode.connect(fieldDep.getValue()); } } - initClass(field.getClassName()); + touchField(instance, fieldDep, field); + } + + private void touchField(VariableReader instance, FieldDependency fieldDep, FieldReference field) { + if (instance == null) { + if (fieldDep.getField() != null) { + initClass(fieldDep.getField().getOwnerName()); + } + } else { + getAnalyzer().linkClass(field.getClassName()); + } } @Override diff --git a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java index 00f5f910e..606232f66 100644 --- a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java @@ -78,11 +78,11 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer { } if (method.hasModifier(ElementModifier.SYNCHRONIZED)) { - processAsyncMethod(methodDep); + processAsyncMethod(); } } - private void processAsyncMethod(MethodDependency methodDep) { + private void processAsyncMethod() { if (asyncSupported) { linkMethod(MONITOR_ENTER_METHOD).use(); } @@ -144,7 +144,7 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer { if (fullType instanceof ValueType.Object) { String prefix = key.substring(0, degree) + "L"; String className = ((ValueType.Object) fullType).getClassName(); - ClassReader cls = getClassSource().get(key); + ClassReader cls = getClassSource().get(className); if (cls != null) { if (cls.getParent() != null) { node.connect(getSubtypeNode(prefix + cls.getParent().replace('.', '/') + ";")); diff --git a/core/src/main/java/org/teavm/dependency/Linker.java b/core/src/main/java/org/teavm/dependency/Linker.java index e3a37ec35..9aab7d06a 100644 --- a/core/src/main/java/org/teavm/dependency/Linker.java +++ b/core/src/main/java/org/teavm/dependency/Linker.java @@ -15,6 +15,7 @@ */ package org.teavm.dependency; +import org.teavm.model.AccessLevel; import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader; @@ -24,6 +25,7 @@ import org.teavm.model.FieldReference; import org.teavm.model.Instruction; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.instructions.GetFieldInstruction; @@ -67,11 +69,37 @@ public class Linker { for (Instruction insn : block) { if (insn instanceof InvokeInstruction) { InvokeInstruction invoke = (InvokeInstruction) insn; + MethodReference calledRef = invoke.getMethod(); if (invoke.getType() == InvocationType.SPECIAL) { - MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod()); + MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef); if (linkedMethod != null) { invoke.setMethod(linkedMethod.getReference()); } + } else if (invoke.getType() == InvocationType.VIRTUAL) { + MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef); + if (linkedMethod == null || linkedMethod.isMissing()) { + continue; + } + calledRef = linkedMethod.getReference(); + ClassReader cls = dependency.getClassSource().get(calledRef.getClassName()); + boolean isFinal = false; + if (cls != null) { + if (cls.hasModifier(ElementModifier.FINAL)) { + isFinal = true; + } else { + MethodReader calledMethod = cls.getMethod(calledRef.getDescriptor()); + if (calledMethod != null) { + if (calledMethod.hasModifier(ElementModifier.FINAL) + || calledMethod.getLevel() == AccessLevel.PRIVATE) { + isFinal = true; + } + } + } + } + if (isFinal) { + invoke.setType(InvocationType.SPECIAL); + invoke.setMethod(calledRef); + } } } else if (insn instanceof GetFieldInstruction) { GetFieldInstruction getField = (GetFieldInstruction) insn; diff --git a/core/src/main/java/org/teavm/model/classes/InterfaceToClassMapping.java b/core/src/main/java/org/teavm/model/classes/InterfaceToClassMapping.java deleted file mode 100644 index bf052dbca..000000000 --- a/core/src/main/java/org/teavm/model/classes/InterfaceToClassMapping.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2016 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.model.classes; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.ElementModifier; -import org.teavm.model.ListableClassReaderSource; - -public class InterfaceToClassMapping { - private Map map = new HashMap<>(); - - public InterfaceToClassMapping(ListableClassReaderSource classSource) { - for (String className : classSource.getClassNames()) { - ClassReader cls = classSource.get(className); - if (cls.hasModifier(ElementModifier.INTERFACE)) { - continue; - } - - map.put(className, className); - for (String iface : getInterfaces(classSource, className)) { - String existing = map.get(iface); - if (existing == null) { - map.put(iface, className); - } else { - map.put(iface, commonSuperClass(classSource, className, existing)); - } - } - } - } - - private static Set getInterfaces(ClassReaderSource classSource, String className) { - Set interfaces = new HashSet<>(); - getInterfaces(classSource, className, interfaces); - return interfaces; - } - - private static void getInterfaces(ClassReaderSource classSource, String className, Set interfaces) { - if (!interfaces.add(className)) { - return; - } - ClassReader cls = classSource.get(className); - if (cls == null) { - return; - } - for (String iface : cls.getInterfaces()) { - getInterfaces(classSource, iface, interfaces); - } - } - - private static String commonSuperClass(ClassReaderSource classSource, String a, String b) { - if (a.equals(b)) { - return a; - } - - List firstPath = pathToRoot(classSource, a); - List secondPath = pathToRoot(classSource, b); - Collections.reverse(firstPath); - Collections.reverse(secondPath); - int min = Math.min(firstPath.size(), secondPath.size()); - for (int i = 1; i < min; ++i) { - if (!firstPath.get(i).equals(secondPath.get(i))) { - return firstPath.get(i - 1); - } - } - - return firstPath.get(0); - } - - private static List pathToRoot(ClassReaderSource classSource, String className) { - List path = new ArrayList<>(); - while (true) { - path.add(className); - ClassReader cls = classSource.get(className); - if (cls == null || cls.getParent() == null) { - break; - } - className = cls.getParent(); - } - return path; - } - - public String mapClass(String className) { - return map.get(className); - } -} diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTable.java b/core/src/main/java/org/teavm/model/classes/VirtualTable.java index 42e2965db..5d222dbab 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTable.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTable.java @@ -15,28 +15,59 @@ */ package org.teavm.model.classes; -import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import org.teavm.model.MethodDescriptor; public class VirtualTable { private String className; - Map entries = new LinkedHashMap<>(); - private Map readonlyEntries; + private VirtualTable parent; + private List methods; + private Set methodSet; + private Map entryMap; - VirtualTable(String className) { + VirtualTable(String className, VirtualTable parent, List methods, + Set methodSet, Map entryMap) { this.className = className; + this.parent = parent; + this.methods = methods; + this.methodSet = methodSet; + this.entryMap = entryMap; } public String getClassName() { return className; } - public Map getEntries() { - if (readonlyEntries == null) { - readonlyEntries = Collections.unmodifiableMap(entries); + public VirtualTable getParent() { + return parent; + } + + public List getMethods() { + return methods; + } + + public VirtualTableEntry getEntry(MethodDescriptor method) { + return entryMap.get(method); + } + + public boolean hasMethod(MethodDescriptor method) { + return methodSet.contains(method); + } + + public VirtualTable findMethodContainer(MethodDescriptor method) { + VirtualTable vt = this; + while (vt != null) { + if (vt.hasMethod(method)) { + return vt; + } + vt = vt.getParent(); } - return readonlyEntries; + return null; + } + + public int size() { + return methods.size() + (parent != null ? parent.size() : 0); } } diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java new file mode 100644 index 000000000..984dddbd3 --- /dev/null +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java @@ -0,0 +1,440 @@ +/* + * Copyright 2019 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.model.classes; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import org.teavm.common.LCATree; +import org.teavm.model.AccessLevel; +import org.teavm.model.ClassReader; +import org.teavm.model.ElementModifier; +import org.teavm.model.ListableClassReaderSource; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; + +public class VirtualTableBuilder { + private ListableClassReaderSource classes; + private Map> methodsUsedAtCallSites = new HashMap<>(); + private Predicate methodCalledVirtually = m -> true; + private Map tables; + private Map> classChildren; + private LCATree classTree; + private ObjectIntMap classTreeIndexes; + private List classList; + private VirtualTableProvider result; + + public VirtualTableBuilder(ListableClassReaderSource classes) { + this.classes = classes; + } + + public void setMethodsUsedAtCallSites(Collection methodsUsedAtCallSites) { + for (MethodReference method : methodsUsedAtCallSites) { + this.methodsUsedAtCallSites.computeIfAbsent(method.getClassName(), k -> new ArrayList<>()) + .add(method.getDescriptor()); + } + } + + public void setMethodCalledVirtually(Predicate methodCalledVirtually) { + this.methodCalledVirtually = methodCalledVirtually; + } + + public VirtualTableProvider build() { + tables = new HashMap<>(); + + buildVirtualTables(); + cleanupVirtualTables(); + + classChildren = new HashMap<>(); + buildClassChildren(); + liftEntries(); + + buildResult(); + tables = null; + return result; + } + + private void buildVirtualTables() { + for (String className : classes.getClassNames()) { + fillClass(className); + } + } + + private void fillClass(String className) { + ClassReader cls = classes.get(className); + if (cls == null) { + return; + } + if (tables.containsKey(className)) { + return; + } + + TableBuilder table = new TableBuilder(); + tables.put(className, table); + + String parent = cls.getParent(); + if (parent != null) { + fillClass(parent); + TableBuilder parentTable = tables.get(parent); + if (parentTable != null) { + copyEntries(parentTable, table); + } + } + + for (String itf : cls.getInterfaces()) { + fillClass(itf); + TableBuilder itfTable = tables.get(itf); + if (itfTable != null) { + copyEntries(itfTable, table); + } + } + + List methodsAtCallSites = methodsUsedAtCallSites.get(className); + if (methodsAtCallSites != null) { + for (MethodDescriptor methodDesc : methodsAtCallSites) { + MethodReader method = cls.getMethod(methodDesc); + if (method != null) { + if (method.hasModifier(ElementModifier.FINAL) + || method.getLevel() == AccessLevel.PRIVATE + || cls.hasModifier(ElementModifier.FINAL)) { + continue; + } + } + table.entries.computeIfAbsent(methodDesc, k -> new EntryBuilder()); + } + } + + for (MethodReader method : cls.getMethods()) { + if (method.hasModifier(ElementModifier.ABSTRACT) + || method.hasModifier(ElementModifier.STATIC) + || method.getName().equals("") + || method.getLevel() == AccessLevel.PRIVATE) { + continue; + } + + EntryBuilder entry = table.entries.get(method.getDescriptor()); + if (entry == null) { + if (method.hasModifier(ElementModifier.FINAL) + || method.getLevel() == AccessLevel.PRIVATE + || cls.hasModifier(ElementModifier.FINAL)) { + continue; + } + entry = new EntryBuilder(); + table.entries.put(method.getDescriptor(), entry); + } + entry.implementor = method.getReference(); + } + } + + private void copyEntries(TableBuilder source, TableBuilder target) { + for (Map.Entry entry : source.entries.entrySet()) { + EntryBuilder targetEntry = target.entries.computeIfAbsent(entry.getKey(), k -> new EntryBuilder()); + targetEntry.addParent(entry.getValue()); + if (entry.getValue().implementor != null && targetEntry.implementor == null) { + targetEntry.implementor = entry.getValue().implementor; + } + } + } + + private void cleanupVirtualTables() { + for (String className : classes.getClassNames()) { + TableBuilder table = tables.get(className); + for (MethodDescriptor method : table.entries.keySet().toArray(new MethodDescriptor[0])) { + EntryBuilder entry = table.entries.get(method); + if (entry.implementor != null && !methodCalledVirtually.test(entry.implementor)) { + entry.implementor = null; + } + } + } + } + + private void buildClassChildren() { + for (String className : classes.getClassNames()) { + ClassReader cls = classes.get(className); + if (cls.hasModifier(ElementModifier.INTERFACE)) { + continue; + } + if (cls.getParent() != null) { + classChildren.computeIfAbsent(cls.getParent(), c -> new ArrayList<>()).add(className); + } + } + } + + private void liftEntries() { + buildClassTree(); + for (Map.Entry> group : groupMethods().entrySet()) { + String commonSuperclass = commonSuperclass(group.getValue()); + Set visited = new HashSet<>(); + for (String cls : group.getValue()) { + liftEntriesAtTable(cls, commonSuperclass, group.getKey(), visited); + } + } + classTree = null; + classTreeIndexes = null; + classList = null; + } + + private void buildClassTree() { + classTree = new LCATree(classes.getClassNames().size()); + classTreeIndexes = new ObjectIntHashMap<>(); + classList = new ArrayList<>(); + classList.add(null); + for (String className : classes.getClassNames()) { + ClassReader cls = classes.get(className); + if (cls.hasModifier(ElementModifier.INTERFACE)) { + continue; + } + insertClassToTree(className); + } + } + + private int insertClassToTree(String className) { + int index = classTreeIndexes.getOrDefault(className, 0); + if (index == 0) { + ClassReader cls = classes.get(className); + int parent = cls != null && cls.getParent() != null ? insertClassToTree(cls.getParent()) : 0; + index = classTree.addNode(parent); + classList.add(className); + classTreeIndexes.put(className, index); + } + return index; + } + + private String commonSuperclass(List classNames) { + int result = classTreeIndexes.get(classNames.get(0)); + for (int i = 1; i < classNames.size(); ++i) { + int next = classTreeIndexes.get(classNames.get(i)); + result = classTree.lcaOf(result, next); + } + return classList.get(result); + } + + private Map> groupMethods() { + Map> groups = new LinkedHashMap<>(); + for (String className : classes.getClassNames()) { + ClassReader cls = classes.get(className); + if (cls.hasModifier(ElementModifier.INTERFACE)) { + continue; + } + + TableBuilder table = tables.get(className); + TableBuilder parentTable = cls.getParent() != null ? tables.get(cls.getParent()) : null; + for (MethodDescriptor method : table.entries.keySet()) { + EntryBuilder entry = table.entries.get(method); + if (entry.implementor == null) { + continue; + } + if (parentTable != null) { + EntryBuilder parentEntry = parentTable.entries.get(method); + if (parentEntry != null && entry.implementor.equals(parentEntry.implementor)) { + continue; + } + } + + groups.computeIfAbsent(method, k -> new ArrayList<>()).add(className); + } + } + + groups.entrySet().removeIf(entry -> entry.getValue().size() == 1); + return groups; + } + + private void liftEntriesAtTable(String className, String toClass, MethodDescriptor method, + Set visited) { + while (visited.add(className)) { + TableBuilder table = tables.get(className); + EntryBuilder entry = table.entries.get(method); + if (entry == null) { + table.entries.put(method, new EntryBuilder()); + } + + if (className.equals(toClass)) { + break; + } + ClassReader cls = classes.get(className); + if (cls == null) { + break; + } + className = cls.getParent(); + } + } + + private void buildResult() { + result = new VirtualTableProvider(); + buildResultForClasses(); + buildResultForInterfaces(); + } + + private void buildResultForClasses() { + for (String className : classes.getClassNames()) { + ClassReader cls = classes.get(className); + if (cls.hasModifier(ElementModifier.INTERFACE) || cls.getParent() != null) { + continue; + } + + buildResultForClass(className, new Context(), null); + } + } + + private void buildResultForClass(String className, Context context, VirtualTable parent) { + TableBuilder table = tables.get(className); + ClassReader cls = classes.get(className); + + int start = context.methods.size(); + Map resultEntries = new HashMap<>(); + for (MethodDescriptor method : table.entries.keySet()) { + EntryBuilder entry = table.entries.get(method); + int index = context.indexes.getOrDefault(method, -1); + if (index < 0) { + index = context.indexes.size(); + context.indexes.put(method, index); + context.methods.add(method); + } + + if (entry.implementor != null) { + VirtualTableEntry resultEntry = new VirtualTableEntry(method, entry.implementor, index); + resultEntries.put(method, resultEntry); + + propagateInterfaceIndexes(cls, method, index); + } + } + + List newMethods = context.methods.subList(start, context.methods.size()); + List readonlyNewMethods = Collections.unmodifiableList( + Arrays.asList(newMethods.toArray(new MethodDescriptor[0]))); + VirtualTable resultTable = new VirtualTable(className, parent, readonlyNewMethods, + new HashSet<>(readonlyNewMethods), resultEntries); + result.virtualTables.put(className, resultTable); + + List children = classChildren.get(className); + if (children != null) { + for (String child : children) { + buildResultForClass(child, context, resultTable); + } + } + + newMethods = context.methods.subList(start, context.methods.size()); + for (MethodDescriptor method : newMethods) { + context.indexes.remove(method); + } + newMethods.clear(); + } + + private void propagateInterfaceIndexes(ClassReader cls, MethodDescriptor method, int index) { + while (true) { + for (String itf : cls.getInterfaces()) { + TableBuilder itfTable = tables.get(itf); + if (itfTable != null) { + EntryBuilder itfEntry = itfTable.entries.get(method); + if (itfEntry != null) { + propagateInterfaceIndex(itfEntry, index); + } + } + } + + if (cls.getParent() == null) { + break; + } + cls = classes.get(cls.getParent()); + if (cls == null) { + break; + } + + TableBuilder table = tables.get(cls.getName()); + EntryBuilder entry = table.entries.get(method); + if (entry == null || entry.implementor != null) { + break; + } + } + } + + private void propagateInterfaceIndex(EntryBuilder entry, int index) { + if (entry.index >= 0) { + return; + } + entry.index = index; + if (entry.parents != null) { + for (EntryBuilder parent : entry.parents) { + propagateInterfaceIndex(parent, index); + } + } + } + + private void buildResultForInterfaces() { + for (String className : classes.getClassNames()) { + ClassReader cls = classes.get(className); + if (!cls.hasModifier(ElementModifier.INTERFACE)) { + continue; + } + + List methods = new ArrayList<>(); + Set methodSet = new HashSet<>(); + TableBuilder table = tables.get(className); + for (MethodDescriptor method : table.entries.keySet()) { + EntryBuilder entry = table.entries.get(method); + if (entry.index < 0) { + continue; + } + if (entry.index >= methods.size()) { + methods.addAll(Collections.nCopies(entry.index - methods.size() + 1, null)); + } + methods.set(entry.index, method); + methodSet.add(method); + } + + List readonlyNewMethods = Collections.unmodifiableList( + Arrays.asList(methods.toArray(new MethodDescriptor[0]))); + VirtualTable resultTable = new VirtualTable(className, null, readonlyNewMethods, + methodSet, Collections.emptyMap()); + result.virtualTables.put(className, resultTable); + } + } + + static class TableBuilder { + Map entries = new LinkedHashMap<>(); + } + + static class EntryBuilder { + MethodReference implementor; + EntryBuilder[] parents; + int index = -1; + + void addParent(EntryBuilder parent) { + if (parents == null) { + parents = new EntryBuilder[] { parent }; + } else { + parents = Arrays.copyOf(parents, parents.length + 1); + parents[parents.length - 1] = parent; + } + } + } + + static class Context { + ObjectIntMap indexes = new ObjectIntHashMap<>(); + List methods = new ArrayList<>(); + } +} diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableEntry.java b/core/src/main/java/org/teavm/model/classes/VirtualTableEntry.java index 92370304e..e10d3973f 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableEntry.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableEntry.java @@ -19,22 +19,16 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; public class VirtualTableEntry { - private VirtualTable virtualTable; private MethodDescriptor method; MethodReference implementor; private int index; - VirtualTableEntry(VirtualTable virtualTable, MethodDescriptor method, MethodReference implementor, int index) { - this.virtualTable = virtualTable; + VirtualTableEntry(MethodDescriptor method, MethodReference implementor, int index) { this.method = method; this.implementor = implementor; this.index = index; } - public VirtualTable getVirtualTable() { - return virtualTable; - } - public MethodDescriptor getMethod() { return method; } diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java index 3ad46e034..fe42b57e2 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java @@ -15,116 +15,25 @@ */ package org.teavm.model.classes; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.ElementModifier; -import org.teavm.model.ListableClassReaderSource; -import org.teavm.model.MethodDescriptor; -import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; public class VirtualTableProvider { - private ClassReaderSource classSource; - private Map> virtualMethodMap = new HashMap<>(); - private Map virtualTables = new LinkedHashMap<>(); - private InterfaceToClassMapping interfaceMapping; + Map virtualTables = new LinkedHashMap<>(); - public VirtualTableProvider(ListableClassReaderSource classSource, Set virtualMethods, - Predicate methodCalledVirtually) { - this.classSource = classSource; - interfaceMapping = new InterfaceToClassMapping(classSource); - - Set classNames = new HashSet<>(classSource.getClassNames()); - for (MethodReference virtualMethod : virtualMethods) { - String cls = interfaceMapping.mapClass(virtualMethod.getClassName()); - if (cls == null) { - cls = virtualMethod.getClassName(); - } - classNames.add(cls); - virtualMethodMap.computeIfAbsent(cls, c -> new LinkedHashSet<>()).add(virtualMethod.getDescriptor()); - } - - for (String className : classNames) { - fillClass(className, methodCalledVirtually); - } - } - - private void fillClass(String className, Predicate methodCalledVirtually) { - if (virtualTables.containsKey(className)) { - return; - } - - VirtualTable table = new VirtualTable(className); - virtualTables.put(className, table); - ClassReader cls = classSource.get(className); - if (cls == null) { - return; - } - if (cls.getParent() != null) { - fillClass(cls.getParent(), methodCalledVirtually); - copyEntriesFromSupertype(table, virtualTables.get(cls.getParent())); - } - for (String itf : cls.getInterfaces()) { - fillClass(itf, methodCalledVirtually); - copyEntriesFromSupertype(table, virtualTables.get(itf)); - } - - Set newDescriptors = virtualMethodMap.get(className); - if (newDescriptors != null) { - for (MethodDescriptor method : newDescriptors) { - if (!table.entries.containsKey(method)) { - MethodReader implementation = classSource.resolveImplementation( - className, method); - MethodReference implementationRef = implementation != null - ? implementation.getReference() - : null; - if (implementationRef != null && !methodCalledVirtually.test(implementationRef)) { - implementationRef = null; - } - table.entries.put(method, new VirtualTableEntry(table, method, implementationRef, - table.entries.size())); - } - } - } - - for (MethodReader method : cls.getMethods()) { - if (method.hasModifier(ElementModifier.ABSTRACT)) { - continue; - } - VirtualTableEntry entry = table.entries.get(method.getDescriptor()); - if (entry != null && methodCalledVirtually.test(method.getReference())) { - entry.implementor = method.getReference(); - } - } - } - - private void copyEntriesFromSupertype(VirtualTable table, VirtualTable supertypeTable) { - for (VirtualTableEntry parentEntry : supertypeTable.entries.values()) { - VirtualTableEntry existingEntry = table.entries.get(parentEntry.getMethod()); - if (existingEntry == null || existingEntry.getImplementor() == null) { - VirtualTableEntry entry = new VirtualTableEntry(table, parentEntry.getMethod(), - parentEntry.getImplementor(), parentEntry.getIndex()); - table.entries.put(entry.getMethod(), entry); - } - } + VirtualTableProvider() { } public VirtualTableEntry lookup(MethodReference method) { - VirtualTable vtable = virtualTables.get(interfaceMapping.mapClass(method.getClassName())); + VirtualTable vtable = virtualTables.get(method.getClassName()); if (vtable == null) { return null; } - return vtable.getEntries().get(method.getDescriptor()); + return vtable.getEntry(method.getDescriptor()); } public VirtualTable lookup(String className) { - return virtualTables.get(interfaceMapping.mapClass(className)); + return virtualTables.get(className); } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 27ae7e75b..afad5a10a 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -908,6 +908,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository { public ClassInitializerInfo getClassInitializerInfo() { return classInitializerInfo; } + + @Override + public TeaVMOptimizationLevel getOptimizationLevel() { + return optimizationLevel; + } }; class PostProcessingClassHolderSource implements ListableClassHolderSource { @@ -954,12 +959,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } return program; }; + for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { MethodDependencyInfo methodDep = dependencyAnalyzer.getMethod(method.getReference()); if (methodDep == null) { cls.removeMethod(method); } else if (!methodDep.isUsed()) { method.getModifiers().add(ElementModifier.ABSTRACT); + method.setProgram(null); } else { MethodReader methodReader = classReader.getMethod(method.getDescriptor()); if (methodReader != null && methodReader.getProgram() != null) { diff --git a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java index 757d124da..9306abf57 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java @@ -44,6 +44,8 @@ public interface TeaVMTargetController { ServiceRepository getServices(); + TeaVMOptimizationLevel getOptimizationLevel(); + boolean isFriendlyToDebugger(); Map getEntryPoints(); diff --git a/core/src/main/resources/org/teavm/backend/c/date.c b/core/src/main/resources/org/teavm/backend/c/date.c index 5d1029a0c..ca8d32f05 100644 --- a/core/src/main/resources/org/teavm/backend/c/date.c +++ b/core/src/main/resources/org/teavm/backend/c/date.c @@ -1,8 +1,16 @@ #include "runtime.h" +#ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE +#endif + +#ifndef __USE_XOPEN #define __USE_XOPEN +#endif + +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include diff --git a/tests/compile-c-unix-fast.sh b/tests/compile-c-unix-fast.sh new file mode 100755 index 000000000..9d2dbeeea --- /dev/null +++ b/tests/compile-c-unix-fast.sh @@ -0,0 +1,2 @@ +SOURCE_DIR=$(pwd) +gcc -g -O0 -lrt -lm all.c -o run_test \ No newline at end of file diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml index 95a56514e..5fda44b4c 100644 --- a/tools/cli/pom.xml +++ b/tools/cli/pom.xml @@ -45,6 +45,12 @@ ${project.version} runtime + + org.teavm + teavm-metaprogramming-api + ${project.version} + runtime + org.teavm teavm-metaprogramming-impl @@ -125,6 +131,13 @@ shade + + + junit:junit + com.fasterxml.jackson.core:jackson-annotations + org.mozilla:rhino + + @@ -132,6 +145,18 @@ + + org.objectweb.asm + org.teavm.asm + + + org.mozilla + org.teavm.rhino + + + com.carrotsearch.hppc + org.teavm.hppc + org.apache.commons org.teavm.apachecommons diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java index 369739061..cb553ded2 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -132,7 +132,7 @@ public final class TeaVMRunner { .withLongOpt("min-heap") .withArgName("size") .hasArg() - .withDescription("Minimum heap size in bytes (for C and WebAssembly)") + .withDescription("Minimum heap size in megabytes (for C and WebAssembly)") .create()); options.addOption(OptionBuilder .withLongOpt("max-toplevel-names") @@ -323,7 +323,7 @@ public final class TeaVMRunner { printUsage(); return; } - tool.setMinHeapSize(size); + tool.setMinHeapSize(size * 1024 * 1024); } }