From 56929b2085d19b8079dce538e63181061d2e06aa Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 24 Nov 2022 17:23:32 +0100 Subject: [PATCH] Wasm: generate debugging information for own debugger --- .../org/teavm/backend/wasm/WasmTarget.java | 28 +++-- .../{dwarf => }/blob/BinaryDataConsumer.java | 2 +- .../backend/wasm/{dwarf => }/blob/Blob.java | 2 +- .../wasm/{dwarf => }/blob/BlobReader.java | 2 +- .../backend/wasm/{dwarf => }/blob/Marker.java | 2 +- .../backend/wasm/debug/DebugClasses.java | 20 ++++ .../wasm/debug/DebugClassesBuilder.java | 52 +++++++++ .../backend/wasm/debug/DebugConstants.java | 28 +++++ .../teavm/backend/wasm/debug/DebugFiles.java | 20 ++++ .../backend/wasm/debug/DebugFilesBuilder.java | 90 +++++++++++++++ .../backend/wasm/debug/DebugInfoBuilder.java | 80 +++++++++++++ .../teavm/backend/wasm/debug/DebugLines.java | 28 +++++ .../backend/wasm/debug/DebugLinesBuilder.java | 109 ++++++++++++++++++ .../backend/wasm/debug/DebugMethods.java | 22 ++++ .../wasm/debug/DebugMethodsBuilder.java | 43 +++++++ .../backend/wasm/debug/DebugPackages.java | 20 ++++ .../wasm/debug/DebugPackagesBuilder.java | 69 +++++++++++ .../wasm/debug/DebugSectionBuilder.java | 35 ++++++ .../backend/wasm/debug/DebugStrings.java | 20 ++++ .../wasm/debug/DebugStringsBuilder.java | 36 ++++++ .../backend/wasm/dwarf/DwarfAbbreviation.java | 2 +- .../backend/wasm/dwarf/DwarfInfoWriter.java | 6 +- .../backend/wasm/dwarf/DwarfPlaceholder.java | 2 +- .../wasm/dwarf/DwarfPlaceholderWriter.java | 2 +- .../wasm/generate/DwarfClassGenerator.java | 2 +- .../wasm/generate/DwarfFunctionGenerator.java | 4 +- .../backend/wasm/generate/DwarfGenerator.java | 4 +- .../wasm/generate/DwarfLinesGenerator.java | 4 +- .../backend/wasm/generate/DwarfStrings.java | 2 +- .../backend/wasm/generate/WasmGenerator.java | 1 + .../backend/wasm/model/WasmFunction.java | 10 ++ .../wasm/render/WasmBinaryRenderer.java | 14 ++- .../render/WasmBinaryRenderingVisitor.java | 46 +++++++- 33 files changed, 773 insertions(+), 34 deletions(-) rename core/src/main/java/org/teavm/backend/wasm/{dwarf => }/blob/BinaryDataConsumer.java (94%) rename core/src/main/java/org/teavm/backend/wasm/{dwarf => }/blob/Blob.java (99%) rename core/src/main/java/org/teavm/backend/wasm/{dwarf => }/blob/BlobReader.java (97%) rename core/src/main/java/org/teavm/backend/wasm/{dwarf => }/blob/Marker.java (96%) create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugClasses.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugClassesBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugConstants.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugFiles.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugFilesBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugInfoBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugLines.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugLinesBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugMethods.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugMethodsBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugPackages.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugPackagesBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugSectionBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugStrings.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/DebugStringsBuilder.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 d39b83b19..a400340b4 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.debug.DebugInfoBuilder; import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator; @@ -550,8 +551,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } var writer = new WasmBinaryWriter(); - var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen); - renderer.render(module, buildDwarf(dwarfGenerator, dwarfClassGen)); + var debugBuilder = debugging ? new DebugInfoBuilder() : null; + var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen, + debugBuilder.lines()); + renderer.render(module, buildDebug(dwarfGenerator, dwarfClassGen, debugBuilder)); try (OutputStream output = buildTarget.createResource(outputName)) { output.write(writer.getData()); @@ -570,15 +573,24 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } - private Supplier> buildDwarf(DwarfGenerator generator, - DwarfClassGenerator classGen) { - if (generator == null) { + private Supplier> buildDebug(DwarfGenerator generator, + DwarfClassGenerator classGen, DebugInfoBuilder debugBuilder) { + if (generator == null || debugBuilder == null) { return null; } return () -> { - classGen.write(); - generator.end(); - return generator.createSections(); + var sections = new ArrayList(); + if (classGen != null) { + classGen.write(); + } + if (generator != null) { + generator.end(); + sections.addAll(generator.createSections()); + } + if (debugBuilder != null) { + sections.addAll(debugBuilder.build()); + } + return sections; }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BinaryDataConsumer.java b/core/src/main/java/org/teavm/backend/wasm/blob/BinaryDataConsumer.java similarity index 94% rename from core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BinaryDataConsumer.java rename to core/src/main/java/org/teavm/backend/wasm/blob/BinaryDataConsumer.java index 7d61ad411..5add5a065 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BinaryDataConsumer.java +++ b/core/src/main/java/org/teavm/backend/wasm/blob/BinaryDataConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.dwarf.blob; +package org.teavm.backend.wasm.blob; public interface BinaryDataConsumer { void accept(byte[] data, int offset, int limit); diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java b/core/src/main/java/org/teavm/backend/wasm/blob/Blob.java similarity index 99% rename from core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java rename to core/src/main/java/org/teavm/backend/wasm/blob/Blob.java index 5f70c8164..a5bc4d569 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java +++ b/core/src/main/java/org/teavm/backend/wasm/blob/Blob.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.dwarf.blob; +package org.teavm.backend.wasm.blob; import java.util.ArrayList; import java.util.List; diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java b/core/src/main/java/org/teavm/backend/wasm/blob/BlobReader.java similarity index 97% rename from core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java rename to core/src/main/java/org/teavm/backend/wasm/blob/BlobReader.java index 96bb14554..6182476bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java +++ b/core/src/main/java/org/teavm/backend/wasm/blob/BlobReader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.dwarf.blob; +package org.teavm.backend.wasm.blob; public class BlobReader { private Blob blob; diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java b/core/src/main/java/org/teavm/backend/wasm/blob/Marker.java similarity index 96% rename from core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java rename to core/src/main/java/org/teavm/backend/wasm/blob/Marker.java index 2e42a4027..adf7c7084 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java +++ b/core/src/main/java/org/teavm/backend/wasm/blob/Marker.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.dwarf.blob; +package org.teavm.backend.wasm.blob; public class Marker { private Blob blob; diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugClasses.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugClasses.java new file mode 100644 index 000000000..fc24933a8 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugClasses.java @@ -0,0 +1,20 @@ +/* + * 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.debug; + +public interface DebugClasses { + int classPtr(String className); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugClassesBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugClassesBuilder.java new file mode 100644 index 000000000..5060f9893 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugClassesBuilder.java @@ -0,0 +1,52 @@ +/* + * 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.debug; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; + +public class DebugClassesBuilder extends DebugSectionBuilder implements DebugClasses { + private DebugPackages packages; + private DebugStrings strings; + private ObjectIntMap classes = new ObjectIntHashMap<>(); + + public DebugClassesBuilder(DebugPackages packages, DebugStrings strings) { + this.packages = packages; + this.strings = strings; + } + + @Override + public int classPtr(String className) { + var result = classes.getOrDefault(className, -1); + if (result < 0) { + result = classes.size(); + classes.put(className, result); + var packagePtr = 0; + var index = 0; + while (true) { + var next = className.indexOf('.', index); + if (next < 0) { + break; + } + packagePtr = packages.packagePtr(packagePtr, className.substring(index, next)); + index = next + 1; + } + blob.writeLEB(packagePtr); + blob.writeLEB(strings.stringPtr(className.substring(index))); + } + return result; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugConstants.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugConstants.java new file mode 100644 index 000000000..06975610a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugConstants.java @@ -0,0 +1,28 @@ +/* + * 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.debug; + +public final class DebugConstants { + private DebugConstants() { + } + + public static final int LOC_START = 0; + public static final int LOC_END = 1; + public static final int LOC_LINE = 2; + public static final int LOC_FILE = 3; + public static final int LOC_PTR = 4; + public static final int LOC_USER = 10; +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugFiles.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugFiles.java new file mode 100644 index 000000000..a4215b892 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugFiles.java @@ -0,0 +1,20 @@ +/* + * 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.debug; + +public interface DebugFiles { + int filePtr(String fileName); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugFilesBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugFilesBuilder.java new file mode 100644 index 000000000..f404dd840 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugFilesBuilder.java @@ -0,0 +1,90 @@ +/* + * 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.debug; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.Objects; + +public class DebugFilesBuilder extends DebugSectionBuilder implements DebugFiles { + private DebugStrings strings; + private ObjectIntMap fileMap = new ObjectIntHashMap<>(); + + public DebugFilesBuilder(DebugStrings strings) { + this.strings = strings; + } + + @Override + public int filePtr(String fileName) { + var index = 0; + var current = 0; + while (true) { + var next = fileName.indexOf('/', index); + if (next < 0) { + break; + } + var dirName = fileName.substring(index, next); + current = filePtr(current, dirName); + index = next + 1; + } + return filePtr(current, fileName.substring(index)); + } + + private int filePtr(int parent, String fileName) { + var data = new FileData(parent, fileName); + var ptr = fileMap.getOrDefault(data, 0); + if (ptr == 0) { + ptr = fileMap.size() + 1; + fileMap.put(data, ptr); + blob.writeLEB(parent); + var extensionIndex = fileName.lastIndexOf('.'); + if (extensionIndex < 0) { + blob.writeLEB(strings.stringPtr(fileName) << 1); + } else { + blob.writeLEB(1 | (strings.stringPtr(fileName.substring(0, extensionIndex)) << 1)); + blob.writeLEB(strings.stringPtr(fileName.substring(extensionIndex + 1))); + } + } + return ptr; + } + + private static class FileData { + private final int parent; + private final String name; + + FileData(int parent, String name) { + this.parent = parent; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FileData fileData = (FileData) o; + return parent == fileData.parent && name.equals(fileData.name); + } + + @Override + public int hashCode() { + return Objects.hash(parent, name); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugInfoBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugInfoBuilder.java new file mode 100644 index 000000000..9d96f2601 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugInfoBuilder.java @@ -0,0 +1,80 @@ +/* + * 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.debug; + +import java.util.ArrayList; +import java.util.List; +import org.teavm.backend.wasm.model.WasmCustomSection; + +public class DebugInfoBuilder { + private DebugStringsBuilder strings; + private DebugFilesBuilder files; + private DebugPackagesBuilder packages; + private DebugClassesBuilder classes; + private DebugMethodsBuilder methods; + private DebugLinesBuilder lines; + + public DebugInfoBuilder() { + strings = new DebugStringsBuilder(); + files = new DebugFilesBuilder(strings); + packages = new DebugPackagesBuilder(strings); + classes = new DebugClassesBuilder(packages, strings); + methods = new DebugMethodsBuilder(classes, strings); + lines = new DebugLinesBuilder(files, methods); + } + + public DebugStrings strings() { + return strings; + } + + public DebugFiles files() { + return files; + } + + public DebugPackages packages() { + return packages; + } + + public DebugClasses classes() { + return classes; + } + + public DebugMethodsBuilder methods() { + return methods; + } + + public DebugLinesBuilder lines() { + return lines; + } + + public List build() { + var result = new ArrayList(); + addSection(result, "teavm_str", strings); + addSection(result, "teavm_file", files); + addSection(result, "teavm_pkg", packages); + addSection(result, "teavm_classes", classes); + addSection(result, "teavm_methods", methods); + addSection(result, "teavm_line", lines); + return result; + } + + private void addSection(List sections, String name, DebugSectionBuilder builder) { + if (builder.isEmpty()) { + return; + } + sections.add(new WasmCustomSection(name, builder.build())); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugLines.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugLines.java new file mode 100644 index 000000000..72fc06506 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugLines.java @@ -0,0 +1,28 @@ +/* + * 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.debug; + +import org.teavm.model.MethodReference; + +public interface DebugLines { + void advance(int ptr); + + void location(String file, int line); + + void start(MethodReference methodReference); + + void end(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugLinesBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugLinesBuilder.java new file mode 100644 index 000000000..f16995ecc --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugLinesBuilder.java @@ -0,0 +1,109 @@ +/* + * 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.debug; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Objects; +import org.teavm.model.MethodReference; + +public class DebugLinesBuilder extends DebugSectionBuilder implements DebugLines { + private DebugFiles files; + private DebugMethods methods; + private int ptr; + private int lastWrittenPtr; + private String file; + private int line = 1; + private Deque states = new ArrayDeque<>(); + + public DebugLinesBuilder(DebugFiles files, DebugMethods methods) { + this.files = files; + this.methods = methods; + } + + @Override + public void advance(int ptr) { + if (ptr < this.ptr) { + throw new IllegalArgumentException(); + } + this.ptr = ptr; + } + + @Override + public void location(String file, int line) { + if (Objects.equals(file, this.file) && this.ptr != lastWrittenPtr && this.line != line) { + if (this.ptr - lastWrittenPtr < 32 && Math.abs(this.line - line) <= 3) { + blob.writeByte(DebugConstants.LOC_USER + 32 * (this.ptr - lastWrittenPtr) + (line - this.line) + 3); + this.line = line; + lastWrittenPtr = ptr; + return; + } + } + if (!Objects.equals(file, this.file)) { + flushPtr(); + this.line = 1; + this.file = file; + blob.writeByte(DebugConstants.LOC_FILE).writeLEB(file != null ? files.filePtr(file) : 0); + } + if (this.line != line) { + flushPtr(); + blob.writeByte(DebugConstants.LOC_LINE).writeSLEB(line - this.line); + this.line = line; + } + } + + private void flushPtr() { + if (ptr != lastWrittenPtr) { + blob.writeLEB(DebugConstants.LOC_PTR); + blob.writeLEB(ptr - lastWrittenPtr); + lastWrittenPtr = ptr; + } + } + + @Override + public void start(MethodReference methodReference) { + flushPtr(); + blob.writeLEB(DebugConstants.LOC_START); + blob.writeLEB(methods.methodPtr(methodReference)); + states.push(new State(file, line)); + file = null; + line = 1; + } + + @Override + public void end() { + flushPtr(); + blob.writeLEB(DebugConstants.LOC_END); + if (!states.isEmpty()) { + var state = states.pop(); + file = state.file; + line = state.line; + } else { + file = null; + line = 1; + } + } + + private static class State { + String file; + int line; + + State(String file, int line) { + this.file = file; + this.line = line; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethods.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethods.java new file mode 100644 index 000000000..a2035f652 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethods.java @@ -0,0 +1,22 @@ +/* + * 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.debug; + +import org.teavm.model.MethodReference; + +public interface DebugMethods { + int methodPtr(MethodReference method); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethodsBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethodsBuilder.java new file mode 100644 index 000000000..6b46d2d0a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugMethodsBuilder.java @@ -0,0 +1,43 @@ +/* + * 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.debug; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import org.teavm.model.MethodReference; + +public class DebugMethodsBuilder extends DebugSectionBuilder implements DebugMethods { + private DebugClasses classes; + private DebugStrings strings; + private ObjectIntMap methods = new ObjectIntHashMap<>(); + + public DebugMethodsBuilder(DebugClasses classes, DebugStrings strings) { + this.classes = classes; + this.strings = strings; + } + + @Override + public int methodPtr(MethodReference method) { + var result = methods.getOrDefault(method, -1); + if (result < 0) { + result = methods.size(); + methods.put(method, result); + blob.writeLEB(classes.classPtr(method.getClassName())); + blob.writeLEB(strings.stringPtr(method.getName())); + } + return result; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackages.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackages.java new file mode 100644 index 000000000..7bb53a81a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackages.java @@ -0,0 +1,20 @@ +/* + * 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.debug; + +public interface DebugPackages { + int packagePtr(int basePackage, String name); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackagesBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackagesBuilder.java new file mode 100644 index 000000000..910b5e99d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugPackagesBuilder.java @@ -0,0 +1,69 @@ +/* + * 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.debug; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.util.Objects; + +public class DebugPackagesBuilder extends DebugSectionBuilder implements DebugPackages { + private DebugStrings strings; + private ObjectIntMap packages = new ObjectIntHashMap<>(); + + public DebugPackagesBuilder(DebugStrings strings) { + this.strings = strings; + } + + @Override + public int packagePtr(int basePackage, String name) { + var key = new PackageInfo(basePackage, name); + var result = packages.getOrDefault(key, -1); + if (result < 0) { + result = packages.size() + 1; + packages.put(key, result); + blob.writeLEB(basePackage); + blob.writeLEB(strings.stringPtr(name)); + } + return result; + } + + private static class PackageInfo { + private int parent; + private String name; + + PackageInfo(int parent, String name) { + this.parent = parent; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PackageInfo that = (PackageInfo) o; + return parent == that.parent && name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(parent, name); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugSectionBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugSectionBuilder.java new file mode 100644 index 000000000..941fa4326 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugSectionBuilder.java @@ -0,0 +1,35 @@ +/* + * 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.debug; + +import org.teavm.backend.wasm.blob.BinaryDataConsumer; +import org.teavm.backend.wasm.blob.Blob; + +public class DebugSectionBuilder { + protected Blob blob = new Blob(); + + public void read(BinaryDataConsumer consumer) { + blob.newReader(consumer).readRemaining(); + } + + public byte[] build() { + return blob.toArray(); + } + + public boolean isEmpty() { + return blob.size() == 0; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugStrings.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugStrings.java new file mode 100644 index 000000000..90e92b4f4 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugStrings.java @@ -0,0 +1,20 @@ +/* + * 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.debug; + +public interface DebugStrings { + int stringPtr(String str); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/DebugStringsBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/DebugStringsBuilder.java new file mode 100644 index 000000000..e97b18ce6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/DebugStringsBuilder.java @@ -0,0 +1,36 @@ +/* + * 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.debug; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.nio.charset.StandardCharsets; + +public class DebugStringsBuilder extends DebugSectionBuilder implements DebugStrings { + private ObjectIntMap strings = new ObjectIntHashMap<>(); + + @Override + public int stringPtr(String str) { + var result = strings.getOrDefault(str, -1); + if (result < 0) { + result = strings.size(); + strings.put(str, result); + blob.writeLEB(str.length()); + blob.write(str.getBytes(StandardCharsets.UTF_8)); + } + return result; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfAbbreviation.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfAbbreviation.java index ce1c1a917..4cb47cc78 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfAbbreviation.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfAbbreviation.java @@ -16,7 +16,7 @@ package org.teavm.backend.wasm.dwarf; import java.util.function.Consumer; -import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.blob.Blob; public class DwarfAbbreviation { int tag; diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java index 05ed5c840..6c215a487 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java @@ -22,9 +22,9 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.function.Consumer; -import org.teavm.backend.wasm.dwarf.blob.BinaryDataConsumer; -import org.teavm.backend.wasm.dwarf.blob.Blob; -import org.teavm.backend.wasm.dwarf.blob.Marker; +import org.teavm.backend.wasm.blob.BinaryDataConsumer; +import org.teavm.backend.wasm.blob.Blob; +import org.teavm.backend.wasm.blob.Marker; public class DwarfInfoWriter { private Blob output = new Blob(); diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholder.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholder.java index 029e566b8..2607d9909 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholder.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholder.java @@ -17,7 +17,7 @@ package org.teavm.backend.wasm.dwarf; import java.util.ArrayList; import java.util.List; -import org.teavm.backend.wasm.dwarf.blob.Marker; +import org.teavm.backend.wasm.blob.Marker; public class DwarfPlaceholder { int ptr = -1; diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholderWriter.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholderWriter.java index 7099f3ae2..a92ceefc9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholderWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfPlaceholderWriter.java @@ -15,7 +15,7 @@ */ package org.teavm.backend.wasm.dwarf; -import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.blob.Blob; public interface DwarfPlaceholderWriter { void write(Blob blob, int actualValue); 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 4b98bbe40..c059f18ad 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 @@ -46,10 +46,10 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.teavm.backend.wasm.blob.Blob; import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; -import org.teavm.backend.wasm.dwarf.blob.Blob; import org.teavm.model.MethodDescriptor; import org.teavm.model.PrimitiveType; import org.teavm.model.ValueType; 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 index d99b117b7..9e445e60b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfFunctionGenerator.java @@ -30,9 +30,9 @@ 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.blob.Blob; +import org.teavm.backend.wasm.blob.Marker; 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; import org.teavm.model.util.VariableType; 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 5b188dbbc..b92b1cf2e 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 @@ -31,10 +31,10 @@ 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.blob.Blob; +import org.teavm.backend.wasm.blob.Marker; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; -import org.teavm.backend.wasm.dwarf.blob.Blob; -import org.teavm.backend.wasm.dwarf.blob.Marker; import org.teavm.backend.wasm.model.WasmCustomSection; public class DwarfGenerator { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java index 196852dbc..5c459cd8f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java @@ -26,8 +26,8 @@ import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_COPY; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_SET_FILE; import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; -import org.teavm.backend.wasm.dwarf.blob.Blob; -import org.teavm.backend.wasm.dwarf.blob.Marker; +import org.teavm.backend.wasm.blob.Blob; +import org.teavm.backend.wasm.blob.Marker; class DwarfLinesGenerator { private static final int MIN_INSN_LEN = 1; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java index de477bb8a..5396dc351 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java @@ -18,7 +18,7 @@ package org.teavm.backend.wasm.generate; import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; import java.nio.charset.StandardCharsets; -import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.blob.Blob; public class DwarfStrings { final Blob blob = new Blob(); 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 5eed903cd..84e8e3fa9 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 @@ -59,6 +59,7 @@ public class WasmGenerator { ClassHolder cls = classSource.get(methodReference.getClassName()); MethodHolder method = cls.getMethod(methodReference.getDescriptor()); WasmFunction function = new WasmFunction(names.forMethod(method.getReference())); + function.setJavaMethod(methodReference); if (!method.hasModifier(ElementModifier.STATIC)) { function.getParameters().add(WasmType.INT32); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java index 9900c581d..aa252be5c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.model.MethodReference; public class WasmFunction { WasmModule module; @@ -32,6 +33,7 @@ public class WasmFunction { private List localVariables = new ArrayList<>(); private List readonlyLocalVariables = Collections.unmodifiableList(localVariables); private List body = new ArrayList<>(); + private MethodReference javaMethod; public WasmFunction(String name) { Objects.requireNonNull(name); @@ -98,4 +100,12 @@ public class WasmFunction { local.index = localVariables.size(); localVariables.add(local); } + + public MethodReference getJavaMethod() { + return javaMethod; + } + + public void setJavaMethod(MethodReference javaMethod) { + this.javaMethod = javaMethod; + } } 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 14dc1c406..a88225566 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 @@ -23,6 +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.debug.DebugLines; import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfFunctionGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator; @@ -57,14 +58,16 @@ public class WasmBinaryRenderer { private boolean obfuscated; private DwarfGenerator dwarfGenerator; private DwarfFunctionGenerator dwarfFunctionGen; + private DebugLines debugLines; public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, - DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen) { + DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen, DebugLines debugLines) { this.output = output; this.version = version; this.obfuscated = obfuscated; this.dwarfGenerator = dwarfGenerator; dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null; + this.debugLines = debugLines; } public void render(WasmModule module) { @@ -288,7 +291,9 @@ public class WasmBinaryRenderer { if (dwarfFunctionGen != null) { dwarfFunctionGen.begin(function, offset); - + } + if (debugLines != null && function.getJavaMethod() != null) { + debugLines.start(function.getJavaMethod()); } var localVariables = function.getLocalVariables(); @@ -319,7 +324,7 @@ public class WasmBinaryRenderer { var importIndexes = this.functionIndexes; var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, - signatureIndexes, dwarfGenerator, offset); + signatureIndexes, dwarfGenerator, function.getJavaMethod() != null ? debugLines : null, offset); for (var part : function.getBody()) { part.acceptVisitor(visitor); } @@ -327,6 +332,9 @@ public class WasmBinaryRenderer { if (dwarfGenerator != null) { dwarfGenerator.endLineNumberSequence(offset + code.getPosition()); } + if (debugLines != null) { + debugLines.end(); + } code.writeByte(0x0B); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index e7d2582e8..22faf21ab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -15,8 +15,12 @@ */ package org.teavm.backend.wasm.render; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; @@ -51,6 +55,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmUnreachable; +import org.teavm.model.MethodReference; class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private WasmBinaryWriter writer; @@ -59,13 +64,16 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private Map importedIndexes; private Map signatureIndexes; private DwarfGenerator dwarfGenerator; + private DebugLines debugLines; private int addressOffset; private int depth; private Map blockDepths = new HashMap<>(); + private List methodStack = new ArrayList<>(); + private List currentMethodStack = new ArrayList<>(); WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmBinaryVersion version, Map functionIndexes, Map importedIndexes, Map signatureIndexes, - DwarfGenerator dwarfGenerator, int addressOffset) { + DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) { this.writer = writer; this.version = version; this.functionIndexes = functionIndexes; @@ -73,6 +81,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { this.signatureIndexes = signatureIndexes; this.dwarfGenerator = dwarfGenerator; this.addressOffset = addressOffset; + this.debugLines = debugLines; } @Override @@ -875,11 +884,38 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { } private void emitLocation(WasmExpression expression) { - if (dwarfGenerator == null || expression.getLocation() == null - || expression.getLocation().getFileName() == null) { + if (expression.getLocation() == null || expression.getLocation().getFileName() == null) { return; } - dwarfGenerator.lineNumber(writer.getPosition() + addressOffset, expression.getLocation().getFileName(), - expression.getLocation().getLine()); + if (dwarfGenerator != null) { + dwarfGenerator.lineNumber(writer.getPosition() + addressOffset, expression.getLocation().getFileName(), + expression.getLocation().getLine()); + } + if (debugLines != null) { + debugLines.advance(writer.getPosition() + addressOffset); + var loc = expression.getLocation(); + var inlining = loc.getInlining(); + while (inlining != null) { + currentMethodStack.add(loc.getInlining().getMethod()); + inlining = inlining.getParent(); + } + Collections.reverse(currentMethodStack); + var commonPart = 0; + while (commonPart < currentMethodStack.size() && commonPart < methodStack.size() + && currentMethodStack.get(commonPart).equals(methodStack.get(commonPart))) { + ++commonPart; + } + while (methodStack.size() > commonPart) { + debugLines.end(); + methodStack.remove(methodStack.size() - 1); + } + while (commonPart < currentMethodStack.size()) { + var method = currentMethodStack.get(commonPart); + methodStack.add(method); + debugLines.start(method); + } + currentMethodStack.clear(); + debugLines.location(loc.getFileName(), loc.getLine()); + } } }