diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 690bf44b3..06621c586 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -31,6 +32,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.analyze.CDependencyListener; import org.teavm.backend.c.generate.BufferedCodeWriter; @@ -262,10 +264,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { generateClasses(classes, classGenerator); generateSpecialFunctions(context, codeWriter); copyResource(codeWriter, "runtime-epilogue.c"); - generateMain(context, codeWriter, classes, classGenerator.getTypes()); + + List types = classGenerator.getTypes().stream() + .filter(c -> ClassGenerator.needsVirtualTable(characteristics, c)) + .collect(Collectors.toList()); + + generateArrayOfClassReferences(context, codeWriter, types); + generateMain(context, codeWriter, classes, types); try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( - buildTarget.createResource(outputName), "UTF-8"))) { + buildTarget.createResource(outputName), StandardCharsets.UTF_8))) { codeWriter.writeTo(writer); } } @@ -383,8 +391,27 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { writer.outdent().println("}"); } + private void generateArrayOfClassReferences(GenerationContext context, CodeWriter writer, + List types) { + writer.print("static JavaClass* teavm_classReferences[" + types.size() + "] = {").indent(); + boolean first = true; + for (ValueType type : types) { + if (!first) { + writer.print(", "); + } + writer.println(); + first = false; + String typeName = context.getNames().forClassInstance(type); + writer.print("(JavaClass*) &" + typeName); + } + if (!first) { + writer.println(); + } + writer.outdent().println("};"); + } + private void generateMain(GenerationContext context, CodeWriter writer, ListableClassHolderSource classes, - Set types) { + List types) { writer.println("int main(int argc, char** argv) {").indent(); writer.println("TeaVM_beforeInit();"); @@ -420,20 +447,22 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } private void generateVirtualTableHeaders(GenerationContext context, CodeWriter writer, - Set types) { + List types) { + writer.println("TeaVM_beforeClasses = (char*) teavm_classReferences[0];"); + writer.println("for (int i = 1; i < " + types.size() + "; ++i) {").indent(); + writer.println("char* c = (char*) teavm_classReferences[i];"); + writer.println("if (c < TeaVM_beforeClasses) TeaVM_beforeClasses = c;"); + writer.outdent().println("}"); + writer.println("TeaVM_beforeClasses -= 4096;"); + String classClassName = context.getNames().forClassInstance(ValueType.object("java.lang.Class")); writer.print("int32_t classHeader = PACK_CLASS(&" + classClassName + ") | "); CodeGeneratorUtil.writeValue(writer, context, RuntimeObject.GC_MARKED); writer.println(";"); - for (ValueType type : types) { - if (!ClassGenerator.needsVirtualTable(context.getCharacteristics(), type)) { - continue; - } - - String typeName = context.getNames().forClassInstance(type); - writer.println("((JavaObject*) &" + typeName + ")->header = classHeader;"); - } + writer.println("for (int i = 0; i < " + types.size() + "; ++i) {").indent(); + writer.println("teavm_classReferences[i]->parent.header = classHeader;"); + writer.outdent().println("}"); } private void generateStringPoolHeaders(GenerationContext context, CodeWriter writer) { diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTable.java b/core/src/main/java/org/teavm/model/classes/VirtualTable.java index 42e2965db..39ebc812e 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTable.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTable.java @@ -23,6 +23,7 @@ import org.teavm.model.MethodDescriptor; public class VirtualTable { private String className; Map entries = new LinkedHashMap<>(); + Map allEntries = new LinkedHashMap<>(); private Map readonlyEntries; VirtualTable(String className) { diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java index 02fbd2559..65c3e38e2 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java @@ -15,12 +15,14 @@ */ package org.teavm.model.classes; +import java.util.Collection; 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.stream.Collectors; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; @@ -40,7 +42,7 @@ public class VirtualTableProvider { interfaceMapping = new InterfaceToClassMapping(classSource); Set classNames = new HashSet<>(classSource.getClassNames()); - for (MethodReference virtualMethod : virtualMethods) { + for (MethodReference virtualMethod : resolveVirtualMethods(classSource, virtualMethods)) { String cls = interfaceMapping.mapClass(virtualMethod.getClassName()); if (cls == null) { cls = virtualMethod.getClassName(); @@ -54,6 +56,19 @@ public class VirtualTableProvider { } } + private Collection resolveVirtualMethods(ClassReaderSource classSource, + Set virtualMethods) { + return virtualMethods.stream() + .map(method -> resolveVirtualMethod(classSource, method)) + .distinct() + .collect(Collectors.toList()); + } + + private MethodReference resolveVirtualMethod(ClassReaderSource classSource, MethodReference methodReference) { + MethodReader method = classSource.resolve(methodReference); + return method != null ? method.getReference() : methodReference; + } + private void fillClass(String className) { if (virtualTables.containsKey(className)) { return; diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index f578aa4c2..d4f7c8b43 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -29,8 +29,8 @@ typedef struct JavaArray JavaArray; typedef struct JavaClass JavaClass; typedef struct JavaString JavaString; -#define PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - (char*) &TeaVM_beforeClasses) >> 3)) -#define UNPACK_CLASS(cls) ((JavaClass*) ((char*) &TeaVM_beforeClasses + ((cls) << 3))) +#define PACK_CLASS(cls) ((int32_t) ((uintptr_t) ((char*) (cls) - TeaVM_beforeClasses) >> 3)) +#define UNPACK_CLASS(cls) ((JavaClass*) (TeaVM_beforeClasses + ((cls) << 3))) #define CLASS_OF(obj) (UNPACK_CLASS(((JavaObject*) (obj))->header)) #define AS(ptr, type) ((type*) (ptr)) @@ -106,7 +106,7 @@ static int32_t gc_regionSize = INT32_C(32768); static int32_t gc_regionMaxCount = INT32_C(0); static int64_t gc_availableBytes = INT64_C(0); -static char TeaVM_beforeClasses[128] = "TEAVM"; +static char *TeaVM_beforeClasses; static double TeaVM_rand() { return rand() / ((double) RAND_MAX + 1);