mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Wasm: refactor debug info parser and fix debugger errors
This commit is contained in:
parent
548ded7c75
commit
b58dd1a37b
|
@ -15,34 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.debug.parser;
|
package org.teavm.backend.wasm.debug.parser;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.teavm.backend.wasm.debug.info.LineInfo;
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
import org.teavm.common.Promise;
|
import org.teavm.backend.wasm.parser.ModuleParser;
|
||||||
|
import org.teavm.common.AsyncInputStream;
|
||||||
|
import org.teavm.common.ByteArrayAsyncInputStream;
|
||||||
|
|
||||||
public class DebugInfoParser {
|
public class DebugInfoParser extends ModuleParser {
|
||||||
private static final int WASM_HEADER_SIZE = 8;
|
|
||||||
private DebugInfoReader reader;
|
|
||||||
private int pos;
|
|
||||||
private int posBefore;
|
|
||||||
private byte[] buffer = new byte[256];
|
|
||||||
private int posInBuffer;
|
|
||||||
private int bufferLimit;
|
|
||||||
private int currentLEB;
|
|
||||||
private int currentLEBShift;
|
|
||||||
private boolean eof;
|
|
||||||
private int bytesInLEB;
|
|
||||||
private int sectionCode;
|
|
||||||
private List<byte[]> chunks = new ArrayList<>();
|
|
||||||
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
|
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
|
||||||
private DebugLinesParser lines;
|
private DebugLinesParser lines;
|
||||||
|
|
||||||
public DebugInfoParser(DebugInfoReader reader) {
|
public DebugInfoParser(AsyncInputStream reader) {
|
||||||
this.reader = reader;
|
super(reader);
|
||||||
|
|
||||||
var strings = addSection(new DebugStringParser());
|
var strings = addSection(new DebugStringParser());
|
||||||
var files = addSection(new DebugFileParser(strings));
|
var files = addSection(new DebugFileParser(strings));
|
||||||
var packages = addSection(new DebugPackageParser(strings));
|
var packages = addSection(new DebugPackageParser(strings));
|
||||||
|
@ -60,225 +49,31 @@ public class DebugInfoParser {
|
||||||
return lines.getLineInfo();
|
return lines.getLineInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Promise<Void> parse() {
|
@Override
|
||||||
return parseHeader().thenAsync(v -> parseSections());
|
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
||||||
}
|
if (code == 0) {
|
||||||
|
var parser = sectionParsers.get(name);
|
||||||
private Promise<Void> parseHeader() {
|
return parser != null ? parser::parse : null;
|
||||||
return fillAtLeast(16).thenVoid(v -> {
|
} else if (code == 10) {
|
||||||
if (remaining() < WASM_HEADER_SIZE) {
|
|
||||||
error("Invalid WebAssembly header");
|
|
||||||
}
|
|
||||||
if (readInt32() != 0x6d736100) {
|
|
||||||
error("Invalid WebAssembly magic number");
|
|
||||||
}
|
|
||||||
if (readInt32() != 1) {
|
|
||||||
error("Unsupported WebAssembly version");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> parseSections() {
|
|
||||||
return readLEB().thenAsync(n -> {
|
|
||||||
if (n == null) {
|
|
||||||
return Promise.VOID;
|
|
||||||
}
|
|
||||||
sectionCode = n;
|
|
||||||
return continueParsingSection().thenAsync(v -> parseSections());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> continueParsingSection() {
|
|
||||||
return requireLEB("Unexpected end of file reading section length").thenAsync(sectionSize -> {
|
|
||||||
if (sectionCode == 0) {
|
|
||||||
return parseSection(pos + sectionSize);
|
|
||||||
} else {
|
|
||||||
if (sectionCode == 10) {
|
|
||||||
lines.setOffset(pos);
|
lines.setOffset(pos);
|
||||||
}
|
}
|
||||||
return skip(sectionSize, "Error skipping section " + sectionCode + " of size " + sectionSize);
|
return null;
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Promise<Void> parseSection(int limit) {
|
public static void main(String[] args) throws IOException {
|
||||||
return readString("Error reading custom section name").thenAsync(name -> {
|
if (args.length != 1) {
|
||||||
var sectionParser = sectionParsers.get(name);
|
System.err.println("Pass single argument - path to wasm file");
|
||||||
if (sectionParser != null) {
|
System.exit(1);
|
||||||
return readBytes(limit - pos, "Error reading section '" + name + "' content")
|
}
|
||||||
.thenVoid(sectionParser::parse);
|
var file = new File(args[0]);
|
||||||
|
var input = new ByteArrayAsyncInputStream(Files.readAllBytes(file.toPath()));
|
||||||
|
var parser = new DebugInfoParser(input);
|
||||||
|
input.readFully(parser::parse);
|
||||||
|
var lineInfo = parser.getLineInfo();
|
||||||
|
if (lineInfo != null) {
|
||||||
|
lineInfo.dump(System.out);
|
||||||
} else {
|
} else {
|
||||||
return skip(limit - pos, "Error skipping section '" + name + "'");
|
System.out.println("No debug information found");
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<String> readString(String error) {
|
|
||||||
return readBytes(error).then(b -> new String(b, StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<byte[]> readBytes(String error) {
|
|
||||||
return requireLEB(error + ": error parsing size")
|
|
||||||
.thenAsync(size -> readBytes(size, error + ": error reading content"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<byte[]> readBytes(int count, String error) {
|
|
||||||
return readBytesImpl(count, error).then(v -> {
|
|
||||||
var result = new byte[count];
|
|
||||||
var i = 0;
|
|
||||||
for (var chunk : chunks) {
|
|
||||||
System.arraycopy(chunk, 0, result, i, chunk.length);
|
|
||||||
i += chunk.length;
|
|
||||||
}
|
|
||||||
chunks.clear();
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> readBytesImpl(int count, String error) {
|
|
||||||
posBefore = pos;
|
|
||||||
var min = Math.min(count, remaining());
|
|
||||||
var chunk = new byte[min];
|
|
||||||
System.arraycopy(buffer, posInBuffer, chunk, 0, min);
|
|
||||||
chunks.add(chunk);
|
|
||||||
pos += min;
|
|
||||||
posInBuffer += min;
|
|
||||||
if (count > min) {
|
|
||||||
if (eof) {
|
|
||||||
error(error);
|
|
||||||
}
|
|
||||||
return fill().thenAsync(x -> readBytesImpl(count - min, error));
|
|
||||||
} else {
|
|
||||||
return Promise.VOID;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Promise<Integer> requireLEB(String errorMessage) {
|
|
||||||
return readLEB().then(n -> {
|
|
||||||
if (n == null) {
|
|
||||||
error(errorMessage);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Integer> readLEB() {
|
|
||||||
posBefore = pos;
|
|
||||||
currentLEB = 0;
|
|
||||||
bytesInLEB = 0;
|
|
||||||
currentLEBShift = 0;
|
|
||||||
return continueLEB();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Integer> continueLEB() {
|
|
||||||
while (posInBuffer < bufferLimit) {
|
|
||||||
var b = buffer[posInBuffer++];
|
|
||||||
++pos;
|
|
||||||
++bytesInLEB;
|
|
||||||
var digit = b & 0x7F;
|
|
||||||
if (((digit << currentLEBShift) >> currentLEBShift) != digit) {
|
|
||||||
error("LEB represents too big number");
|
|
||||||
}
|
|
||||||
currentLEB |= digit << currentLEBShift;
|
|
||||||
if ((b & 0x80) == 0) {
|
|
||||||
return Promise.of(currentLEB);
|
|
||||||
}
|
|
||||||
currentLEBShift += 7;
|
|
||||||
}
|
|
||||||
return fill().thenAsync(bytesRead -> {
|
|
||||||
if (eof) {
|
|
||||||
if (bytesInLEB > 0) {
|
|
||||||
error("Unexpected end of file reached reading LEB");
|
|
||||||
}
|
|
||||||
return Promise.of(null);
|
|
||||||
} else {
|
|
||||||
return continueLEB();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private int readInt32() {
|
|
||||||
posBefore = pos;
|
|
||||||
var result = (buffer[posInBuffer] & 255)
|
|
||||||
| ((buffer[posInBuffer + 1] & 255) << 8)
|
|
||||||
| ((buffer[posInBuffer + 2] & 255) << 16)
|
|
||||||
| ((buffer[posInBuffer + 3] & 255) << 24);
|
|
||||||
posInBuffer += 4;
|
|
||||||
pos += 4;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int remaining() {
|
|
||||||
return bufferLimit - posInBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> skip(int bytes, String error) {
|
|
||||||
if (bytes <= remaining()) {
|
|
||||||
posInBuffer += bytes;
|
|
||||||
pos += bytes;
|
|
||||||
return Promise.VOID;
|
|
||||||
} else {
|
|
||||||
posBefore = pos;
|
|
||||||
pos += remaining();
|
|
||||||
bytes -= remaining();
|
|
||||||
posInBuffer = 0;
|
|
||||||
bufferLimit = 0;
|
|
||||||
return skipImpl(bytes, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> skipImpl(int bytes, String error) {
|
|
||||||
return reader.skip(bytes).thenAsync(bytesSkipped -> {
|
|
||||||
if (bytesSkipped < 0) {
|
|
||||||
error(error);
|
|
||||||
}
|
|
||||||
pos += bytesSkipped;
|
|
||||||
return bytes > bytesSkipped ? skipImpl(bytes - bytesSkipped, error) : Promise.VOID;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> fillAtLeast(int least) {
|
|
||||||
return fill().thenAsync(x -> fillAtLeastImpl(least));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> fillAtLeastImpl(int least) {
|
|
||||||
if (eof || least <= remaining()) {
|
|
||||||
return Promise.VOID;
|
|
||||||
}
|
|
||||||
return reader.read(buffer, bufferLimit, Math.min(least, buffer.length - bufferLimit))
|
|
||||||
.thenAsync(bytesRead -> {
|
|
||||||
if (bytesRead < 0) {
|
|
||||||
eof = true;
|
|
||||||
} else {
|
|
||||||
bufferLimit += bytesRead;
|
|
||||||
}
|
|
||||||
return fillAtLeastImpl(least);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> fill() {
|
|
||||||
return readNext(buffer.length - remaining());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<Void> readNext(int bytes) {
|
|
||||||
if (eof) {
|
|
||||||
return Promise.VOID;
|
|
||||||
}
|
|
||||||
if (posInBuffer > 0 && posInBuffer < bufferLimit) {
|
|
||||||
System.arraycopy(buffer, posInBuffer, buffer, 0, bufferLimit - posInBuffer);
|
|
||||||
}
|
|
||||||
bufferLimit -= posInBuffer;
|
|
||||||
posInBuffer = 0;
|
|
||||||
return reader.read(buffer, bufferLimit, bytes).thenVoid(bytesRead -> {
|
|
||||||
if (bytesRead < 0) {
|
|
||||||
eof = true;
|
|
||||||
} else {
|
|
||||||
bufferLimit += bytesRead;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void error(String message) {
|
|
||||||
throw new ParseException(message, posBefore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
* 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 java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.teavm.common.AsyncInputStream;
|
||||||
|
import org.teavm.common.Promise;
|
||||||
|
|
||||||
|
public class ModuleParser {
|
||||||
|
private static final int WASM_HEADER_SIZE = 8;
|
||||||
|
private AsyncInputStream reader;
|
||||||
|
private int pos;
|
||||||
|
private int posBefore;
|
||||||
|
private byte[] buffer = new byte[256];
|
||||||
|
private int posInBuffer;
|
||||||
|
private int bufferLimit;
|
||||||
|
private int currentLEB;
|
||||||
|
private int currentLEBShift;
|
||||||
|
private boolean eof;
|
||||||
|
private int bytesInLEB;
|
||||||
|
private int sectionCode;
|
||||||
|
private List<byte[]> chunks = new ArrayList<>();
|
||||||
|
|
||||||
|
public ModuleParser(AsyncInputStream inputStream) {
|
||||||
|
this.reader = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Promise<Void> parse() {
|
||||||
|
return parseHeader().thenAsync(v -> parseSections());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> parseHeader() {
|
||||||
|
return fillAtLeast(16).thenVoid(v -> {
|
||||||
|
if (remaining() < WASM_HEADER_SIZE) {
|
||||||
|
error("Invalid WebAssembly header");
|
||||||
|
}
|
||||||
|
if (readInt32() != 0x6d736100) {
|
||||||
|
error("Invalid WebAssembly magic number");
|
||||||
|
}
|
||||||
|
if (readInt32() != 1) {
|
||||||
|
error("Unsupported WebAssembly version");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> parseSections() {
|
||||||
|
return readLEB().thenAsync(n -> {
|
||||||
|
if (n == null) {
|
||||||
|
return Promise.VOID;
|
||||||
|
}
|
||||||
|
sectionCode = n;
|
||||||
|
return continueParsingSection().thenAsync(v -> parseSections());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> continueParsingSection() {
|
||||||
|
return requireLEB("Unexpected end of file reading section length").thenAsync(sectionSize -> {
|
||||||
|
if (sectionCode == 0) {
|
||||||
|
return parseSection(pos + sectionSize);
|
||||||
|
} else {
|
||||||
|
var consumer = getSectionConsumer(sectionCode, pos, null);
|
||||||
|
if (consumer == null) {
|
||||||
|
return skip(sectionSize, "Error skipping section " + sectionCode + " of size " + sectionSize);
|
||||||
|
} else {
|
||||||
|
return readBytes(sectionSize, "Error reading section " + sectionCode + " content")
|
||||||
|
.thenVoid(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> parseSection(int limit) {
|
||||||
|
return readString("Error reading custom section name").thenAsync(name -> {
|
||||||
|
var consumer = getSectionConsumer(0, pos, name);
|
||||||
|
if (consumer != null) {
|
||||||
|
return readBytes(limit - pos, "Error reading section '" + name + "' content").thenVoid(consumer);
|
||||||
|
} else {
|
||||||
|
return skip(limit - pos, "Error skipping section '" + name + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<String> readString(String error) {
|
||||||
|
return readBytes(error).then(b -> new String(b, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<byte[]> readBytes(String error) {
|
||||||
|
return requireLEB(error + ": error parsing size")
|
||||||
|
.thenAsync(size -> readBytes(size, error + ": error reading content"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<byte[]> readBytes(int count, String error) {
|
||||||
|
return readBytesImpl(count, error).then(v -> {
|
||||||
|
var result = new byte[count];
|
||||||
|
var i = 0;
|
||||||
|
for (var chunk : chunks) {
|
||||||
|
System.arraycopy(chunk, 0, result, i, chunk.length);
|
||||||
|
i += chunk.length;
|
||||||
|
}
|
||||||
|
chunks.clear();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> readBytesImpl(int count, String error) {
|
||||||
|
posBefore = pos;
|
||||||
|
var min = Math.min(count, remaining());
|
||||||
|
var chunk = new byte[min];
|
||||||
|
System.arraycopy(buffer, posInBuffer, chunk, 0, min);
|
||||||
|
chunks.add(chunk);
|
||||||
|
pos += min;
|
||||||
|
posInBuffer += min;
|
||||||
|
if (count > min) {
|
||||||
|
if (eof) {
|
||||||
|
error(error);
|
||||||
|
}
|
||||||
|
return fill().thenAsync(x -> readBytesImpl(count - min, error));
|
||||||
|
} else {
|
||||||
|
return Promise.VOID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Integer> requireLEB(String errorMessage) {
|
||||||
|
return readLEB().then(n -> {
|
||||||
|
if (n == null) {
|
||||||
|
error(errorMessage);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Integer> readLEB() {
|
||||||
|
posBefore = pos;
|
||||||
|
currentLEB = 0;
|
||||||
|
bytesInLEB = 0;
|
||||||
|
currentLEBShift = 0;
|
||||||
|
return continueLEB();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Integer> continueLEB() {
|
||||||
|
while (posInBuffer < bufferLimit) {
|
||||||
|
var b = buffer[posInBuffer++];
|
||||||
|
++pos;
|
||||||
|
++bytesInLEB;
|
||||||
|
var digit = b & 0x7F;
|
||||||
|
if (((digit << currentLEBShift) >> currentLEBShift) != digit) {
|
||||||
|
error("LEB represents too big number");
|
||||||
|
}
|
||||||
|
currentLEB |= digit << currentLEBShift;
|
||||||
|
if ((b & 0x80) == 0) {
|
||||||
|
return Promise.of(currentLEB);
|
||||||
|
}
|
||||||
|
currentLEBShift += 7;
|
||||||
|
}
|
||||||
|
return fill().thenAsync(bytesRead -> {
|
||||||
|
if (eof) {
|
||||||
|
if (bytesInLEB > 0) {
|
||||||
|
error("Unexpected end of file reached reading LEB");
|
||||||
|
}
|
||||||
|
return Promise.of(null);
|
||||||
|
} else {
|
||||||
|
return continueLEB();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readInt32() {
|
||||||
|
posBefore = pos;
|
||||||
|
var result = (buffer[posInBuffer] & 255)
|
||||||
|
| ((buffer[posInBuffer + 1] & 255) << 8)
|
||||||
|
| ((buffer[posInBuffer + 2] & 255) << 16)
|
||||||
|
| ((buffer[posInBuffer + 3] & 255) << 24);
|
||||||
|
posInBuffer += 4;
|
||||||
|
pos += 4;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int remaining() {
|
||||||
|
return bufferLimit - posInBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> skip(int bytes, String error) {
|
||||||
|
if (bytes <= remaining()) {
|
||||||
|
posInBuffer += bytes;
|
||||||
|
pos += bytes;
|
||||||
|
return Promise.VOID;
|
||||||
|
} else {
|
||||||
|
posBefore = pos;
|
||||||
|
pos += remaining();
|
||||||
|
bytes -= remaining();
|
||||||
|
posInBuffer = 0;
|
||||||
|
bufferLimit = 0;
|
||||||
|
return skipImpl(bytes, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> skipImpl(int bytes, String error) {
|
||||||
|
return reader.skip(bytes).thenAsync(bytesSkipped -> {
|
||||||
|
if (bytesSkipped < 0) {
|
||||||
|
error(error);
|
||||||
|
}
|
||||||
|
pos += bytesSkipped;
|
||||||
|
return bytes > bytesSkipped ? skipImpl(bytes - bytesSkipped, error) : Promise.VOID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> fillAtLeast(int least) {
|
||||||
|
return fill().thenAsync(x -> fillAtLeastImpl(least));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> fillAtLeastImpl(int least) {
|
||||||
|
if (eof || least <= remaining()) {
|
||||||
|
return Promise.VOID;
|
||||||
|
}
|
||||||
|
return reader.read(buffer, bufferLimit, Math.min(least, buffer.length - bufferLimit))
|
||||||
|
.thenAsync(bytesRead -> {
|
||||||
|
if (bytesRead < 0) {
|
||||||
|
eof = true;
|
||||||
|
} else {
|
||||||
|
bufferLimit += bytesRead;
|
||||||
|
}
|
||||||
|
return fillAtLeastImpl(least);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> fill() {
|
||||||
|
return readNext(buffer.length - remaining());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> readNext(int bytes) {
|
||||||
|
if (eof) {
|
||||||
|
return Promise.VOID;
|
||||||
|
}
|
||||||
|
if (posInBuffer > 0 && posInBuffer < bufferLimit) {
|
||||||
|
System.arraycopy(buffer, posInBuffer, buffer, 0, bufferLimit - posInBuffer);
|
||||||
|
}
|
||||||
|
bufferLimit -= posInBuffer;
|
||||||
|
posInBuffer = 0;
|
||||||
|
return reader.read(buffer, bufferLimit, bytes).thenVoid(bytesRead -> {
|
||||||
|
if (bytesRead < 0) {
|
||||||
|
eof = true;
|
||||||
|
} else {
|
||||||
|
bufferLimit += bytesRead;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void error(String message) {
|
||||||
|
throw new ParseException(message, posBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,13 +13,13 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.debug.parser;
|
package org.teavm.backend.wasm.parser;
|
||||||
|
|
||||||
class ParseException extends RuntimeException {
|
public class ParseException extends RuntimeException {
|
||||||
final String message;
|
public final String message;
|
||||||
final int pos;
|
public final int pos;
|
||||||
|
|
||||||
ParseException(String message, int pos) {
|
public ParseException(String message, int pos) {
|
||||||
super("Error parsing at " + pos + ": " + message);
|
super("Error parsing at " + pos + ": " + message);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.pos = pos;
|
this.pos = pos;
|
|
@ -13,11 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.debug.parser;
|
package org.teavm.common;
|
||||||
|
|
||||||
import org.teavm.common.Promise;
|
public interface AsyncInputStream {
|
||||||
|
|
||||||
public interface DebugInfoReader {
|
|
||||||
Promise<Integer> skip(int amount);
|
Promise<Integer> skip(int amount);
|
||||||
|
|
||||||
Promise<Integer> read(byte[] buffer, int offset, int count);
|
Promise<Integer> read(byte[] buffer, int offset, int count);
|
|
@ -13,34 +13,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.debugging;
|
package org.teavm.common;
|
||||||
|
|
||||||
import org.teavm.backend.wasm.debug.parser.DebugInfoParser;
|
import java.util.function.Supplier;
|
||||||
import org.teavm.backend.wasm.debug.parser.DebugInfoReader;
|
|
||||||
import org.teavm.common.CompletablePromise;
|
|
||||||
import org.teavm.common.Promise;
|
|
||||||
|
|
||||||
class DebugInfoReaderImpl implements DebugInfoReader {
|
public class ByteArrayAsyncInputStream implements AsyncInputStream {
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
private int ptr;
|
private int ptr;
|
||||||
private CompletablePromise<Integer> promise;
|
private CompletablePromise<Integer> promise;
|
||||||
private byte[] target;
|
private byte[] target;
|
||||||
private int offset;
|
private int offset;
|
||||||
private int count;
|
private int count;
|
||||||
|
private Throwable errorOccurred;
|
||||||
|
|
||||||
DebugInfoReaderImpl(byte[] data) {
|
public ByteArrayAsyncInputStream(byte[] data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugInfoParser read() {
|
|
||||||
var debugInfoParser = new DebugInfoParser(this);
|
|
||||||
Promise.runNow(() -> {
|
|
||||||
debugInfoParser.parse().catchVoid(Throwable::printStackTrace);
|
|
||||||
complete();
|
|
||||||
});
|
|
||||||
return debugInfoParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<Integer> skip(int amount) {
|
public Promise<Integer> skip(int amount) {
|
||||||
promise = new CompletablePromise<>();
|
promise = new CompletablePromise<>();
|
||||||
|
@ -57,7 +46,12 @@ class DebugInfoReaderImpl implements DebugInfoReader {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void complete() {
|
public void readFully(Supplier<Promise<?>> command) {
|
||||||
|
Promise.runNow(() -> {
|
||||||
|
command.get().catchError(e -> {
|
||||||
|
errorOccurred = e;
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
});
|
||||||
while (promise != null) {
|
while (promise != null) {
|
||||||
var p = promise;
|
var p = promise;
|
||||||
count = Math.min(count, data.length - ptr);
|
count = Math.min(count, data.length - ptr);
|
||||||
|
@ -72,5 +66,11 @@ class DebugInfoReaderImpl implements DebugInfoReader {
|
||||||
}
|
}
|
||||||
p.complete(count);
|
p.complete(count);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
var e = errorOccurred;
|
||||||
|
errorOccurred = null;
|
||||||
|
if (e != null) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,8 @@ import java.util.Set;
|
||||||
import org.teavm.backend.wasm.debug.info.LineInfo;
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
import org.teavm.backend.wasm.debug.info.LineInfoFileCommand;
|
import org.teavm.backend.wasm.debug.info.LineInfoFileCommand;
|
||||||
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.parser.DebugInfoParser;
|
||||||
|
import org.teavm.common.ByteArrayAsyncInputStream;
|
||||||
import org.teavm.common.Promise;
|
import org.teavm.common.Promise;
|
||||||
import org.teavm.debugging.information.DebugInformation;
|
import org.teavm.debugging.information.DebugInformation;
|
||||||
import org.teavm.debugging.information.DebugInformationProvider;
|
import org.teavm.debugging.information.DebugInformationProvider;
|
||||||
|
@ -316,6 +318,9 @@ public class Debugger {
|
||||||
for (var wasmLineInfo : wasmLineInfoBySource(location.getFileName())) {
|
for (var wasmLineInfo : wasmLineInfoBySource(location.getFileName())) {
|
||||||
for (var sequence : wasmLineInfo.sequences()) {
|
for (var sequence : wasmLineInfo.sequences()) {
|
||||||
for (var loc : sequence.unpack().locations()) {
|
for (var loc : sequence.unpack().locations()) {
|
||||||
|
if (loc.location() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (loc.location().line() == location.getLine()
|
if (loc.location().line() == location.getLine()
|
||||||
&& loc.location().file().fullName().equals(location.getFileName())) {
|
&& loc.location().file().fullName().equals(location.getFileName())) {
|
||||||
var jsLocation = new JavaScriptLocation(wasmScriptMap.get(wasmLineInfo),
|
var jsLocation = new JavaScriptLocation(wasmScriptMap.get(wasmLineInfo),
|
||||||
|
@ -513,16 +518,23 @@ public class Debugger {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var decoder = Base64.getDecoder();
|
var decoder = Base64.getDecoder();
|
||||||
var reader = new DebugInfoReaderImpl(decoder.decode(source));
|
var reader = new ByteArrayAsyncInputStream(decoder.decode(source));
|
||||||
var parser = reader.read();
|
var parser = new DebugInfoParser(reader);
|
||||||
|
try {
|
||||||
|
reader.readFully(parser::parse);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
if (parser.getLineInfo() != null) {
|
if (parser.getLineInfo() != null) {
|
||||||
wasmLineInfoMap.put(script, parser.getLineInfo());
|
wasmLineInfoMap.put(script, parser.getLineInfo());
|
||||||
wasmScriptMap.put(parser.getLineInfo(), script);
|
wasmScriptMap.put(parser.getLineInfo(), script);
|
||||||
for (var sequence : parser.getLineInfo().sequences()) {
|
for (var sequence : parser.getLineInfo().sequences()) {
|
||||||
for (var command : sequence.commands()) {
|
for (var command : sequence.commands()) {
|
||||||
if (command instanceof LineInfoFileCommand) {
|
if (command instanceof LineInfoFileCommand) {
|
||||||
addWasmInfoFile(((LineInfoFileCommand) command).file().fullName(),
|
var file = ((LineInfoFileCommand) command).file();
|
||||||
parser.getLineInfo());
|
if (file != null) {
|
||||||
|
addWasmInfoFile(file.fullName(), parser.getLineInfo());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user