C: fix bugs, introduce new virtual table builder

This commit is contained in:
Alexey Andreev 2019-05-23 15:18:25 +03:00
parent 7f875aa568
commit 8b3df6f730
24 changed files with 837 additions and 377 deletions

View File

@ -560,7 +560,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T cast(TObject obj) { public T cast(TObject obj) {
if (obj != null && !isAssignableFrom((TClass<?>) (Object) obj.getClass())) { 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; return (T) obj;
} }

View File

@ -185,7 +185,7 @@ public class TObject {
} }
} }
boolean isEmptyMonitor() { final boolean isEmptyMonitor() {
Monitor monitor = this.monitor; Monitor monitor = this.monitor;
if (monitor == null) { if (monitor == null) {
return true; return true;
@ -246,7 +246,7 @@ public class TObject {
return getClass().getName() + "@" + TInteger.toHexString(identity()); return getClass().getName() + "@" + TInteger.toHexString(identity());
} }
int identity() { final int identity() {
if (PlatformDetector.isLowLevel()) { if (PlatformDetector.isLowLevel()) {
Monitor monitor = this.monitor; Monitor monitor = this.monitor;
if (monitor == null) { if (monitor == null) {

View File

@ -27,7 +27,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Deque; import java.util.Deque;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -100,6 +99,7 @@ import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType; 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.transformation.ClassPatch;
import org.teavm.model.util.AsyncMethodFinder; import org.teavm.model.util.AsyncMethodFinder;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.CallSite;
import org.teavm.runtime.CallSiteLocation;
import org.teavm.runtime.EventQueue; import org.teavm.runtime.EventQueue;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
import org.teavm.runtime.Fiber; 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()); dependencyAnalyzer.addDependencyListener(new ExceptionHandlingDependencyListener());
} }
@ -535,7 +540,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
} }
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
Set<MethodReference> virtualMethods = new LinkedHashSet<>(); VirtualTableBuilder builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build();
}
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
Set<MethodReference> virtualMethods = new HashSet<>();
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className); 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) { private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
@ -631,6 +643,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private void generateAllFile(ListableClassHolderSource classes, List<? extends ValueType> types, private void generateAllFile(ListableClassHolderSource classes, List<? extends ValueType> types,
BuildTarget buildTarget) throws IOException { BuildTarget buildTarget) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(false); 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); IncludeManager includes = new SimpleIncludeManager(writer);
includes.init("all.c"); includes.init("all.c");
includes.includePath("runtime.c"); includes.includePath("runtime.c");

View File

@ -24,6 +24,8 @@ import org.teavm.model.AnnotationReader;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class InteropDependencyListener extends AbstractDependencyListener { public class InteropDependencyListener extends AbstractDependencyListener {
@Override @Override
@ -31,11 +33,16 @@ public class InteropDependencyListener extends AbstractDependencyListener {
if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), className, false)) { if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), className, false)) {
ClassReader cls = agent.getClassSource().get(className); ClassReader cls = agent.getClassSource().get(className);
if (cls != null) { if (cls != null) {
for (FieldReader field : cls.getFields()) { reachFields(agent, cls);
if (!field.hasModifier(ElementModifier.STATIC)) { }
agent.linkField(field.getReference()); }
} }
}
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; 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")); 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);
}
}
} }
} }

View File

@ -18,7 +18,6 @@ package org.teavm.backend.c.generate;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -436,7 +435,9 @@ public class ClassGenerator {
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
className = "java.lang.Object"; 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) ? context.getNames().forClassClass(className)
: "TeaVM_Class"; : "TeaVM_Class";
if (className != null) { if (className != null) {
@ -444,7 +445,6 @@ public class ClassGenerator {
} }
String name = context.getNames().forClassInstance(type); String name = context.getNames().forClassInstance(type);
ClassReader cls = className != null ? context.getClassSource().get(className) : null;
String enumConstants; String enumConstants;
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
enumConstants = writeEnumConstants(cls, name); enumConstants = writeEnumConstants(cls, name);
@ -459,29 +459,15 @@ public class ClassGenerator {
codeWriter.print("alignas(8) ").print(structName).print(" ").print(name).println(" = {").indent(); codeWriter.print("alignas(8) ").print(structName).print(" ").print(name).println(" = {").indent();
if (className != null) { if (className != null) {
codeWriter.println(".parent = {").indent();
generateRuntimeClassInitializer(type, enumConstants);
codeWriter.outdent().println("},");
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className); VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
if (virtualTable != null) { if (cls.hasModifier(ElementModifier.INTERFACE)) {
List<VirtualTableEntry> entries = new ArrayList<>(virtualTable.getEntries().values()); generateRuntimeClassInitializer(type, enumConstants);
for (int i = 0; i < entries.size(); ++i) { } else if (virtualTable != null) {
VirtualTableEntry entry = entries.get(i); generateVirtualTableContent(virtualTable, virtualTable, type, enumConstants);
String methodName = context.getNames().forVirtualMethod( } else {
new MethodReference(className, entry.getMethod())); codeWriter.println(".parent = {").indent();
String implName = entry.getImplementor() != null generateRuntimeClassInitializer(type, enumConstants);
? "&" + context.getNames().forMethod(entry.getImplementor()) codeWriter.outdent().println("}");
: "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();
}
} }
} else { } else {
generateRuntimeClassInitializer(type, enumConstants); generateRuntimeClassInitializer(type, enumConstants);
@ -490,6 +476,34 @@ public class ClassGenerator {
codeWriter.outdent().println("};"); 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) { private String writeEnumConstants(ClassReader cls, String baseName) {
List<FieldReader> fields = cls.getFields().stream() List<FieldReader> fields = cls.getFields().stream()
.filter(f -> f.hasModifier(ElementModifier.ENUM)) .filter(f -> f.hasModifier(ElementModifier.ENUM))
@ -633,17 +647,30 @@ public class ClassGenerator {
String name = context.getNames().forClassClass(className); String name = context.getNames().forClassClass(className);
headerWriter.print("typedef struct ").print(name).println(" {").indent(); headerWriter.print("typedef struct ").print(name).println(" {").indent();
headerWriter.println("TeaVM_Class parent;");
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className); VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
if (virtualTable != null) { if (virtualTable != null) {
for (VirtualTableEntry entry : virtualTable.getEntries().values()) { String parentName = "TeaVM_Class";
String methodName = context.getNames().forVirtualMethod( int index = 0;
new MethodReference(className, entry.getMethod())); if (virtualTable.getParent() != null) {
headerWriter.printType(entry.getMethod().getResultType()) headerIncludes.includeClass(virtualTable.getParent().getClassName());
.print(" (*").print(methodName).print(")("); parentName = context.getNames().forClassClass(virtualTable.getParent().getClassName());
codeGenerator.generateMethodParameters(headerWriter, entry.getMethod(), false, false); index = virtualTable.getParent().size();
headerWriter.println(");"); }
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++);
} }
} }

View File

@ -422,87 +422,17 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
pushLocation(expr.getLocation()); pushLocation(expr.getLocation());
switch (expr.getType()) { switch (expr.getType()) {
case CONSTRUCTOR: { case CONSTRUCTOR:
String receiver = allocTemporaryVariable(CVariableType.PTR); generateCallToConstructor(expr.getMethod(), expr.getArguments());
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);
break; break;
}
case SPECIAL: case SPECIAL:
case STATIC: { case STATIC:
MethodReader method = context.getClassSource().resolve(expr.getMethod()); generateDirectCall(expr.getMethod(), expr.getArguments());
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(")");
}
break; break;
}
case DYNAMIC: { case DYNAMIC: {
VirtualTable vtable = context.getVirtualTableProvider().lookup(expr.getMethod().getClassName()); generateVirtualCall(expr.getMethod(), expr.getArguments());
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);
}
break; break;
} }
} }
@ -510,7 +440,100 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
popLocation(expr.getLocation()); popLocation(expr.getLocation());
} }
private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) { private void generateCallToConstructor(MethodReference reference, List<? extends Expr> 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<? extends Expr> 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<? extends Expr> 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<? extends Expr> arguments) {
writer.print("(");
for (Expr arg : arguments) {
arg.acceptVisitor(this);
writer.print(", ");
}
printDefaultValue(reference.getReturnType());
writer.print(")");
}
private void generateWrappedNativeCall(MethodReader method, List<? extends Expr> arguments) {
List<String> temporaries = new ArrayList<>(); List<String> temporaries = new ArrayList<>();
List<String> stringTemporaries = new ArrayList<>(); List<String> stringTemporaries = new ArrayList<>();
String resultTmp = null; String resultTmp = null;
@ -518,13 +541,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
resultTmp = allocTemporaryVariable(typeToCType(method.getResultType())); 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))); temporaries.add(allocTemporaryVariable(parameterTypeForCall(method, i)));
} }
boolean stringResult = method.getResultType().isObject(String.class); boolean stringResult = method.getResultType().isObject(String.class);
writer.print("("); writer.print("(");
for (int i = 0; i < expr.getArguments().size(); ++i) { for (int i = 0; i < arguments.size(); ++i) {
String tmp = temporaries.get(i); String tmp = temporaries.get(i);
writer.print(tmp + " = "); writer.print(tmp + " = ");
ValueType type = method.hasModifier(ElementModifier.STATIC) 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); : i == 0 ? ValueType.object(method.getOwnerName()) : method.parameterType(i - 1);
if (type.isObject(String.class)) { if (type.isObject(String.class)) {
writer.print("teavm_stringToC("); writer.print("teavm_stringToC(");
expr.getArguments().get(i).acceptVisitor(this); arguments.get(i).acceptVisitor(this);
writer.print(")"); writer.print(")");
stringTemporaries.add(tmp); stringTemporaries.add(tmp);
} else if (isPrimitiveArray(type)) { } else if (isPrimitiveArray(type)) {
writer.print("TEAVM_ARRAY_DATAN("); writer.print("TEAVM_ARRAY_DATAN(");
expr.getArguments().get(i).acceptVisitor(this); arguments.get(i).acceptVisitor(this);
writer.print(", ").printStrictType(((ValueType.Array) type).getItemType()).print(")"); writer.print(", ").printStrictType(((ValueType.Array) type).getItemType()).print(")");
} else if (isPrimitiveBuffer(type)) { } else if (isPrimitiveBuffer(type)) {
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD("); writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
String typeName = ((ValueType.Object) type).getClassName(); String typeName = ((ValueType.Object) type).getClassName();
expr.getArguments().get(i).acceptVisitor(this); arguments.get(i).acceptVisitor(this);
includes.includeClass(typeName); includes.includeClass(typeName);
writer.print(", ").print(names.forClass(typeName)).print(", ") writer.print(", ").print(names.forClass(typeName)).print(", ")
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")"); .print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")"); writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
} else { } else {
expr.getArguments().get(i).acceptVisitor(this); arguments.get(i).acceptVisitor(this);
} }
writer.print(", "); writer.print(", ");

View File

@ -239,6 +239,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
dep.use(); dep.use();
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters")); 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)); dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class));
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference( MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(

View File

@ -26,6 +26,7 @@ import org.teavm.interop.Import;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
@ -34,11 +35,11 @@ public abstract class LowLevelNameProvider {
private ClassReaderSource classSource; private ClassReaderSource classSource;
protected Set<String> occupiedTopLevelNames = new HashSet<>(); protected Set<String> occupiedTopLevelNames = new HashSet<>();
protected Map<String, Set<String>> occupiedVtableNames = new HashMap<>(); protected Set<String> occupiedVtableNames = new HashSet<>();
protected Map<String, Set<String>> occupiedClassNames = new HashMap<>(); protected Map<String, Set<String>> occupiedClassNames = new HashMap<>();
protected Map<MethodReference, String> methodNames = new HashMap<>(); protected Map<MethodReference, String> methodNames = new HashMap<>();
protected Map<MethodReference, String> virtualMethodNames = new HashMap<>(); protected Map<MethodDescriptor, String> virtualMethodNames = new HashMap<>();
protected Map<FieldReference, String> staticFieldNames = new HashMap<>(); protected Map<FieldReference, String> staticFieldNames = new HashMap<>();
protected Map<FieldReference, String> memberFieldNames = new HashMap<>(); protected Map<FieldReference, String> memberFieldNames = new HashMap<>();
@ -50,7 +51,6 @@ public abstract class LowLevelNameProvider {
protected Map<ValueType, String> classInstanceNames = new HashMap<>(); protected Map<ValueType, String> classInstanceNames = new HashMap<>();
protected Map<ValueType, String> supertypeNames = new HashMap<>(); protected Map<ValueType, String> supertypeNames = new HashMap<>();
public LowLevelNameProvider(ClassReaderSource classSource) { public LowLevelNameProvider(ClassReaderSource classSource) {
this.classSource = 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 -> { return virtualMethodNames.computeIfAbsent(method, k -> {
Set<String> occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(), Set<String> occupied = occupiedVtableNames;
c -> new HashSet<>(Arrays.asList("parent"))); return pickUnoccupied("virt_" + sanitize(k.getName()), occupied);
return pickUnoccupied("virt_" + k.getName(), occupied);
}); });
} }

View File

@ -123,6 +123,7 @@ import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
@ -776,6 +777,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
VirtualTableBuilder builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build();
}
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
Set<MethodReference> virtualMethods = new HashSet<>(); Set<MethodReference> virtualMethods = new HashSet<>();
for (String className : classes.getClassNames()) { 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 @Override

View File

@ -163,7 +163,7 @@ public class WasmClassGenerator {
ClassBinaryData itemBinaryData = binaryDataMap.get(itemType); ClassBinaryData itemBinaryData = binaryDataMap.get(itemType);
VirtualTable vtable = vtableProvider.lookup("java.lang.Object"); 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); DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue(); DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
@ -250,7 +250,7 @@ public class WasmClassGenerator {
int flags = 0; int flags = 0;
VirtualTable vtable = vtableProvider.lookup(name); 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); DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue(); DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
@ -376,19 +376,28 @@ public class WasmClassGenerator {
private void fillVirtualTable(VirtualTable vtable, DataValue array) { private void fillVirtualTable(VirtualTable vtable, DataValue array) {
int index = 0; int index = 0;
for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) { List<VirtualTable> tables = new ArrayList<>();
int methodIndex; while (vtable != null) {
if (vtableEntry.getImplementor() == null) { tables.add(vtable);
methodIndex = -1; vtable = vtable.getParent();
} else { }
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> { for (int i = tables.size() - 1; i >= 0; --i) {
int result = functionTable.size(); vtable = tables.get(i);
functionTable.add(names.forMethod(implementor)); for (MethodDescriptor method : vtable.getMethods()) {
return result; 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);
}
} }
} }

View File

@ -160,7 +160,7 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
fieldDep.getValue().connect(receiverNode); fieldDep.getValue().connect(receiverNode);
} }
} }
initClass(field.getClassName()); touchField(instance, fieldDep, field);
} }
@Override @Override
@ -174,7 +174,17 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
valueNode.connect(fieldDep.getValue()); 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 @Override

View File

@ -78,11 +78,11 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
} }
if (method.hasModifier(ElementModifier.SYNCHRONIZED)) { if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
processAsyncMethod(methodDep); processAsyncMethod();
} }
} }
private void processAsyncMethod(MethodDependency methodDep) { private void processAsyncMethod() {
if (asyncSupported) { if (asyncSupported) {
linkMethod(MONITOR_ENTER_METHOD).use(); linkMethod(MONITOR_ENTER_METHOD).use();
} }
@ -144,7 +144,7 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
if (fullType instanceof ValueType.Object) { if (fullType instanceof ValueType.Object) {
String prefix = key.substring(0, degree) + "L"; String prefix = key.substring(0, degree) + "L";
String className = ((ValueType.Object) fullType).getClassName(); String className = ((ValueType.Object) fullType).getClassName();
ClassReader cls = getClassSource().get(key); ClassReader cls = getClassSource().get(className);
if (cls != null) { if (cls != null) {
if (cls.getParent() != null) { if (cls.getParent() != null) {
node.connect(getSubtypeNode(prefix + cls.getParent().replace('.', '/') + ";")); node.connect(getSubtypeNode(prefix + cls.getParent().replace('.', '/') + ";"));

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import org.teavm.model.AccessLevel;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
@ -24,6 +25,7 @@ import org.teavm.model.FieldReference;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.GetFieldInstruction;
@ -67,11 +69,37 @@ public class Linker {
for (Instruction insn : block) { for (Instruction insn : block) {
if (insn instanceof InvokeInstruction) { if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn; InvokeInstruction invoke = (InvokeInstruction) insn;
MethodReference calledRef = invoke.getMethod();
if (invoke.getType() == InvocationType.SPECIAL) { if (invoke.getType() == InvocationType.SPECIAL) {
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod()); MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef);
if (linkedMethod != null) { if (linkedMethod != null) {
invoke.setMethod(linkedMethod.getReference()); 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) { } else if (insn instanceof GetFieldInstruction) {
GetFieldInstruction getField = (GetFieldInstruction) insn; GetFieldInstruction getField = (GetFieldInstruction) insn;

View File

@ -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<String, String> 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<String> getInterfaces(ClassReaderSource classSource, String className) {
Set<String> interfaces = new HashSet<>();
getInterfaces(classSource, className, interfaces);
return interfaces;
}
private static void getInterfaces(ClassReaderSource classSource, String className, Set<String> 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<String> firstPath = pathToRoot(classSource, a);
List<String> 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<String> pathToRoot(ClassReaderSource classSource, String className) {
List<String> 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);
}
}

View File

@ -15,28 +15,59 @@
*/ */
package org.teavm.model.classes; package org.teavm.model.classes;
import java.util.Collections; import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
public class VirtualTable { public class VirtualTable {
private String className; private String className;
Map<MethodDescriptor, VirtualTableEntry> entries = new LinkedHashMap<>(); private VirtualTable parent;
private Map<MethodDescriptor, VirtualTableEntry> readonlyEntries; private List<? extends MethodDescriptor> methods;
private Set<MethodDescriptor> methodSet;
private Map<MethodDescriptor, VirtualTableEntry> entryMap;
VirtualTable(String className) { VirtualTable(String className, VirtualTable parent, List<? extends MethodDescriptor> methods,
Set<MethodDescriptor> methodSet, Map<MethodDescriptor, VirtualTableEntry> entryMap) {
this.className = className; this.className = className;
this.parent = parent;
this.methods = methods;
this.methodSet = methodSet;
this.entryMap = entryMap;
} }
public String getClassName() { public String getClassName() {
return className; return className;
} }
public Map<MethodDescriptor, VirtualTableEntry> getEntries() { public VirtualTable getParent() {
if (readonlyEntries == null) { return parent;
readonlyEntries = Collections.unmodifiableMap(entries); }
public List<? extends MethodDescriptor> 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);
} }
} }

View File

@ -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<String, List<MethodDescriptor>> methodsUsedAtCallSites = new HashMap<>();
private Predicate<MethodReference> methodCalledVirtually = m -> true;
private Map<String, TableBuilder> tables;
private Map<String, List<String>> classChildren;
private LCATree classTree;
private ObjectIntMap<String> classTreeIndexes;
private List<String> classList;
private VirtualTableProvider result;
public VirtualTableBuilder(ListableClassReaderSource classes) {
this.classes = classes;
}
public void setMethodsUsedAtCallSites(Collection<? extends MethodReference> methodsUsedAtCallSites) {
for (MethodReference method : methodsUsedAtCallSites) {
this.methodsUsedAtCallSites.computeIfAbsent(method.getClassName(), k -> new ArrayList<>())
.add(method.getDescriptor());
}
}
public void setMethodCalledVirtually(Predicate<MethodReference> 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<MethodDescriptor> 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("<init>")
|| 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<MethodDescriptor, EntryBuilder> 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<MethodDescriptor, List<String>> group : groupMethods().entrySet()) {
String commonSuperclass = commonSuperclass(group.getValue());
Set<String> 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<String> 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<MethodDescriptor, List<String>> groupMethods() {
Map<MethodDescriptor, List<String>> 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<String> 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<MethodDescriptor, VirtualTableEntry> 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<MethodDescriptor> newMethods = context.methods.subList(start, context.methods.size());
List<? extends MethodDescriptor> 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<String> 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<MethodDescriptor> methods = new ArrayList<>();
Set<MethodDescriptor> 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<? extends MethodDescriptor> 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<MethodDescriptor, EntryBuilder> 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<MethodDescriptor> indexes = new ObjectIntHashMap<>();
List<MethodDescriptor> methods = new ArrayList<>();
}
}

View File

@ -19,22 +19,16 @@ import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class VirtualTableEntry { public class VirtualTableEntry {
private VirtualTable virtualTable;
private MethodDescriptor method; private MethodDescriptor method;
MethodReference implementor; MethodReference implementor;
private int index; private int index;
VirtualTableEntry(VirtualTable virtualTable, MethodDescriptor method, MethodReference implementor, int index) { VirtualTableEntry(MethodDescriptor method, MethodReference implementor, int index) {
this.virtualTable = virtualTable;
this.method = method; this.method = method;
this.implementor = implementor; this.implementor = implementor;
this.index = index; this.index = index;
} }
public VirtualTable getVirtualTable() {
return virtualTable;
}
public MethodDescriptor getMethod() { public MethodDescriptor getMethod() {
return method; return method;
} }

View File

@ -15,116 +15,25 @@
*/ */
package org.teavm.model.classes; package org.teavm.model.classes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; 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; import org.teavm.model.MethodReference;
public class VirtualTableProvider { public class VirtualTableProvider {
private ClassReaderSource classSource; Map<String, VirtualTable> virtualTables = new LinkedHashMap<>();
private Map<String, Set<MethodDescriptor>> virtualMethodMap = new HashMap<>();
private Map<String, VirtualTable> virtualTables = new LinkedHashMap<>();
private InterfaceToClassMapping interfaceMapping;
public VirtualTableProvider(ListableClassReaderSource classSource, Set<MethodReference> virtualMethods, VirtualTableProvider() {
Predicate<MethodReference> methodCalledVirtually) {
this.classSource = classSource;
interfaceMapping = new InterfaceToClassMapping(classSource);
Set<String> 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<MethodReference> 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<MethodDescriptor> 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);
}
}
} }
public VirtualTableEntry lookup(MethodReference method) { public VirtualTableEntry lookup(MethodReference method) {
VirtualTable vtable = virtualTables.get(interfaceMapping.mapClass(method.getClassName())); VirtualTable vtable = virtualTables.get(method.getClassName());
if (vtable == null) { if (vtable == null) {
return null; return null;
} }
return vtable.getEntries().get(method.getDescriptor()); return vtable.getEntry(method.getDescriptor());
} }
public VirtualTable lookup(String className) { public VirtualTable lookup(String className) {
return virtualTables.get(interfaceMapping.mapClass(className)); return virtualTables.get(className);
} }
} }

View File

@ -908,6 +908,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
public ClassInitializerInfo getClassInitializerInfo() { public ClassInitializerInfo getClassInitializerInfo() {
return classInitializerInfo; return classInitializerInfo;
} }
@Override
public TeaVMOptimizationLevel getOptimizationLevel() {
return optimizationLevel;
}
}; };
class PostProcessingClassHolderSource implements ListableClassHolderSource { class PostProcessingClassHolderSource implements ListableClassHolderSource {
@ -954,12 +959,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
return program; return program;
}; };
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodDependencyInfo methodDep = dependencyAnalyzer.getMethod(method.getReference()); MethodDependencyInfo methodDep = dependencyAnalyzer.getMethod(method.getReference());
if (methodDep == null) { if (methodDep == null) {
cls.removeMethod(method); cls.removeMethod(method);
} else if (!methodDep.isUsed()) { } else if (!methodDep.isUsed()) {
method.getModifiers().add(ElementModifier.ABSTRACT); method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null);
} else { } else {
MethodReader methodReader = classReader.getMethod(method.getDescriptor()); MethodReader methodReader = classReader.getMethod(method.getDescriptor());
if (methodReader != null && methodReader.getProgram() != null) { if (methodReader != null && methodReader.getProgram() != null) {

View File

@ -44,6 +44,8 @@ public interface TeaVMTargetController {
ServiceRepository getServices(); ServiceRepository getServices();
TeaVMOptimizationLevel getOptimizationLevel();
boolean isFriendlyToDebugger(); boolean isFriendlyToDebugger();
Map<? extends String, ? extends TeaVMEntryPoint> getEntryPoints(); Map<? extends String, ? extends TeaVMEntryPoint> getEntryPoints();

View File

@ -1,8 +1,16 @@
#include "runtime.h" #include "runtime.h"
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE #define _XOPEN_SOURCE
#endif
#ifndef __USE_XOPEN
#define __USE_XOPEN #define __USE_XOPEN
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif
#include <time.h> #include <time.h>

2
tests/compile-c-unix-fast.sh Executable file
View File

@ -0,0 +1,2 @@
SOURCE_DIR=$(pwd)
gcc -g -O0 -lrt -lm all.c -o run_test

View File

@ -45,6 +45,12 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-metaprogramming-api</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-metaprogramming-impl</artifactId> <artifactId>teavm-metaprogramming-impl</artifactId>
@ -125,6 +131,13 @@
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
<exclude>com.fasterxml.jackson.core:jackson-annotations</exclude>
<exclude>org.mozilla:rhino</exclude>
</excludes>
</artifactSet>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
@ -132,6 +145,18 @@
</transformer> </transformer>
</transformers> </transformers>
<relocations> <relocations>
<relocation>
<pattern>org.objectweb.asm</pattern>
<shadedPattern>org.teavm.asm</shadedPattern>
</relocation>
<relocation>
<pattern>org.mozilla</pattern>
<shadedPattern>org.teavm.rhino</shadedPattern>
</relocation>
<relocation>
<pattern>com.carrotsearch.hppc</pattern>
<shadedPattern>org.teavm.hppc</shadedPattern>
</relocation>
<relocation> <relocation>
<pattern>org.apache.commons</pattern> <pattern>org.apache.commons</pattern>
<shadedPattern>org.teavm.apachecommons</shadedPattern> <shadedPattern>org.teavm.apachecommons</shadedPattern>

View File

@ -132,7 +132,7 @@ public final class TeaVMRunner {
.withLongOpt("min-heap") .withLongOpt("min-heap")
.withArgName("size") .withArgName("size")
.hasArg() .hasArg()
.withDescription("Minimum heap size in bytes (for C and WebAssembly)") .withDescription("Minimum heap size in megabytes (for C and WebAssembly)")
.create()); .create());
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("max-toplevel-names") .withLongOpt("max-toplevel-names")
@ -323,7 +323,7 @@ public final class TeaVMRunner {
printUsage(); printUsage();
return; return;
} }
tool.setMinHeapSize(size); tool.setMinHeapSize(size * 1024 * 1024);
} }
} }