Wasm: working on parser

This commit is contained in:
Alexey Andreev 2022-12-02 18:07:56 +01:00
parent b58dd1a37b
commit aaed8e312a
3 changed files with 256 additions and 0 deletions

View File

@ -0,0 +1,105 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.disasm;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.parser.CodeSectionListener;
import org.teavm.backend.wasm.parser.CodeSectionParser;
import org.teavm.backend.wasm.parser.ModuleParser;
import org.teavm.common.ByteArrayAsyncInputStream;
public class DisasmCodeSectionListener implements CodeSectionListener {
private PrintWriter writer;
public DisasmCodeSectionListener(PrintWriter writer) {
this.writer = writer;
}
@Override
public void address(int address) {
writer.print("(; ");
for (int i = 7; i >= 0; --i) {
var digit = (address >>> (i * 4)) & 0xF;
writer.print(Character.forDigit(digit, 16));
}
writer.print(" ;)");
writer.println();
}
@Override
public void sectionStart(int functionCount) {
writer.println(" .code functions=" + functionCount);
}
@Override
public boolean functionStart(int index, int size) {
writer.println(" function fn_" + index);
return true;
}
@Override
public void localsStart(int count) {
writer.println(" locals " + count);
}
@Override
public void local(int start, int count, WasmType type) {
for (int i = 0; i < count; ++i) {
writer.println(" local " + (i + start) + ": " + type);
}
}
@Override
public void localsEnd() {
writer.println(" end_locals");
}
@Override
public void functionEnd() {
writer.println(" end_function");
}
@Override
public void sectionEnd() {
writer.println(" end_code");
}
public static void main(String[] args) throws IOException {
var file = new File(args[0]);
var bytes = Files.readAllBytes(file.toPath());
var input = new ByteArrayAsyncInputStream(bytes);
var parser = new ModuleParser(input) {
@Override
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
if (code == 10) {
return bytes -> {
var writer = new PrintWriter(System.out);
var sectionParser = new CodeSectionParser(new DisasmCodeSectionListener(writer));
sectionParser.parse(bytes);
writer.flush();
};
}
return null;
}
};
input.readFully(parser::parse);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
import org.teavm.backend.wasm.model.WasmType;
public interface CodeSectionListener {
void address(int address);
void sectionStart(int functionCount);
boolean functionStart(int index, int size);
void localsStart(int count);
void local(int start, int count, WasmType type);
void localsEnd();
void functionEnd();
void sectionEnd();
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
import org.teavm.backend.wasm.model.WasmType;
public class CodeSectionParser {
private CodeSectionListener listener;
private byte[] data;
private int ptr;
private int lastReportedPtr = -1;
public CodeSectionParser(CodeSectionListener listener) {
this.listener = listener;
}
public void parse(byte[] data) {
this.data = data;
ptr = 0;
try {
parseFunctions();
} finally {
this.data = null;
}
}
private void parseFunctions() {
reportAddress();
int count = readLEB();
listener.sectionStart(count);
for (var i = 0; i < count; ++i) {
parseFunction(i);
}
reportAddress();
listener.sectionEnd();
}
private void parseFunction(int index) {
reportAddress();
var functionSize = readLEB();
var end = ptr + functionSize;
if (listener.functionStart(index, functionSize)) {
parseLocals();
}
ptr = end;
reportAddress();
listener.functionEnd();
}
private void parseLocals() {
reportAddress();
var localEntries = readLEB();
listener.localsStart(localEntries);
var localIndex = 0;
for (int i = 0; i < localEntries; ++i) {
reportAddress();
var countInGroup = readLEB();
var type = readType();
listener.local(localIndex, countInGroup, type);
localIndex += countInGroup;
}
reportAddress();
listener.localsEnd();
}
private WasmType readType() {
var typeId = data[ptr];
switch (typeId) {
case 0x7F:
return WasmType.INT32;
case 0x7E:
return WasmType.INT64;
case 0x7D:
return WasmType.FLOAT32;
case 0x7C:
return WasmType.FLOAT64;
default:
return null;
}
}
private void reportAddress() {
if (ptr != lastReportedPtr) {
lastReportedPtr = ptr;
listener.address(ptr);
}
}
private int readLEB() {
var result = 0;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7F) << shift;
if ((digit & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
}