Wasm: generate nested DIEs for namespaces/classes/methods

This commit is contained in:
Alexey Andreev 2022-11-20 16:12:00 +01:00
parent 6222241651
commit 8fac3237ba
6 changed files with 246 additions and 39 deletions

View File

@ -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<Collection<? extends WasmCustomSection>> buildDwarf(DwarfGenerator generator) {
private Supplier<Collection<? extends WasmCustomSection>> 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<MethodHolder> 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;
}

View File

@ -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;

View File

@ -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<String, Subprogram> subprogramsByFunctionName = new HashMap<>();
final List<Subprogram> 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<String, Namespace> namespaces = new LinkedHashMap<>();
final Map<String, ClassType> 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<MethodDescriptor, Subprogram> 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();
}
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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<String, Integer> 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);