From 31d89ebec24e36e44ee41656af3b4ae3fb34f6f4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 29 Aug 2024 18:41:02 +0200 Subject: [PATCH] wasm gc: support emitting disassembly in HTML with link references, add setting to emit disassembly in JUnit tests --- .../wasm/disasm/BaseDisassemblyListener.java | 10 +++ .../backend/wasm/disasm/Disassembler.java | 52 +++++++++--- .../wasm/disasm/DisassemblyCodeListener.java | 29 +++++-- .../DisassemblyCodeSectionListener.java | 15 ++-- .../DisassemblyGlobalSectionListener.java | 7 +- .../wasm/disasm/DisassemblyHTMLWriter.java | 85 +++++++++++++++++++ .../wasm/disasm/DisassemblyTextWriter.java | 54 ++++++++++++ .../DisassemblyTypeSectionListener.java | 14 +-- .../wasm/disasm/DisassemblyWriter.java | 27 +++++- tests/build.gradle.kts | 2 + .../java/org/teavm/junit/PropertyNames.java | 1 + .../java/org/teavm/junit/TeaVMTestRunner.java | 3 +- .../junit/WebAssemblyGCPlatformSupport.java | 29 ++++++- 13 files changed, 290 insertions(+), 38 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyHTMLWriter.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTextWriter.java diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java index 5b7fc421b..72cb0dcc0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java @@ -102,23 +102,33 @@ public abstract class BaseDisassemblyListener { } protected void writeGlobalRef(int index) { + writer.startLink("g" + index); writeRef(nameProvider.global(index), index); + writer.endLink(); } protected void writeFunctionRef(int index) { + writer.startLink("f" + index); writeRef(nameProvider.function(index), index); + writer.endLink(); } protected void writeTypeRef(int index) { + writer.startLink("t" + index); writeRef(nameProvider.type(index), index); + writer.endLink(); } protected void writeFieldRef(int typeIndex, int index) { + writer.startLink("f" + typeIndex + "." + index); writeRef(nameProvider.field(typeIndex, index), index); + writer.endLink(); } protected void writeLocalRef(int functionIndex, int index) { + writer.startLink("l" + functionIndex + "." + index); writeRef(nameProvider.local(functionIndex, index), index); + writer.endLink(); } private void writeRef(String name, int index) { diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/Disassembler.java b/core/src/main/java/org/teavm/backend/wasm/disasm/Disassembler.java index 99164b868..cdc493f2c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/Disassembler.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/Disassembler.java @@ -16,10 +16,9 @@ package org.teavm.backend.wasm.disasm; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.Writer; import java.nio.file.Files; import java.util.function.Consumer; import org.teavm.backend.wasm.parser.AddressListener; @@ -35,12 +34,10 @@ import org.teavm.common.AsyncInputStream; import org.teavm.common.ByteArrayAsyncInputStream; public final class Disassembler { - private PrintWriter out; private DisassemblyWriter writer; - public Disassembler(Writer writer) { - out = new PrintWriter(writer); - this.writer = new DisassemblyWriter(out, true); + public Disassembler(DisassemblyWriter writer) { + this.writer = writer; } public void startModule() { @@ -52,9 +49,11 @@ public final class Disassembler { } public void disassemble(byte[] bytes) { + writer.prologue(); startModule(); read(bytes); endModule(); + writer.epilogue(); } public void read(byte[] bytes) { @@ -97,12 +96,12 @@ public final class Disassembler { }; if (code == 1) { return bytes -> { - writer.write("(; type section size: " + bytes.length + " ;)"); + writer.write("(; type section size: " + bytes.length + " ;)").eol(); var typeWriter = new DisassemblyTypeSectionListener(writer, nameProvider); writer.setAddressOffset(pos); var sectionParser = new TypeSectionParser(typeWriter); sectionParser.parse(writer.addressListener, bytes); - out.flush(); + writer.flush(); }; } else if (code == 2) { return bytes -> { @@ -111,23 +110,23 @@ public final class Disassembler { }; } else if (code == 6) { return bytes -> { - writer.write("(; global section size: " + bytes.length + " ;)"); + writer.write("(; global section size: " + bytes.length + " ;)").eol(); var globalWriter = new DisassemblyGlobalSectionListener(writer, nameProvider); writer.setAddressOffset(pos); var sectionParser = new GlobalSectionParser(globalWriter); sectionParser.setFunctionIndexOffset(importListener.count); sectionParser.parse(writer.addressListener, bytes); - out.flush(); + writer.flush(); }; } else if (code == 10) { return bytes -> { var disassembler = new DisassemblyCodeSectionListener(writer, nameProvider); writer.setAddressOffset(pos); - writer.write("(; code section size: " + bytes.length + " ;)"); + writer.write("(; code section size: " + bytes.length + " ;)").eol(); var sectionParser = new CodeSectionParser(disassembler); sectionParser.setFunctionIndexOffset(importListener.count); sectionParser.parse(writer.addressListener, bytes); - out.flush(); + writer.flush(); }; } else { return null; @@ -146,9 +145,34 @@ public final class Disassembler { } public static void main(String[] args) throws IOException { - var file = new File(args[0]); + String fileName = null; + String outFileName = null; + var htmlMode = false; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg.equals("--html")) { + htmlMode = true; + } else if (arg.equals("--output") || arg.equals("-o")) { + outFileName = args[++i]; + } else { + fileName = arg; + } + } + var file = new File(fileName); var bytes = Files.readAllBytes(file.toPath()); - var disassembler = new Disassembler(new OutputStreamWriter(System.out)); + var output = outFileName != null ? new FileOutputStream(outFileName) : System.out; + var writer = new PrintWriter(output); + var disassemblyWriter = htmlMode + ? new DisassemblyHTMLWriter(writer) + : new DisassemblyTextWriter(writer); + disassemblyWriter.setWithAddress(true); + if (htmlMode) { + disassemblyWriter.write("
").eol();
+        }
+        var disassembler = new Disassembler(disassemblyWriter);
         disassembler.disassemble(bytes);
+        if (htmlMode) {
+            disassemblyWriter.write("
").eol(); + } } } diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java index 8ddacd637..61dbfaa0c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java @@ -43,6 +43,10 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements this.currentFunctionId = currentFunctionId; } + public void reset() { + blockIdGen = 0; + } + @Override public void error(int depth) { writer.address(); @@ -56,7 +60,9 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements public int startBlock(boolean loop, WasmHollowType type) { writer.address(); var label = blockIdGen++; - writer.write(loop ? "loop" : "block").write(" $label_" + label); + writer.startLinkTarget("start" + label).startLink("end" + label).write(loop ? "loop" : "block") + .endLink().endLinkTarget(); + writer.write(" $label_" + label); writeBlockType(type); writer.indent().eol(); return label; @@ -66,7 +72,8 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements public int startConditionalBlock(WasmHollowType type) { writer.address(); var label = blockIdGen++; - writer.write("if ").write(" $label_" + label); + writer.startLinkTarget("start" + label).startLink("end" + label).write("if").endLink().endLinkTarget(); + writer.write(" $label_" + label); writeBlockType(type); writer.indent().eol(); return label; @@ -75,14 +82,16 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements @Override public void startElseSection(int token) { writer.address(); - writer.outdent().write("else (; $label_" + token + " ;)").indent().eol(); + writer.outdent().startLink("start" + token).write("else").endLink(); + writer.write(" (; $label_" + token + " ;)").indent().eol(); } @Override public int startTry(WasmHollowType type) { writer.address(); var label = blockIdGen++; - writer.write("try ").write(" $label_" + label); + writer.startLinkTarget("start" + label).startLink("end" + label).write("try").endLink().endLinkTarget(); + writer.write(" $label_" + label); writeBlockType(type); writer.indent().eol(); return label; @@ -96,12 +105,14 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements @Override public void endBlock(int token, boolean loop) { - writer.address().outdent().write("end (; $label_" + token + " ;)").eol(); + writer.address().outdent(); + writer.startLinkTarget("end" + token).startLink("start" + token).write("end").endLink().endLinkTarget(); + writer.write(" (; $label_" + token + " ;)").eol(); } @Override public void branch(BranchOpcode opcode, int depth, int target) { - writer.address(); + writer.address().startLink("start" + target); switch (opcode) { case BR: writer.write("br"); @@ -110,7 +121,7 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements writer.write("br_if"); break; } - writer.write(" $label_" + target).eol(); + writer.endLink().write(" $label_" + target).eol(); } @Override @@ -202,7 +213,9 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements @Override public void callReference(int typeIndex) { writer.address(); - writer.write("call_ref " + typeIndex).eol(); + writer.write("call_ref "); + writeTypeRef(typeIndex); + writer.eol(); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java index 24e3ed716..e99b078ab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java @@ -31,12 +31,13 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl @Override public boolean functionStart(int index, int size) { currentFunctionId = index; - writer.address().write("(func ").write("(; " + index + " ;)"); + writer.address().write("(func "); + writer.startLinkTarget("f" + index).write("(; " + index + " ;)"); var name = nameProvider.function(index); if (name != null) { writer.write(" $").write(name); } - writer.indent().eol(); + writer.endLinkTarget().indent().eol(); return true; } @@ -49,11 +50,14 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl public void local(int start, int count, WasmHollowType type) { writer.address(); for (int i = 0; i < count; ++i) { - writer.write("(local (; " + (i + start) + " ;) "); - var name = nameProvider.local(currentFunctionId, i + start); + writer.write("(local "); + var id = i + start; + writer.startLinkTarget("l" + currentFunctionId + "." + id).write("(; " + id + " ;)"); + var name = nameProvider.local(currentFunctionId, id); if (name != null) { - writer.write("$").write(name).write(" "); + writer.write(" ").write("$").write(name); } + writer.endLinkTarget().write(" "); writeType(type); writer.write(")").eol(); } @@ -62,6 +66,7 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl @Override public CodeListener code() { codeListener.setCurrentFunctionId(currentFunctionId); + codeListener.reset(); return codeListener; } diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyGlobalSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyGlobalSectionListener.java index d257825ce..ddeab3e7c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyGlobalSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyGlobalSectionListener.java @@ -29,11 +29,13 @@ public class DisassemblyGlobalSectionListener extends BaseDisassemblyListener im @Override public CodeListener startGlobal(int index, WasmHollowType type, boolean mutable) { - writer.address().write("(global (; ").write(String.valueOf(index)).write(" ;) "); + writer.address().write("(global "); + writer.startLinkTarget("g" + index).write("(; ").write(String.valueOf(index)).write(" ;)"); var name = nameProvider.global(index); if (name != null) { - writer.write("$").write(name).write(" "); + writer.write("$").write(name); } + writer.endLinkTarget().write(" "); if (mutable) { writer.write("(mut "); writeType(type); @@ -42,6 +44,7 @@ public class DisassemblyGlobalSectionListener extends BaseDisassemblyListener im writeType(type); } writer.indent().eol(); + codeListener.reset(); return codeListener; } diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyHTMLWriter.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyHTMLWriter.java new file mode 100644 index 000000000..61b90628d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyHTMLWriter.java @@ -0,0 +1,85 @@ +/* + * 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.disasm; + +import java.io.PrintWriter; + +public class DisassemblyHTMLWriter extends DisassemblyWriter { + public DisassemblyHTMLWriter(PrintWriter out) { + super(out); + } + + @Override + public DisassemblyWriter prologue() { + return writeExact("
");
+    }
+
+    @Override
+    public DisassemblyWriter epilogue() {
+        return writeExact("
"); + } + + @Override + public DisassemblyWriter startLink(String s) { + writeExact(""); + return this; + } + + @Override + public DisassemblyWriter endLink() { + writeExact(""); + return this; + } + + @Override + public DisassemblyWriter startLinkTarget(String s) { + writeExact(""); + return this; + } + + @Override + public DisassemblyWriter endLinkTarget() { + writeExact(""); + return this; + } + + @Override + public DisassemblyWriter write(String s) { + StringBuilder sb = null; + var i = 0; + for (; i < s.length(); ++i) { + var c = s.charAt(i); + if (c == '<') { + sb = new StringBuilder(); + sb.append(s, 0, i); + break; + } + } + if (sb != null) { + for (; i < s.length(); ++i) { + var c = s.charAt(i); + if (c == '<') { + sb.append("<"); + } else { + sb.append(c); + } + } + s = sb.toString(); + } + writeExact(s); + return this; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTextWriter.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTextWriter.java new file mode 100644 index 000000000..de1f1f87b --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTextWriter.java @@ -0,0 +1,54 @@ +/* + * 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.disasm; + +import java.io.PrintWriter; + +public class DisassemblyTextWriter extends DisassemblyWriter { + public DisassemblyTextWriter(PrintWriter out) { + super(out); + } + + @Override + public DisassemblyWriter startLink(String s) { + return this; + } + + @Override + public DisassemblyWriter endLink() { + return this; + } + + @Override + public DisassemblyWriter startLinkTarget(String s) { + return this; + } + + @Override + public DisassemblyWriter endLinkTarget() { + return this; + } + + @Override + public DisassemblyWriter prologue() { + return this; + } + + @Override + public DisassemblyWriter epilogue() { + return this; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTypeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTypeSectionListener.java index d8a57b939..28c6eeec3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTypeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyTypeSectionListener.java @@ -43,11 +43,13 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl @Override public void startType(int index, boolean open, int[] supertypes) { currentTypeIndex = index; - writer.address().write("(type (; ").write(String.valueOf(index)).write(" ;) "); + writer.address().write("(type "); + writer.startLinkTarget("t" + index).write("(; ").write(String.valueOf(index)).write(" ;) "); var name = nameProvider.type(index); if (name != null) { - writer.write("$").write(name).write(" "); + writer.write("$").write(name); } + writer.endLinkTarget().write(" "); if (!open || supertypes.length > 0) { currentTypeNeedsClosing = true; writer.write("(sub "); @@ -81,11 +83,13 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl public void field(WasmHollowStorageType hollowType, boolean mutable) { writer.address().write("(field "); if (needsFieldIndex) { - writer.write("(; " + fieldIndex++ + " ;) "); - var name = nameProvider.field(currentTypeIndex, fieldIndex); + var index = fieldIndex++; + writer.startLinkTarget("f" + currentTypeIndex + "." + index).write("(; " + index + " ;)"); + var name = nameProvider.field(currentTypeIndex, index); if (name != null) { - writer.write("$").write(name); + writer.write(" ").write("$").write(name); } + writer.endLinkTarget().write(" "); } if (mutable) { writer.write("(mut "); diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyWriter.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyWriter.java index 79b5bd3a5..5227ebdee 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyWriter.java @@ -18,7 +18,7 @@ package org.teavm.backend.wasm.disasm; import java.io.PrintWriter; import org.teavm.backend.wasm.parser.AddressListener; -public class DisassemblyWriter { +public abstract class DisassemblyWriter { private PrintWriter out; private boolean withAddress; private int indentLevel; @@ -27,8 +27,11 @@ public class DisassemblyWriter { private boolean lineStarted; private int addressOffset; - public DisassemblyWriter(PrintWriter out, boolean withAddress) { + public DisassemblyWriter(PrintWriter out) { this.out = out; + } + + public void setWithAddress(boolean withAddress) { this.withAddress = withAddress; } @@ -84,11 +87,31 @@ public class DisassemblyWriter { } public DisassemblyWriter write(String s) { + return writeExact(s); + } + + protected DisassemblyWriter writeExact(String s) { startLine(); out.print(s); return this; } + public abstract DisassemblyWriter startLink(String s); + + public abstract DisassemblyWriter endLink(); + + public abstract DisassemblyWriter startLinkTarget(String s); + + public abstract DisassemblyWriter endLinkTarget(); + + public abstract DisassemblyWriter prologue(); + + public abstract DisassemblyWriter epilogue(); + + public void flush() { + out.flush(); + } + public final AddressListener addressListener = new AddressListener() { @Override public void address(int address) { diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 00ac6d8a9..01f140854 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -61,6 +61,8 @@ tasks.test { systemProperty("teavm.junit.wasm-gc", providers.gradleProperty("teavm.tests.wasm-gc").orElse("false").get()) systemProperty("teavm.junit.wasm-gc.runner", browser) + systemProperty("teavm.junit.wasm-gc.disasm", providers.gradleProperty("teavm.tests.wasm-gc.disasm") + .orElse("false").get()) systemProperty("teavm.junit.wasi", providers.gradleProperty("teavm.tests.wasi").orElse("true").get()) systemProperty("teavm.junit.wasi.runner", providers.gradleProperty("teavm.tests.wasi.runner") diff --git a/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java b/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java index 0f0adfcfa..e279e96f2 100644 --- a/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java +++ b/tools/junit/src/main/java/org/teavm/junit/PropertyNames.java @@ -26,6 +26,7 @@ final class PropertyNames { static final String WASI_ENABLED = "teavm.junit.wasi"; static final String WASI_RUNNER = "teavm.junit.wasi.runner"; static final String WASM_GC_ENABLED = "teavm.junit.wasm-gc"; + static final String WASM_GC_DISASM = "teavm.junit.wasm-gc.disasm"; static final String C_COMPILER = "teavm.junit.c.compiler"; static final String C_LINE_NUMBERS = "teavm.junit.c.lineNumbers"; static final String MINIFIED = "teavm.junit.minified"; diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java index e7a4a8569..5f048391d 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java @@ -106,7 +106,8 @@ public class TeaVMTestRunner extends Runner implements Filterable { platforms.add(new JSPlatformSupport(classSource, referenceCache)); platforms.add(new WebAssemblyPlatformSupport(classSource, referenceCache)); - platforms.add(new WebAssemblyGCPlatformSupport(classSource, referenceCache)); + platforms.add(new WebAssemblyGCPlatformSupport(classSource, referenceCache, + Boolean.parseBoolean(System.getProperty(PropertyNames.WASM_GC_DISASM)))); platforms.add(new WasiPlatformSupport(classSource, referenceCache)); platforms.add(new CPlatformSupport(classSource, referenceCache)); diff --git a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java index 0c0b19958..c7475778a 100644 --- a/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java +++ b/tools/junit/src/main/java/org/teavm/junit/WebAssemblyGCPlatformSupport.java @@ -20,8 +20,12 @@ import static org.teavm.junit.PropertyNames.SOURCE_DIRS; import static org.teavm.junit.PropertyNames.WASM_GC_ENABLED; import static org.teavm.junit.PropertyNames.WASM_RUNNER; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.lang.reflect.AnnotatedElement; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -29,6 +33,8 @@ import java.util.StringTokenizer; import java.util.function.Consumer; import java.util.function.Supplier; import org.teavm.backend.wasm.WasmGCTarget; +import org.teavm.backend.wasm.disasm.Disassembler; +import org.teavm.backend.wasm.disasm.DisassemblyHTMLWriter; import org.teavm.browserrunner.BrowserRunner; import org.teavm.model.ClassHolderSource; import org.teavm.model.MethodReference; @@ -36,8 +42,11 @@ import org.teavm.model.ReferenceCache; import org.teavm.vm.TeaVM; class WebAssemblyGCPlatformSupport extends TestPlatformSupport { - WebAssemblyGCPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache) { + private boolean disassembly; + + WebAssemblyGCPlatformSupport(ClassHolderSource classSource, ReferenceCache referenceCache, boolean disassembly) { super(classSource, referenceCache); + this.disassembly = disassembly; } @Override @@ -117,6 +126,9 @@ class WebAssemblyGCPlatformSupport extends TestPlatformSupport { } catch (IOException e) { throw new RuntimeException(e); } + if (disassembly) { + writeDisassembly(outputPath, "classTest", configuration); + } } @Override @@ -130,5 +142,20 @@ class WebAssemblyGCPlatformSupport extends TestPlatformSupport { } catch (IOException e) { throw new RuntimeException(e); } + if (disassembly) { + writeDisassembly(outputPathForMethod, "test", configuration); + } + } + + private void writeDisassembly(File outputPath, String name, TeaVMTestConfiguration configuration) { + var binPath = getOutputFile(outputPath, name, configuration.getSuffix(), getExtension()); + var htmlPath = getOutputFile(outputPath, name, configuration.getSuffix(), ".wast.html"); + try (var writer = new OutputStreamWriter(new FileOutputStream(htmlPath))) { + var disasmWriter = new DisassemblyHTMLWriter(new PrintWriter(writer)); + disasmWriter.setWithAddress(true); + new Disassembler(disasmWriter).disassemble(Files.readAllBytes(binPath.toPath())); + } catch (IOException e) { + throw new RuntimeException(e); + } } }