mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: support name section in disassembler
This commit is contained in:
parent
5a513fd6fd
commit
d6b2afd096
|
@ -21,11 +21,13 @@ import org.teavm.backend.wasm.parser.WasmHollowType;
|
||||||
|
|
||||||
public abstract class BaseDisassemblyListener implements AddressListener {
|
public abstract class BaseDisassemblyListener implements AddressListener {
|
||||||
protected final DisassemblyWriter writer;
|
protected final DisassemblyWriter writer;
|
||||||
|
protected final NameProvider nameProvider;
|
||||||
protected int address;
|
protected int address;
|
||||||
private int addressOffset;
|
private int addressOffset;
|
||||||
|
|
||||||
public BaseDisassemblyListener(DisassemblyWriter writer) {
|
public BaseDisassemblyListener(DisassemblyWriter writer, NameProvider nameProvider) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
|
this.nameProvider = nameProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAddressOffset(int addressOffset) {
|
public void setAddressOffset(int addressOffset) {
|
||||||
|
@ -103,7 +105,7 @@ public abstract class BaseDisassemblyListener implements AddressListener {
|
||||||
}
|
}
|
||||||
} else if (type instanceof WasmHollowType.CompositeReference) {
|
} else if (type instanceof WasmHollowType.CompositeReference) {
|
||||||
writer.write("(ref null ");
|
writer.write("(ref null ");
|
||||||
writeTypeId(((WasmHollowType.CompositeReference) type).index);
|
writeTypeRef(((WasmHollowType.CompositeReference) type).index);
|
||||||
writer.write(")");
|
writer.write(")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +113,31 @@ public abstract class BaseDisassemblyListener implements AddressListener {
|
||||||
writer.write("unknown");
|
writer.write("unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeTypeId(int index) {
|
protected void writeGlobalRef(int index) {
|
||||||
writer.write(String.valueOf(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,13 @@ import java.io.PrintWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.function.Consumer;
|
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.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.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.backend.wasm.parser.TypeSectionParser;
|
||||||
import org.teavm.common.AsyncInputStream;
|
import org.teavm.common.AsyncInputStream;
|
||||||
import org.teavm.common.ByteArrayAsyncInputStream;
|
import org.teavm.common.ByteArrayAsyncInputStream;
|
||||||
|
@ -52,32 +57,59 @@ public final class Disassembler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void read(byte[] bytes) {
|
public void read(byte[] bytes) {
|
||||||
|
var nameAccumulator = new NameAccumulatingSectionListener();
|
||||||
var input = new ByteArrayAsyncInputStream(bytes);
|
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);
|
input.readFully(parser::parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModuleParser createParser(AsyncInputStream input) {
|
public ModuleParser createNameParser(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) {
|
||||||
return Disassembler.this.getSectionConsumer(code, pos, name);
|
return Disassembler.this.getNameSectionConsumer(code, name, listener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
public ModuleParser createParser(AsyncInputStream input, NameProvider nameProvider) {
|
||||||
|
return new ModuleParser(input) {
|
||||||
|
@Override
|
||||||
|
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
||||||
|
return Disassembler.this.getSectionConsumer(code, pos, nameProvider);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Consumer<byte[]> getSectionConsumer(int code, int pos, NameProvider nameProvider) {
|
||||||
|
var importListener = new ImportSectionListener() {
|
||||||
|
int count;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void function(int typeIndex) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
};
|
||||||
if (code == 1) {
|
if (code == 1) {
|
||||||
return bytes -> {
|
return bytes -> {
|
||||||
var disassembler = new DisassemblyTypeSectionListener(writer);
|
var typeWriter = new DisassemblyTypeSectionListener(writer, nameProvider);
|
||||||
disassembler.setAddressOffset(pos);
|
typeWriter.setAddressOffset(pos);
|
||||||
var sectionParser = new TypeSectionParser(disassembler, disassembler);
|
var sectionParser = new TypeSectionParser(typeWriter, typeWriter);
|
||||||
sectionParser.parse(bytes);
|
sectionParser.parse(bytes);
|
||||||
out.flush();
|
out.flush();
|
||||||
};
|
};
|
||||||
|
} else if (code == 2) {
|
||||||
|
return bytes -> {
|
||||||
|
var parser = new ImportSectionParser(AddressListener.EMPTY, importListener);
|
||||||
|
parser.parse(bytes);
|
||||||
|
};
|
||||||
} else if (code == 10) {
|
} else if (code == 10) {
|
||||||
return bytes -> {
|
return bytes -> {
|
||||||
var disassembler = new DisassemblyCodeSectionListener(writer);
|
var disassembler = new DisassemblyCodeSectionListener(writer, nameProvider);
|
||||||
disassembler.setAddressOffset(pos);
|
disassembler.setAddressOffset(pos);
|
||||||
var sectionParser = new CodeSectionParser(disassembler, disassembler);
|
var sectionParser = new CodeSectionParser(disassembler, disassembler);
|
||||||
sectionParser.parse(bytes);
|
sectionParser.parse(bytes);
|
||||||
|
@ -88,6 +120,17 @@ public final class Disassembler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Consumer<byte[]> 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 {
|
public static void main(String[] args) throws IOException {
|
||||||
var file = new File(args[0]);
|
var file = new File(args[0]);
|
||||||
var bytes = Files.readAllBytes(file.toPath());
|
var bytes = Files.readAllBytes(file.toPath());
|
||||||
|
|
|
@ -36,14 +36,21 @@ import org.teavm.backend.wasm.parser.WasmHollowType;
|
||||||
public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements AddressListener,
|
public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements AddressListener,
|
||||||
CodeSectionListener, CodeListener {
|
CodeSectionListener, CodeListener {
|
||||||
private int blockIdGen;
|
private int blockIdGen;
|
||||||
|
private int currentFunctionId;
|
||||||
|
|
||||||
public DisassemblyCodeSectionListener(DisassemblyWriter writer) {
|
public DisassemblyCodeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) {
|
||||||
super(writer);
|
super(writer, nameProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean functionStart(int index, int size) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +64,10 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
writer.address(address);
|
writer.address(address);
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
writer.write("(local (; " + (i + start) + " ;) ");
|
writer.write("(local (; " + (i + start) + " ;) ");
|
||||||
|
var name = nameProvider.local(currentFunctionId, i + start);
|
||||||
|
if (name != null) {
|
||||||
|
writer.write("$").write(name).write(" ");
|
||||||
|
}
|
||||||
writeType(type);
|
writeType(type);
|
||||||
writer.write(")").eol();
|
writer.write(")").eol();
|
||||||
}
|
}
|
||||||
|
@ -78,7 +89,6 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
writer.outdent().write(")").eol();
|
writer.outdent().write(")").eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(int depth) {
|
public void error(int depth) {
|
||||||
writer.address(address);
|
writer.address(address);
|
||||||
|
@ -202,23 +212,31 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
writer.write("local.set");
|
writer.write("local.set");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
writer.write(" " + index).eol();
|
writer.write(" ");
|
||||||
|
writeLocalRef(currentFunctionId, index);
|
||||||
|
writer.eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getGlobal(int globalIndex) {
|
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
|
@Override
|
||||||
public void setGlobal(int globalIndex) {
|
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
|
@Override
|
||||||
public void call(int functionIndex) {
|
public void call(int functionIndex) {
|
||||||
writer.address(address);
|
writer.address(address);
|
||||||
writer.write("call " + functionIndex).eol();
|
writer.write("call ");
|
||||||
|
writeFunctionRef(functionIndex);
|
||||||
|
writer.eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -791,12 +809,16 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void structNew(int typeIndex) {
|
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
|
@Override
|
||||||
public void structNewDefault(int typeIndex) {
|
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
|
@Override
|
||||||
|
@ -809,18 +831,27 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
} else {
|
} else {
|
||||||
writer.write("struct.get_u");
|
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
|
@Override
|
||||||
public void structSet(int typeIndex, int fieldIndex) {
|
public void structSet(int typeIndex, int fieldIndex) {
|
||||||
writer.address(address).write("struct.set ").write(Integer.toString(typeIndex)).write(" ")
|
writer.address(address).write("struct.set ");
|
||||||
.write(Integer.toString(fieldIndex)).eol();
|
writeTypeRef(typeIndex);
|
||||||
|
writer.write(" ");
|
||||||
|
writeFieldRef(typeIndex, fieldIndex);
|
||||||
|
writer.eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void arrayNewDefault(int typeIndex) {
|
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
|
@Override
|
||||||
|
@ -833,17 +864,23 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
} else {
|
} else {
|
||||||
writer.write("array.get_u");
|
writer.write("array.get_u");
|
||||||
}
|
}
|
||||||
writer.write(" ").write(Integer.toString(typeIndex)).eol();
|
writer.write(" ");
|
||||||
|
writeTypeRef(typeIndex);
|
||||||
|
writer.eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void arraySet(int typeIndex) {
|
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
|
@Override
|
||||||
public void functionReference(int functionIndex) {
|
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
|
@Override
|
||||||
|
@ -855,4 +892,5 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
|
||||||
public void int31Get(WasmSignedType signedType) {
|
public void int31Get(WasmSignedType signedType) {
|
||||||
writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol();
|
writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,12 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
|
||||||
TypeSectionListener {
|
TypeSectionListener {
|
||||||
private boolean currentTypeNeedsClosing;
|
private boolean currentTypeNeedsClosing;
|
||||||
private boolean emittingReturn;
|
private boolean emittingReturn;
|
||||||
|
private int currentTypeIndex;
|
||||||
private int fieldIndex;
|
private int fieldIndex;
|
||||||
private boolean needsFieldIndex;
|
private boolean needsFieldIndex;
|
||||||
|
|
||||||
public DisassemblyTypeSectionListener(DisassemblyWriter writer) {
|
public DisassemblyTypeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) {
|
||||||
super(writer);
|
super(writer, nameProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,7 +44,12 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startType(int index, boolean open, int[] supertypes) {
|
public void startType(int index, boolean open, int[] supertypes) {
|
||||||
|
currentTypeIndex = index;
|
||||||
writer.address(address).write("(type (; ").write(String.valueOf(index)).write(" ;) ");
|
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) {
|
if (!open || supertypes.length > 0) {
|
||||||
currentTypeNeedsClosing = true;
|
currentTypeNeedsClosing = true;
|
||||||
writer.write("(sub ");
|
writer.write("(sub ");
|
||||||
|
@ -78,6 +84,10 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
|
||||||
writer.address(address).write("(field ");
|
writer.address(address).write("(field ");
|
||||||
if (needsFieldIndex) {
|
if (needsFieldIndex) {
|
||||||
writer.write("(; " + fieldIndex++ + " ;) ");
|
writer.write("(; " + fieldIndex++ + " ;) ");
|
||||||
|
var name = nameProvider.field(currentTypeIndex, fieldIndex);
|
||||||
|
if (name != null) {
|
||||||
|
writer.write("$").write(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mutable) {
|
if (mutable) {
|
||||||
writer.write("(mut ");
|
writer.write("(mut ");
|
||||||
|
@ -97,7 +107,7 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void funcType(int paramCount) {
|
public void funcType(int paramCount) {
|
||||||
writer.address(address).write("(struct ").indent().eol();
|
writer.address(address).write("(func ").indent().eol();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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<String> functions = new ArrayList<>();
|
||||||
|
private List<List<String>> locals = new ArrayList<>();
|
||||||
|
private List<String> types = new ArrayList<>();
|
||||||
|
private List<String> globals = new ArrayList<>();
|
||||||
|
private List<List<String>> 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<String> list) {
|
||||||
|
if (list == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[][] buildIndirectMap(List<List<String>> 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<String> 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<List<String>> target) {
|
||||||
|
return index -> {
|
||||||
|
if (index >= target.size()) {
|
||||||
|
target.addAll(Collections.nCopies(index - target.size() + 1, null));
|
||||||
|
}
|
||||||
|
var list = new ArrayList<String>();
|
||||||
|
target.set(index, list);
|
||||||
|
return listener(list);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -16,5 +16,9 @@
|
||||||
package org.teavm.backend.wasm.parser;
|
package org.teavm.backend.wasm.parser;
|
||||||
|
|
||||||
public interface AddressListener {
|
public interface AddressListener {
|
||||||
void address(int address);
|
default void address(int address) {
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressListener EMPTY = new AddressListener() {
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.parser;
|
package org.teavm.backend.wasm.parser;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public abstract class BaseSectionParser {
|
public abstract class BaseSectionParser {
|
||||||
protected AddressListener addressListener;
|
protected AddressListener addressListener;
|
||||||
protected byte[] data;
|
protected byte[] data;
|
||||||
|
@ -188,4 +190,11 @@ public abstract class BaseSectionParser {
|
||||||
| ((data[ptr++] & 0xFF) << 8)
|
| ((data[ptr++] & 0xFF) << 8)
|
||||||
| (data[ptr++] & 0xFF);
|
| (data[ptr++] & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String readString() {
|
||||||
|
var size = readLEB();
|
||||||
|
var result = new String(data, ptr, size, StandardCharsets.UTF_8);
|
||||||
|
ptr += size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,17 @@ public class CodeSectionParser extends BaseSectionParser {
|
||||||
private CodeSectionListener listener;
|
private CodeSectionListener listener;
|
||||||
private CodeListener codeListener;
|
private CodeListener codeListener;
|
||||||
private List<Block> blockStack = new ArrayList<>();
|
private List<Block> blockStack = new ArrayList<>();
|
||||||
|
private int functionIndexOffset;
|
||||||
|
|
||||||
public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) {
|
public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) {
|
||||||
super(addressListener);
|
super(addressListener);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFunctionIndexOffset(int functionIndexOffset) {
|
||||||
|
this.functionIndexOffset = functionIndexOffset;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseContent() {
|
protected void parseContent() {
|
||||||
reportAddress();
|
reportAddress();
|
||||||
|
@ -54,7 +59,7 @@ public class CodeSectionParser extends BaseSectionParser {
|
||||||
reportAddress();
|
reportAddress();
|
||||||
var functionSize = readLEB();
|
var functionSize = readLEB();
|
||||||
var end = ptr + functionSize;
|
var end = ptr + functionSize;
|
||||||
if (listener.functionStart(index, functionSize)) {
|
if (listener.functionStart(index + functionIndexOffset, functionSize)) {
|
||||||
parseLocals();
|
parseLocals();
|
||||||
codeListener = listener.code();
|
codeListener = listener.code();
|
||||||
if (codeListener != null) {
|
if (codeListener != null) {
|
||||||
|
@ -130,7 +135,7 @@ public class CodeSectionParser extends BaseSectionParser {
|
||||||
codeListener.opcode(Opcode.RETURN);
|
codeListener.opcode(Opcode.RETURN);
|
||||||
break;
|
break;
|
||||||
case 0x10:
|
case 0x10:
|
||||||
codeListener.call(readLEB());
|
codeListener.call(readLEB() + functionIndexOffset);
|
||||||
break;
|
break;
|
||||||
case 0x11:
|
case 0x11:
|
||||||
codeListener.indirectCall(readLEB(), readLEB());
|
codeListener.indirectCall(readLEB(), readLEB());
|
||||||
|
@ -624,7 +629,7 @@ public class CodeSectionParser extends BaseSectionParser {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD2:
|
case 0xD2:
|
||||||
codeListener.functionReference(readLEB());
|
codeListener.functionReference(readLEB() + functionIndexOffset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD3:
|
case 0xD3:
|
||||||
|
|
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user