mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
C: fix bugs, introduce new virtual table builder
This commit is contained in:
parent
7f875aa568
commit
8b3df6f730
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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) {
|
||||||
|
reachFields(agent, cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reachFields(DependencyAgent agent, ClassReader cls) {
|
||||||
for (FieldReader field : cls.getFields()) {
|
for (FieldReader field : cls.getFields()) {
|
||||||
if (!field.hasModifier(ElementModifier.STATIC)) {
|
if (!field.hasModifier(ElementModifier.STATIC)) {
|
||||||
agent.linkField(field.getReference());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
|
||||||
|
if (cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||||
|
generateRuntimeClassInitializer(type, enumConstants);
|
||||||
|
} else if (virtualTable != null) {
|
||||||
|
generateVirtualTableContent(virtualTable, virtualTable, type, enumConstants);
|
||||||
|
} else {
|
||||||
codeWriter.println(".parent = {").indent();
|
codeWriter.println(".parent = {").indent();
|
||||||
generateRuntimeClassInitializer(type, enumConstants);
|
generateRuntimeClassInitializer(type, enumConstants);
|
||||||
codeWriter.outdent().println("},");
|
codeWriter.outdent().println("}");
|
||||||
|
|
||||||
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
|
|
||||||
if (virtualTable != null) {
|
|
||||||
List<VirtualTableEntry> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} 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());
|
||||||
|
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(")(");
|
.print(" (*").print(methodName).print(")(");
|
||||||
codeGenerator.generateMethodParameters(headerWriter, entry.getMethod(), false, false);
|
codeGenerator.generateMethodParameters(headerWriter, method, false, false);
|
||||||
headerWriter.println(");");
|
headerWriter.print(")");
|
||||||
|
} else {
|
||||||
|
headerWriter.print("void (*pad" + padIndex++ + ")()");
|
||||||
|
}
|
||||||
|
headerWriter.println("; // " + index++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -422,14 +422,31 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
|
|
||||||
pushLocation(expr.getLocation());
|
pushLocation(expr.getLocation());
|
||||||
switch (expr.getType()) {
|
switch (expr.getType()) {
|
||||||
case CONSTRUCTOR: {
|
case CONSTRUCTOR:
|
||||||
|
generateCallToConstructor(expr.getMethod(), expr.getArguments());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPECIAL:
|
||||||
|
case STATIC:
|
||||||
|
generateDirectCall(expr.getMethod(), expr.getArguments());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNAMIC: {
|
||||||
|
generateVirtualCall(expr.getMethod(), expr.getArguments());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popLocation(expr.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateCallToConstructor(MethodReference reference, List<? extends Expr> arguments) {
|
||||||
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
||||||
writer.print("(" + receiver + " = ");
|
writer.print("(" + receiver + " = ");
|
||||||
allocObject(expr.getMethod().getClassName());
|
allocObject(reference.getClassName());
|
||||||
writer.print(", ");
|
writer.print(", ");
|
||||||
|
|
||||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
MethodReader method = context.getClassSource().resolve(reference);
|
||||||
MethodReference reference = expr.getMethod();
|
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
reference = method.getReference();
|
reference = method.getReference();
|
||||||
}
|
}
|
||||||
|
@ -438,79 +455,85 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
writer.print(names.forMethod(reference));
|
writer.print(names.forMethod(reference));
|
||||||
|
|
||||||
writer.print("(" + receiver);
|
writer.print("(" + receiver);
|
||||||
for (Expr arg : expr.getArguments()) {
|
for (Expr arg : arguments) {
|
||||||
writer.print(", ");
|
writer.print(", ");
|
||||||
arg.acceptVisitor(this);
|
arg.acceptVisitor(this);
|
||||||
}
|
}
|
||||||
writer.print("), " + receiver + ")");
|
writer.print("), " + receiver + ")");
|
||||||
|
|
||||||
freeTemporaryVariable(CVariableType.PTR);
|
freeTemporaryVariable(CVariableType.PTR);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
private void generateDirectCall(MethodReference reference, List<? extends Expr> arguments) {
|
||||||
}
|
MethodReader method = context.getClassSource().resolve(reference);
|
||||||
case SPECIAL:
|
|
||||||
case STATIC: {
|
|
||||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
|
||||||
if (method != null && isWrappedNativeCall(method)) {
|
if (method != null && isWrappedNativeCall(method)) {
|
||||||
generateWrappedNativeCall(method, expr);
|
generateWrappedNativeCall(method, arguments);
|
||||||
} else {
|
} else {
|
||||||
MethodReference reference = expr.getMethod();
|
if (method == null || method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||||
if (method != null) {
|
generateNoMethodCall(reference, arguments);
|
||||||
reference = method.getReference();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reference = method.getReference();
|
||||||
includes.includeClass(reference.getClassName());
|
includes.includeClass(reference.getClassName());
|
||||||
writer.print(names.forMethod(reference));
|
writer.print(names.forMethod(reference));
|
||||||
|
|
||||||
writer.print("(");
|
writer.print("(");
|
||||||
if (!expr.getArguments().isEmpty()) {
|
if (!arguments.isEmpty()) {
|
||||||
expr.getArguments().get(0).acceptVisitor(this);
|
arguments.get(0).acceptVisitor(this);
|
||||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
for (int i = 1; i < arguments.size(); ++i) {
|
||||||
writer.print(", ");
|
writer.print(", ");
|
||||||
expr.getArguments().get(i).acceptVisitor(this);
|
arguments.get(i).acceptVisitor(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.print(")");
|
writer.print(")");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
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();
|
||||||
}
|
}
|
||||||
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());
|
if (vtableClass == null) {
|
||||||
writer.print(")");
|
generateNoMethodCall(reference, arguments);
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
||||||
writer.print("((").print(receiver).print(" = ");
|
writer.print("((").print(receiver).print(" = ");
|
||||||
expr.getArguments().get(0).acceptVisitor(this);
|
arguments.get(0).acceptVisitor(this);
|
||||||
|
|
||||||
includes.includeClass(expr.getMethod().getClassName());
|
includes.includeClass(vtableClass);
|
||||||
writer.print("), TEAVM_METHOD(")
|
writer.print("), TEAVM_METHOD(")
|
||||||
.print(receiver).print(", ")
|
.print(receiver).print(", ")
|
||||||
.print(names.forClassClass(expr.getMethod().getClassName())).print(", ")
|
.print(names.forClassClass(vtableClass)).print(", ")
|
||||||
.print(names.forVirtualMethod(expr.getMethod()))
|
.print(names.forVirtualMethod(reference.getDescriptor()))
|
||||||
.print(")(").print(receiver);
|
.print(")(").print(receiver);
|
||||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
for (int i = 1; i < arguments.size(); ++i) {
|
||||||
writer.print(", ");
|
writer.print(", ");
|
||||||
expr.getArguments().get(i).acceptVisitor(this);
|
arguments.get(i).acceptVisitor(this);
|
||||||
}
|
}
|
||||||
writer.print("))");
|
writer.print("))");
|
||||||
|
|
||||||
freeTemporaryVariable(CVariableType.PTR);
|
freeTemporaryVariable(CVariableType.PTR);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
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(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
popLocation(expr.getLocation());
|
private void generateWrappedNativeCall(MethodReader method, List<? extends Expr> arguments) {
|
||||||
}
|
|
||||||
|
|
||||||
private void generateWrappedNativeCall(MethodReader method, InvocationExpr expr) {
|
|
||||||
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(", ");
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,21 +376,30 @@ 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) {
|
||||||
|
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();
|
int result = functionTable.size();
|
||||||
functionTable.add(names.forMethod(implementor));
|
functionTable.add(names.forMethod(implementor));
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
array.setInt(index++, methodIndex);
|
array.setInt(index++, methodIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<ValueType> getRegisteredClasses() {
|
public Collection<ValueType> getRegisteredClasses() {
|
||||||
return binaryDataMap.keySet();
|
return binaryDataMap.keySet();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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('.', '/') + ";"));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
return readonlyEntries;
|
|
||||||
|
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 null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return methods.size() + (parent != null ? parent.size() : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
2
tests/compile-c-unix-fast.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
SOURCE_DIR=$(pwd)
|
||||||
|
gcc -g -O0 -lrt -lm all.c -o run_test
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user