mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: support writing debug info, support it in disassembler
This commit is contained in:
parent
7aec0763fa
commit
c2eb11e056
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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;
|
||||||
|
|
||||||
|
public enum WasmDebugInfoLevel {
|
||||||
|
NONE,
|
||||||
|
DEOBFUSCATION,
|
||||||
|
FULL
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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;
|
||||||
|
|
||||||
|
public enum WasmDebugInfoLocation {
|
||||||
|
EMBEDDED,
|
||||||
|
EXTERNAL
|
||||||
|
}
|
|
@ -21,6 +21,9 @@ import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugLines;
|
||||||
|
import org.teavm.backend.wasm.debug.ExternalDebugFile;
|
||||||
|
import org.teavm.backend.wasm.debug.GCDebugInfoBuilder;
|
||||||
import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
||||||
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
||||||
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||||
|
@ -63,6 +66,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private boolean obfuscated;
|
private boolean obfuscated;
|
||||||
|
private WasmDebugInfoLocation debugLocation;
|
||||||
|
private WasmDebugInfoLevel debugLevel;
|
||||||
private List<WasmGCIntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
private List<WasmGCIntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
||||||
private Map<MethodReference, WasmGCIntrinsic> customIntrinsics = new HashMap<>();
|
private Map<MethodReference, WasmGCIntrinsic> customIntrinsics = new HashMap<>();
|
||||||
private List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories = new ArrayList<>();
|
private List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories = new ArrayList<>();
|
||||||
|
@ -77,6 +82,14 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
this.strict = strict;
|
this.strict = strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDebugLevel(WasmDebugInfoLevel debugLevel) {
|
||||||
|
this.debugLevel = debugLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebugLocation(WasmDebugInfoLocation debugLocation) {
|
||||||
|
this.debugLocation = debugLocation;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) {
|
public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) {
|
||||||
intrinsicFactories.add(intrinsicFactory);
|
intrinsicFactories.add(intrinsicFactory);
|
||||||
|
@ -172,6 +185,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
customGeneratorFactories, customCustomGenerators,
|
customGeneratorFactories, customCustomGenerators,
|
||||||
controller.getProperties());
|
controller.getProperties());
|
||||||
var intrinsics = new WasmGCIntrinsics(classes, controller.getServices(), intrinsicFactories, customIntrinsics);
|
var intrinsics = new WasmGCIntrinsics(classes, controller.getServices(), intrinsicFactories, customIntrinsics);
|
||||||
|
var debugInfoBuilder = new GCDebugInfoBuilder();
|
||||||
var declarationsGenerator = new WasmGCDeclarationsGenerator(
|
var declarationsGenerator = new WasmGCDeclarationsGenerator(
|
||||||
module,
|
module,
|
||||||
classes,
|
classes,
|
||||||
|
@ -231,7 +245,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
customGenerators.contributeToModule(module);
|
customGenerators.contributeToModule(module);
|
||||||
adjustModuleMemory(module);
|
adjustModuleMemory(module);
|
||||||
|
|
||||||
emitWasmFile(module, buildTarget, outputName);
|
emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustModuleMemory(WasmModule module) {
|
private void adjustModuleMemory(WasmModule module) {
|
||||||
|
@ -248,13 +262,22 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
module.setMaxMemorySize(pages);
|
module.setMaxMemorySize(pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
|
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName,
|
||||||
|
GCDebugInfoBuilder debugInfoBuilder) throws IOException {
|
||||||
var binaryWriter = new WasmBinaryWriter();
|
var binaryWriter = new WasmBinaryWriter();
|
||||||
|
DebugLines debugLines = null;
|
||||||
|
if (debugLevel != WasmDebugInfoLevel.NONE) {
|
||||||
|
debugLines = debugInfoBuilder.lines();
|
||||||
|
}
|
||||||
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
|
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
|
||||||
null, null, null, null, WasmBinaryStatsCollector.EMPTY);
|
null, null, debugLines, null, WasmBinaryStatsCollector.EMPTY);
|
||||||
optimizeIndexes(module);
|
optimizeIndexes(module);
|
||||||
module.prepareForRendering();
|
module.prepareForRendering();
|
||||||
binaryRenderer.render(module);
|
if (debugLocation == WasmDebugInfoLocation.EMBEDDED) {
|
||||||
|
binaryRenderer.render(module, debugInfoBuilder::build);
|
||||||
|
} else {
|
||||||
|
binaryRenderer.render(module);
|
||||||
|
}
|
||||||
var data = binaryWriter.getData();
|
var data = binaryWriter.getData();
|
||||||
if (!outputName.endsWith(".wasm")) {
|
if (!outputName.endsWith(".wasm")) {
|
||||||
outputName += ".wasm";
|
outputName += ".wasm";
|
||||||
|
@ -262,6 +285,14 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
try (var output = buildTarget.createResource(outputName)) {
|
try (var output = buildTarget.createResource(outputName)) {
|
||||||
output.write(data);
|
output.write(data);
|
||||||
}
|
}
|
||||||
|
if (debugLocation == WasmDebugInfoLocation.EXTERNAL) {
|
||||||
|
var debugInfoData = ExternalDebugFile.write(debugInfoBuilder.build());
|
||||||
|
if (debugInfoData != null) {
|
||||||
|
try (var output = buildTarget.createResource(outputName + ".tdbg")) {
|
||||||
|
output.write(debugInfoData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void optimizeIndexes(WasmModule module) {
|
private void optimizeIndexes(WasmModule module) {
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.List;
|
||||||
|
import org.teavm.backend.wasm.model.WasmCustomSection;
|
||||||
|
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||||
|
|
||||||
|
public final class ExternalDebugFile {
|
||||||
|
private ExternalDebugFile() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] write(List<WasmCustomSection> sections) {
|
||||||
|
if (sections.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var writer = new WasmBinaryWriter();
|
||||||
|
writer.writeInt32(0x67626474);
|
||||||
|
writer.writeInt32(1);
|
||||||
|
for (var section : sections) {
|
||||||
|
var data = section.getData();
|
||||||
|
writer.writeAsciiString(section.getName());
|
||||||
|
writer.writeLEB(data.length);
|
||||||
|
writer.writeBytes(data);
|
||||||
|
}
|
||||||
|
return writer.getData();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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 GCDebugInfoBuilder {
|
||||||
|
private DebugStringsBuilder strings;
|
||||||
|
private DebugFilesBuilder files;
|
||||||
|
private DebugPackagesBuilder packages;
|
||||||
|
private DebugClassesBuilder classes;
|
||||||
|
private DebugMethodsBuilder methods;
|
||||||
|
private DebugLinesBuilder lines;
|
||||||
|
|
||||||
|
public GCDebugInfoBuilder() {
|
||||||
|
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 DebugMethods methods() {
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugLines lines() {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WasmCustomSection> build() {
|
||||||
|
var result = new ArrayList<WasmCustomSection>();
|
||||||
|
addSection(result, strings);
|
||||||
|
addSection(result, files);
|
||||||
|
addSection(result, packages);
|
||||||
|
addSection(result, classes);
|
||||||
|
addSection(result, methods);
|
||||||
|
addSection(result, lines);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSection(List<WasmCustomSection> sections, DebugSectionBuilder builder) {
|
||||||
|
if (builder.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sections.add(new WasmCustomSection(builder.name(), builder.build()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,17 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugClassParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugFileParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugLinesParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugMethodParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugPackageParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugSectionParser;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugStringParser;
|
||||||
import org.teavm.backend.wasm.parser.AddressListener;
|
import org.teavm.backend.wasm.parser.AddressListener;
|
||||||
import org.teavm.backend.wasm.parser.CodeSectionParser;
|
import org.teavm.backend.wasm.parser.CodeSectionParser;
|
||||||
import org.teavm.backend.wasm.parser.FunctionSectionListener;
|
import org.teavm.backend.wasm.parser.FunctionSectionListener;
|
||||||
|
@ -41,9 +51,27 @@ public final class Disassembler {
|
||||||
private WasmHollowFunctionType[] functionTypes;
|
private WasmHollowFunctionType[] functionTypes;
|
||||||
private int[] functionTypeRefs;
|
private int[] functionTypeRefs;
|
||||||
private int importFunctionCount;
|
private int importFunctionCount;
|
||||||
|
private Map<String, DebugSectionParser> debugSectionParsers = new HashMap<>();
|
||||||
|
private DebugLinesParser debugLines;
|
||||||
|
private LineInfo lineInfo;
|
||||||
|
|
||||||
public Disassembler(DisassemblyWriter writer) {
|
public Disassembler(DisassemblyWriter writer) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
|
installDebugParsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installDebugParsers() {
|
||||||
|
var strings = addDebugSection(new DebugStringParser());
|
||||||
|
var files = addDebugSection(new DebugFileParser(strings));
|
||||||
|
var packages = addDebugSection(new DebugPackageParser(strings));
|
||||||
|
var classes = addDebugSection(new DebugClassParser(strings, packages));
|
||||||
|
var methods = addDebugSection(new DebugMethodParser(strings, classes));
|
||||||
|
debugLines = addDebugSection(new DebugLinesParser(files, methods));
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends DebugSectionParser> T addDebugSection(T section) {
|
||||||
|
debugSectionParsers.put(section.name(), section);
|
||||||
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startModule() {
|
public void startModule() {
|
||||||
|
@ -65,18 +93,25 @@ public final class Disassembler {
|
||||||
public void read(byte[] bytes) {
|
public void read(byte[] bytes) {
|
||||||
var nameAccumulator = new NameAccumulatingSectionListener();
|
var nameAccumulator = new NameAccumulatingSectionListener();
|
||||||
var input = new ByteArrayAsyncInputStream(bytes);
|
var input = new ByteArrayAsyncInputStream(bytes);
|
||||||
var nameParser = createNameParser(input, nameAccumulator);
|
var preparationParser = createPreparationParser(input, nameAccumulator);
|
||||||
input.readFully(nameParser::parse);
|
input.readFully(preparationParser::parse);
|
||||||
|
lineInfo = debugLines.getLineInfo();
|
||||||
|
|
||||||
input = new ByteArrayAsyncInputStream(bytes);
|
input = new ByteArrayAsyncInputStream(bytes);
|
||||||
var parser = createParser(input, nameAccumulator.buildProvider());
|
var parser = createParser(input, nameAccumulator.buildProvider());
|
||||||
input.readFully(parser::parse);
|
input.readFully(parser::parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModuleParser createNameParser(AsyncInputStream input, NameSectionListener listener) {
|
public ModuleParser createPreparationParser(AsyncInputStream input, NameSectionListener listener) {
|
||||||
return new ModuleParser(input) {
|
return new ModuleParser(input) {
|
||||||
@Override
|
@Override
|
||||||
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
||||||
|
if (code == 0) {
|
||||||
|
var debugSection = debugSectionParsers.get(name);
|
||||||
|
if (debugSection != null) {
|
||||||
|
return debugSection::parse;
|
||||||
|
}
|
||||||
|
}
|
||||||
return Disassembler.this.getNameSectionConsumer(code, name, listener);
|
return Disassembler.this.getNameSectionConsumer(code, name, listener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -139,11 +174,14 @@ public final class Disassembler {
|
||||||
disassembler.setFunctionTypes(functionTypes);
|
disassembler.setFunctionTypes(functionTypes);
|
||||||
disassembler.setFunctionTypeRefs(functionTypeRefs);
|
disassembler.setFunctionTypeRefs(functionTypeRefs);
|
||||||
writer.setAddressOffset(pos);
|
writer.setAddressOffset(pos);
|
||||||
|
writer.setDebugLines(lineInfo);
|
||||||
|
writer.startSection();
|
||||||
writer.write("(; code section size: " + bytes.length + " ;)").eol();
|
writer.write("(; code section size: " + bytes.length + " ;)").eol();
|
||||||
var sectionParser = new CodeSectionParser(disassembler);
|
var sectionParser = new CodeSectionParser(disassembler);
|
||||||
sectionParser.setFunctionIndexOffset(importFunctionCount);
|
sectionParser.setFunctionIndexOffset(importFunctionCount);
|
||||||
sectionParser.parse(writer.addressListener, bytes);
|
sectionParser.parse(writer.addressListener, bytes);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
|
writer.setDebugLines(null);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -24,7 +24,11 @@ public class DisassemblyHTMLWriter extends DisassemblyWriter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DisassemblyWriter prologue() {
|
public DisassemblyWriter prologue() {
|
||||||
return writeExact("<html><body><pre>");
|
writeExact("<html>\n<head>");
|
||||||
|
writeExact("<style>\n");
|
||||||
|
writeExact("em { color: gray; }\n");
|
||||||
|
writeExact("</style></head>\n");
|
||||||
|
return writeExact("<body><pre>");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -82,4 +86,14 @@ public class DisassemblyHTMLWriter extends DisassemblyWriter {
|
||||||
writeExact(s);
|
writeExact(s);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startAnnotation() {
|
||||||
|
writeExact("<em>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endAnnotation() {
|
||||||
|
writeExact("</em>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,27 @@
|
||||||
package org.teavm.backend.wasm.disasm;
|
package org.teavm.backend.wasm.disasm;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoCommandVisitor;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoEnterCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoExitCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoFileCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoLineCommand;
|
||||||
import org.teavm.backend.wasm.parser.AddressListener;
|
import org.teavm.backend.wasm.parser.AddressListener;
|
||||||
|
|
||||||
public abstract class DisassemblyWriter {
|
public abstract class DisassemblyWriter {
|
||||||
private PrintWriter out;
|
private PrintWriter out;
|
||||||
private boolean withAddress;
|
private boolean withAddress;
|
||||||
private int indentLevel;
|
private int indentLevel;
|
||||||
|
private int addressWithinSection;
|
||||||
private int address;
|
private int address;
|
||||||
private boolean hasAddress;
|
private boolean hasAddress;
|
||||||
private boolean lineStarted;
|
private boolean lineStarted;
|
||||||
private int addressOffset;
|
private int addressOffset;
|
||||||
|
private LineInfo debugLines;
|
||||||
|
private int currentSequenceIndex;
|
||||||
|
private int currentCommandIndex = -1;
|
||||||
|
private int lineInfoIndent;
|
||||||
|
|
||||||
public DisassemblyWriter(PrintWriter out) {
|
public DisassemblyWriter(PrintWriter out) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
|
@ -39,6 +50,15 @@ public abstract class DisassemblyWriter {
|
||||||
this.addressOffset = addressOffset;
|
this.addressOffset = addressOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDebugLines(LineInfo debugLines) {
|
||||||
|
this.debugLines = debugLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startSection() {
|
||||||
|
addressWithinSection = -1;
|
||||||
|
currentSequenceIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public DisassemblyWriter address() {
|
public DisassemblyWriter address() {
|
||||||
hasAddress = true;
|
hasAddress = true;
|
||||||
return this;
|
return this;
|
||||||
|
@ -63,6 +83,9 @@ public abstract class DisassemblyWriter {
|
||||||
private void startLine() {
|
private void startLine() {
|
||||||
if (!lineStarted) {
|
if (!lineStarted) {
|
||||||
lineStarted = true;
|
lineStarted = true;
|
||||||
|
if (debugLines != null) {
|
||||||
|
printDebugLine();
|
||||||
|
}
|
||||||
if (withAddress) {
|
if (withAddress) {
|
||||||
if (hasAddress) {
|
if (hasAddress) {
|
||||||
hasAddress = false;
|
hasAddress = false;
|
||||||
|
@ -77,6 +100,85 @@ public abstract class DisassemblyWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void printDebugLine() {
|
||||||
|
if (currentSequenceIndex >= debugLines.sequences().size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var force = false;
|
||||||
|
if (currentCommandIndex < 0) {
|
||||||
|
if (addressWithinSection < debugLines.sequences().get(currentSequenceIndex).startAddress()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentCommandIndex = 0;
|
||||||
|
force = true;
|
||||||
|
} else {
|
||||||
|
if (addressWithinSection >= debugLines.sequences().get(currentSequenceIndex).endAddress()) {
|
||||||
|
printSingleDebugAnnotation("<end debug line sequence>");
|
||||||
|
++currentSequenceIndex;
|
||||||
|
currentCommandIndex = -1;
|
||||||
|
lineInfoIndent = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sequence = debugLines.sequences().get(currentSequenceIndex);
|
||||||
|
if (currentCommandIndex >= sequence.commands().size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var command = sequence.commands().get(currentCommandIndex);
|
||||||
|
if (!force) {
|
||||||
|
if (currentCommandIndex + 1 < sequence.commands().size()
|
||||||
|
&& addressWithinSection >= sequence.commands().get(currentCommandIndex + 1).address()) {
|
||||||
|
command = sequence.commands().get(++currentCommandIndex);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command.acceptVisitor(new LineInfoCommandVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoEnterCommand command) {
|
||||||
|
printSingleDebugAnnotation(" at " + command.method().fullName());
|
||||||
|
++lineInfoIndent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoExitCommand command) {
|
||||||
|
--lineInfoIndent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoFileCommand command) {
|
||||||
|
if (command.file() == null) {
|
||||||
|
printSingleDebugAnnotation("at <unknown>:" + command.line());
|
||||||
|
} else {
|
||||||
|
printSingleDebugAnnotation(" at " + command.file().name() + ":" + command.line());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoLineCommand command) {
|
||||||
|
printSingleDebugAnnotation(" at " + command.line());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printSingleDebugAnnotation(String text) {
|
||||||
|
out.print(" ");
|
||||||
|
for (int i = 0; i < indentLevel; ++i) {
|
||||||
|
out.print(" ");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lineInfoIndent; ++i) {
|
||||||
|
out.print(" ");
|
||||||
|
}
|
||||||
|
startAnnotation();
|
||||||
|
out.print("(;");
|
||||||
|
write(text);
|
||||||
|
out.print(" ;)");
|
||||||
|
endAnnotation();
|
||||||
|
out.print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
private void printAddress() {
|
private void printAddress() {
|
||||||
out.print("(; ");
|
out.print("(; ");
|
||||||
for (int i = 7; i >= 0; --i) {
|
for (int i = 7; i >= 0; --i) {
|
||||||
|
@ -108,15 +210,20 @@ public abstract class DisassemblyWriter {
|
||||||
|
|
||||||
public abstract DisassemblyWriter epilogue();
|
public abstract DisassemblyWriter epilogue();
|
||||||
|
|
||||||
|
protected void startAnnotation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void endAnnotation() {
|
||||||
|
}
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public final AddressListener addressListener = new AddressListener() {
|
public final AddressListener addressListener = new AddressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void address(int address) {
|
public void address(int address) {
|
||||||
|
addressWithinSection = address;
|
||||||
DisassemblyWriter.this.address = address + addressOffset;
|
DisassemblyWriter.this.address = address + addressOffset;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,8 @@ import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.teavm.backend.wasm.WasmDebugInfoLevel;
|
||||||
|
import org.teavm.backend.wasm.WasmDebugInfoLocation;
|
||||||
import org.teavm.backend.wasm.WasmGCTarget;
|
import org.teavm.backend.wasm.WasmGCTarget;
|
||||||
import org.teavm.backend.wasm.disasm.Disassembler;
|
import org.teavm.backend.wasm.disasm.Disassembler;
|
||||||
import org.teavm.backend.wasm.disasm.DisassemblyHTMLWriter;
|
import org.teavm.backend.wasm.disasm.DisassemblyHTMLWriter;
|
||||||
|
@ -69,6 +71,8 @@ class WebAssemblyGCPlatformSupport extends TestPlatformSupport<WasmGCTarget> {
|
||||||
var target = new WasmGCTarget();
|
var target = new WasmGCTarget();
|
||||||
target.setObfuscated(false);
|
target.setObfuscated(false);
|
||||||
target.setStrict(true);
|
target.setStrict(true);
|
||||||
|
target.setDebugLevel(WasmDebugInfoLevel.DEOBFUSCATION);
|
||||||
|
target.setDebugLocation(WasmDebugInfoLocation.EMBEDDED);
|
||||||
var sourceDirs = System.getProperty(SOURCE_DIRS);
|
var sourceDirs = System.getProperty(SOURCE_DIRS);
|
||||||
if (sourceDirs != null) {
|
if (sourceDirs != null) {
|
||||||
var dirs = new ArrayList<File>();
|
var dirs = new ArrayList<File>();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user