From 8fac3237ba51499be6ddd35b8fb94da0aabfccef Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 20 Nov 2022 16:12:00 +0100 Subject: [PATCH] Wasm: generate nested DIEs for namespaces/classes/methods --- .../org/teavm/backend/wasm/WasmTarget.java | 22 +- .../backend/wasm/dwarf/DwarfConstants.java | 5 + .../wasm/generate/DwarfClassGenerator.java | 188 ++++++++++++++++++ .../backend/wasm/generate/DwarfGenerator.java | 24 ++- .../wasm/generate/WasmClassGenerator.java | 10 +- .../wasm/render/WasmBinaryRenderer.java | 36 +--- 6 files changed, 246 insertions(+), 39 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 0a85d5c71..85496b476 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -40,6 +40,7 @@ import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames; import org.teavm.backend.lowlevel.transform.CoroutineTransformation; import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmDependencyListener; @@ -445,9 +446,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { BinaryWriter binaryWriter = new BinaryWriter(256); var names = new NameProviderWithSpecialNames(new WasmNameProvider(), controller.getUnprocessedClassSource()); var metadataRequirements = new ClassMetadataRequirements(controller.getDependencyInfo()); + var dwarfClassGen = debugging ? new DwarfClassGenerator() : null; var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements, - controller.getClassInitializerInfo(), characteristics); + controller.getClassInitializerInfo(), characteristics, dwarfClassGen); Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false); var stringPool = classGenerator.getStringPool(); @@ -501,7 +503,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter, asyncMethods::contains); - generateMethods(classes, context, generator, classGenerator, binaryWriter, module); + generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen); new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module); exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames())); generateIsSupertypeFunctions(tagRegistry, module, classGenerator); @@ -545,8 +547,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } var writer = new WasmBinaryWriter(); - var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator); - renderer.render(module, buildDwarf(dwarfGenerator)); + var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen); + renderer.render(module, buildDwarf(dwarfGenerator, dwarfClassGen)); try (OutputStream output = buildTarget.createResource(outputName)) { output.write(writer.getData()); @@ -565,11 +567,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } - private Supplier> buildDwarf(DwarfGenerator generator) { + private Supplier> buildDwarf(DwarfGenerator generator, + DwarfClassGenerator classGen) { if (generator == null) { return null; } return () -> { + classGen.write(generator.getInfoWriter(), generator.strings); generator.end(); return generator.createSections(); }; @@ -710,7 +714,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void generateMethods(ListableClassHolderSource classes, WasmGenerationContext context, - WasmGenerator generator, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmModule module) { + WasmGenerator generator, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmModule module, + DwarfClassGenerator dwarfClassGen) { List methods = new ArrayList<>(); for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); @@ -772,6 +777,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } else { generateStub(context.names, module, method, implementor); } + if (dwarfClassGen != null) { + var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); + var dwarfSubprogram = dwarfClass.getSubprogram(method.getDescriptor()); + dwarfClassGen.registerSubprogram(context.names.forMethod(method.getReference()), dwarfSubprogram); + } if (controller.wasCancelled()) { return; } diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java index b3d951b75..48f64499f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java @@ -20,15 +20,20 @@ public final class DwarfConstants { public static final int DW_UT_COMPILE = 0x01; + public static final int DW_TAG_CLASS_TYPE = 0x02; public static final int DW_TAG_COMPILE_UNIT = 0x11; public static final int DW_TAG_SUBPROGRAM = 0x2E; + public static final int DW_TAG_NAMESPACE = 0x39; public static final int DW_AT_NAME = 0x03; public static final int DW_AT_STMT_LIST = 0x10; public static final int DW_AT_LOW_PC = 0x11; public static final int DW_AT_HIGH_PC = 0x12; + public static final int DW_AT_LANGUAGE = 0x13; public static final int DW_AT_PRODUCER = 0x25; + public static final int DW_LANG_JAVA = 0x0b; + public static final int DW_CHILDREN_YES = 1; public static final int DW_CHILDREN_NO = 0; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java new file mode 100644 index 000000000..f788713a7 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java @@ -0,0 +1,188 @@ +/* + * Copyright 2022 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.backend.wasm.generate; + +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_HIGH_PC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_LOW_PC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_NAME; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_ADDR; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_CLASS_TYPE; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_NAMESPACE; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_SUBPROGRAM; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; +import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; +import org.teavm.model.MethodDescriptor; + +public class DwarfClassGenerator { + final Namespace root = new Namespace(null); + final Map subprogramsByFunctionName = new HashMap<>(); + final List rootSubprograms = new ArrayList<>(); + private DwarfInfoWriter infoWriter; + private DwarfStrings strings; + private DwarfAbbreviation nsAbbrev; + private DwarfAbbreviation classTypeAbbrev; + private DwarfAbbreviation methodAbbrev; + + public ClassType getClass(String fullName) { + var index = 0; + var ns = root; + while (true) { + var next = fullName.indexOf('.', index); + if (next < 0) { + break; + } + ns = ns.getNamespace(fullName.substring(index, next)); + index = next + 1; + } + return ns.getClass(fullName.substring(index)); + } + + public void registerSubprogram(String functionName, Subprogram subprogram) { + subprogramsByFunctionName.put(functionName, subprogram); + } + + public Subprogram getSubprogram(String functionName) { + return subprogramsByFunctionName.get(functionName); + } + + public Subprogram createSubprogram(String functionName) { + var subprogram = new Subprogram(functionName); + subprogramsByFunctionName.put(functionName, subprogram); + rootSubprograms.add(subprogram); + return subprogram; + } + + public void write(DwarfInfoWriter infoWriter, DwarfStrings strings) { + this.infoWriter = infoWriter; + this.strings = strings; + root.writeChildren(); + for (var subprogram : rootSubprograms) { + subprogram.write(); + } + this.infoWriter = null; + this.strings = null; + methodAbbrev = null; + } + + private DwarfAbbreviation getMethodAbbrev() { + if (methodAbbrev == null) { + methodAbbrev = infoWriter.abbreviation(DW_TAG_SUBPROGRAM, true, data -> { + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + data.writeLEB(DW_AT_LOW_PC).writeLEB(DW_FORM_ADDR); + data.writeLEB(DW_AT_HIGH_PC).writeLEB(DW_FORM_ADDR); + }); + } + return methodAbbrev; + } + + private DwarfAbbreviation getNsAbbrev() { + if (nsAbbrev == null) { + nsAbbrev = infoWriter.abbreviation(DW_TAG_NAMESPACE, true, data -> { + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + }); + } + return nsAbbrev; + } + + private DwarfAbbreviation getClassTypeAbbrev() { + if (classTypeAbbrev == null) { + classTypeAbbrev = infoWriter.abbreviation(DW_TAG_CLASS_TYPE, true, data -> { + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + }); + } + return classTypeAbbrev; + } + + public class Namespace { + public final String name; + final Map namespaces = new LinkedHashMap<>(); + final Map classes = new LinkedHashMap<>(); + + private Namespace(String name) { + this.name = name; + } + + private Namespace getNamespace(String name) { + return namespaces.computeIfAbsent(name, Namespace::new); + } + + private void write() { + infoWriter.tag(getNsAbbrev()); + infoWriter.writeInt(strings.stringRef(name)); + writeChildren(); + infoWriter.emptyTag(); + } + + private void writeChildren() { + for (var child : namespaces.values()) { + child.write(); + } + for (var child : classes.values()) { + child.write(); + } + } + + ClassType getClass(String name) { + return classes.computeIfAbsent(name, ClassType::new); + } + } + + public class ClassType { + public final String name; + final Map subprograms = new LinkedHashMap<>(); + + private ClassType(String name) { + this.name = name; + } + + public Subprogram getSubprogram(MethodDescriptor desc) { + return subprograms.computeIfAbsent(desc, d -> new Subprogram(d.getName())); + } + + private void write() { + infoWriter.tag(getClassTypeAbbrev()); + infoWriter.writeInt(strings.stringRef(name)); + for (var child : subprograms.values()) { + child.write(); + } + infoWriter.emptyTag(); + } + } + + public class Subprogram { + public final String name; + public int startAddress; + public int endAddress; + + private Subprogram(String name) { + this.name = name; + } + + private void write() { + infoWriter.tag(getMethodAbbrev()); + infoWriter.writeInt(strings.stringRef(name)); + infoWriter.writeInt(startAddress); + infoWriter.writeInt(endAddress); + infoWriter.emptyTag(); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java index 3f97ff8d5..5b188dbbc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java @@ -16,11 +16,21 @@ package org.teavm.backend.wasm.generate; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DWARF_VERSION; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_HIGH_PC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_LANGUAGE; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_LOW_PC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_NAME; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_PRODUCER; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_STMT_LIST; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_ADDR; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_DATA2; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_SEC_OFFSET; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LANG_JAVA; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_COMPILE_UNIT; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_UT_COMPILE; import java.util.ArrayList; import java.util.Collection; -import org.teavm.backend.wasm.dwarf.DwarfConstants; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; import org.teavm.backend.wasm.dwarf.blob.Blob; @@ -64,13 +74,15 @@ public class DwarfGenerator { private void compilationUnit() { infoWriter.tag(infoWriter.abbreviation(DW_TAG_COMPILE_UNIT, true, data -> { - data.writeLEB(DwarfConstants.DW_AT_PRODUCER).writeLEB(DwarfConstants.DW_FORM_STRP); - data.writeLEB(DwarfConstants.DW_AT_NAME).writeLEB(DwarfConstants.DW_FORM_STRP); - data.writeLEB(DwarfConstants.DW_AT_STMT_LIST).writeLEB(DwarfConstants.DW_FORM_SEC_OFFSET); - data.writeLEB(DwarfConstants.DW_AT_LOW_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); - data.writeLEB(DwarfConstants.DW_AT_HIGH_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); + data.writeLEB(DW_AT_PRODUCER).writeLEB(DW_FORM_STRP); + data.writeLEB(DW_AT_LANGUAGE).writeLEB(DW_FORM_DATA2); + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + data.writeLEB(DW_AT_STMT_LIST).writeLEB(DW_FORM_SEC_OFFSET); + data.writeLEB(DW_AT_LOW_PC).writeLEB(DW_FORM_ADDR); + data.writeLEB(DW_AT_HIGH_PC).writeLEB(DW_FORM_ADDR); })); infoWriter.writeInt(strings.stringRef("TeaVM")); + infoWriter.writeShort(DW_LANG_JAVA); infoWriter.writeInt(strings.stringRef("classes.wasm")); infoWriter.writeInt(0); infoWriter.writeInt(0); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index c20aee5d1..fe7b4c24a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -97,6 +97,7 @@ public class WasmClassGenerator { private int classCount; private ClassMetadataRequirements metadataRequirements; private ClassInitializerInfo classInitializerInfo; + private DwarfClassGenerator dwarfClassGenerator; private static final int CLASS_SIZE = 1; private static final int CLASS_FLAGS = 2; @@ -117,7 +118,8 @@ public class WasmClassGenerator { public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter, NameProvider names, ClassMetadataRequirements metadataRequirements, - ClassInitializerInfo classInitializerInfo, Characteristics characteristics) { + ClassInitializerInfo classInitializerInfo, Characteristics characteristics, + DwarfClassGenerator dwarfClassGenerator) { this.processedClassSource = processedClassSource; this.classSource = classSource; this.vtableProvider = vtableProvider; @@ -128,6 +130,7 @@ public class WasmClassGenerator { this.metadataRequirements = metadataRequirements; this.classInitializerInfo = classInitializerInfo; this.characteristics = characteristics; + this.dwarfClassGenerator = dwarfClassGenerator; } public WasmStringPool getStringPool() { @@ -173,13 +176,16 @@ public class WasmClassGenerator { binaryData.start = binaryWriter.append(binaryData.data); } else if (type instanceof ValueType.Object) { String className = ((ValueType.Object) type).getClassName(); - ClassReader cls = classSource.get(className); + var cls = classSource.get(className); if (cls != null) { calculateLayout(cls, binaryData); if (binaryData.start >= 0) { binaryData.start = binaryWriter.append(createStructure(binaryData)); } + if (dwarfClassGenerator != null) { + dwarfClassGenerator.getClass(className); + } } } else if (type instanceof ValueType.Array) { ValueType itemType = ((ValueType.Array) type).getItemType(); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index 0051a0056..9ebf5e2ad 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -15,7 +15,6 @@ */ package org.teavm.backend.wasm.render; -import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_SUBPROGRAM; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -24,9 +23,7 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; -import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; -import org.teavm.backend.wasm.dwarf.DwarfConstants; -import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; +import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmCustomSection; import org.teavm.backend.wasm.model.WasmFunction; @@ -59,16 +56,15 @@ public class WasmBinaryRenderer { private Map functionIndexes = new HashMap<>(); private boolean obfuscated; private DwarfGenerator dwarfGenerator; - private DwarfInfoWriter infoWriter; - private DwarfAbbreviation methodAbbrev; + private DwarfClassGenerator dwarfClassGen; public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, - DwarfGenerator dwarfGenerator) { + DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen) { this.output = output; this.version = version; this.obfuscated = obfuscated; this.dwarfGenerator = dwarfGenerator; - infoWriter = dwarfGenerator != null ? dwarfGenerator.getInfoWriter() : null; + this.dwarfClassGen = dwarfClassGen; } public void render(WasmModule module) { @@ -328,28 +324,18 @@ public class WasmBinaryRenderer { } code.writeByte(0x0B); - if (dwarfGenerator != null && function.getName() != null) { - infoWriter.tag(getMethodAbbrev()); - infoWriter.writeInt(dwarfGenerator.strings.stringRef(function.getName())); - infoWriter.writeInt(offset); - infoWriter.writeInt(offset + code.getPosition() - 1); - infoWriter.emptyTag(); + if (dwarfClassGen != null && function.getName() != null) { + var dwarfSubprogram = dwarfClassGen.getSubprogram(function.getName()); + if (dwarfSubprogram == null) { + dwarfSubprogram = dwarfClassGen.createSubprogram(function.getName()); + } + dwarfSubprogram.startAddress = offset; + dwarfSubprogram.endAddress = offset + code.getPosition(); } return code.getData(); } - private DwarfAbbreviation getMethodAbbrev() { - if (methodAbbrev == null) { - methodAbbrev = infoWriter.abbreviation(DW_TAG_SUBPROGRAM, true, data -> { - data.writeLEB(DwarfConstants.DW_AT_NAME).writeLEB(DwarfConstants.DW_FORM_STRP); - data.writeLEB(DwarfConstants.DW_AT_LOW_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); - data.writeLEB(DwarfConstants.DW_AT_HIGH_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); - }); - } - return methodAbbrev; - } - private void renderInitializer(WasmBinaryWriter output, int value) { output.writeByte(0x41); output.writeLEB(value);