mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Wasm: add parser for own debug information
This commit is contained in:
parent
56929b2085
commit
d1feec7ae6
|
@ -24,6 +24,7 @@ public class DebugClassesBuilder extends DebugSectionBuilder implements DebugCla
|
||||||
private ObjectIntMap<String> classes = new ObjectIntHashMap<>();
|
private ObjectIntMap<String> classes = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
public DebugClassesBuilder(DebugPackages packages, DebugStrings strings) {
|
public DebugClassesBuilder(DebugPackages packages, DebugStrings strings) {
|
||||||
|
super(DebugConstants.SECTION_CLASSES);
|
||||||
this.packages = packages;
|
this.packages = packages;
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,11 @@ public final class DebugConstants {
|
||||||
public static final int LOC_FILE = 3;
|
public static final int LOC_FILE = 3;
|
||||||
public static final int LOC_PTR = 4;
|
public static final int LOC_PTR = 4;
|
||||||
public static final int LOC_USER = 10;
|
public static final int LOC_USER = 10;
|
||||||
|
|
||||||
|
public static final String SECTION_STRINGS = "teavm_str";
|
||||||
|
public static final String SECTION_FILES = "teavm_file";
|
||||||
|
public static final String SECTION_PACKAGES = "teavm_pkg";
|
||||||
|
public static final String SECTION_CLASSES = "teavm_cls";
|
||||||
|
public static final String SECTION_METHODS = "teavm_mtd";
|
||||||
|
public static final String SECTION_LINES = "teavm_line";
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class DebugFilesBuilder extends DebugSectionBuilder implements DebugFiles
|
||||||
private ObjectIntMap<FileData> fileMap = new ObjectIntHashMap<>();
|
private ObjectIntMap<FileData> fileMap = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
public DebugFilesBuilder(DebugStrings strings) {
|
public DebugFilesBuilder(DebugStrings strings) {
|
||||||
|
super(DebugConstants.SECTION_FILES);
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,19 +62,19 @@ public class DebugInfoBuilder {
|
||||||
|
|
||||||
public List<WasmCustomSection> build() {
|
public List<WasmCustomSection> build() {
|
||||||
var result = new ArrayList<WasmCustomSection>();
|
var result = new ArrayList<WasmCustomSection>();
|
||||||
addSection(result, "teavm_str", strings);
|
addSection(result, strings);
|
||||||
addSection(result, "teavm_file", files);
|
addSection(result, files);
|
||||||
addSection(result, "teavm_pkg", packages);
|
addSection(result, packages);
|
||||||
addSection(result, "teavm_classes", classes);
|
addSection(result, classes);
|
||||||
addSection(result, "teavm_methods", methods);
|
addSection(result, methods);
|
||||||
addSection(result, "teavm_line", lines);
|
addSection(result, lines);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSection(List<WasmCustomSection> sections, String name, DebugSectionBuilder builder) {
|
private void addSection(List<WasmCustomSection> sections, DebugSectionBuilder builder) {
|
||||||
if (builder.isEmpty()) {
|
if (builder.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sections.add(new WasmCustomSection(name, builder.build()));
|
sections.add(new WasmCustomSection(builder.name(), builder.build()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ public class DebugLinesBuilder extends DebugSectionBuilder implements DebugLines
|
||||||
private Deque<State> states = new ArrayDeque<>();
|
private Deque<State> states = new ArrayDeque<>();
|
||||||
|
|
||||||
public DebugLinesBuilder(DebugFiles files, DebugMethods methods) {
|
public DebugLinesBuilder(DebugFiles files, DebugMethods methods) {
|
||||||
|
super(DebugConstants.SECTION_LINES);
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.methods = methods;
|
this.methods = methods;
|
||||||
}
|
}
|
||||||
|
@ -45,8 +46,8 @@ public class DebugLinesBuilder extends DebugSectionBuilder implements DebugLines
|
||||||
@Override
|
@Override
|
||||||
public void location(String file, int line) {
|
public void location(String file, int line) {
|
||||||
if (Objects.equals(file, this.file) && this.ptr != lastWrittenPtr && this.line != line) {
|
if (Objects.equals(file, this.file) && this.ptr != lastWrittenPtr && this.line != line) {
|
||||||
if (this.ptr - lastWrittenPtr < 32 && Math.abs(this.line - line) <= 3) {
|
if (this.ptr - lastWrittenPtr < 32 && Math.abs(line - this.line) <= 3) {
|
||||||
blob.writeByte(DebugConstants.LOC_USER + 32 * (this.ptr - lastWrittenPtr) + (line - this.line) + 3);
|
blob.writeByte(DebugConstants.LOC_USER + (this.ptr - lastWrittenPtr) + 32 * (line - this.line + 3));
|
||||||
this.line = line;
|
this.line = line;
|
||||||
lastWrittenPtr = ptr;
|
lastWrittenPtr = ptr;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,6 +25,7 @@ public class DebugMethodsBuilder extends DebugSectionBuilder implements DebugMe
|
||||||
private ObjectIntMap<MethodReference> methods = new ObjectIntHashMap<>();
|
private ObjectIntMap<MethodReference> methods = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
public DebugMethodsBuilder(DebugClasses classes, DebugStrings strings) {
|
public DebugMethodsBuilder(DebugClasses classes, DebugStrings strings) {
|
||||||
|
super(DebugConstants.SECTION_METHODS);
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class DebugPackagesBuilder extends DebugSectionBuilder implements DebugPa
|
||||||
private ObjectIntMap<PackageInfo> packages = new ObjectIntHashMap<>();
|
private ObjectIntMap<PackageInfo> packages = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
public DebugPackagesBuilder(DebugStrings strings) {
|
public DebugPackagesBuilder(DebugStrings strings) {
|
||||||
|
super(DebugConstants.SECTION_PACKAGES);
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,21 @@ import org.teavm.backend.wasm.blob.BinaryDataConsumer;
|
||||||
import org.teavm.backend.wasm.blob.Blob;
|
import org.teavm.backend.wasm.blob.Blob;
|
||||||
|
|
||||||
public class DebugSectionBuilder {
|
public class DebugSectionBuilder {
|
||||||
|
private String name;
|
||||||
protected Blob blob = new Blob();
|
protected Blob blob = new Blob();
|
||||||
|
|
||||||
|
protected DebugSectionBuilder(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public void read(BinaryDataConsumer consumer) {
|
public void read(BinaryDataConsumer consumer) {
|
||||||
blob.newReader(consumer).readRemaining();
|
blob.newReader(consumer).readRemaining();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] build() {
|
public byte[] build() {
|
||||||
return blob.toArray();
|
return blob.toArray();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ import java.nio.charset.StandardCharsets;
|
||||||
public class DebugStringsBuilder extends DebugSectionBuilder implements DebugStrings {
|
public class DebugStringsBuilder extends DebugSectionBuilder implements DebugStrings {
|
||||||
private ObjectIntMap<String> strings = new ObjectIntHashMap<>();
|
private ObjectIntMap<String> strings = new ObjectIntHashMap<>();
|
||||||
|
|
||||||
|
public DebugStringsBuilder() {
|
||||||
|
super(DebugConstants.SECTION_STRINGS);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int stringPtr(String str) {
|
public int stringPtr(String str) {
|
||||||
var result = strings.getOrDefault(str, -1);
|
var result = strings.getOrDefault(str, -1);
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class ClassInfo {
|
||||||
|
public abstract PackageInfo pkg();
|
||||||
|
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public String fullName() {
|
||||||
|
var pkg = pkg();
|
||||||
|
return pkg != null ? pkg.fullName() + "." + name() : name();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class FileInfo {
|
||||||
|
public abstract FileInfo parent();
|
||||||
|
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public String fullName() {
|
||||||
|
var parent = this.parent();
|
||||||
|
return parent != null ? parent.fullName() + "/" + name() : name();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class InliningLocation {
|
||||||
|
private Location location;
|
||||||
|
private MethodInfo method;
|
||||||
|
|
||||||
|
public InliningLocation(Location location, MethodInfo method) {
|
||||||
|
this.location = location;
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location location() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class InstructionLocation {
|
||||||
|
private int address;
|
||||||
|
private Location location;
|
||||||
|
|
||||||
|
public InstructionLocation(int address, Location location) {
|
||||||
|
this.address = address;
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int address() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location location() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.common.CollectionUtil;
|
||||||
|
|
||||||
|
public class LineInfo {
|
||||||
|
private LineInfoSequence[] sequences;
|
||||||
|
private List<? extends LineInfoSequence> sequenceList;
|
||||||
|
|
||||||
|
public LineInfo(LineInfoSequence[] sequences) {
|
||||||
|
this.sequences = sequences.clone();
|
||||||
|
sequenceList = Collections.unmodifiableList(Arrays.asList(this.sequences));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends LineInfoSequence> sequences() {
|
||||||
|
return sequenceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineInfoSequence find(int address) {
|
||||||
|
var index = CollectionUtil.binarySearch(sequenceList, address, LineInfoSequence::endAddress);
|
||||||
|
if (index < 0) {
|
||||||
|
index = -index - 1;
|
||||||
|
}
|
||||||
|
if (index >= sequenceList.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var sequence = sequenceList.get(index);
|
||||||
|
return address >= sequence.startAddress() ? sequence : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dump(PrintStream out) {
|
||||||
|
for (var i = 0; i < sequences.length; ++i) {
|
||||||
|
var sequence = sequences[i];
|
||||||
|
out.println("Sequence " + i + ": " + sequence.method().fullName() + " [" + sequence.startAddress() + ".."
|
||||||
|
+ sequence.endAddress() + ")");
|
||||||
|
for (var location : sequence.unpack().locations()) {
|
||||||
|
out.println(" at " + location.address() + " " + location.location().file().fullName() + ":"
|
||||||
|
+ location.location().line());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class LineInfoCommand {
|
||||||
|
private int address;
|
||||||
|
|
||||||
|
public LineInfoCommand(int address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int address() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void acceptVisitor(LineInfoCommandVisitor visitor);
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class LineInfoCommandExecutor implements LineInfoCommandVisitor {
|
||||||
|
private FileInfo file;
|
||||||
|
private int line = 1;
|
||||||
|
private int address;
|
||||||
|
private InliningLocation inliningLocation;
|
||||||
|
|
||||||
|
public InstructionLocation createLocation() {
|
||||||
|
return file != null ? new InstructionLocation(address, new Location(file, line, inliningLocation)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoEnterCommand command) {
|
||||||
|
address = command.address();
|
||||||
|
inliningLocation = new InliningLocation(new Location(file, line, inliningLocation), command.method());
|
||||||
|
file = null;
|
||||||
|
line = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoExitCommand command) {
|
||||||
|
address = command.address();
|
||||||
|
inliningLocation = inliningLocation.location().inlining();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoFileCommand command) {
|
||||||
|
address = command.address();
|
||||||
|
file = command.file();
|
||||||
|
line = command.line();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LineInfoLineCommand command) {
|
||||||
|
address = command.address();
|
||||||
|
line = command.line();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public interface LineInfoCommandVisitor {
|
||||||
|
void visit(LineInfoEnterCommand command);
|
||||||
|
|
||||||
|
void visit(LineInfoExitCommand command);
|
||||||
|
|
||||||
|
void visit(LineInfoFileCommand command);
|
||||||
|
|
||||||
|
void visit(LineInfoLineCommand command);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class LineInfoEnterCommand extends LineInfoCommand {
|
||||||
|
private MethodInfo method;
|
||||||
|
|
||||||
|
public LineInfoEnterCommand(int address, MethodInfo method) {
|
||||||
|
super(address);
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class LineInfoExitCommand extends LineInfoCommand {
|
||||||
|
public LineInfoExitCommand(int address) {
|
||||||
|
super(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class LineInfoFileCommand extends LineInfoCommand {
|
||||||
|
private FileInfo file;
|
||||||
|
private int line;
|
||||||
|
|
||||||
|
public LineInfoFileCommand(int address, FileInfo file, int line) {
|
||||||
|
super(address);
|
||||||
|
this.file = file;
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo file() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int line() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class LineInfoLineCommand extends LineInfoCommand {
|
||||||
|
private int line;
|
||||||
|
|
||||||
|
public LineInfoLineCommand(int address, int line) {
|
||||||
|
super(address);
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int line() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptVisitor(LineInfoCommandVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LineInfoSequence {
|
||||||
|
private int startAddress;
|
||||||
|
private int endAddress;
|
||||||
|
private MethodInfo method;
|
||||||
|
private LineInfoCommand[] commands;
|
||||||
|
private List<LineInfoCommand> commandList;
|
||||||
|
|
||||||
|
public LineInfoSequence(int startAddress, int endAddress, MethodInfo method, LineInfoCommand[] commands) {
|
||||||
|
this.startAddress = startAddress;
|
||||||
|
this.endAddress = endAddress;
|
||||||
|
this.method = method;
|
||||||
|
this.commands = commands.clone();
|
||||||
|
commandList = Collections.unmodifiableList(Arrays.asList(this.commands));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int startAddress() {
|
||||||
|
return startAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int endAddress() {
|
||||||
|
return endAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends LineInfoCommand> commands() {
|
||||||
|
return commandList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineInfoUnpackedSequence unpack() {
|
||||||
|
var commandExecutor = new LineInfoCommandExecutor();
|
||||||
|
var locations = new ArrayList<InstructionLocation>();
|
||||||
|
for (var command : commands) {
|
||||||
|
command.acceptVisitor(commandExecutor);
|
||||||
|
locations.add(commandExecutor.createLocation());
|
||||||
|
}
|
||||||
|
return new LineInfoUnpackedSequence(startAddress, endAddress, method, locations);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.common.CollectionUtil;
|
||||||
|
|
||||||
|
public class LineInfoUnpackedSequence {
|
||||||
|
private int startAddress;
|
||||||
|
private int endAddress;
|
||||||
|
private MethodInfo method;
|
||||||
|
private List<? extends InstructionLocation> locations;
|
||||||
|
|
||||||
|
LineInfoUnpackedSequence(int startAddress, int endAddress, MethodInfo method,
|
||||||
|
List<? extends InstructionLocation> locations) {
|
||||||
|
this.startAddress = startAddress;
|
||||||
|
this.endAddress = endAddress;
|
||||||
|
this.method = method;
|
||||||
|
this.locations = locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int startAddress() {
|
||||||
|
return startAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int endAddress() {
|
||||||
|
return endAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<? extends InstructionLocation> locations() {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstructionLocation find(int address) {
|
||||||
|
if (address < startAddress || address >= endAddress) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var index = CollectionUtil.binarySearch(locations, address, InstructionLocation::address);
|
||||||
|
if (index < 0) {
|
||||||
|
index = -index;
|
||||||
|
}
|
||||||
|
return locations.get(Math.min(locations.size() - 1, index));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public class Location {
|
||||||
|
private FileInfo file;
|
||||||
|
private int line;
|
||||||
|
private InliningLocation inlining;
|
||||||
|
|
||||||
|
public Location(FileInfo file, int line, InliningLocation inlining) {
|
||||||
|
this.file = file;
|
||||||
|
this.line = line;
|
||||||
|
this.inlining = inlining;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo file() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int line() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InliningLocation inlining() {
|
||||||
|
return inlining;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class MethodInfo {
|
||||||
|
public abstract ClassInfo cls();
|
||||||
|
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public String fullName() {
|
||||||
|
return cls().fullName() + "." + name();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.info;
|
||||||
|
|
||||||
|
public abstract class PackageInfo {
|
||||||
|
public abstract PackageInfo parent();
|
||||||
|
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public final String fullName() {
|
||||||
|
var parent = this.parent();
|
||||||
|
return parent != null ? parent.fullName() + "." + name() : name();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
class BlobReader {
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
|
public BlobReader(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ClassInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.PackageInfo;
|
||||||
|
|
||||||
|
public class DebugClassParser extends DebugSectionParser {
|
||||||
|
private DebugStringParser strings;
|
||||||
|
private DebugPackageParser packages;
|
||||||
|
private ClassInfoImpl[] classes;
|
||||||
|
|
||||||
|
public DebugClassParser(DebugStringParser strings, DebugPackageParser packages) {
|
||||||
|
super(DebugConstants.SECTION_CLASSES, strings, packages);
|
||||||
|
this.strings = strings;
|
||||||
|
this.packages = packages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassInfo getClass(int ptr) {
|
||||||
|
return classes[ptr];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
var classes = new ArrayList<ClassInfoImpl>();
|
||||||
|
while (ptr < data.length) {
|
||||||
|
var pkg = packages.getPackage(readLEB());
|
||||||
|
var name = strings.getString(readLEB());
|
||||||
|
classes.add(new ClassInfoImpl(pkg, name));
|
||||||
|
}
|
||||||
|
this.classes = classes.toArray(new ClassInfoImpl[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassInfoImpl extends ClassInfo {
|
||||||
|
private PackageInfo pkg;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
ClassInfoImpl(PackageInfo pkg, String name) {
|
||||||
|
this.pkg = pkg;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PackageInfo pkg() {
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.FileInfo;
|
||||||
|
|
||||||
|
public class DebugFileParser extends DebugSectionParser {
|
||||||
|
private DebugStringParser strings;
|
||||||
|
private FileInfoImpl[] files;
|
||||||
|
|
||||||
|
public DebugFileParser(DebugStringParser strings) {
|
||||||
|
super(DebugConstants.SECTION_FILES, strings);
|
||||||
|
this.strings = strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo getFile(int index) {
|
||||||
|
return index > 0 ? files[index - 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
var builders = new ArrayList<FileBuilder>();
|
||||||
|
while (ptr < data.length) {
|
||||||
|
var parent = readLEB();
|
||||||
|
var nameIndex = readLEB();
|
||||||
|
var name = (nameIndex & 1) == 0
|
||||||
|
? strings.getString(nameIndex >>> 1)
|
||||||
|
: strings.getString(nameIndex >>> 1) + "." + strings.getString(readLEB());
|
||||||
|
builders.add(new FileBuilder(parent, name));
|
||||||
|
}
|
||||||
|
this.files = new FileInfoImpl[builders.size()];
|
||||||
|
for (var i = 0; i < files.length; ++i) {
|
||||||
|
files[i] = new FileInfoImpl(builders.get(i).name);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < files.length; ++i) {
|
||||||
|
files[i].parent = getFile(builders.get(i).parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FileInfoImpl extends FileInfo {
|
||||||
|
private FileInfo parent;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private FileInfoImpl(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileInfo parent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FileBuilder {
|
||||||
|
int parent;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
FileBuilder(int parent, String name) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
|
import org.teavm.common.Promise;
|
||||||
|
|
||||||
|
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<>();
|
||||||
|
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
|
||||||
|
private DebugLinesParser lines;
|
||||||
|
|
||||||
|
public DebugInfoParser(DebugInfoReader reader) {
|
||||||
|
this.reader = reader;
|
||||||
|
|
||||||
|
var strings = addSection(new DebugStringParser());
|
||||||
|
var files = addSection(new DebugFileParser(strings));
|
||||||
|
var packages = addSection(new DebugPackageParser(strings));
|
||||||
|
var classes = addSection(new DebugClassParser(strings, packages));
|
||||||
|
var methods = addSection(new DebugMethodParser(strings, classes));
|
||||||
|
lines = addSection(new DebugLinesParser(files, methods));
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends DebugSectionParser> T addSection(T section) {
|
||||||
|
sectionParsers.put(section.name(), section);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineInfo getLineInfo() {
|
||||||
|
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 {
|
||||||
|
return skip(sectionSize, "Error skipping section " + sectionCode + " of size " + sectionSize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Promise<Void> parseSection(int limit) {
|
||||||
|
return readString("Error reading custom section name").thenAsync(name -> {
|
||||||
|
var sectionParser = sectionParsers.get(name);
|
||||||
|
System.out.println("Section '" + name + "': " + (sectionParser != null ? "parsed" : "unknown"));
|
||||||
|
if (sectionParser != null) {
|
||||||
|
return readBytes(limit - pos, "Error reading section '" + name + "' content")
|
||||||
|
.thenVoid(sectionParser::parse);
|
||||||
|
} else {
|
||||||
|
return skip(limit - pos, "Error skipping section '" + name + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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,24 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import org.teavm.common.Promise;
|
||||||
|
|
||||||
|
public interface DebugInfoReader {
|
||||||
|
Promise<Integer> skip(int amount);
|
||||||
|
|
||||||
|
Promise<Integer> read(byte[] buffer, int offset, int count);
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.FileInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoEnterCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoExitCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoFileCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoLineCommand;
|
||||||
|
import org.teavm.backend.wasm.debug.info.LineInfoSequence;
|
||||||
|
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
||||||
|
|
||||||
|
public class DebugLinesParser extends DebugSectionParser {
|
||||||
|
private DebugFileParser files;
|
||||||
|
private DebugMethodParser methods;
|
||||||
|
private LineInfo lineInfo;
|
||||||
|
private List<LineInfoSequence> sequences = new ArrayList<>();
|
||||||
|
private List<LineInfoCommand> commands = new ArrayList<>();
|
||||||
|
private Deque<State> stateStack = new ArrayDeque<>();
|
||||||
|
private FileInfo file;
|
||||||
|
private int line = 1;
|
||||||
|
private int address;
|
||||||
|
private MethodInfo currentMethod;
|
||||||
|
private int sequenceStartAddress;
|
||||||
|
|
||||||
|
public DebugLinesParser(
|
||||||
|
DebugFileParser files,
|
||||||
|
DebugMethodParser methods
|
||||||
|
) {
|
||||||
|
super(DebugConstants.SECTION_LINES, files, methods);
|
||||||
|
this.files = files;
|
||||||
|
this.methods = methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineInfo getLineInfo() {
|
||||||
|
return lineInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
while (ptr < data.length) {
|
||||||
|
var cmd = data[ptr++] & 0xFF;
|
||||||
|
switch (cmd) {
|
||||||
|
case DebugConstants.LOC_START:
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
case DebugConstants.LOC_END:
|
||||||
|
end();
|
||||||
|
break;
|
||||||
|
case DebugConstants.LOC_LINE:
|
||||||
|
moveToLine();
|
||||||
|
break;
|
||||||
|
case DebugConstants.LOC_FILE:
|
||||||
|
moveToFile();
|
||||||
|
break;
|
||||||
|
case DebugConstants.LOC_PTR:
|
||||||
|
advanceAddress();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (cmd >= DebugConstants.LOC_USER) {
|
||||||
|
specialCommand(cmd);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Invalid command at " + ptr + ": " + cmd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineInfo = new LineInfo(sequences.toArray(new LineInfoSequence[0]));
|
||||||
|
sequences = null;
|
||||||
|
commands = null;
|
||||||
|
stateStack = null;
|
||||||
|
currentMethod = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
var method = methods.getMethod(readLEB());
|
||||||
|
if (currentMethod == null) {
|
||||||
|
currentMethod = method;
|
||||||
|
sequenceStartAddress = address;
|
||||||
|
} else {
|
||||||
|
stateStack.push(new State(file, line));
|
||||||
|
commands.add(new LineInfoEnterCommand(address, method));
|
||||||
|
}
|
||||||
|
file = null;
|
||||||
|
line = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void end() {
|
||||||
|
if (stateStack.isEmpty()) {
|
||||||
|
if (currentMethod != null) {
|
||||||
|
sequences.add(new LineInfoSequence(sequenceStartAddress, address, currentMethod,
|
||||||
|
commands.toArray(new LineInfoCommand[0])));
|
||||||
|
}
|
||||||
|
commands.clear();
|
||||||
|
currentMethod = null;
|
||||||
|
file = null;
|
||||||
|
line = 1;
|
||||||
|
} else {
|
||||||
|
var state = stateStack.pop();
|
||||||
|
file = state.file;
|
||||||
|
line = state.line;
|
||||||
|
commands.add(new LineInfoExitCommand(address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveToLine() {
|
||||||
|
line = line + readSignedLEB();
|
||||||
|
if (currentMethod != null) {
|
||||||
|
commands.add(new LineInfoLineCommand(address, line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveToFile() {
|
||||||
|
file = files.getFile(readLEB());
|
||||||
|
line = 1;
|
||||||
|
if (ptr < data.length && data[ptr] == DebugConstants.LOC_LINE) {
|
||||||
|
++ptr;
|
||||||
|
line = line + readSignedLEB();
|
||||||
|
}
|
||||||
|
if (currentMethod != null) {
|
||||||
|
commands.add(new LineInfoFileCommand(address, file, line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advanceAddress() {
|
||||||
|
address += readLEB();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void specialCommand(int cmd) {
|
||||||
|
cmd -= DebugConstants.LOC_USER;
|
||||||
|
address += cmd % 32;
|
||||||
|
cmd /= 32;
|
||||||
|
line += cmd - 3;
|
||||||
|
if (currentMethod != null) {
|
||||||
|
commands.add(new LineInfoFileCommand(address, file, line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class State {
|
||||||
|
FileInfo file;
|
||||||
|
int line;
|
||||||
|
|
||||||
|
State(FileInfo file, int line) {
|
||||||
|
this.file = file;
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.ClassInfo;
|
||||||
|
import org.teavm.backend.wasm.debug.info.MethodInfo;
|
||||||
|
|
||||||
|
public class DebugMethodParser extends DebugSectionParser {
|
||||||
|
private DebugStringParser strings;
|
||||||
|
private DebugClassParser classes;
|
||||||
|
private MethodInfoImpl[] methods;
|
||||||
|
|
||||||
|
public DebugMethodParser(DebugStringParser strings, DebugClassParser classes) {
|
||||||
|
super(DebugConstants.SECTION_METHODS);
|
||||||
|
this.strings = strings;
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo getMethod(int ptr) {
|
||||||
|
return methods[ptr];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
var methods = new ArrayList<MethodInfoImpl>();
|
||||||
|
while (ptr < data.length) {
|
||||||
|
var cls = classes.getClass(readLEB());
|
||||||
|
var name = strings.getString(readLEB());
|
||||||
|
methods.add(new MethodInfoImpl(cls, name));
|
||||||
|
}
|
||||||
|
this.methods = methods.toArray(new MethodInfoImpl[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodInfoImpl extends MethodInfo {
|
||||||
|
private ClassInfo cls;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
MethodInfoImpl(ClassInfo cls, String name) {
|
||||||
|
this.cls = cls;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassInfo cls() {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
import org.teavm.backend.wasm.debug.info.PackageInfo;
|
||||||
|
|
||||||
|
public class DebugPackageParser extends DebugSectionParser {
|
||||||
|
private DebugStringParser strings;
|
||||||
|
private PackageInfoImpl[] packages;
|
||||||
|
|
||||||
|
public DebugPackageParser(DebugStringParser strings) {
|
||||||
|
super(DebugConstants.SECTION_PACKAGES, strings);
|
||||||
|
this.strings = strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PackageInfo getPackage(int index) {
|
||||||
|
return index == 0 ? null : packages[index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
var packageBuilders = new ArrayList<PackageBuilder>();
|
||||||
|
while (ptr < data.length) {
|
||||||
|
packageBuilders.add(new PackageBuilder(readLEB(), strings.getString(readLEB())));
|
||||||
|
}
|
||||||
|
packages = new PackageInfoImpl[packageBuilders.size()];
|
||||||
|
for (var i = 0; i < packages.length; ++i) {
|
||||||
|
packages[i] = new PackageInfoImpl(packageBuilders.get(i).name);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < packages.length; ++i) {
|
||||||
|
packages[i].parent = getPackage(packageBuilders.get(i).parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PackageInfoImpl extends PackageInfo {
|
||||||
|
private PackageInfo parent;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
PackageInfoImpl(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PackageInfo parent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PackageBuilder {
|
||||||
|
int parent;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
PackageBuilder(int parent, String name) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class DebugSectionParser {
|
||||||
|
private String name;
|
||||||
|
private List<DebugSectionParser> dependantSections = new ArrayList<>();
|
||||||
|
byte[] data;
|
||||||
|
int ptr;
|
||||||
|
private boolean ready;
|
||||||
|
private int unsatisfiedDependencies;
|
||||||
|
|
||||||
|
protected DebugSectionParser(String name, DebugSectionParser... dependencies) {
|
||||||
|
this.name = name;
|
||||||
|
for (var dependency : dependencies) {
|
||||||
|
dependency.dependantSections.add(this);
|
||||||
|
}
|
||||||
|
unsatisfiedDependencies = dependencies.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ready() {
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
if (unsatisfiedDependencies == 0) {
|
||||||
|
parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parse() {
|
||||||
|
doParse();
|
||||||
|
ready = true;
|
||||||
|
data = null;
|
||||||
|
for (var dependant : dependantSections) {
|
||||||
|
if (--dependant.unsatisfiedDependencies == 0 && dependant.data != null) {
|
||||||
|
dependant.parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void doParse();
|
||||||
|
|
||||||
|
protected int readLEB() {
|
||||||
|
var result = 0;
|
||||||
|
var shift = 0;
|
||||||
|
while (true) {
|
||||||
|
var b = data[ptr++];
|
||||||
|
result |= (b & 0x7F) << shift;
|
||||||
|
if ((b & 0x80) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int readSignedLEB() {
|
||||||
|
var result = 0;
|
||||||
|
var shift = 0;
|
||||||
|
while (true) {
|
||||||
|
var b = data[ptr++];
|
||||||
|
result |= (b & 0x7F) << shift;
|
||||||
|
if ((b & 0x80) == 0) {
|
||||||
|
if ((b & 0x40) != 0) {
|
||||||
|
result |= -1 << (shift + 7);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String readString() {
|
||||||
|
var length = readLEB();
|
||||||
|
var result = new String(data, ptr, length, StandardCharsets.UTF_8);
|
||||||
|
ptr += length;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.teavm.backend.wasm.debug.DebugConstants;
|
||||||
|
|
||||||
|
public class DebugStringParser extends DebugSectionParser {
|
||||||
|
private String[] strings;
|
||||||
|
|
||||||
|
public DebugStringParser() {
|
||||||
|
super(DebugConstants.SECTION_STRINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doParse() {
|
||||||
|
var strings = new ArrayList<String>();
|
||||||
|
while (ptr < data.length) {
|
||||||
|
strings.add(readString());
|
||||||
|
}
|
||||||
|
this.strings = strings.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(int ref) {
|
||||||
|
return strings[ref];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.debug.parser;
|
||||||
|
|
||||||
|
class ParseException extends RuntimeException {
|
||||||
|
final String message;
|
||||||
|
final int pos;
|
||||||
|
|
||||||
|
ParseException(String message, int pos) {
|
||||||
|
super("Error parsing at " + pos + ": " + message);
|
||||||
|
this.message = message;
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
}
|
54
core/src/main/java/org/teavm/common/CollectionUtil.java
Normal file
54
core/src/main/java/org/teavm/common/CollectionUtil.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public final class CollectionUtil {
|
||||||
|
private CollectionUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K extends Comparable<K>> int binarySearch(List<? extends T> list, K key,
|
||||||
|
Function<T, K> keyExtractor) {
|
||||||
|
return binarySearch(list, key, keyExtractor, Comparable::compareTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, K> int binarySearch(List<? extends T> list, K key, Function<T, K> keyExtractor,
|
||||||
|
Comparator<K> comparator) {
|
||||||
|
var l = 0;
|
||||||
|
var u = list.size() - 1;
|
||||||
|
while (true) {
|
||||||
|
var i = (l + u) / 2;
|
||||||
|
var t = keyExtractor.apply(list.get(i));
|
||||||
|
var cmp = comparator.compare(t, key);
|
||||||
|
if (cmp == 0) {
|
||||||
|
return i;
|
||||||
|
} else if (cmp > 0) {
|
||||||
|
l = i + i;
|
||||||
|
if (l > u) {
|
||||||
|
return -i - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u = i - 1;
|
||||||
|
if (u < l) {
|
||||||
|
return -i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,14 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.common;
|
package org.teavm.common;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class Promise<T> {
|
public class Promise<T> {
|
||||||
|
private static ThreadLocal<Queue<Runnable>> processing = new ThreadLocal<>();
|
||||||
public static final Promise<Void> VOID = Promise.of(null);
|
public static final Promise<Void> VOID = Promise.of(null);
|
||||||
|
|
||||||
private T value;
|
private T value;
|
||||||
|
@ -187,37 +190,50 @@ public class Promise<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
<S> void passValue(Function<? super T, S> f, Promise<? super S> target) {
|
<S> void passValue(Function<? super T, S> f, Promise<? super S> target) {
|
||||||
if (state == State.COMPLETED) {
|
runAction(() -> {
|
||||||
|
if (state == State.COMPLETED) {
|
||||||
|
S next;
|
||||||
|
try {
|
||||||
|
next = f.apply(value);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
target.completeWithError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target.complete(next);
|
||||||
|
} else {
|
||||||
|
target.completeWithError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<S> void passValueAsync(Function<T, Promise<S>> f, Promise<S> target) {
|
||||||
|
runAction(() -> {
|
||||||
|
if (state == State.COMPLETED) {
|
||||||
|
Promise<S> next;
|
||||||
|
try {
|
||||||
|
next = f.apply(value);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
target.completeWithError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target.completeAsync(next);
|
||||||
|
} else {
|
||||||
|
target.completeWithError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<S> void passError(Function<Throwable, S> f, Promise<? super S> target) {
|
||||||
|
runAction(() -> {
|
||||||
S next;
|
S next;
|
||||||
try {
|
try {
|
||||||
next = f.apply(value);
|
next = f.apply(error);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
target.completeWithError(e);
|
target.completeWithError(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
target.complete(next);
|
target.complete(next);
|
||||||
} else {
|
});
|
||||||
target.completeWithError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<S> void passValueAsync(Function<T, Promise<S>> f, Promise<S> target) {
|
|
||||||
if (state == State.COMPLETED) {
|
|
||||||
target.completeAsync(f.apply(value));
|
|
||||||
} else {
|
|
||||||
target.completeWithError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<S> void passError(Function<Throwable, S> f, Promise<? super S> target) {
|
|
||||||
S next;
|
|
||||||
try {
|
|
||||||
next = f.apply(error);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
target.completeWithError(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
target.complete(next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void complete(T value) {
|
void complete(T value) {
|
||||||
|
@ -245,21 +261,23 @@ public class Promise<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeImpl(T value) {
|
private void completeImpl(T value) {
|
||||||
state = State.COMPLETED;
|
runAction(() -> {
|
||||||
this.value = value;
|
state = State.COMPLETED;
|
||||||
|
this.value = value;
|
||||||
|
|
||||||
if (thenList != null) {
|
if (thenList != null) {
|
||||||
List<Then<T>> list = thenList;
|
List<Then<T>> list = thenList;
|
||||||
thenList = null;
|
thenList = null;
|
||||||
for (Then<T> then : list) {
|
for (Then<T> then : list) {
|
||||||
if (then.promise) {
|
if (then.promise) {
|
||||||
passValueAsync((Function<T, Promise<Object>>) then.f, (Promise<Object>) then.target);
|
passValueAsync((Function<T, Promise<Object>>) then.f, (Promise<Object>) then.target);
|
||||||
} else {
|
} else {
|
||||||
passValue(then.f, (Promise<Object>) then.target);
|
passValue(then.f, (Promise<Object>) then.target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catchList = null;
|
||||||
catchList = null;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void completeWithError(Throwable e) {
|
void completeWithError(Throwable e) {
|
||||||
|
@ -270,19 +288,36 @@ public class Promise<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void completeWithErrorImpl(Throwable e) {
|
void completeWithErrorImpl(Throwable e) {
|
||||||
state = State.ERRORED;
|
runAction(() -> {
|
||||||
this.error = e;
|
state = State.ERRORED;
|
||||||
|
this.error = e;
|
||||||
|
|
||||||
if (catchList != null) {
|
if (catchList != null) {
|
||||||
List<Catch> list = catchList;
|
List<Catch> list = catchList;
|
||||||
thenList = null;
|
thenList = null;
|
||||||
for (Catch c : list) {
|
for (Catch c : list) {
|
||||||
passError(c.f, (Promise<Object>) c.target);
|
passError(c.f, (Promise<Object>) c.target);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
thenList = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runAction(Runnable action) {
|
||||||
|
var queue = processing.get();
|
||||||
|
if (queue != null) {
|
||||||
|
queue.add(action);
|
||||||
} else {
|
} else {
|
||||||
e.printStackTrace();
|
queue = new ArrayDeque<>();
|
||||||
|
queue.add(action);
|
||||||
|
processing.set(queue);
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
queue.remove().run();
|
||||||
|
}
|
||||||
|
processing.remove();
|
||||||
}
|
}
|
||||||
thenList = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user