From 87d63168d2b15c6d5083662633999929c2293be1 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 5 Dec 2022 20:03:57 +0100 Subject: [PATCH] Wasm: working on control flow analyzer for debugger --- .../wasm/debug/info/ControlFlowInfo.java | 71 ++++++ .../backend/wasm/debug/info/DebugInfo.java | 56 +++++ .../wasm/debug/info/FunctionControlFlow.java | 40 ++++ .../info/FunctionControlFlowBuilder.java | 43 ++++ .../info/FunctionControlFlowIterator.java | 66 ++++++ .../backend/wasm/debug/info/LineInfo.java | 8 +- .../wasm/debug/parser/ControlFlowParser.java | 206 ++++++++++++++++++ .../wasm/debug/parser/DebugInfoParser.java | 26 ++- .../wasm/debug/parser/DebugLinesParser.java | 8 +- .../DisassemblyCodeSectionListener.java | 2 +- .../backend/wasm/parser/CodeListener.java | 2 +- .../wasm/parser/CodeSectionListener.java | 23 +- .../wasm/parser/CodeSectionParser.java | 12 +- .../java/org/teavm/debugging/Debugger.java | 61 ++++-- tools/chrome-rdp/pom.xml | 5 + 15 files changed, 569 insertions(+), 60 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/info/ControlFlowInfo.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/info/DebugInfo.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlow.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowBuilder.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowIterator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/debug/parser/ControlFlowParser.java diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/ControlFlowInfo.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/ControlFlowInfo.java new file mode 100644 index 000000000..ada47104d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/ControlFlowInfo.java @@ -0,0 +1,71 @@ +/* + * 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 ControlFlowInfo { + private List functions; + + public ControlFlowInfo(FunctionControlFlow[] functions) { + this.functions = Collections.unmodifiableList(Arrays.asList(functions)); + } + + public List functions() { + return functions; + } + + public FunctionControlFlow find(int address) { + var index = CollectionUtil.binarySearch(functions, address, FunctionControlFlow::end); + if (index < 0) { + index = -index - 1; + } + if (index > functions.size()) { + return null; + } + var fn = functions.get(index); + if (fn.start() > address) { + return fn; + } + return fn; + } + + public void dump(PrintStream out) { + for (int i = 0; i < functions.size(); ++i) { + var range = functions.get(i); + out.println("Range #" + i + ": [" + range.start() + ".." + range.end() + ")"); + for (var iter = range.iterator(); iter.hasNext(); iter.next()) { + out.print(" " + Integer.toHexString(iter.address())); + if (iter.isCall()) { + out.print(" (call)"); + } + out.print(" -> "); + var followers = iter.targets(); + for (var j = 0; j < followers.length; ++j) { + if (j > 0) { + out.print(", "); + } + out.print(Integer.toHexString(followers[j])); + } + out.println(); + } + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/DebugInfo.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/DebugInfo.java new file mode 100644 index 000000000..c41449567 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/DebugInfo.java @@ -0,0 +1,56 @@ +/* + * 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; + +public class DebugInfo { + private LineInfo lines; + private ControlFlowInfo controlFlow; + private int offset; + + public DebugInfo(LineInfo lines, ControlFlowInfo controlFlow, int offset) { + this.lines = lines; + this.controlFlow = controlFlow; + this.offset = offset; + } + + public LineInfo lines() { + return lines; + } + + public ControlFlowInfo controlFlow() { + return controlFlow; + } + + public int offset() { + return offset; + } + + public void dump(PrintStream out) { + if (offset != 0) { + out.println("Code section offset: " + Integer.toHexString(offset)); + } + if (lines != null) { + out.println("LINES"); + lines.dump(out); + } + if (controlFlow != null) { + out.println("CONTROL FLOW"); + controlFlow.dump(out); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlow.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlow.java new file mode 100644 index 000000000..aa43442cf --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlow.java @@ -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 FunctionControlFlow { + private int start; + private int end; + int[] offsets; + int[] data; + + FunctionControlFlow(int[] offsets, int[] data) { + this.offsets = offsets; + this.data = data; + } + + public int start() { + return start; + } + + public int end() { + return end; + } + + public FunctionControlFlowIterator iterator() { + return new FunctionControlFlowIterator(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowBuilder.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowBuilder.java new file mode 100644 index 000000000..56849275f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowBuilder.java @@ -0,0 +1,43 @@ +/* + * 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 com.carrotsearch.hppc.IntArrayList; + +public class FunctionControlFlowBuilder { + private IntArrayList offsets = new IntArrayList(); + private IntArrayList data = new IntArrayList(); + + public void addBranch(int position, int[] targets) { + offsets.add(data.size() << 1); + data.add(position); + data.add(targets); + } + + public void addCall(int position, int[] targets) { + offsets.add((data.size() << 1) | 1); + data.add(position); + data.add(targets); + } + + public boolean isEmpty() { + return offsets.isEmpty(); + } + + public FunctionControlFlow build() { + return new FunctionControlFlow(offsets.toArray(), data.toArray()); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowIterator.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowIterator.java new file mode 100644 index 000000000..547c7d796 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/FunctionControlFlowIterator.java @@ -0,0 +1,66 @@ +/* + * 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.Arrays; + +public class FunctionControlFlowIterator { + private FunctionControlFlow controlFlow; + private int index; + private boolean valid; + private int offset; + private boolean isCall; + + FunctionControlFlowIterator(FunctionControlFlow controlFlow) { + this.controlFlow = controlFlow; + } + + public boolean hasNext() { + return index < controlFlow.offsets.length; + } + + public void next() { + ++index; + valid = false; + } + + private void fill() { + if (!valid) { + valid = true; + var n = controlFlow.offsets[index]; + offset = n >>> 1; + isCall = (n & 1) != 0; + } + } + + public int address() { + fill(); + return controlFlow.data[offset]; + } + + public int[] targets() { + fill(); + var nextOffset = index < controlFlow.offsets.length - 1 + ? controlFlow.offsets[index + 1] >>> 1 + : controlFlow.data.length; + return Arrays.copyOfRange(controlFlow.data, offset + 1, nextOffset); + } + + public boolean isCall() { + fill(); + return isCall; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/info/LineInfo.java b/core/src/main/java/org/teavm/backend/wasm/debug/info/LineInfo.java index 98ca9e12f..bc2e8960d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/debug/info/LineInfo.java +++ b/core/src/main/java/org/teavm/backend/wasm/debug/info/LineInfo.java @@ -22,20 +22,14 @@ import java.util.List; import org.teavm.common.CollectionUtil; public class LineInfo { - private int offset; private LineInfoSequence[] sequences; private List sequenceList; - public LineInfo(int offset, LineInfoSequence[] sequences) { - this.offset = offset; + public LineInfo(LineInfoSequence[] sequences) { this.sequences = sequences.clone(); sequenceList = Collections.unmodifiableList(Arrays.asList(this.sequences)); } - public int offset() { - return offset; - } - public List sequences() { return sequenceList; } diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/parser/ControlFlowParser.java b/core/src/main/java/org/teavm/backend/wasm/debug/parser/ControlFlowParser.java new file mode 100644 index 000000000..0f1848248 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/debug/parser/ControlFlowParser.java @@ -0,0 +1,206 @@ +/* + * 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 com.carrotsearch.hppc.IntArrayList; +import java.util.ArrayList; +import java.util.List; +import org.teavm.backend.wasm.debug.info.ControlFlowInfo; +import org.teavm.backend.wasm.debug.info.FunctionControlFlow; +import org.teavm.backend.wasm.debug.info.FunctionControlFlowBuilder; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.parser.AddressListener; +import org.teavm.backend.wasm.parser.BranchOpcode; +import org.teavm.backend.wasm.parser.CodeListener; +import org.teavm.backend.wasm.parser.CodeSectionListener; +import org.teavm.backend.wasm.parser.Opcode; + +public class ControlFlowParser implements CodeSectionListener, CodeListener, AddressListener { + private int previousAddress; + private int address; + private FunctionControlFlowBuilder cfb; + private List branches = new ArrayList<>(); + private List ranges = new ArrayList<>(); + private List pendingBranches = new ArrayList<>(); + private List blocks = new ArrayList<>(); + + public ControlFlowInfo build() { + return new ControlFlowInfo(ranges.toArray(new FunctionControlFlow[0])); + } + + @Override + public void address(int address) { + previousAddress = this.address; + this.address = address; + flush(); + } + + @Override + public boolean functionStart(int index, int size) { + cfb = new FunctionControlFlowBuilder(); + return true; + } + + @Override + public CodeListener code() { + return this; + } + + @Override + public int startBlock(boolean loop, WasmType type) { + return startBlock(loop); + } + + @Override + public int startConditionalBlock(WasmType type) { + return startBlock(false); + } + + private int startBlock(boolean loop) { + var token = blocks.size(); + var branch = !loop ? newBranch(false) : null; + var block = new Block(branch, address); + blocks.add(block); + if (branch != null) { + block.pendingBranches.add(branch); + } + return token; + } + + @Override + public void startElseSection(int token) { + var block = blocks.get(blocks.size() - 1); + var lastBranch = branches.get(branches.size() - 1); + if (lastBranch.address != previousAddress) { + lastBranch = new Branch(previousAddress, false); + branches.add(lastBranch); + } + block.pendingBranches.add(lastBranch); + block.branch.targets.add(address); + } + + @Override + public void endBlock(int token, boolean loop) { + var block = blocks.remove(blocks.size() - 1); + pendingBranches.addAll(block.pendingBranches); + if (loop) { + var branch = newBranch(false); + branch.targets.add(block.address); + } + } + + @Override + public void call(int functionIndex) { + call(); + } + + @Override + public void indirectCall(int typeIndex, int tableIndex) { + call(); + } + + private void call() { + newPendingBranch(true); + } + + @Override + public void opcode(Opcode opcode) { + switch (opcode) { + case RETURN: + case UNREACHABLE: { + newBranch(false); + break; + } + default: + break; + } + } + + @Override + public void branch(BranchOpcode opcode, int depth, int target) { + var branch = newBranch(false); + if (opcode == BranchOpcode.BR_IF) { + pendingBranches.add(branch); + } + var block = blocks.get(target); + block.pendingBranches.add(branch); + } + + @Override + public void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) { + var branch = newPendingBranch(false); + for (var target : targets) { + blocks.get(target).pendingBranches.add(branch); + } + blocks.get(defaultTarget).pendingBranches.add(branch); + } + + private Branch newPendingBranch(boolean isCall) { + var branch = newBranch(isCall); + pendingBranches.add(branch); + return branch; + } + + private Branch newBranch(boolean isCall) { + var branch = new Branch(address, isCall); + branches.add(branch); + return branch; + } + + private void flush() { + for (var branch : pendingBranches) { + branch.targets.add(address); + } + pendingBranches.clear(); + } + + @Override + public void functionEnd() { + for (var branch : branches) { + if (branch.isCall) { + cfb.addCall(branch.address, branch.targets.toArray()); + } else { + cfb.addBranch(branch.address, branch.targets.toArray()); + } + } + ranges.add(cfb.build()); + branches.clear(); + pendingBranches.clear(); + blocks.clear(); + } + + private static class Block { + Branch branch; + final int address; + List pendingBranches = new ArrayList<>(); + + Block(Branch branch, int address) { + this.branch = branch; + this.address = address; + } + } + + private static class Branch { + final int address; + final IntArrayList targets = new IntArrayList(); + final boolean isCall; + + Branch(int address, boolean isCall) { + this.address = address; + this.isCall = isCall; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugInfoParser.java b/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugInfoParser.java index cb7b8d088..7ca083967 100644 --- a/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugInfoParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugInfoParser.java @@ -21,7 +21,9 @@ import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; -import org.teavm.backend.wasm.debug.info.LineInfo; +import org.teavm.backend.wasm.debug.info.ControlFlowInfo; +import org.teavm.backend.wasm.debug.info.DebugInfo; +import org.teavm.backend.wasm.parser.CodeSectionParser; import org.teavm.backend.wasm.parser.ModuleParser; import org.teavm.common.AsyncInputStream; import org.teavm.common.ByteArrayAsyncInputStream; @@ -29,6 +31,8 @@ import org.teavm.common.ByteArrayAsyncInputStream; public class DebugInfoParser extends ModuleParser { private Map sectionParsers = new HashMap<>(); private DebugLinesParser lines; + private ControlFlowInfo controlFlow; + private int offset; public DebugInfoParser(AsyncInputStream reader) { super(reader); @@ -45,8 +49,8 @@ public class DebugInfoParser extends ModuleParser { return section; } - public LineInfo getLineInfo() { - return lines.getLineInfo(); + public DebugInfo getDebugInfo() { + return new DebugInfo(lines.getLineInfo(), controlFlow, offset); } @Override @@ -55,11 +59,19 @@ public class DebugInfoParser extends ModuleParser { var parser = sectionParsers.get(name); return parser != null ? parser::parse : null; } else if (code == 10) { - lines.setOffset(pos); + this.offset = pos; + return this::parseCode; } return null; } + private void parseCode(byte[] data) { + var builder = new ControlFlowParser(); + var codeParser = new CodeSectionParser(builder, builder); + codeParser.parse(data); + controlFlow = builder.build(); + } + public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("Pass single argument - path to wasm file"); @@ -69,9 +81,9 @@ public class DebugInfoParser extends ModuleParser { 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); + var debugInfo = parser.getDebugInfo(); + if (debugInfo != null) { + debugInfo.dump(System.out); } else { System.out.println("No debug information found"); } diff --git a/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugLinesParser.java b/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugLinesParser.java index 124a690fd..6b525364b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugLinesParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/debug/parser/DebugLinesParser.java @@ -42,7 +42,6 @@ public class DebugLinesParser extends DebugSectionParser { private int address; private MethodInfo currentMethod; private int sequenceStartAddress; - private int offset; public DebugLinesParser( DebugFileParser files, @@ -57,13 +56,8 @@ public class DebugLinesParser extends DebugSectionParser { return lineInfo; } - public void setOffset(int offset) { - this.offset = offset; - } - @Override protected void doParse() { - address = offset; while (ptr < data.length) { var cmd = data[ptr++] & 0xFF; switch (cmd) { @@ -91,7 +85,7 @@ public class DebugLinesParser extends DebugSectionParser { break; } } - lineInfo = new LineInfo(offset, sequences.toArray(new LineInfoSequence[0])); + lineInfo = new LineInfo(sequences.toArray(new LineInfoSequence[0])); sequences = null; commands = null; stateStack = null; diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java index 373eb537c..144656ba4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java @@ -152,7 +152,7 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect } @Override - public void endBlock(int token) { + public void endBlock(int token, boolean loop) { writer.address(address).outdent().write("end (; $label_" + token + " ;)").eol(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java index 2f5e18940..45966ba46 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java @@ -40,7 +40,7 @@ public interface CodeListener { default void startElseSection(int token) { } - default void endBlock(int token) { + default void endBlock(int token, boolean loop) { } default void branch(BranchOpcode opcode, int depth, int target) { diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionListener.java index b8f3ba91c..8166516c3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionListener.java @@ -18,17 +18,26 @@ package org.teavm.backend.wasm.parser; import org.teavm.backend.wasm.model.WasmType; public interface CodeSectionListener { - void sectionStart(int functionCount); + default void sectionStart(int functionCount) { + } - boolean functionStart(int index, int size); + default boolean functionStart(int index, int size) { + return false; + } - void localsStart(int count); + default void localsStart(int count) { + } - void local(int start, int count, WasmType type); + default void local(int start, int count, WasmType type) { + } - CodeListener code(); + default CodeListener code() { + return null; + } - void functionEnd(); + default void functionEnd() { + } - void sectionEnd(); + default void sectionEnd() { + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java index 58de59a91..1cb664c90 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java @@ -68,10 +68,10 @@ public class CodeSectionParser { var end = ptr + functionSize; if (listener.functionStart(index, functionSize)) { parseLocals(); - } - codeListener = listener.code(); - if (codeListener != null) { - parseCode(); + codeListener = listener.code(); + if (codeListener != null) { + parseCode(); + } } ptr = end; reportAddress(); @@ -625,7 +625,7 @@ public class CodeSectionParser { } blockStack.remove(blockStack.size() - 1); reportAddress(); - codeListener.endBlock(token); + codeListener.endBlock(token, isLoop); ++ptr; return true; } @@ -656,7 +656,7 @@ public class CodeSectionParser { } blockStack.remove(blockStack.size() - 1); reportAddress(); - codeListener.endBlock(token); + codeListener.endBlock(token, false); ++ptr; return true; } diff --git a/core/src/main/java/org/teavm/debugging/Debugger.java b/core/src/main/java/org/teavm/debugging/Debugger.java index bdfb4ed12..3ede0404e 100644 --- a/core/src/main/java/org/teavm/debugging/Debugger.java +++ b/core/src/main/java/org/teavm/debugging/Debugger.java @@ -25,7 +25,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.teavm.backend.wasm.debug.info.LineInfo; +import org.teavm.backend.wasm.debug.info.DebugInfo; import org.teavm.backend.wasm.debug.info.LineInfoFileCommand; import org.teavm.backend.wasm.debug.info.MethodInfo; import org.teavm.backend.wasm.debug.parser.DebugInfoParser; @@ -55,9 +55,9 @@ public class Debugger { private List temporaryBreakpoints = new ArrayList<>(); private Map debugInformationMap = new HashMap<>(); private Map> debugInformationFileMap = new HashMap<>(); - private Map wasmLineInfoMap = new HashMap<>(); - private Map wasmScriptMap = new HashMap<>(); - private Map> wasmInfoFileMap = new HashMap<>(); + private Map wasmDebugInfoMap = new HashMap<>(); + private Map wasmScriptMap = new HashMap<>(); + private Map> wasmInfoFileMap = new HashMap<>(); private Map scriptMap = new HashMap<>(); private Map breakpointMap = new HashMap<>(); private Set breakpoints = new LinkedHashSet<>(); @@ -130,12 +130,14 @@ public class Debugger { } break; case WASM: { - var info = wasmLineInfoMap.get(script); + var info = wasmDebugInfoMap.get(script); if (info != null) { return enterMethod ? javaScriptDebugger.stepInto() : javaScriptDebugger.stepOver(); } break; } + case UNKNOWN: + break; } enterMethod = false; first = false; @@ -240,7 +242,7 @@ public class Debugger { return list != null ? new ArrayList<>(list) : Collections.emptyList(); } - private List wasmLineInfoBySource(String sourceFile) { + private List wasmLineInfoBySource(String sourceFile) { var list = wasmInfoFileMap.get(sourceFile); return list != null ? new ArrayList<>(list) : Collections.emptyList(); } @@ -315,16 +317,19 @@ public class Debugger { })); } } - for (var wasmLineInfo : wasmLineInfoBySource(location.getFileName())) { - for (var sequence : wasmLineInfo.sequences()) { + for (var wasmDebugInfo : wasmLineInfoBySource(location.getFileName())) { + if (wasmDebugInfo.lines() == null) { + continue; + } + for (var sequence : wasmDebugInfo.lines().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), - 0, loc.address()); + var jsLocation = new JavaScriptLocation(wasmScriptMap.get(wasmDebugInfo), + 0, loc.address() + wasmDebugInfo.offset()); promises.add(javaScriptDebugger.createBreakpoint(jsLocation).thenVoid(jsBreakpoint -> { jsBreakpoints.add(jsBreakpoint); breakpointMap.put(jsBreakpoint, breakpoint); @@ -427,15 +432,20 @@ public class Debugger { } private List mapWasmFrames(JavaScriptCallFrame frame) { - var lineInfo = wasmLineInfoMap.get(frame.getLocation().getScript()); + var debugInfo = wasmDebugInfoMap.get(frame.getLocation().getScript()); + if (debugInfo == null) { + return Collections.emptyList(); + } + var lineInfo = debugInfo.lines(); if (lineInfo == null) { return Collections.emptyList(); } - var sequence = lineInfo.find(frame.getLocation().getColumn()); + var address = frame.getLocation().getColumn() - debugInfo.offset(); + var sequence = lineInfo.find(address); if (sequence == null) { return Collections.emptyList(); } - var instructionLocation = sequence.unpack().find(frame.getLocation().getColumn()); + var instructionLocation = sequence.unpack().find(address); if (instructionLocation == null) { return Collections.emptyList(); } @@ -525,15 +535,18 @@ public class Debugger { } 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) { - var file = ((LineInfoFileCommand) command).file(); - if (file != null) { - addWasmInfoFile(file.fullName(), parser.getLineInfo()); + var debugInfo = parser.getDebugInfo(); + if (debugInfo != null) { + wasmDebugInfoMap.put(script, debugInfo); + wasmScriptMap.put(debugInfo, script); + if (debugInfo.lines() != null) { + for (var sequence : debugInfo.lines().sequences()) { + for (var command : sequence.commands()) { + if (command instanceof LineInfoFileCommand) { + var file = ((LineInfoFileCommand) command).file(); + if (file != null) { + addWasmInfoFile(file.fullName(), debugInfo); + } } } } @@ -542,13 +555,13 @@ public class Debugger { }); } - private void addWasmInfoFile(String sourceFile, LineInfo wasmLineInfo) { + private void addWasmInfoFile(String sourceFile, DebugInfo debugInfo) { var list = wasmInfoFileMap.get(sourceFile); if (list == null) { list = new HashSet<>(); wasmInfoFileMap.put(sourceFile, list); } - list.add(wasmLineInfo); + list.add(debugInfo); allSourceFiles.add(sourceFile); } diff --git a/tools/chrome-rdp/pom.xml b/tools/chrome-rdp/pom.xml index ceb25de37..0d817e463 100644 --- a/tools/chrome-rdp/pom.xml +++ b/tools/chrome-rdp/pom.xml @@ -50,6 +50,11 @@ javax.websocket javax.websocket-api + + com.carrotsearch + hppc + true + com.fasterxml.jackson.core