From f938db798b25328823ac80e91d2cdf5995364c79 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 22 Nov 2022 17:53:12 +0100 Subject: [PATCH] Wasm: add information about parameters and local variables to DWARF --- .../org/teavm/backend/wasm/WasmTarget.java | 23 ++- .../backend/wasm/dwarf/DwarfConstants.java | 22 +- .../wasm/generate/DwarfClassGenerator.java | 191 +++++++++++++++--- .../wasm/generate/DwarfFunctionGenerator.java | 184 +++++++++++++++++ .../backend/wasm/generate/WasmGenerator.java | 4 +- .../teavm/backend/wasm/model/WasmLocal.java | 10 + .../wasm/render/WasmBinaryRenderer.java | 83 ++------ 7 files changed, 412 insertions(+), 105 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.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 c7cd5cc31..d39b83b19 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -446,7 +446,14 @@ 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 dwarfGenerator = debugging ? new DwarfGenerator() : null; + if (dwarfGenerator != null) { + dwarfGenerator.begin(); + } + var dwarfClassGen = debugging + ? new DwarfClassGenerator(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings) + : null; var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements, controller.getClassInitializerInfo(), characteristics, dwarfClassGen); @@ -496,10 +503,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { classGenerator, stringPool, obfuscated); context.addIntrinsic(exceptionHandlingIntrinsic); - var dwarfGenerator = debugging ? new DwarfGenerator() : null; - if (dwarfGenerator != null) { - dwarfGenerator.begin(); - } var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter, asyncMethods::contains); @@ -547,11 +550,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } var writer = new WasmBinaryWriter(); - if (dwarfClassGen != null) { - dwarfClassGen.write(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings); - } var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen); - renderer.render(module, buildDwarf(dwarfGenerator)); + renderer.render(module, buildDwarf(dwarfGenerator, dwarfClassGen)); try (OutputStream output = buildTarget.createResource(outputName)) { output.write(writer.getData()); @@ -570,11 +570,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.end(); return generator.createSections(); }; @@ -781,6 +783,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (dwarfClassGen != null) { var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); var dwarfSubprogram = dwarfClass.getSubprogram(method.getDescriptor()); + dwarfSubprogram.isStatic = method.hasModifier(ElementModifier.STATIC); dwarfClassGen.registerSubprogram(context.names.forMethod(method.getReference()), dwarfSubprogram); } if (controller.wasCancelled()) { 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 5de555cda..103f4d974 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 @@ -21,20 +21,36 @@ 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_FORMAL_PARAMETER = 0x05; public static final int DW_TAG_COMPILE_UNIT = 0x11; + public static final int DW_TAG_BASE_TYPE = 0x24; public static final int DW_TAG_SUBPROGRAM = 0x2E; + public static final int DW_TAG_VARIABLE = 0x34; public static final int DW_TAG_NAMESPACE = 0x39; + public static final int DW_TAG_UNSPECIFIED_TYPE = 0x3B; + public static final int DW_AT_LOCATION = 0x02; public static final int DW_AT_NAME = 0x03; + public static final int DW_AT_BYTE_SIZE = 0x0B; 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_AT_DECLARATION = 0x3c; + public static final int DW_AT_DECLARATION = 0x3C; + public static final int DW_AT_ENCODING = 0x3E; public static final int DW_AT_SPECIFICATION = 0x47; + public static final int DW_AT_TYPE = 0x49; public static final int DW_AT_LINKAGE_NAME = 0x6E; + public static final int DW_ATE_ADDRESS = 0x01; + public static final int DW_ATE_BOOLEAN = 0x02; + public static final int DW_ATE_FLOAT = 0x04; + public static final int DW_ATE_SIGNED = 0x05; + public static final int DW_ATE_UNSIGNED = 0x07; + public static final int DW_ATE_UNSIGNED_CHAR = 0x08; + public static final int DW_ATE_UTF = 0x10; + public static final int DW_LANG_JAVA = 0x0b; public static final int DW_CHILDREN_YES = 1; @@ -46,13 +62,17 @@ public final class DwarfConstants { public static final int DW_FORM_ADDR = 0x01; public static final int DW_FORM_DATA2 = 0x05; public static final int DW_FORM_DATA4 = 0x06; + public static final int DW_FORM_DATA1 = 0x0B; public static final int DW_FORM_FLAG = 0x0C; public static final int DW_FORM_STRP = 0x0E; public static final int DW_FORM_REF4 = 0x13; public static final int DW_FORM_SEC_OFFSET = 0x17; + public static final int DW_FORM_EXPRLOC = 0x18; public static final int DW_FORM_FLAG_PRESENT = 0x19; public static final int DW_FORM_LINE_STRP = 0x1F; + public static final int DW_OP_WASM_LOCATION = 0xED; + public static final int DW_LNS_COPY = 0x01; public static final int DW_LNS_ADVANCE_PC = 0x02; public static final int DW_LNS_ADVANCE_LINE = 0x03; 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 index 643d0971a..6c4bc6a86 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfClassGenerator.java @@ -15,13 +15,22 @@ */ package org.teavm.backend.wasm.generate; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_ATE_BOOLEAN; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_ATE_FLOAT; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_ATE_SIGNED; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_ATE_UTF; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_BYTE_SIZE; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_DECLARATION; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_ENCODING; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_NAME; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_DATA1; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_FLAG_PRESENT; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_BASE_TYPE; 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 static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_UNSPECIFIED_TYPE; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -31,16 +40,36 @@ import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; import org.teavm.model.MethodDescriptor; +import org.teavm.model.PrimitiveType; +import org.teavm.model.ValueType; +import org.teavm.model.util.VariableType; public class DwarfClassGenerator { + private static final ValueType objectType = ValueType.object("java.lang.Object"); final Namespace root = new Namespace(null); final Map subprogramsByFunctionName = new HashMap<>(); final List rootSubprograms = new ArrayList<>(); - private DwarfInfoWriter infoWriter; - private DwarfStrings strings; + private final DwarfInfoWriter writer; + private final DwarfStrings strings; private DwarfAbbreviation nsAbbrev; private DwarfAbbreviation classTypeAbbrev; private DwarfAbbreviation methodAbbrev; + private DwarfPlaceholder[] primitiveTypes = new DwarfPlaceholder[PrimitiveType.values().length]; + private DwarfPlaceholder unspecifiedType; + private DwarfAbbreviation baseTypeAbbrev; + private List postponedWrites = new ArrayList<>(); + + public DwarfClassGenerator(DwarfInfoWriter writer, DwarfStrings strings) { + this.writer = writer; + this.strings = strings; + } + + public void flushTypes() { + for (var postponedWrite : postponedWrites) { + postponedWrite.run(); + } + postponedWrites.clear(); + } public ClassType getClass(String fullName) { var index = 0; @@ -64,21 +93,16 @@ public class DwarfClassGenerator { return subprogramsByFunctionName.get(functionName); } - public void write(DwarfInfoWriter infoWriter, DwarfStrings strings) { - this.infoWriter = infoWriter; - this.strings = strings; + public void write() { 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 -> { + methodAbbrev = writer.abbreviation(DW_TAG_SUBPROGRAM, true, data -> { data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); data.writeLEB(DW_AT_DECLARATION).writeLEB(DW_FORM_FLAG_PRESENT); }); @@ -88,7 +112,7 @@ public class DwarfClassGenerator { private DwarfAbbreviation getNsAbbrev() { if (nsAbbrev == null) { - nsAbbrev = infoWriter.abbreviation(DW_TAG_NAMESPACE, true, data -> { + nsAbbrev = writer.abbreviation(DW_TAG_NAMESPACE, true, data -> { data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); }); } @@ -97,13 +121,127 @@ public class DwarfClassGenerator { private DwarfAbbreviation getClassTypeAbbrev() { if (classTypeAbbrev == null) { - classTypeAbbrev = infoWriter.abbreviation(DW_TAG_CLASS_TYPE, true, data -> { + classTypeAbbrev = writer.abbreviation(DW_TAG_CLASS_TYPE, true, data -> { data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); }); } return classTypeAbbrev; } + public DwarfPlaceholder getTypePtr(VariableType type) { + switch (type) { + case INT: + return getPrimitivePtr(ValueType.Primitive.INTEGER); + case LONG: + return getPrimitivePtr(ValueType.Primitive.LONG); + case FLOAT: + return getPrimitivePtr(ValueType.Primitive.FLOAT); + case DOUBLE: + return getPrimitivePtr(ValueType.Primitive.DOUBLE); + default: + return getTypePtr(objectType); + } + } + + public DwarfPlaceholder getTypePtr(ValueType type) { + if (type instanceof ValueType.Primitive) { + return getPrimitivePtr((ValueType.Primitive) type); + } else if (type instanceof ValueType.Object) { + return getClassType(((ValueType.Object) type).getClassName()); + } + return getClassType("java.lang.Object"); + } + + private DwarfPlaceholder getUnspecifiedType() { + if (unspecifiedType == null) { + unspecifiedType = writer.placeholder(4); + var abbrev = writer.abbreviation(DW_TAG_UNSPECIFIED_TYPE, false, blob -> { + blob.writeInt(DW_AT_NAME).writeInt(DW_FORM_STRP); + }); + writer.mark(unspecifiedType).tag(abbrev).writeInt(strings.stringRef("")); + } + return unspecifiedType; + } + + private DwarfPlaceholder getClassType(String name) { + return getClass(name).ptr; + } + + private DwarfPlaceholder getPrimitivePtr(ValueType.Primitive type) { + var result = primitiveTypes[type.getKind().ordinal()]; + if (result == null) { + String name; + int byteSize; + int encoding; + switch (type.getKind()) { + case BOOLEAN: + name = "boolean"; + byteSize = 1; + encoding = DW_ATE_BOOLEAN; + break; + case BYTE: + name = "byte"; + byteSize = 1; + encoding = DW_ATE_SIGNED; + break; + case SHORT: + name = "short"; + byteSize = 2; + encoding = DW_ATE_SIGNED; + break; + case CHARACTER: + name = "char"; + byteSize = 2; + encoding = DW_ATE_UTF; + break; + case INTEGER: + name = "int"; + byteSize = 4; + encoding = DW_ATE_SIGNED; + break; + case LONG: + name = "long"; + byteSize = 8; + encoding = DW_ATE_SIGNED; + break; + case FLOAT: + name = "float"; + encoding = DW_ATE_FLOAT; + byteSize = 4; + break; + case DOUBLE: + name = "double"; + encoding = DW_ATE_FLOAT; + byteSize = 8; + break; + default: + throw new IllegalArgumentException(); + } + var ptr = writer.placeholder(4); + postponedWrites.add(() -> { + writer.mark(ptr).tag(getBaseTypeAbbrev()); + writer.writeInt(strings.stringRef(name)); + writer.writeByte(byteSize); + writer.writeByte(encoding); + }); + result = ptr; + primitiveTypes[type.getKind().ordinal()] = ptr; + } + + return result; + } + + private DwarfAbbreviation getBaseTypeAbbrev() { + if (baseTypeAbbrev == null) { + baseTypeAbbrev = writer.abbreviation(DW_TAG_BASE_TYPE, false, blob -> { + blob.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + blob.writeLEB(DW_AT_BYTE_SIZE).writeLEB(DW_FORM_DATA1); + blob.writeLEB(DW_AT_ENCODING).writeLEB(DW_FORM_DATA1); + }); + } + return baseTypeAbbrev; + } + public class Namespace { public final String name; final Map namespaces = new LinkedHashMap<>(); @@ -118,10 +256,10 @@ public class DwarfClassGenerator { } private void write() { - infoWriter.tag(getNsAbbrev()); - infoWriter.writeInt(strings.stringRef(name)); + writer.tag(getNsAbbrev()); + writer.writeInt(strings.stringRef(name)); writeChildren(); - infoWriter.emptyTag(); + writer.emptyTag(); } private void writeChildren() { @@ -140,39 +278,44 @@ public class DwarfClassGenerator { public class ClassType { public final String name; + final DwarfPlaceholder ptr; final Map subprograms = new LinkedHashMap<>(); private ClassType(String name) { + ptr = writer.placeholder(4); this.name = name; } public Subprogram getSubprogram(MethodDescriptor desc) { - return subprograms.computeIfAbsent(desc, d -> new Subprogram(d.getName())); + return subprograms.computeIfAbsent(desc, d -> new Subprogram(d.getName(), desc)); } private void write() { - infoWriter.tag(getClassTypeAbbrev()); - infoWriter.writeInt(strings.stringRef(name)); + writer.mark(ptr).tag(getClassTypeAbbrev()); + writer.writeInt(strings.stringRef(name)); for (var child : subprograms.values()) { child.write(); } - infoWriter.emptyTag(); + writer.emptyTag(); } } public class Subprogram { public final String name; - public DwarfPlaceholder ref; + public boolean isStatic; + public final MethodDescriptor descriptor; + public final DwarfPlaceholder ref; - private Subprogram(String name) { + private Subprogram(String name, MethodDescriptor descriptor) { this.name = name; + this.descriptor = descriptor; + ref = writer.placeholder(4); } private void write() { - ref = infoWriter.placeholder(4); - infoWriter.mark(ref).tag(getMethodAbbrev()); - infoWriter.writeInt(strings.stringRef(name)); - infoWriter.emptyTag(); + writer.mark(ref).tag(getMethodAbbrev()); + writer.writeInt(strings.stringRef(name)); + writer.emptyTag(); } } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.java new file mode 100644 index 000000000..965f714a4 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.java @@ -0,0 +1,184 @@ +/* + * 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_LOCATION; +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_SPECIFICATION; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_AT_TYPE; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_ADDR; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_EXPRLOC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_REF4; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_OP_WASM_LOCATION; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_FORMAL_PARAMETER; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_SUBPROGRAM; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_VARIABLE; +import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; +import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.dwarf.blob.Marker; +import org.teavm.backend.wasm.model.WasmFunction; + +public class DwarfFunctionGenerator { + private DwarfClassGenerator classGen; + private DwarfGenerator generator; + private WasmFunction function; + private int offset; + private Marker endProgramMarker; + private DwarfAbbreviation methodAbbrev; + private DwarfAbbreviation functionAbbrev; + private DwarfAbbreviation parameterAbbrev; + private DwarfAbbreviation variableAbbrev; + private DwarfClassGenerator.Subprogram subprogram; + + public DwarfFunctionGenerator(DwarfClassGenerator classGen, DwarfGenerator generator) { + this.classGen = classGen; + this.generator = generator; + } + + public void begin(WasmFunction function, int offset) { + if (function.getName() == null) { + return; + } + + subprogram = classGen.getSubprogram(function.getName()); + var writer = generator.getInfoWriter(); + var strings = generator.strings; + writer.tag(subprogram != null ? getMethodAbbrev() : getFunctionAbbrev()); + if (subprogram != null) { + writer.ref(subprogram.ref, Blob::writeInt); + } else { + writer.writeInt(strings.stringRef(subprogram != null ? subprogram.name : function.getName())); + } + writer.writeInt(offset); + endProgramMarker = writer.marker(); + writer.skip(4); + + this.function = function; + this.offset = offset; + + writeLocals(); + } + + private void writeLocals() { + if (subprogram == null) { + return; + } + var descriptor = subprogram.descriptor; + if (descriptor == null) { + return; + } + + var writer = generator.getInfoWriter(); + var strings = generator.strings; + var offset = subprogram.isStatic ? 0 : 1; + int count = Math.min(function.getLocalVariables().size() - offset, descriptor.parameterCount()); + for (var i = 0; i < count; ++i) { + var local = function.getLocalVariables().get(i + offset); + if (local.getName() == null) { + continue; + } + writer.tag(getParameterAbbrev()); + writer.writeInt(strings.stringRef(local.getName())); + writer.ref(classGen.getTypePtr(descriptor.parameterType(i)), Blob::writeInt); + + var operations = new Blob(); + operations.writeByte(DW_OP_WASM_LOCATION).writeByte(0).writeLEB(i + 1); + writer.writeLEB(operations.size()); + operations.newReader(writer::write).readRemaining(); + } + + for (var i = count + offset; i < function.getLocalVariables().size(); ++i) { + var local = function.getLocalVariables().get(i); + if (local.getName() == null || local.getJavaType() == null) { + continue; + } + writer.tag(getVariableAbbrev()); + writer.writeInt(strings.stringRef(local.getName())); + writer.ref(classGen.getTypePtr(local.getJavaType()), Blob::writeInt); + + var operations = new Blob(); + operations.writeByte(DW_OP_WASM_LOCATION).writeByte(0).writeLEB(i + 1); + writer.writeLEB(operations.size()); + operations.newReader(writer::write).readRemaining(); + } + } + + public void end(int size) { + if (function == null) { + return; + } + + var writer = generator.getInfoWriter(); + if (endProgramMarker != null) { + var backup = writer.marker(); + endProgramMarker.rewind(); + writer.writeInt(offset + size); + backup.rewind(); + } + writer.emptyTag(); + classGen.flushTypes(); + subprogram = null; + endProgramMarker = null; + function = null; + } + + private DwarfAbbreviation getMethodAbbrev() { + if (methodAbbrev == null) { + methodAbbrev = generator.getInfoWriter().abbreviation(DW_TAG_SUBPROGRAM, true, data -> { + data.writeLEB(DW_AT_SPECIFICATION).writeLEB(DW_FORM_REF4); + data.writeLEB(DW_AT_LOW_PC).writeLEB(DW_FORM_ADDR); + data.writeLEB(DW_AT_HIGH_PC).writeLEB(DW_FORM_ADDR); + }); + } + return methodAbbrev; + } + + private DwarfAbbreviation getFunctionAbbrev() { + if (functionAbbrev == null) { + functionAbbrev = generator.getInfoWriter().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 functionAbbrev; + } + + private DwarfAbbreviation getParameterAbbrev() { + if (parameterAbbrev == null) { + parameterAbbrev = generator.getInfoWriter().abbreviation(DW_TAG_FORMAL_PARAMETER, false, data -> { + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + data.writeLEB(DW_AT_TYPE).writeLEB(DW_FORM_REF4); + data.writeLEB(DW_AT_LOCATION).writeLEB(DW_FORM_EXPRLOC); + }); + } + return parameterAbbrev; + } + + private DwarfAbbreviation getVariableAbbrev() { + if (variableAbbrev == null) { + variableAbbrev = generator.getInfoWriter().abbreviation(DW_TAG_VARIABLE, false, data -> { + data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP); + data.writeLEB(DW_AT_TYPE).writeLEB(DW_FORM_REF4); + data.writeLEB(DW_AT_LOCATION).writeLEB(DW_FORM_EXPRLOC); + }); + } + return variableAbbrev; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index f81eeeb15..5eed903cd 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -85,7 +85,9 @@ public class WasmGenerator { WasmType type = variable.getType() != null ? WasmGeneratorUtil.mapType(variable.getType()) : WasmType.INT32; - function.add(new WasmLocal(type, variable.getName())); + var local = new WasmLocal(type, variable.getName()); + local.setJavaType(variable.getType()); + function.add(local); } WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function, diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmLocal.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmLocal.java index 189e6eabd..3ca56b93e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmLocal.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmLocal.java @@ -16,12 +16,14 @@ package org.teavm.backend.wasm.model; import java.util.Objects; +import org.teavm.model.util.VariableType; public class WasmLocal { WasmFunction function; int index; private String name; private WasmType type; + private VariableType javaType; public WasmLocal(WasmType type, String name) { Objects.requireNonNull(type); @@ -57,4 +59,12 @@ public class WasmLocal { public int getIndex() { return index; } + + public void setJavaType(VariableType javaType) { + this.javaType = javaType; + } + + public VariableType getJavaType() { + return javaType; + } } 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 4b1a1d0fe..14dc1c406 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,14 +15,6 @@ */ package org.teavm.backend.wasm.render; -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_AT_SPECIFICATION; -import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_ADDR; -import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_REF4; -import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; -import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_SUBPROGRAM; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -31,14 +23,11 @@ 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.blob.Blob; -import org.teavm.backend.wasm.dwarf.blob.Marker; import org.teavm.backend.wasm.generate.DwarfClassGenerator; +import org.teavm.backend.wasm.generate.DwarfFunctionGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmCustomSection; import org.teavm.backend.wasm.model.WasmFunction; -import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmType; @@ -67,9 +56,7 @@ public class WasmBinaryRenderer { private Map functionIndexes = new HashMap<>(); private boolean obfuscated; private DwarfGenerator dwarfGenerator; - private DwarfClassGenerator dwarfClassGen; - private DwarfAbbreviation methodAbbrev; - private DwarfAbbreviation functionAbbrev; + private DwarfFunctionGenerator dwarfFunctionGen; public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen) { @@ -77,7 +64,7 @@ public class WasmBinaryRenderer { this.version = version; this.obfuscated = obfuscated; this.dwarfGenerator = dwarfGenerator; - this.dwarfClassGen = dwarfClassGen; + dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null; } public void render(WasmModule module) { @@ -299,35 +286,21 @@ public class WasmBinaryRenderer { private byte[] renderFunction(WasmFunction function, int offset) { var code = new WasmBinaryWriter(); - Marker endProgramMarker; - if (dwarfClassGen != null && function.getName() != null) { - var dwarfSubprogram = dwarfClassGen.getSubprogram(function.getName()); - var writer = dwarfGenerator.getInfoWriter(); - var strings = dwarfGenerator.strings; - writer.tag(dwarfSubprogram != null ? getMethodAbbrev() : getFunctionAbbrev()); - if (dwarfSubprogram != null) { - writer.ref(dwarfSubprogram.ref, Blob::writeInt); - } else { - writer.writeInt(strings.stringRef( - dwarfSubprogram != null ? dwarfSubprogram.name : function.getName())); - } - writer.writeInt(offset); - endProgramMarker = writer.marker(); - writer.skip(4); - } else { - endProgramMarker = null; + if (dwarfFunctionGen != null) { + dwarfFunctionGen.begin(function, offset); + } - List localVariables = function.getLocalVariables(); + var localVariables = function.getLocalVariables(); int parameterCount = Math.min(function.getParameters().size(), localVariables.size()); localVariables = localVariables.subList(parameterCount, localVariables.size()); if (localVariables.isEmpty()) { code.writeLEB(0); } else { - List localEntries = new ArrayList<>(); - LocalEntry currentEntry = new LocalEntry(localVariables.get(0).getType()); + var localEntries = new ArrayList(); + var currentEntry = new LocalEntry(localVariables.get(0).getType()); for (int i = 1; i < localVariables.size(); ++i) { - WasmType type = localVariables.get(i).getType(); + var type = localVariables.get(i).getType(); if (currentEntry.type == type) { currentEntry.count++; } else { @@ -338,7 +311,7 @@ public class WasmBinaryRenderer { localEntries.add(currentEntry); code.writeLEB(localEntries.size()); - for (LocalEntry entry : localEntries) { + for (var entry : localEntries) { code.writeLEB(entry.count); code.writeType(entry.type, version); } @@ -347,7 +320,7 @@ public class WasmBinaryRenderer { var importIndexes = this.functionIndexes; var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, signatureIndexes, dwarfGenerator, offset); - for (WasmExpression part : function.getBody()) { + for (var part : function.getBody()) { part.acceptVisitor(visitor); } @@ -357,13 +330,8 @@ public class WasmBinaryRenderer { code.writeByte(0x0B); - if (endProgramMarker != null) { - var dwarfWriter = dwarfGenerator.getInfoWriter(); - var backup = dwarfWriter.marker(); - endProgramMarker.rewind(); - dwarfWriter.writeInt(offset + code.getPosition()); - backup.rewind(); - dwarfWriter.emptyTag(); + if (dwarfFunctionGen != null) { + dwarfFunctionGen.end(code.getPosition()); } return code.getData(); @@ -465,27 +433,4 @@ public class WasmBinaryRenderer { output.writeBytes(data); } - - - private DwarfAbbreviation getMethodAbbrev() { - if (methodAbbrev == null) { - methodAbbrev = dwarfGenerator.getInfoWriter().abbreviation(DW_TAG_SUBPROGRAM, true, data -> { - data.writeLEB(DW_AT_SPECIFICATION).writeLEB(DW_FORM_REF4); - data.writeLEB(DW_AT_LOW_PC).writeLEB(DW_FORM_ADDR); - data.writeLEB(DW_AT_HIGH_PC).writeLEB(DW_FORM_ADDR); - }); - } - return methodAbbrev; - } - - private DwarfAbbreviation getFunctionAbbrev() { - if (functionAbbrev == null) { - functionAbbrev = dwarfGenerator.getInfoWriter().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 functionAbbrev; - } }