mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
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 {
|
||||
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<>();
|
||||
public class DebugInfoParser extends ModuleParser {
|
||||
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
|
||||
private DebugLinesParser lines;
|
||||
|
||||
public DebugInfoParser(DebugInfoReader reader) {
|
||||
this.reader = reader;
|
||||
|
||||
public DebugInfoParser(AsyncInputStream reader) {
|
||||
super(reader);
|
||||
var strings = addSection(new DebugStringParser());
|
||||
var files = addSection(new DebugFileParser(strings));
|
||||
var packages = addSection(new DebugPackageParser(strings));
|
||||
|
@ -60,225 +49,31 @@ public class DebugInfoParser {
|
|||
return lines.getLineInfo();
|
||||
}
|
||||
|
||||
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 {
|
||||
if (sectionCode == 10) {
|
||||
@Override
|
||||
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
|
||||
if (code == 0) {
|
||||
var parser = sectionParsers.get(name);
|
||||
return parser != null ? parser::parse : null;
|
||||
} else if (code == 10) {
|
||||
lines.setOffset(pos);
|
||||
}
|
||||
return skip(sectionSize, "Error skipping section " + sectionCode + " of size " + sectionSize);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
private Promise<Void> parseSection(int limit) {
|
||||
return readString("Error reading custom section name").thenAsync(name -> {
|
||||
var sectionParser = sectionParsers.get(name);
|
||||
if (sectionParser != null) {
|
||||
return readBytes(limit - pos, "Error reading section '" + name + "' content")
|
||||
.thenVoid(sectionParser::parse);
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Pass single argument - path to wasm file");
|
||||
System.exit(1);
|
||||
}
|
||||
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 {
|
||||
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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.debug.parser;
|
||||
package org.teavm.backend.wasm.parser;
|
||||
|
||||
class ParseException extends RuntimeException {
|
||||
final String message;
|
||||
final int pos;
|
||||
public class ParseException extends RuntimeException {
|
||||
public final String message;
|
||||
public final int pos;
|
||||
|
||||
ParseException(String message, int pos) {
|
||||
public ParseException(String message, int pos) {
|
||||
super("Error parsing at " + pos + ": " + message);
|
||||
this.message = message;
|
||||
this.pos = pos;
|
|
@ -13,11 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.debug.parser;
|
||||
package org.teavm.common;
|
||||
|
||||
import org.teavm.common.Promise;
|
||||
|
||||
public interface DebugInfoReader {
|
||||
public interface AsyncInputStream {
|
||||
Promise<Integer> skip(int amount);
|
||||
|
||||
Promise<Integer> read(byte[] buffer, int offset, int count);
|
|
@ -13,34 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.debugging;
|
||||
package org.teavm.common;
|
||||
|
||||
import org.teavm.backend.wasm.debug.parser.DebugInfoParser;
|
||||
import org.teavm.backend.wasm.debug.parser.DebugInfoReader;
|
||||
import org.teavm.common.CompletablePromise;
|
||||
import org.teavm.common.Promise;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class DebugInfoReaderImpl implements DebugInfoReader {
|
||||
public class ByteArrayAsyncInputStream implements AsyncInputStream {
|
||||
private byte[] data;
|
||||
private int ptr;
|
||||
private CompletablePromise<Integer> promise;
|
||||
private byte[] target;
|
||||
private int offset;
|
||||
private int count;
|
||||
private Throwable errorOccurred;
|
||||
|
||||
DebugInfoReaderImpl(byte[] data) {
|
||||
public ByteArrayAsyncInputStream(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
DebugInfoParser read() {
|
||||
var debugInfoParser = new DebugInfoParser(this);
|
||||
Promise.runNow(() -> {
|
||||
debugInfoParser.parse().catchVoid(Throwable::printStackTrace);
|
||||
complete();
|
||||
});
|
||||
return debugInfoParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Promise<Integer> skip(int amount) {
|
||||
promise = new CompletablePromise<>();
|
||||
|
@ -57,7 +46,12 @@ class DebugInfoReaderImpl implements DebugInfoReader {
|
|||
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) {
|
||||
var p = promise;
|
||||
count = Math.min(count, data.length - ptr);
|
||||
|
@ -72,5 +66,11 @@ class DebugInfoReaderImpl implements DebugInfoReader {
|
|||
}
|
||||
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.LineInfoFileCommand;
|
||||
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.debugging.information.DebugInformation;
|
||||
import org.teavm.debugging.information.DebugInformationProvider;
|
||||
|
@ -316,6 +318,9 @@ public class Debugger {
|
|||
for (var wasmLineInfo : wasmLineInfoBySource(location.getFileName())) {
|
||||
for (var sequence : wasmLineInfo.sequences()) {
|
||||
for (var loc : sequence.unpack().locations()) {
|
||||
if (loc.location() == null) {
|
||||
continue;
|
||||
}
|
||||
if (loc.location().line() == location.getLine()
|
||||
&& loc.location().file().fullName().equals(location.getFileName())) {
|
||||
var jsLocation = new JavaScriptLocation(wasmScriptMap.get(wasmLineInfo),
|
||||
|
@ -513,16 +518,23 @@ public class Debugger {
|
|||
return;
|
||||
}
|
||||
var decoder = Base64.getDecoder();
|
||||
var reader = new DebugInfoReaderImpl(decoder.decode(source));
|
||||
var parser = reader.read();
|
||||
var reader = new ByteArrayAsyncInputStream(decoder.decode(source));
|
||||
var parser = new DebugInfoParser(reader);
|
||||
try {
|
||||
reader.readFully(parser::parse);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (parser.getLineInfo() != null) {
|
||||
wasmLineInfoMap.put(script, parser.getLineInfo());
|
||||
wasmScriptMap.put(parser.getLineInfo(), script);
|
||||
for (var sequence : parser.getLineInfo().sequences()) {
|
||||
for (var command : sequence.commands()) {
|
||||
if (command instanceof LineInfoFileCommand) {
|
||||
addWasmInfoFile(((LineInfoFileCommand) command).file().fullName(),
|
||||
parser.getLineInfo());
|
||||
var file = ((LineInfoFileCommand) command).file();
|
||||
if (file != null) {
|
||||
addWasmInfoFile(file.fullName(), parser.getLineInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user