From d6b2afd0965941d2612befa3badd1336ade82bd2 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 28 Aug 2024 21:02:33 +0200 Subject: [PATCH] wasm gc: support name section in disassembler --- .../wasm/disasm/BaseDisassemblyListener.java | 34 ++++- .../backend/wasm/disasm/Disassembler.java | 59 ++++++-- .../DisassemblyCodeSectionListener.java | 72 +++++++--- .../DisassemblyTypeSectionListener.java | 16 ++- .../NameAccumulatingSectionListener.java | 134 ++++++++++++++++++ .../backend/wasm/disasm/NameProvider.java | 55 +++++++ .../backend/wasm/parser/AddressListener.java | 6 +- .../wasm/parser/BaseSectionParser.java | 9 ++ .../wasm/parser/CodeSectionParser.java | 11 +- .../wasm/parser/ImportSectionListener.java | 27 ++++ .../wasm/parser/ImportSectionParser.java | 49 +++++++ .../wasm/parser/NameIndirectMapListener.java | 20 +++ .../backend/wasm/parser/NameMapListener.java | 20 +++ .../wasm/parser/NameSectionListener.java | 39 +++++ .../wasm/parser/NameSectionParser.java | 78 ++++++++++ 15 files changed, 593 insertions(+), 36 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/disasm/NameAccumulatingSectionListener.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/disasm/NameProvider.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/NameIndirectMapListener.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/NameMapListener.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/NameSectionListener.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/parser/NameSectionParser.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 8ef97351f..fbb31855a 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 @@ -21,11 +21,13 @@ import org.teavm.backend.wasm.parser.WasmHollowType; public abstract class BaseDisassemblyListener implements AddressListener { protected final DisassemblyWriter writer; + protected final NameProvider nameProvider; protected int address; private int addressOffset; - public BaseDisassemblyListener(DisassemblyWriter writer) { + public BaseDisassemblyListener(DisassemblyWriter writer, NameProvider nameProvider) { this.writer = writer; + this.nameProvider = nameProvider; } public void setAddressOffset(int addressOffset) { @@ -103,7 +105,7 @@ public abstract class BaseDisassemblyListener implements AddressListener { } } else if (type instanceof WasmHollowType.CompositeReference) { writer.write("(ref null "); - writeTypeId(((WasmHollowType.CompositeReference) type).index); + writeTypeRef(((WasmHollowType.CompositeReference) type).index); writer.write(")"); return; } @@ -111,7 +113,31 @@ public abstract class BaseDisassemblyListener implements AddressListener { writer.write("unknown"); } - protected void writeTypeId(int index) { - writer.write(String.valueOf(index)); + protected void writeGlobalRef(int index) { + writeRef(nameProvider.global(index), index); + } + + protected void writeFunctionRef(int index) { + writeRef(nameProvider.function(index), index); + } + + protected void writeTypeRef(int index) { + writeRef(nameProvider.type(index), index); + } + + protected void writeFieldRef(int typeIndex, int index) { + writeRef(nameProvider.field(typeIndex, index), index); + } + + protected void writeLocalRef(int functionIndex, int index) { + writeRef(nameProvider.local(functionIndex, index), index); + } + + private void writeRef(String name, int index) { + if (name == null) { + writer.write(Integer.toString(index)); + } else { + writer.write("(; ").write(Integer.toString(index)).write(" ;) $").write(name); + } } } 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 95f033932..798e61ed4 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 @@ -22,8 +22,13 @@ 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; import org.teavm.backend.wasm.parser.CodeSectionParser; +import org.teavm.backend.wasm.parser.ImportSectionListener; +import org.teavm.backend.wasm.parser.ImportSectionParser; import org.teavm.backend.wasm.parser.ModuleParser; +import org.teavm.backend.wasm.parser.NameSectionListener; +import org.teavm.backend.wasm.parser.NameSectionParser; import org.teavm.backend.wasm.parser.TypeSectionParser; import org.teavm.common.AsyncInputStream; import org.teavm.common.ByteArrayAsyncInputStream; @@ -52,32 +57,59 @@ public final class Disassembler { } public void read(byte[] bytes) { + var nameAccumulator = new NameAccumulatingSectionListener(); var input = new ByteArrayAsyncInputStream(bytes); - var parser = createParser(input); + var nameParser = createNameParser(input, nameAccumulator); + input.readFully(nameParser::parse); + + input = new ByteArrayAsyncInputStream(bytes); + var parser = createParser(input, nameAccumulator.buildProvider()); input.readFully(parser::parse); } - public ModuleParser createParser(AsyncInputStream input) { + public ModuleParser createNameParser(AsyncInputStream input, NameSectionListener listener) { return new ModuleParser(input) { @Override protected Consumer getSectionConsumer(int code, int pos, String name) { - return Disassembler.this.getSectionConsumer(code, pos, name); + return Disassembler.this.getNameSectionConsumer(code, name, listener); } }; } - public Consumer getSectionConsumer(int code, int pos, String name) { + public ModuleParser createParser(AsyncInputStream input, NameProvider nameProvider) { + return new ModuleParser(input) { + @Override + protected Consumer getSectionConsumer(int code, int pos, String name) { + return Disassembler.this.getSectionConsumer(code, pos, nameProvider); + } + }; + } + + public Consumer getSectionConsumer(int code, int pos, NameProvider nameProvider) { + var importListener = new ImportSectionListener() { + int count; + + @Override + public void function(int typeIndex) { + ++count; + } + }; if (code == 1) { return bytes -> { - var disassembler = new DisassemblyTypeSectionListener(writer); - disassembler.setAddressOffset(pos); - var sectionParser = new TypeSectionParser(disassembler, disassembler); + var typeWriter = new DisassemblyTypeSectionListener(writer, nameProvider); + typeWriter.setAddressOffset(pos); + var sectionParser = new TypeSectionParser(typeWriter, typeWriter); sectionParser.parse(bytes); out.flush(); }; + } else if (code == 2) { + return bytes -> { + var parser = new ImportSectionParser(AddressListener.EMPTY, importListener); + parser.parse(bytes); + }; } else if (code == 10) { return bytes -> { - var disassembler = new DisassemblyCodeSectionListener(writer); + var disassembler = new DisassemblyCodeSectionListener(writer, nameProvider); disassembler.setAddressOffset(pos); var sectionParser = new CodeSectionParser(disassembler, disassembler); sectionParser.parse(bytes); @@ -88,6 +120,17 @@ public final class Disassembler { } } + public Consumer getNameSectionConsumer(int code, String name, NameSectionListener listener) { + if (code == 0 && name.equals("name")) { + return bytes -> { + var parser = new NameSectionParser(listener); + parser.parse(bytes); + }; + } else { + return null; + } + } + public static void main(String[] args) throws IOException { var file = new File(args[0]); var bytes = Files.readAllBytes(file.toPath()); 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 234088daa..ba426baa9 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 @@ -36,14 +36,21 @@ import org.teavm.backend.wasm.parser.WasmHollowType; public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements AddressListener, CodeSectionListener, CodeListener { private int blockIdGen; + private int currentFunctionId; - public DisassemblyCodeSectionListener(DisassemblyWriter writer) { - super(writer); + public DisassemblyCodeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) { + super(writer, nameProvider); } @Override public boolean functionStart(int index, int size) { - writer.address(address).write("(func (; " + index + " ;)").indent().eol(); + currentFunctionId = index; + writer.address(address).write("(func ").write("(; " + index + " ;)"); + var name = nameProvider.function(index); + if (name != null) { + writer.write(" $").write(name); + } + writer.indent().eol(); return true; } @@ -57,6 +64,10 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl writer.address(address); for (int i = 0; i < count; ++i) { writer.write("(local (; " + (i + start) + " ;) "); + var name = nameProvider.local(currentFunctionId, i + start); + if (name != null) { + writer.write("$").write(name).write(" "); + } writeType(type); writer.write(")").eol(); } @@ -78,7 +89,6 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl writer.outdent().write(")").eol(); } - @Override public void error(int depth) { writer.address(address); @@ -202,23 +212,31 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl writer.write("local.set"); break; } - writer.write(" " + index).eol(); + writer.write(" "); + writeLocalRef(currentFunctionId, index); + writer.eol(); } @Override public void getGlobal(int globalIndex) { - writer.address(address).write("global.get ").write(Integer.toString(globalIndex)).eol(); + writer.address(address).write("global.get "); + writeGlobalRef(globalIndex); + writer.eol(); } @Override public void setGlobal(int globalIndex) { - writer.address(address).write("global.set ").write(Integer.toString(globalIndex)).eol(); + writer.address(address).write("global.set "); + writeGlobalRef(globalIndex); + writer.eol(); } @Override public void call(int functionIndex) { writer.address(address); - writer.write("call " + functionIndex).eol(); + writer.write("call "); + writeFunctionRef(functionIndex); + writer.eol(); } @Override @@ -791,12 +809,16 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl @Override public void structNew(int typeIndex) { - writer.address(address).write("struct.new ").write(Integer.toString(typeIndex)).eol(); + writer.address(address).write("struct.new "); + writeTypeRef(typeIndex); + writer.eol(); } @Override public void structNewDefault(int typeIndex) { - writer.address(address).write("struct.new_default ").write(Integer.toString(typeIndex)).eol(); + writer.address(address).write("struct.new_default "); + writeTypeRef(typeIndex); + writer.eol(); } @Override @@ -809,18 +831,27 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl } else { writer.write("struct.get_u"); } - writer.write(" ").write(Integer.toString(typeIndex)).write(" ").write(Integer.toString(fieldIndex)).eol(); + writer.write(" "); + writeTypeRef(typeIndex); + writer.write(" "); + writeFieldRef(typeIndex, fieldIndex); + writer.eol(); } @Override public void structSet(int typeIndex, int fieldIndex) { - writer.address(address).write("struct.set ").write(Integer.toString(typeIndex)).write(" ") - .write(Integer.toString(fieldIndex)).eol(); + writer.address(address).write("struct.set "); + writeTypeRef(typeIndex); + writer.write(" "); + writeFieldRef(typeIndex, fieldIndex); + writer.eol(); } @Override public void arrayNewDefault(int typeIndex) { - writer.address(address).write("array.new_default ").write(Integer.toString(typeIndex)).eol(); + writer.address(address).write("array.new_default "); + writeTypeRef(typeIndex); + writer.eol(); } @Override @@ -833,17 +864,23 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl } else { writer.write("array.get_u"); } - writer.write(" ").write(Integer.toString(typeIndex)).eol(); + writer.write(" "); + writeTypeRef(typeIndex); + writer.eol(); } @Override public void arraySet(int typeIndex) { - writer.address(address).write("array.set ").write(Integer.toString(typeIndex)).eol(); + writer.address(address).write("array.set "); + writeTypeRef(typeIndex); + writer.eol(); } @Override public void functionReference(int functionIndex) { - writer.address(address).write("ref.func ").write(Integer.toString(functionIndex)).eol(); + writer.address(address).write("ref.func "); + writeFunctionRef(functionIndex); + writer.eol(); } @Override @@ -855,4 +892,5 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl public void int31Get(WasmSignedType signedType) { writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol(); } + } 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 d7979780d..b30751ac4 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 @@ -24,11 +24,12 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl TypeSectionListener { private boolean currentTypeNeedsClosing; private boolean emittingReturn; + private int currentTypeIndex; private int fieldIndex; private boolean needsFieldIndex; - public DisassemblyTypeSectionListener(DisassemblyWriter writer) { - super(writer); + public DisassemblyTypeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) { + super(writer, nameProvider); } @Override @@ -43,7 +44,12 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl @Override public void startType(int index, boolean open, int[] supertypes) { + currentTypeIndex = index; writer.address(address).write("(type (; ").write(String.valueOf(index)).write(" ;) "); + var name = nameProvider.type(index); + if (name != null) { + writer.write("$").write(name).write(" "); + } if (!open || supertypes.length > 0) { currentTypeNeedsClosing = true; writer.write("(sub "); @@ -78,6 +84,10 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl writer.address(address).write("(field "); if (needsFieldIndex) { writer.write("(; " + fieldIndex++ + " ;) "); + var name = nameProvider.field(currentTypeIndex, fieldIndex); + if (name != null) { + writer.write("$").write(name); + } } if (mutable) { writer.write("(mut "); @@ -97,7 +107,7 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl @Override public void funcType(int paramCount) { - writer.address(address).write("(struct ").indent().eol(); + writer.address(address).write("(func ").indent().eol(); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/NameAccumulatingSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/NameAccumulatingSectionListener.java new file mode 100644 index 000000000..41455bf0b --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/NameAccumulatingSectionListener.java @@ -0,0 +1,134 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.teavm.backend.wasm.parser.NameIndirectMapListener; +import org.teavm.backend.wasm.parser.NameMapListener; +import org.teavm.backend.wasm.parser.NameSectionListener; + +public class NameAccumulatingSectionListener implements NameSectionListener { + private List functions = new ArrayList<>(); + private List> locals = new ArrayList<>(); + private List types = new ArrayList<>(); + private List globals = new ArrayList<>(); + private List> fields = new ArrayList<>(); + + @Override + public NameMapListener functions() { + return listener(functions); + } + + @Override + public NameIndirectMapListener locals() { + return indirectListener(locals); + } + + @Override + public NameMapListener types() { + return listener(types); + } + + @Override + public NameMapListener globals() { + return listener(globals); + } + + @Override + public NameIndirectMapListener fields() { + return indirectListener(fields); + } + + public NameProvider buildProvider() { + return new NameProvider() { + String[] functionArray = buildMap(functions); + String[][] localArray = buildIndirectMap(locals); + String[] typeArray = buildMap(types); + String[] globalArray = buildMap(globals); + String[][] fieldArray = buildIndirectMap(fields); + + @Override + public String function(int index) { + return fetch(functionArray, index); + } + + @Override + public String local(int functionIndex, int index) { + return fetch(localArray, functionIndex, index); + } + + @Override + public String type(int index) { + return fetch(typeArray, index); + } + + @Override + public String global(int index) { + return fetch(globalArray, index); + } + + @Override + public String field(int typeIndex, int index) { + return fetch(fieldArray, typeIndex, index); + } + + private String fetch(String[] array, int index) { + return array != null && index < array.length ? array[index] : null; + } + + private String fetch(String[][] array, int mapIndex, int index) { + return array != null && mapIndex < array.length ? fetch(array[mapIndex], index) : null; + } + }; + } + + private String[] buildMap(List list) { + if (list == null) { + return null; + } + return list.toArray(new String[0]); + } + + private String[][] buildIndirectMap(List> list) { + var result = new String[list.size()][]; + for (int i = 0; i < list.size(); i++) { + result[i] = buildMap(list.get(i)); + } + return result; + } + + private NameMapListener listener(List target) { + return (index, name) -> { + if (index >= target.size()) { + target.addAll(Collections.nCopies(index - target.size() + 1, null)); + } + target.set(index, name); + }; + } + + private NameIndirectMapListener indirectListener(List> target) { + return index -> { + if (index >= target.size()) { + target.addAll(Collections.nCopies(index - target.size() + 1, null)); + } + var list = new ArrayList(); + target.set(index, list); + return listener(list); + }; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/NameProvider.java b/core/src/main/java/org/teavm/backend/wasm/disasm/NameProvider.java new file mode 100644 index 000000000..83461851f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/NameProvider.java @@ -0,0 +1,55 @@ +/* + * 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; + +public interface NameProvider { + String function(int index); + + String local(int functionIndex, int index); + + String type(int index); + + String global(int index); + + String field(int typeIndex, int index); + + NameProvider EMPTY = new NameProvider() { + @Override + public String function(int index) { + return null; + } + + @Override + public String local(int functionIndex, int index) { + return null; + } + + @Override + public String type(int index) { + return null; + } + + @Override + public String global(int index) { + return null; + } + + @Override + public String field(int typeIndex, int index) { + return null; + } + }; +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/AddressListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/AddressListener.java index 695df701a..304d64f1a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/AddressListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/AddressListener.java @@ -16,5 +16,9 @@ package org.teavm.backend.wasm.parser; public interface AddressListener { - void address(int address); + default void address(int address) { + } + + AddressListener EMPTY = new AddressListener() { + }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/BaseSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/BaseSectionParser.java index f8e25b70b..10b71b6e8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/BaseSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/BaseSectionParser.java @@ -15,6 +15,8 @@ */ package org.teavm.backend.wasm.parser; +import java.nio.charset.StandardCharsets; + public abstract class BaseSectionParser { protected AddressListener addressListener; protected byte[] data; @@ -188,4 +190,11 @@ public abstract class BaseSectionParser { | ((data[ptr++] & 0xFF) << 8) | (data[ptr++] & 0xFF); } + + protected String readString() { + var size = readLEB(); + var result = new String(data, ptr, size, StandardCharsets.UTF_8); + ptr += size; + return result; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java index c64401a26..f7e743e0c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java @@ -32,12 +32,17 @@ public class CodeSectionParser extends BaseSectionParser { private CodeSectionListener listener; private CodeListener codeListener; private List blockStack = new ArrayList<>(); + private int functionIndexOffset; public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) { super(addressListener); this.listener = listener; } + public void setFunctionIndexOffset(int functionIndexOffset) { + this.functionIndexOffset = functionIndexOffset; + } + @Override protected void parseContent() { reportAddress(); @@ -54,7 +59,7 @@ public class CodeSectionParser extends BaseSectionParser { reportAddress(); var functionSize = readLEB(); var end = ptr + functionSize; - if (listener.functionStart(index, functionSize)) { + if (listener.functionStart(index + functionIndexOffset, functionSize)) { parseLocals(); codeListener = listener.code(); if (codeListener != null) { @@ -130,7 +135,7 @@ public class CodeSectionParser extends BaseSectionParser { codeListener.opcode(Opcode.RETURN); break; case 0x10: - codeListener.call(readLEB()); + codeListener.call(readLEB() + functionIndexOffset); break; case 0x11: codeListener.indirectCall(readLEB(), readLEB()); @@ -624,7 +629,7 @@ public class CodeSectionParser extends BaseSectionParser { break; case 0xD2: - codeListener.functionReference(readLEB()); + codeListener.functionReference(readLEB() + functionIndexOffset); break; case 0xD3: diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java new file mode 100644 index 000000000..aaddcffbe --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionListener.java @@ -0,0 +1,27 @@ +/* + * 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.parser; + +public interface ImportSectionListener { + default void startEntry(String module, String name) { + } + + default void function(int typeIndex) { + } + + default void endEntry() { + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java new file mode 100644 index 000000000..2caffdcbb --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/ImportSectionParser.java @@ -0,0 +1,49 @@ +/* + * 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.parser; + +public class ImportSectionParser extends BaseSectionParser { + private final ImportSectionListener listener; + + public ImportSectionParser(AddressListener addressListener, ImportSectionListener listener) { + super(addressListener); + this.listener = listener; + } + + @Override + protected void parseContent() { + var count = readLEB(); + for (var i = 0; i < count; ++i) { + readEntry(); + } + } + + private void readEntry() { + reportAddress(); + var module = readString(); + var name = readString(); + listener.startEntry(module, name); + reportAddress(); + var type = data[ptr++]; + if (type == 0) { + var typeIndex = readLEB(); + listener.function(typeIndex); + } else { + throw new ParseException("Unsupported import type", ptr); + } + listener.endEntry(); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/NameIndirectMapListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/NameIndirectMapListener.java new file mode 100644 index 000000000..bc1c01bd9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/NameIndirectMapListener.java @@ -0,0 +1,20 @@ +/* + * 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.parser; + +public interface NameIndirectMapListener { + NameMapListener map(int index); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/NameMapListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/NameMapListener.java new file mode 100644 index 000000000..425632d56 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/NameMapListener.java @@ -0,0 +1,20 @@ +/* + * 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.parser; + +public interface NameMapListener { + void name(int index, String name); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionListener.java new file mode 100644 index 000000000..3a92089bb --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionListener.java @@ -0,0 +1,39 @@ +/* + * 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.parser; + +public interface NameSectionListener { + default NameMapListener functions() { + return null; + } + + default NameIndirectMapListener locals() { + return null; + } + + default NameMapListener types() { + return null; + } + + default NameMapListener globals() { + return null; + } + + + default NameIndirectMapListener fields() { + return null; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionParser.java new file mode 100644 index 000000000..5ae24a13d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/parser/NameSectionParser.java @@ -0,0 +1,78 @@ +/* + * 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.parser; + +public class NameSectionParser extends BaseSectionParser { + private final NameSectionListener listener; + + public NameSectionParser(NameSectionListener listener) { + super(AddressListener.EMPTY); + this.listener = listener; + } + + @Override + protected void parseContent() { + while (ptr < data.length) { + var sectionType = data[ptr++]; + var sectionLength = readLEB(); + var next = ptr + sectionLength; + switch (sectionType) { + case 1: + parseNameMapSubsection(listener.functions()); + break; + case 2: + parseIndirectNameMap(listener.locals()); + break; + case 4: + parseNameMapSubsection(listener.types()); + break; + case 7: + parseNameMapSubsection(listener.globals()); + break; + case 10: + parseIndirectNameMap(listener.fields()); + break; + } + ptr = next; + } + } + + private void parseNameMapSubsection(NameMapListener subsectionListener) { + if (subsectionListener == null) { + return; + } + parseNameMap(subsectionListener); + } + + private void parseNameMap(NameMapListener subsectionListener) { + var count = readLEB(); + for (var i = 0; i < count; ++i) { + var index = readLEB(); + var name = readString(); + if (subsectionListener != null) { + subsectionListener.name(index, name); + } + } + } + + private void parseIndirectNameMap(NameIndirectMapListener subsectionListener) { + var count = readLEB(); + for (var i = 0; i < count; ++i) { + var index = readLEB(); + parseNameMap(subsectionListener.map(index)); + } + } +}