mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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")
|
||||
public T cast(TObject obj) {
|
||||
if (obj != null && !isAssignableFrom((TClass<?>) (Object) obj.getClass())) {
|
||||
throw new TClassCastException(obj.getClass().getName() + " is not subtype of " + name);
|
||||
throw new TClassCastException(obj.getClass().getName() + " is not subtype of " + getName());
|
||||
}
|
||||
return (T) obj;
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ public class TObject {
|
|||
}
|
||||
}
|
||||
|
||||
boolean isEmptyMonitor() {
|
||||
final boolean isEmptyMonitor() {
|
||||
Monitor monitor = this.monitor;
|
||||
if (monitor == null) {
|
||||
return true;
|
||||
|
@ -246,7 +246,7 @@ public class TObject {
|
|||
return getClass().getName() + "@" + TInteger.toHexString(identity());
|
||||
}
|
||||
|
||||
int identity() {
|
||||
final int identity() {
|
||||
if (PlatformDetector.isLowLevel()) {
|
||||
Monitor monitor = this.monitor;
|
||||
if (monitor == null) {
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -100,6 +99,7 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableBuilder;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
@ -115,6 +115,8 @@ import org.teavm.model.lowlevel.ShadowStackTransformer;
|
|||
import org.teavm.model.transformation.ClassPatch;
|
||||
import org.teavm.model.util.AsyncMethodFinder;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.CallSite;
|
||||
import org.teavm.runtime.CallSiteLocation;
|
||||
import org.teavm.runtime.EventQueue;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.Fiber;
|
||||
|
@ -266,6 +268,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
}
|
||||
}
|
||||
|
||||
dependencyAnalyzer.linkClass(CallSite.class.getName());
|
||||
dependencyAnalyzer.linkClass(CallSiteLocation.class.getName());
|
||||
|
||||
dependencyAnalyzer.addDependencyListener(new ExceptionHandlingDependencyListener());
|
||||
}
|
||||
|
||||
|
@ -535,7 +540,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
}
|
||||
|
||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||
Set<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()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
|
@ -560,7 +572,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
}
|
||||
}
|
||||
|
||||
return new VirtualTableProvider(classes, virtualMethods, controller::isVirtual);
|
||||
return virtualMethods;
|
||||
}
|
||||
|
||||
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
||||
|
@ -631,6 +643,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private void generateAllFile(ListableClassHolderSource classes, List<? extends ValueType> types,
|
||||
BuildTarget buildTarget) throws IOException {
|
||||
BufferedCodeWriter writer = new BufferedCodeWriter(false);
|
||||
writer.println("#define _XOPEN_SOURCE");
|
||||
writer.println("#define __USE_XOPEN");
|
||||
writer.println("#define _GNU_SOURCE");
|
||||
|
||||
IncludeManager includes = new SimpleIncludeManager(writer);
|
||||
includes.init("all.c");
|
||||
includes.includePath("runtime.c");
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.teavm.model.AnnotationReader;
|
|||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class InteropDependencyListener extends AbstractDependencyListener {
|
||||
@Override
|
||||
|
@ -31,11 +33,16 @@ public class InteropDependencyListener extends AbstractDependencyListener {
|
|||
if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), className, false)) {
|
||||
ClassReader cls = agent.getClassSource().get(className);
|
||||
if (cls != null) {
|
||||
reachFields(agent, cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reachFields(DependencyAgent agent, ClassReader cls) {
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
if (!field.hasModifier(ElementModifier.STATIC)) {
|
||||
agent.linkField(field.getReference());
|
||||
}
|
||||
}
|
||||
reachType(agent, field.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +58,22 @@ public class InteropDependencyListener extends AbstractDependencyListener {
|
|||
return;
|
||||
}
|
||||
|
||||
if (method.getReference().getReturnType().isObject("java.lang.String")) {
|
||||
MethodReference reference = method.getReference();
|
||||
if (reference.getReturnType().isObject("java.lang.String")) {
|
||||
method.getResult().propagate(agent.getType("java.lang.String"));
|
||||
}
|
||||
|
||||
for (int i = 0; i < reference.parameterCount(); ++i) {
|
||||
reachType(agent, reference.parameterType(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void reachType(DependencyAgent agent, ValueType type) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
String fieldClassName = ((ValueType.Object) type).getClassName();
|
||||
if (agent.getClassHierarchy().isSuperType(Structure.class.getName(), fieldClassName, false)) {
|
||||
agent.linkClass(fieldClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.teavm.backend.c.generate;
|
|||
import java.io.IOException;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -436,7 +435,9 @@ public class ClassGenerator {
|
|||
} else if (type instanceof ValueType.Array) {
|
||||
className = "java.lang.Object";
|
||||
}
|
||||
String structName = className != null
|
||||
ClassReader cls = className != null ? context.getClassSource().get(className) : null;
|
||||
|
||||
String structName = className != null && (cls == null || !cls.hasModifier(ElementModifier.INTERFACE))
|
||||
? context.getNames().forClassClass(className)
|
||||
: "TeaVM_Class";
|
||||
if (className != null) {
|
||||
|
@ -444,7 +445,6 @@ public class ClassGenerator {
|
|||
}
|
||||
String name = context.getNames().forClassInstance(type);
|
||||
|
||||
ClassReader cls = className != null ? context.getClassSource().get(className) : null;
|
||||
String enumConstants;
|
||||
if (cls != null && cls.hasModifier(ElementModifier.ENUM)) {
|
||||
enumConstants = writeEnumConstants(cls, name);
|
||||
|
@ -459,29 +459,15 @@ public class ClassGenerator {
|
|||
codeWriter.print("alignas(8) ").print(structName).print(" ").print(name).println(" = {").indent();
|
||||
|
||||
if (className != null) {
|
||||
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();
|
||||
generateRuntimeClassInitializer(type, enumConstants);
|
||||
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();
|
||||
}
|
||||
codeWriter.outdent().println("}");
|
||||
}
|
||||
} else {
|
||||
generateRuntimeClassInitializer(type, enumConstants);
|
||||
|
@ -490,6 +476,34 @@ public class ClassGenerator {
|
|||
codeWriter.outdent().println("};");
|
||||
}
|
||||
|
||||
private void generateVirtualTableContent(VirtualTable current, VirtualTable original, ValueType type,
|
||||
String enumConstants) {
|
||||
codeWriter.println(".parent = {").indent();
|
||||
if (current.getParent() == null) {
|
||||
generateRuntimeClassInitializer(type, enumConstants);
|
||||
} else {
|
||||
generateVirtualTableContent(current.getParent(), original, type, enumConstants);
|
||||
}
|
||||
codeWriter.outdent().print("}");
|
||||
|
||||
for (MethodDescriptor method : current.getMethods()) {
|
||||
if (method == null) {
|
||||
continue;
|
||||
}
|
||||
VirtualTableEntry entry = original.getEntry(method);
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
codeWriter.println(",");
|
||||
String methodName = context.getNames().forVirtualMethod(method);
|
||||
String implName = "&" + context.getNames().forMethod(entry.getImplementor());
|
||||
includes.includeClass(entry.getImplementor().getClassName());
|
||||
codeWriter.print(".").print(methodName).print(" = ").print(implName);
|
||||
}
|
||||
codeWriter.println();
|
||||
}
|
||||
|
||||
private String writeEnumConstants(ClassReader cls, String baseName) {
|
||||
List<FieldReader> fields = cls.getFields().stream()
|
||||
.filter(f -> f.hasModifier(ElementModifier.ENUM))
|
||||
|
@ -633,17 +647,30 @@ public class ClassGenerator {
|
|||
String name = context.getNames().forClassClass(className);
|
||||
|
||||
headerWriter.print("typedef struct ").print(name).println(" {").indent();
|
||||
headerWriter.println("TeaVM_Class parent;");
|
||||
|
||||
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
|
||||
if (virtualTable != null) {
|
||||
for (VirtualTableEntry entry : virtualTable.getEntries().values()) {
|
||||
String methodName = context.getNames().forVirtualMethod(
|
||||
new MethodReference(className, entry.getMethod()));
|
||||
headerWriter.printType(entry.getMethod().getResultType())
|
||||
String parentName = "TeaVM_Class";
|
||||
int index = 0;
|
||||
if (virtualTable.getParent() != null) {
|
||||
headerIncludes.includeClass(virtualTable.getParent().getClassName());
|
||||
parentName = context.getNames().forClassClass(virtualTable.getParent().getClassName());
|
||||
index = virtualTable.getParent().size();
|
||||
}
|
||||
|
||||
headerWriter.println(parentName + " parent;");
|
||||
int padIndex = 0;
|
||||
for (MethodDescriptor method : virtualTable.getMethods()) {
|
||||
if (method != null) {
|
||||
String methodName = context.getNames().forVirtualMethod(method);
|
||||
headerWriter.printType(method.getResultType())
|
||||
.print(" (*").print(methodName).print(")(");
|
||||
codeGenerator.generateMethodParameters(headerWriter, entry.getMethod(), false, false);
|
||||
headerWriter.println(");");
|
||||
codeGenerator.generateMethodParameters(headerWriter, method, false, false);
|
||||
headerWriter.print(")");
|
||||
} else {
|
||||
headerWriter.print("void (*pad" + padIndex++ + ")()");
|
||||
}
|
||||
headerWriter.println("; // " + index++);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -422,14 +422,31 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
|
||||
pushLocation(expr.getLocation());
|
||||
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);
|
||||
writer.print("(" + receiver + " = ");
|
||||
allocObject(expr.getMethod().getClassName());
|
||||
allocObject(reference.getClassName());
|
||||
writer.print(", ");
|
||||
|
||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
||||
MethodReference reference = expr.getMethod();
|
||||
MethodReader method = context.getClassSource().resolve(reference);
|
||||
if (method != null) {
|
||||
reference = method.getReference();
|
||||
}
|
||||
|
@ -438,79 +455,85 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
writer.print(names.forMethod(reference));
|
||||
|
||||
writer.print("(" + receiver);
|
||||
for (Expr arg : expr.getArguments()) {
|
||||
for (Expr arg : arguments) {
|
||||
writer.print(", ");
|
||||
arg.acceptVisitor(this);
|
||||
}
|
||||
writer.print("), " + receiver + ")");
|
||||
|
||||
freeTemporaryVariable(CVariableType.PTR);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SPECIAL:
|
||||
case STATIC: {
|
||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
||||
private void generateDirectCall(MethodReference reference, List<? extends Expr> arguments) {
|
||||
MethodReader method = context.getClassSource().resolve(reference);
|
||||
if (method != null && isWrappedNativeCall(method)) {
|
||||
generateWrappedNativeCall(method, expr);
|
||||
generateWrappedNativeCall(method, arguments);
|
||||
} else {
|
||||
MethodReference reference = expr.getMethod();
|
||||
if (method != null) {
|
||||
reference = method.getReference();
|
||||
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 (!expr.getArguments().isEmpty()) {
|
||||
expr.getArguments().get(0).acceptVisitor(this);
|
||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||
if (!arguments.isEmpty()) {
|
||||
arguments.get(0).acceptVisitor(this);
|
||||
for (int i = 1; i < arguments.size(); ++i) {
|
||||
writer.print(", ");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
}
|
||||
}
|
||||
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());
|
||||
writer.print(")");
|
||||
} else {
|
||||
if (vtableClass == null) {
|
||||
generateNoMethodCall(reference, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
String receiver = allocTemporaryVariable(CVariableType.PTR);
|
||||
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(")
|
||||
.print(receiver).print(", ")
|
||||
.print(names.forClassClass(expr.getMethod().getClassName())).print(", ")
|
||||
.print(names.forVirtualMethod(expr.getMethod()))
|
||||
.print(names.forClassClass(vtableClass)).print(", ")
|
||||
.print(names.forVirtualMethod(reference.getDescriptor()))
|
||||
.print(")(").print(receiver);
|
||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||
for (int i = 1; i < arguments.size(); ++i) {
|
||||
writer.print(", ");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
}
|
||||
writer.print("))");
|
||||
|
||||
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, InvocationExpr expr) {
|
||||
private void generateWrappedNativeCall(MethodReader method, List<? extends Expr> arguments) {
|
||||
List<String> temporaries = new ArrayList<>();
|
||||
List<String> stringTemporaries = new ArrayList<>();
|
||||
String resultTmp = null;
|
||||
|
@ -518,13 +541,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
resultTmp = allocTemporaryVariable(typeToCType(method.getResultType()));
|
||||
}
|
||||
|
||||
for (int i = 0; i < expr.getArguments().size(); ++i) {
|
||||
for (int i = 0; i < arguments.size(); ++i) {
|
||||
temporaries.add(allocTemporaryVariable(parameterTypeForCall(method, i)));
|
||||
}
|
||||
boolean stringResult = method.getResultType().isObject(String.class);
|
||||
|
||||
writer.print("(");
|
||||
for (int i = 0; i < expr.getArguments().size(); ++i) {
|
||||
for (int i = 0; i < arguments.size(); ++i) {
|
||||
String tmp = temporaries.get(i);
|
||||
writer.print(tmp + " = ");
|
||||
ValueType type = method.hasModifier(ElementModifier.STATIC)
|
||||
|
@ -532,23 +555,23 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
: i == 0 ? ValueType.object(method.getOwnerName()) : method.parameterType(i - 1);
|
||||
if (type.isObject(String.class)) {
|
||||
writer.print("teavm_stringToC(");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
writer.print(")");
|
||||
stringTemporaries.add(tmp);
|
||||
} else if (isPrimitiveArray(type)) {
|
||||
writer.print("TEAVM_ARRAY_DATAN(");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
writer.print(", ").printStrictType(((ValueType.Array) type).getItemType()).print(")");
|
||||
} else if (isPrimitiveBuffer(type)) {
|
||||
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
|
||||
String typeName = ((ValueType.Object) type).getClassName();
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
includes.includeClass(typeName);
|
||||
writer.print(", ").print(names.forClass(typeName)).print(", ")
|
||||
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
|
||||
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
|
||||
} else {
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
arguments.get(i).acceptVisitor(this);
|
||||
}
|
||||
|
||||
writer.print(", ");
|
||||
|
|
|
@ -239,6 +239,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
dep.use();
|
||||
|
||||
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"));
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(String.class, "hashCode", int.class))
|
||||
.propagate(0, "java.lang.String")
|
||||
.use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(String.class, "equals", Object.class, boolean.class))
|
||||
.propagate(0, "java.lang.String")
|
||||
.propagate(1, "java.lang.String")
|
||||
.use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class));
|
||||
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.teavm.interop.Import;
|
|||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -34,11 +35,11 @@ public abstract class LowLevelNameProvider {
|
|||
private ClassReaderSource classSource;
|
||||
|
||||
protected Set<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<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> memberFieldNames = new HashMap<>();
|
||||
|
@ -50,7 +51,6 @@ public abstract class LowLevelNameProvider {
|
|||
protected Map<ValueType, String> classInstanceNames = new HashMap<>();
|
||||
protected Map<ValueType, String> supertypeNames = new HashMap<>();
|
||||
|
||||
|
||||
public LowLevelNameProvider(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
@ -62,11 +62,10 @@ public abstract class LowLevelNameProvider {
|
|||
});
|
||||
}
|
||||
|
||||
public String forVirtualMethod(MethodReference method) {
|
||||
public String forVirtualMethod(MethodDescriptor method) {
|
||||
return virtualMethodNames.computeIfAbsent(method, k -> {
|
||||
Set<String> occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(),
|
||||
c -> new HashSet<>(Arrays.asList("parent")));
|
||||
return pickUnoccupied("virt_" + k.getName(), occupied);
|
||||
Set<String> occupied = occupiedVtableNames;
|
||||
return pickUnoccupied("virt_" + sanitize(k.getName()), occupied);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableBuilder;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
@ -776,6 +777,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
}
|
||||
|
||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||
VirtualTableBuilder builder = new VirtualTableBuilder(classes);
|
||||
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
|
||||
builder.setMethodCalledVirtually(controller::isVirtual);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
|
||||
Set<MethodReference> virtualMethods = new HashSet<>();
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
|
@ -801,7 +809,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
}
|
||||
}
|
||||
|
||||
return new VirtualTableProvider(classes, virtualMethods, controller::isVirtual);
|
||||
return virtualMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -163,7 +163,7 @@ public class WasmClassGenerator {
|
|||
ClassBinaryData itemBinaryData = binaryDataMap.get(itemType);
|
||||
|
||||
VirtualTable vtable = vtableProvider.lookup("java.lang.Object");
|
||||
int vtableSize = vtable != null ? vtable.getEntries().size() : 0;
|
||||
int vtableSize = vtable != null ? vtable.size() : 0;
|
||||
DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
|
||||
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
|
||||
|
||||
|
@ -250,7 +250,7 @@ public class WasmClassGenerator {
|
|||
int flags = 0;
|
||||
|
||||
VirtualTable vtable = vtableProvider.lookup(name);
|
||||
int vtableSize = vtable != null ? vtable.getEntries().size() : 0;
|
||||
int vtableSize = vtable != null ? vtable.size() : 0;
|
||||
|
||||
DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
|
||||
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
|
||||
|
@ -376,21 +376,30 @@ public class WasmClassGenerator {
|
|||
|
||||
private void fillVirtualTable(VirtualTable vtable, DataValue array) {
|
||||
int index = 0;
|
||||
for (VirtualTableEntry vtableEntry : vtable.getEntries().values()) {
|
||||
int methodIndex;
|
||||
if (vtableEntry.getImplementor() == null) {
|
||||
methodIndex = -1;
|
||||
} else {
|
||||
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
|
||||
List<VirtualTable> tables = new ArrayList<>();
|
||||
while (vtable != null) {
|
||||
tables.add(vtable);
|
||||
vtable = vtable.getParent();
|
||||
}
|
||||
for (int i = tables.size() - 1; i >= 0; --i) {
|
||||
vtable = tables.get(i);
|
||||
for (MethodDescriptor method : vtable.getMethods()) {
|
||||
int methodIndex = -1;
|
||||
if (method != null) {
|
||||
VirtualTableEntry entry = vtable.getEntry(method);
|
||||
if (entry != null) {
|
||||
methodIndex = functions.computeIfAbsent(entry.getImplementor(), implementor -> {
|
||||
int result = functionTable.size();
|
||||
functionTable.add(names.forMethod(implementor));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
array.setInt(index++, methodIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ValueType> getRegisteredClasses() {
|
||||
return binaryDataMap.keySet();
|
||||
|
|
|
@ -160,7 +160,7 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
|
|||
fieldDep.getValue().connect(receiverNode);
|
||||
}
|
||||
}
|
||||
initClass(field.getClassName());
|
||||
touchField(instance, fieldDep, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -174,7 +174,17 @@ abstract class AbstractInstructionAnalyzer extends AbstractInstructionReader {
|
|||
valueNode.connect(fieldDep.getValue());
|
||||
}
|
||||
}
|
||||
initClass(field.getClassName());
|
||||
touchField(instance, fieldDep, field);
|
||||
}
|
||||
|
||||
private void touchField(VariableReader instance, FieldDependency fieldDep, FieldReference field) {
|
||||
if (instance == null) {
|
||||
if (fieldDep.getField() != null) {
|
||||
initClass(fieldDep.getField().getOwnerName());
|
||||
}
|
||||
} else {
|
||||
getAnalyzer().linkClass(field.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -78,11 +78,11 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
|||
}
|
||||
|
||||
if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
|
||||
processAsyncMethod(methodDep);
|
||||
processAsyncMethod();
|
||||
}
|
||||
}
|
||||
|
||||
private void processAsyncMethod(MethodDependency methodDep) {
|
||||
private void processAsyncMethod() {
|
||||
if (asyncSupported) {
|
||||
linkMethod(MONITOR_ENTER_METHOD).use();
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
|
|||
if (fullType instanceof ValueType.Object) {
|
||||
String prefix = key.substring(0, degree) + "L";
|
||||
String className = ((ValueType.Object) fullType).getClassName();
|
||||
ClassReader cls = getClassSource().get(key);
|
||||
ClassReader cls = getClassSource().get(className);
|
||||
if (cls != null) {
|
||||
if (cls.getParent() != null) {
|
||||
node.connect(getSubtypeNode(prefix + cls.getParent().replace('.', '/') + ";"));
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
|
@ -24,6 +25,7 @@ import org.teavm.model.FieldReference;
|
|||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
|
@ -67,11 +69,37 @@ public class Linker {
|
|||
for (Instruction insn : block) {
|
||||
if (insn instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
||||
MethodReference calledRef = invoke.getMethod();
|
||||
if (invoke.getType() == InvocationType.SPECIAL) {
|
||||
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod());
|
||||
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef);
|
||||
if (linkedMethod != null) {
|
||||
invoke.setMethod(linkedMethod.getReference());
|
||||
}
|
||||
} else if (invoke.getType() == InvocationType.VIRTUAL) {
|
||||
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(calledRef);
|
||||
if (linkedMethod == null || linkedMethod.isMissing()) {
|
||||
continue;
|
||||
}
|
||||
calledRef = linkedMethod.getReference();
|
||||
ClassReader cls = dependency.getClassSource().get(calledRef.getClassName());
|
||||
boolean isFinal = false;
|
||||
if (cls != null) {
|
||||
if (cls.hasModifier(ElementModifier.FINAL)) {
|
||||
isFinal = true;
|
||||
} else {
|
||||
MethodReader calledMethod = cls.getMethod(calledRef.getDescriptor());
|
||||
if (calledMethod != null) {
|
||||
if (calledMethod.hasModifier(ElementModifier.FINAL)
|
||||
|| calledMethod.getLevel() == AccessLevel.PRIVATE) {
|
||||
isFinal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isFinal) {
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setMethod(calledRef);
|
||||
}
|
||||
}
|
||||
} else if (insn instanceof GetFieldInstruction) {
|
||||
GetFieldInstruction getField = (GetFieldInstruction) insn;
|
||||
|
|
|
@ -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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
|
||||
public class VirtualTable {
|
||||
private String className;
|
||||
Map<MethodDescriptor, VirtualTableEntry> entries = new LinkedHashMap<>();
|
||||
private Map<MethodDescriptor, VirtualTableEntry> readonlyEntries;
|
||||
private VirtualTable parent;
|
||||
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.parent = parent;
|
||||
this.methods = methods;
|
||||
this.methodSet = methodSet;
|
||||
this.entryMap = entryMap;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public Map<MethodDescriptor, VirtualTableEntry> getEntries() {
|
||||
if (readonlyEntries == null) {
|
||||
readonlyEntries = Collections.unmodifiableMap(entries);
|
||||
public VirtualTable getParent() {
|
||||
return parent;
|
||||
}
|
||||
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;
|
||||
|
||||
public class VirtualTableEntry {
|
||||
private VirtualTable virtualTable;
|
||||
private MethodDescriptor method;
|
||||
MethodReference implementor;
|
||||
private int index;
|
||||
|
||||
VirtualTableEntry(VirtualTable virtualTable, MethodDescriptor method, MethodReference implementor, int index) {
|
||||
this.virtualTable = virtualTable;
|
||||
VirtualTableEntry(MethodDescriptor method, MethodReference implementor, int index) {
|
||||
this.method = method;
|
||||
this.implementor = implementor;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public VirtualTable getVirtualTable() {
|
||||
return virtualTable;
|
||||
}
|
||||
|
||||
public MethodDescriptor getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
|
|
@ -15,116 +15,25 @@
|
|||
*/
|
||||
package org.teavm.model.classes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class VirtualTableProvider {
|
||||
private ClassReaderSource classSource;
|
||||
private Map<String, Set<MethodDescriptor>> virtualMethodMap = new HashMap<>();
|
||||
private Map<String, VirtualTable> virtualTables = new LinkedHashMap<>();
|
||||
private InterfaceToClassMapping interfaceMapping;
|
||||
Map<String, VirtualTable> virtualTables = new LinkedHashMap<>();
|
||||
|
||||
public VirtualTableProvider(ListableClassReaderSource classSource, Set<MethodReference> virtualMethods,
|
||||
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);
|
||||
}
|
||||
}
|
||||
VirtualTableProvider() {
|
||||
}
|
||||
|
||||
public VirtualTableEntry lookup(MethodReference method) {
|
||||
VirtualTable vtable = virtualTables.get(interfaceMapping.mapClass(method.getClassName()));
|
||||
VirtualTable vtable = virtualTables.get(method.getClassName());
|
||||
if (vtable == null) {
|
||||
return null;
|
||||
}
|
||||
return vtable.getEntries().get(method.getDescriptor());
|
||||
return vtable.getEntry(method.getDescriptor());
|
||||
}
|
||||
|
||||
public VirtualTable lookup(String className) {
|
||||
return virtualTables.get(interfaceMapping.mapClass(className));
|
||||
return virtualTables.get(className);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -908,6 +908,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
public ClassInitializerInfo getClassInitializerInfo() {
|
||||
return classInitializerInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeaVMOptimizationLevel getOptimizationLevel() {
|
||||
return optimizationLevel;
|
||||
}
|
||||
};
|
||||
|
||||
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
||||
|
@ -954,12 +959,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
}
|
||||
return program;
|
||||
};
|
||||
|
||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||
MethodDependencyInfo methodDep = dependencyAnalyzer.getMethod(method.getReference());
|
||||
if (methodDep == null) {
|
||||
cls.removeMethod(method);
|
||||
} else if (!methodDep.isUsed()) {
|
||||
method.getModifiers().add(ElementModifier.ABSTRACT);
|
||||
method.setProgram(null);
|
||||
} else {
|
||||
MethodReader methodReader = classReader.getMethod(method.getDescriptor());
|
||||
if (methodReader != null && methodReader.getProgram() != null) {
|
||||
|
|
|
@ -44,6 +44,8 @@ public interface TeaVMTargetController {
|
|||
|
||||
ServiceRepository getServices();
|
||||
|
||||
TeaVMOptimizationLevel getOptimizationLevel();
|
||||
|
||||
boolean isFriendlyToDebugger();
|
||||
|
||||
Map<? extends String, ? extends TeaVMEntryPoint> getEntryPoints();
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
#include "runtime.h"
|
||||
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#ifndef __USE_XOPEN
|
||||
#define __USE_XOPEN
|
||||
#endif
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <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>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-metaprogramming-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-metaprogramming-impl</artifactId>
|
||||
|
@ -125,6 +131,13 @@
|
|||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
<exclude>com.fasterxml.jackson.core:jackson-annotations</exclude>
|
||||
<exclude>org.mozilla:rhino</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
|
@ -132,6 +145,18 @@
|
|||
</transformer>
|
||||
</transformers>
|
||||
<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>
|
||||
<pattern>org.apache.commons</pattern>
|
||||
<shadedPattern>org.teavm.apachecommons</shadedPattern>
|
||||
|
|
|
@ -132,7 +132,7 @@ public final class TeaVMRunner {
|
|||
.withLongOpt("min-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Minimum heap size in bytes (for C and WebAssembly)")
|
||||
.withDescription("Minimum heap size in megabytes (for C and WebAssembly)")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("max-toplevel-names")
|
||||
|
@ -323,7 +323,7 @@ public final class TeaVMRunner {
|
|||
printUsage();
|
||||
return;
|
||||
}
|
||||
tool.setMinHeapSize(size);
|
||||
tool.setMinHeapSize(size * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user