Wasm: add information about parameters and local variables to DWARF

This commit is contained in:
Alexey Andreev 2022-11-22 17:53:12 +01:00
parent 13cc56feb5
commit f938db798b
7 changed files with 412 additions and 105 deletions

View File

@ -446,7 +446,14 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
BinaryWriter binaryWriter = new BinaryWriter(256); BinaryWriter binaryWriter = new BinaryWriter(256);
var names = new NameProviderWithSpecialNames(new WasmNameProvider(), controller.getUnprocessedClassSource()); var names = new NameProviderWithSpecialNames(new WasmNameProvider(), controller.getUnprocessedClassSource());
var metadataRequirements = new ClassMetadataRequirements(controller.getDependencyInfo()); 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(), var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements, vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements,
controller.getClassInitializerInfo(), characteristics, dwarfClassGen); controller.getClassInitializerInfo(), characteristics, dwarfClassGen);
@ -496,10 +503,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
classGenerator, stringPool, obfuscated); classGenerator, stringPool, obfuscated);
context.addIntrinsic(exceptionHandlingIntrinsic); context.addIntrinsic(exceptionHandlingIntrinsic);
var dwarfGenerator = debugging ? new DwarfGenerator() : null;
if (dwarfGenerator != null) {
dwarfGenerator.begin();
}
var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter, var generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter,
asyncMethods::contains); asyncMethods::contains);
@ -547,11 +550,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
var writer = new WasmBinaryWriter(); var writer = new WasmBinaryWriter();
if (dwarfClassGen != null) {
dwarfClassGen.write(dwarfGenerator.getInfoWriter(), dwarfGenerator.strings);
}
var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen); 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)) { try (OutputStream output = buildTarget.createResource(outputName)) {
output.write(writer.getData()); output.write(writer.getData());
@ -570,11 +570,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) { if (generator == null) {
return null; return null;
} }
return () -> { return () -> {
classGen.write();
generator.end(); generator.end();
return generator.createSections(); return generator.createSections();
}; };
@ -781,6 +783,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (dwarfClassGen != null) { if (dwarfClassGen != null) {
var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); var dwarfClass = dwarfClassGen.getClass(method.getOwnerName());
var dwarfSubprogram = dwarfClass.getSubprogram(method.getDescriptor()); var dwarfSubprogram = dwarfClass.getSubprogram(method.getDescriptor());
dwarfSubprogram.isStatic = method.hasModifier(ElementModifier.STATIC);
dwarfClassGen.registerSubprogram(context.names.forMethod(method.getReference()), dwarfSubprogram); dwarfClassGen.registerSubprogram(context.names.forMethod(method.getReference()), dwarfSubprogram);
} }
if (controller.wasCancelled()) { if (controller.wasCancelled()) {

View File

@ -21,20 +21,36 @@ public final class DwarfConstants {
public static final int DW_UT_COMPILE = 0x01; public static final int DW_UT_COMPILE = 0x01;
public static final int DW_TAG_CLASS_TYPE = 0x02; 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_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_SUBPROGRAM = 0x2E;
public static final int DW_TAG_VARIABLE = 0x34;
public static final int DW_TAG_NAMESPACE = 0x39; 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_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_STMT_LIST = 0x10;
public static final int DW_AT_LOW_PC = 0x11; public static final int DW_AT_LOW_PC = 0x11;
public static final int DW_AT_HIGH_PC = 0x12; public static final int DW_AT_HIGH_PC = 0x12;
public static final int DW_AT_LANGUAGE = 0x13; public static final int DW_AT_LANGUAGE = 0x13;
public static final int DW_AT_PRODUCER = 0x25; 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_SPECIFICATION = 0x47;
public static final int DW_AT_TYPE = 0x49;
public static final int DW_AT_LINKAGE_NAME = 0x6E; 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_LANG_JAVA = 0x0b;
public static final int DW_CHILDREN_YES = 1; 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_ADDR = 0x01;
public static final int DW_FORM_DATA2 = 0x05; public static final int DW_FORM_DATA2 = 0x05;
public static final int DW_FORM_DATA4 = 0x06; 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_FLAG = 0x0C;
public static final int DW_FORM_STRP = 0x0E; public static final int DW_FORM_STRP = 0x0E;
public static final int DW_FORM_REF4 = 0x13; public static final int DW_FORM_REF4 = 0x13;
public static final int DW_FORM_SEC_OFFSET = 0x17; 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_FLAG_PRESENT = 0x19;
public static final int DW_FORM_LINE_STRP = 0x1F; 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_COPY = 0x01;
public static final int DW_LNS_ADVANCE_PC = 0x02; public static final int DW_LNS_ADVANCE_PC = 0x02;
public static final int DW_LNS_ADVANCE_LINE = 0x03; public static final int DW_LNS_ADVANCE_LINE = 0x03;

View File

@ -15,13 +15,22 @@
*/ */
package org.teavm.backend.wasm.generate; 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_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_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_FLAG_PRESENT;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_STRP; 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_CLASS_TYPE;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_NAMESPACE; 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_SUBPROGRAM;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_UNSPECIFIED_TYPE;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; 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.DwarfInfoWriter;
import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.PrimitiveType;
import org.teavm.model.ValueType;
import org.teavm.model.util.VariableType;
public class DwarfClassGenerator { public class DwarfClassGenerator {
private static final ValueType objectType = ValueType.object("java.lang.Object");
final Namespace root = new Namespace(null); final Namespace root = new Namespace(null);
final Map<String, Subprogram> subprogramsByFunctionName = new HashMap<>(); final Map<String, Subprogram> subprogramsByFunctionName = new HashMap<>();
final List<Subprogram> rootSubprograms = new ArrayList<>(); final List<Subprogram> rootSubprograms = new ArrayList<>();
private DwarfInfoWriter infoWriter; private final DwarfInfoWriter writer;
private DwarfStrings strings; private final DwarfStrings strings;
private DwarfAbbreviation nsAbbrev; private DwarfAbbreviation nsAbbrev;
private DwarfAbbreviation classTypeAbbrev; private DwarfAbbreviation classTypeAbbrev;
private DwarfAbbreviation methodAbbrev; private DwarfAbbreviation methodAbbrev;
private DwarfPlaceholder[] primitiveTypes = new DwarfPlaceholder[PrimitiveType.values().length];
private DwarfPlaceholder unspecifiedType;
private DwarfAbbreviation baseTypeAbbrev;
private List<Runnable> 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) { public ClassType getClass(String fullName) {
var index = 0; var index = 0;
@ -64,21 +93,16 @@ public class DwarfClassGenerator {
return subprogramsByFunctionName.get(functionName); return subprogramsByFunctionName.get(functionName);
} }
public void write(DwarfInfoWriter infoWriter, DwarfStrings strings) { public void write() {
this.infoWriter = infoWriter;
this.strings = strings;
root.writeChildren(); root.writeChildren();
for (var subprogram : rootSubprograms) { for (var subprogram : rootSubprograms) {
subprogram.write(); subprogram.write();
} }
this.infoWriter = null;
this.strings = null;
methodAbbrev = null;
} }
private DwarfAbbreviation getMethodAbbrev() { private DwarfAbbreviation getMethodAbbrev() {
if (methodAbbrev == null) { 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_NAME).writeLEB(DW_FORM_STRP);
data.writeLEB(DW_AT_DECLARATION).writeLEB(DW_FORM_FLAG_PRESENT); data.writeLEB(DW_AT_DECLARATION).writeLEB(DW_FORM_FLAG_PRESENT);
}); });
@ -88,7 +112,7 @@ public class DwarfClassGenerator {
private DwarfAbbreviation getNsAbbrev() { private DwarfAbbreviation getNsAbbrev() {
if (nsAbbrev == null) { 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); data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP);
}); });
} }
@ -97,13 +121,127 @@ public class DwarfClassGenerator {
private DwarfAbbreviation getClassTypeAbbrev() { private DwarfAbbreviation getClassTypeAbbrev() {
if (classTypeAbbrev == null) { 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); data.writeLEB(DW_AT_NAME).writeLEB(DW_FORM_STRP);
}); });
} }
return classTypeAbbrev; 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("<unspecified>"));
}
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 class Namespace {
public final String name; public final String name;
final Map<String, Namespace> namespaces = new LinkedHashMap<>(); final Map<String, Namespace> namespaces = new LinkedHashMap<>();
@ -118,10 +256,10 @@ public class DwarfClassGenerator {
} }
private void write() { private void write() {
infoWriter.tag(getNsAbbrev()); writer.tag(getNsAbbrev());
infoWriter.writeInt(strings.stringRef(name)); writer.writeInt(strings.stringRef(name));
writeChildren(); writeChildren();
infoWriter.emptyTag(); writer.emptyTag();
} }
private void writeChildren() { private void writeChildren() {
@ -140,39 +278,44 @@ public class DwarfClassGenerator {
public class ClassType { public class ClassType {
public final String name; public final String name;
final DwarfPlaceholder ptr;
final Map<MethodDescriptor, Subprogram> subprograms = new LinkedHashMap<>(); final Map<MethodDescriptor, Subprogram> subprograms = new LinkedHashMap<>();
private ClassType(String name) { private ClassType(String name) {
ptr = writer.placeholder(4);
this.name = name; this.name = name;
} }
public Subprogram getSubprogram(MethodDescriptor desc) { 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() { private void write() {
infoWriter.tag(getClassTypeAbbrev()); writer.mark(ptr).tag(getClassTypeAbbrev());
infoWriter.writeInt(strings.stringRef(name)); writer.writeInt(strings.stringRef(name));
for (var child : subprograms.values()) { for (var child : subprograms.values()) {
child.write(); child.write();
} }
infoWriter.emptyTag(); writer.emptyTag();
} }
} }
public class Subprogram { public class Subprogram {
public final String name; 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.name = name;
this.descriptor = descriptor;
ref = writer.placeholder(4);
} }
private void write() { private void write() {
ref = infoWriter.placeholder(4); writer.mark(ref).tag(getMethodAbbrev());
infoWriter.mark(ref).tag(getMethodAbbrev()); writer.writeInt(strings.stringRef(name));
infoWriter.writeInt(strings.stringRef(name)); writer.emptyTag();
infoWriter.emptyTag();
} }
} }
} }

View File

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

View File

@ -85,7 +85,9 @@ public class WasmGenerator {
WasmType type = variable.getType() != null WasmType type = variable.getType() != null
? WasmGeneratorUtil.mapType(variable.getType()) ? WasmGeneratorUtil.mapType(variable.getType())
: WasmType.INT32; : 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, WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,

View File

@ -16,12 +16,14 @@
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
import java.util.Objects; import java.util.Objects;
import org.teavm.model.util.VariableType;
public class WasmLocal { public class WasmLocal {
WasmFunction function; WasmFunction function;
int index; int index;
private String name; private String name;
private WasmType type; private WasmType type;
private VariableType javaType;
public WasmLocal(WasmType type, String name) { public WasmLocal(WasmType type, String name) {
Objects.requireNonNull(type); Objects.requireNonNull(type);
@ -57,4 +59,12 @@ public class WasmLocal {
public int getIndex() { public int getIndex() {
return index; return index;
} }
public void setJavaType(VariableType javaType) {
this.javaType = javaType;
}
public VariableType getJavaType() {
return javaType;
}
} }

View File

@ -15,14 +15,6 @@
*/ */
package org.teavm.backend.wasm.render; 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -31,14 +23,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; 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.DwarfClassGenerator;
import org.teavm.backend.wasm.generate.DwarfFunctionGenerator;
import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator;
import org.teavm.backend.wasm.model.WasmCustomSection; import org.teavm.backend.wasm.model.WasmCustomSection;
import org.teavm.backend.wasm.model.WasmFunction; 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.WasmMemorySegment;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -67,9 +56,7 @@ public class WasmBinaryRenderer {
private Map<String, Integer> functionIndexes = new HashMap<>(); private Map<String, Integer> functionIndexes = new HashMap<>();
private boolean obfuscated; private boolean obfuscated;
private DwarfGenerator dwarfGenerator; private DwarfGenerator dwarfGenerator;
private DwarfClassGenerator dwarfClassGen; private DwarfFunctionGenerator dwarfFunctionGen;
private DwarfAbbreviation methodAbbrev;
private DwarfAbbreviation functionAbbrev;
public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated,
DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen) { DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen) {
@ -77,7 +64,7 @@ public class WasmBinaryRenderer {
this.version = version; this.version = version;
this.obfuscated = obfuscated; this.obfuscated = obfuscated;
this.dwarfGenerator = dwarfGenerator; this.dwarfGenerator = dwarfGenerator;
this.dwarfClassGen = dwarfClassGen; dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null;
} }
public void render(WasmModule module) { public void render(WasmModule module) {
@ -299,35 +286,21 @@ public class WasmBinaryRenderer {
private byte[] renderFunction(WasmFunction function, int offset) { private byte[] renderFunction(WasmFunction function, int offset) {
var code = new WasmBinaryWriter(); var code = new WasmBinaryWriter();
Marker endProgramMarker; if (dwarfFunctionGen != null) {
if (dwarfClassGen != null && function.getName() != null) { dwarfFunctionGen.begin(function, offset);
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;
} }
List<WasmLocal> localVariables = function.getLocalVariables(); var localVariables = function.getLocalVariables();
int parameterCount = Math.min(function.getParameters().size(), localVariables.size()); int parameterCount = Math.min(function.getParameters().size(), localVariables.size());
localVariables = localVariables.subList(parameterCount, localVariables.size()); localVariables = localVariables.subList(parameterCount, localVariables.size());
if (localVariables.isEmpty()) { if (localVariables.isEmpty()) {
code.writeLEB(0); code.writeLEB(0);
} else { } else {
List<LocalEntry> localEntries = new ArrayList<>(); var localEntries = new ArrayList<LocalEntry>();
LocalEntry currentEntry = new LocalEntry(localVariables.get(0).getType()); var currentEntry = new LocalEntry(localVariables.get(0).getType());
for (int i = 1; i < localVariables.size(); ++i) { for (int i = 1; i < localVariables.size(); ++i) {
WasmType type = localVariables.get(i).getType(); var type = localVariables.get(i).getType();
if (currentEntry.type == type) { if (currentEntry.type == type) {
currentEntry.count++; currentEntry.count++;
} else { } else {
@ -338,7 +311,7 @@ public class WasmBinaryRenderer {
localEntries.add(currentEntry); localEntries.add(currentEntry);
code.writeLEB(localEntries.size()); code.writeLEB(localEntries.size());
for (LocalEntry entry : localEntries) { for (var entry : localEntries) {
code.writeLEB(entry.count); code.writeLEB(entry.count);
code.writeType(entry.type, version); code.writeType(entry.type, version);
} }
@ -347,7 +320,7 @@ public class WasmBinaryRenderer {
var importIndexes = this.functionIndexes; var importIndexes = this.functionIndexes;
var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes,
signatureIndexes, dwarfGenerator, offset); signatureIndexes, dwarfGenerator, offset);
for (WasmExpression part : function.getBody()) { for (var part : function.getBody()) {
part.acceptVisitor(visitor); part.acceptVisitor(visitor);
} }
@ -357,13 +330,8 @@ public class WasmBinaryRenderer {
code.writeByte(0x0B); code.writeByte(0x0B);
if (endProgramMarker != null) { if (dwarfFunctionGen != null) {
var dwarfWriter = dwarfGenerator.getInfoWriter(); dwarfFunctionGen.end(code.getPosition());
var backup = dwarfWriter.marker();
endProgramMarker.rewind();
dwarfWriter.writeInt(offset + code.getPosition());
backup.rewind();
dwarfWriter.emptyTag();
} }
return code.getData(); return code.getData();
@ -465,27 +433,4 @@ public class WasmBinaryRenderer {
output.writeBytes(data); 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;
}
} }