Wasm: support local variables in debugger

This commit is contained in:
Alexey Andreev 2022-12-13 21:18:41 +01:00
parent 7e3197429d
commit 6808d9e517
22 changed files with 522 additions and 15 deletions

View File

@ -552,8 +552,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
var writer = new WasmBinaryWriter(); var writer = new WasmBinaryWriter();
var debugBuilder = debugging ? new DebugInfoBuilder() : null; var debugBuilder = debugging ? new DebugInfoBuilder() : null;
var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator, dwarfClassGen, var renderer = new WasmBinaryRenderer(
debugBuilder != null ? debugBuilder.lines() : null); writer, version, obfuscated, dwarfGenerator, dwarfClassGen,
debugBuilder != null ? debugBuilder.lines() : null,
debugBuilder != null ? debugBuilder.variables() : null
);
renderer.render(module, buildDebug(dwarfGenerator, dwarfClassGen, debugBuilder)); renderer.render(module, buildDebug(dwarfGenerator, dwarfClassGen, debugBuilder));
try (OutputStream output = buildTarget.createResource(outputName)) { try (OutputStream output = buildTarget.createResource(outputName)) {

View File

@ -32,4 +32,5 @@ public final class DebugConstants {
public static final String SECTION_CLASSES = "teavm_cls"; public static final String SECTION_CLASSES = "teavm_cls";
public static final String SECTION_METHODS = "teavm_mtd"; public static final String SECTION_METHODS = "teavm_mtd";
public static final String SECTION_LINES = "teavm_line"; public static final String SECTION_LINES = "teavm_line";
public static final String SECTION_VARIABLES = "teavm_var";
} }

View File

@ -25,6 +25,7 @@ public class DebugInfoBuilder {
private DebugPackagesBuilder packages; private DebugPackagesBuilder packages;
private DebugClassesBuilder classes; private DebugClassesBuilder classes;
private DebugMethodsBuilder methods; private DebugMethodsBuilder methods;
private DebugVariablesBuilder variables;
private DebugLinesBuilder lines; private DebugLinesBuilder lines;
public DebugInfoBuilder() { public DebugInfoBuilder() {
@ -33,6 +34,7 @@ public class DebugInfoBuilder {
packages = new DebugPackagesBuilder(strings); packages = new DebugPackagesBuilder(strings);
classes = new DebugClassesBuilder(packages, strings); classes = new DebugClassesBuilder(packages, strings);
methods = new DebugMethodsBuilder(classes, strings); methods = new DebugMethodsBuilder(classes, strings);
variables = new DebugVariablesBuilder(strings);
lines = new DebugLinesBuilder(files, methods); lines = new DebugLinesBuilder(files, methods);
} }
@ -52,11 +54,15 @@ public class DebugInfoBuilder {
return classes; return classes;
} }
public DebugMethodsBuilder methods() { public DebugMethods methods() {
return methods; return methods;
} }
public DebugLinesBuilder lines() { public DebugVariables variables() {
return variables;
}
public DebugLines lines() {
return lines; return lines;
} }
@ -67,6 +73,7 @@ public class DebugInfoBuilder {
addSection(result, packages); addSection(result, packages);
addSection(result, classes); addSection(result, classes);
addSection(result, methods); addSection(result, methods);
addSection(result, variables);
addSection(result, lines); addSection(result, lines);
return result; return result;
} }

View File

@ -19,7 +19,7 @@ import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap; import com.carrotsearch.hppc.ObjectIntMap;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
public class DebugMethodsBuilder extends DebugSectionBuilder implements DebugMethods { public class DebugMethodsBuilder extends DebugSectionBuilder implements DebugMethods {
private DebugClasses classes; private DebugClasses classes;
private DebugStrings strings; private DebugStrings strings;
private ObjectIntMap<MethodReference> methods = new ObjectIntHashMap<>(); private ObjectIntMap<MethodReference> methods = new ObjectIntHashMap<>();

View File

@ -0,0 +1,28 @@
/*
* 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;
import org.teavm.backend.wasm.debug.info.VariableType;
public interface DebugVariables {
void startSequence(int pointer);
void type(String name, VariableType type);
void range(String name, int start, int end, int pointer);
void endSequence();
}

View File

@ -0,0 +1,101 @@
/*
* 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;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.teavm.backend.wasm.debug.info.VariableType;
public class DebugVariablesBuilder extends DebugSectionBuilder implements DebugVariables {
private DebugStrings strings;
private int sequenceStart;
private int lastSequenceStart;
private Map<String, VarInfo> variables = new LinkedHashMap<>();
public DebugVariablesBuilder(DebugStrings strings) {
super(DebugConstants.SECTION_VARIABLES);
this.strings = strings;
}
@Override
public void startSequence(int pointer) {
sequenceStart = pointer;
}
@Override
public void type(String name, VariableType type) {
getInfo(name).type = type;
}
@Override
public void range(String name, int start, int end, int pointer) {
getInfo(name).ranges.add(new Range(start, end, pointer));
}
private VarInfo getInfo(String name) {
var info = variables.get(name);
if (info == null) {
info = new VarInfo();
variables.put(name, info);
}
return info;
}
@Override
public void endSequence() {
if (variables.isEmpty()) {
return;
}
blob.writeLEB(sequenceStart - lastSequenceStart);
lastSequenceStart = sequenceStart;
blob.writeLEB(variables.size());
for (var variable : variables.entrySet()) {
blob.writeLEB(strings.stringPtr(variable.getKey()));
var info = variable.getValue();
blob.writeLEB(info.type.ordinal());
blob.writeLEB(info.ranges.size());
var lastPtr = sequenceStart;
var lastPointer = 0;
for (var range : info.ranges) {
blob.writeSLEB(range.start - lastPtr);
blob.writeLEB(range.end - range.start);
blob.writeSLEB(range.pointer - lastPointer);
lastPointer = range.pointer;
lastPointer = range.end;
}
}
variables.clear();
}
private static class VarInfo {
VariableType type = VariableType.UNDEFINED;
List<Range> ranges = new ArrayList<>();
}
private static class Range {
int start;
int end;
int pointer;
Range(int start, int end, int pointer) {
this.start = start;
this.end = end;
this.pointer = pointer;
}
}
}

View File

@ -18,16 +18,22 @@ package org.teavm.backend.wasm.debug.info;
import java.io.PrintStream; import java.io.PrintStream;
public class DebugInfo { public class DebugInfo {
private VariablesInfo variables;
private LineInfo lines; private LineInfo lines;
private ControlFlowInfo controlFlow; private ControlFlowInfo controlFlow;
private int offset; private int offset;
public DebugInfo(LineInfo lines, ControlFlowInfo controlFlow, int offset) { public DebugInfo(VariablesInfo variables, LineInfo lines, ControlFlowInfo controlFlow, int offset) {
this.variables = variables;
this.lines = lines; this.lines = lines;
this.controlFlow = controlFlow; this.controlFlow = controlFlow;
this.offset = offset; this.offset = offset;
} }
public VariablesInfo variables() {
return variables;
}
public LineInfo lines() { public LineInfo lines() {
return lines; return lines;
} }
@ -52,5 +58,9 @@ public class DebugInfo {
out.println("CONTROL FLOW"); out.println("CONTROL FLOW");
controlFlow.dump(out); controlFlow.dump(out);
} }
if (variables != null) {
out.println("VARIABLES");
variables.dump(out);
}
} }
} }

View File

@ -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;
import java.util.Collection;
public abstract class VariableInfo {
public abstract String name();
public abstract VariableType type();
public abstract Collection<? extends VariableRangeInfo> ranges();
}

View File

@ -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 VariableRangeInfo {
public abstract VariableInfo variable();
public abstract int start();
public abstract int end();
public abstract int index();
}

View File

@ -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 enum VariableType {
INT,
LONG,
FLOAT,
DOUBLE,
OBJECT,
ADDRESS,
UNDEFINED
}

View File

@ -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 java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public abstract class VariablesInfo {
public abstract List<? extends VariableRangeInfo> ranges();
public Collection<? extends VariableRangeInfo> find(int address) {
var result = new ArrayList<VariableRangeInfo>();
for (var range : ranges()) {
if (address >= range.start() && address < range.end()) {
result.add(range);
}
}
return result;
}
public void dump(PrintStream out) {
for (var range : ranges()) {
out.println(range.variable().name() + ": " + range.variable().type() + " - "
+ Integer.toHexString(range.start()) + ".." + Integer.toHexString(range.end()) + " at "
+ range.index());
}
}
}

View File

@ -31,6 +31,7 @@ import org.teavm.common.ByteArrayAsyncInputStream;
public class DebugInfoParser extends ModuleParser { public class DebugInfoParser extends ModuleParser {
private Map<String, DebugSectionParser> sectionParsers = new HashMap<>(); private Map<String, DebugSectionParser> sectionParsers = new HashMap<>();
private DebugLinesParser lines; private DebugLinesParser lines;
private DebugVariablesParser variables;
private ControlFlowInfo controlFlow; private ControlFlowInfo controlFlow;
private int offset; private int offset;
@ -41,6 +42,7 @@ public class DebugInfoParser extends ModuleParser {
var packages = addSection(new DebugPackageParser(strings)); var packages = addSection(new DebugPackageParser(strings));
var classes = addSection(new DebugClassParser(strings, packages)); var classes = addSection(new DebugClassParser(strings, packages));
var methods = addSection(new DebugMethodParser(strings, classes)); var methods = addSection(new DebugMethodParser(strings, classes));
variables = addSection(new DebugVariablesParser(strings));
lines = addSection(new DebugLinesParser(files, methods)); lines = addSection(new DebugLinesParser(files, methods));
} }
@ -50,7 +52,7 @@ public class DebugInfoParser extends ModuleParser {
} }
public DebugInfo getDebugInfo() { public DebugInfo getDebugInfo() {
return new DebugInfo(lines.getLineInfo(), controlFlow, offset); return new DebugInfo(variables.getVariablesInfo(), lines.getLineInfo(), controlFlow, offset);
} }
@Override @Override

View File

@ -0,0 +1,154 @@
/*
* 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 java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.teavm.backend.wasm.debug.DebugConstants;
import org.teavm.backend.wasm.debug.info.VariableInfo;
import org.teavm.backend.wasm.debug.info.VariableRangeInfo;
import org.teavm.backend.wasm.debug.info.VariableType;
import org.teavm.backend.wasm.debug.info.VariablesInfo;
public class DebugVariablesParser extends DebugSectionParser {
private static final VariableType[] typeByOrdinal = VariableType.values();
private DebugStringParser strings;
private VariablesInfoImpl variablesInfo;
public DebugVariablesParser(DebugStringParser strings) {
super(DebugConstants.SECTION_VARIABLES, strings);
this.strings = strings;
}
public VariablesInfoImpl getVariablesInfo() {
return variablesInfo;
}
@Override
protected void doParse() {
var lastAddress = 0;
var ranges = new ArrayList<VariableRangeInfo>();
var localRanges = new ArrayList<VariableRangeInfoImpl>();
while (ptr < data.length) {
var baseAddress = lastAddress + readLEB();
lastAddress = baseAddress;
var variableCount = readLEB();
for (var i = 0; i < variableCount; ++i) {
var name = strings.getString(readLEB());
var type = typeByOrdinal[readLEB()];
var rangeCount = readLEB();
var varInfo = new VariableInfoImpl(name, type);
var address = baseAddress;
var lastLocation = 0;
for (var j = 0; j < rangeCount; ++j) {
var start = address + readSignedLEB();
var size = readLEB();
var end = start + size;
address = end;
var location = lastLocation + readSignedLEB();
lastLocation = location;
var rangeInfo = new VariableRangeInfoImpl(varInfo, start, end, location);
ranges.add(rangeInfo);
localRanges.add(rangeInfo);
}
localRanges.clear();
varInfo.ranges = Collections.unmodifiableList(Arrays.asList(
localRanges.toArray(new VariableRangeInfoImpl[0])));
}
}
ranges.sort(Comparator.comparing(VariableRangeInfo::start));
this.variablesInfo = new VariablesInfoImpl(Collections.unmodifiableList(Arrays.asList(
ranges.toArray(new VariableRangeInfoImpl[0]))));
}
private static class VariablesInfoImpl extends VariablesInfo {
private List<VariableRangeInfoImpl> ranges;
VariablesInfoImpl(List<VariableRangeInfoImpl> ranges) {
this.ranges = ranges;
}
@Override
public List<? extends VariableRangeInfo> ranges() {
return ranges;
}
}
private static class VariableInfoImpl extends VariableInfo {
private String name;
private VariableType type;
private List<VariableRangeInfoImpl> ranges;
VariableInfoImpl(String name, VariableType type) {
this.name = name;
this.type = type;
}
@Override
public String name() {
return name;
}
@Override
public VariableType type() {
return type;
}
@Override
public Collection<? extends VariableRangeInfo> ranges() {
return ranges;
}
}
private static class VariableRangeInfoImpl extends VariableRangeInfo {
private VariableInfoImpl variableInfo;
private int start;
private int end;
private int index;
VariableRangeInfoImpl(VariableInfoImpl variableInfo, int start, int end, int index) {
this.variableInfo = variableInfo;
this.start = start;
this.end = end;
this.index = index;
}
@Override
public VariableInfo variable() {
return variableInfo;
}
@Override
public int start() {
return start;
}
@Override
public int end() {
return end;
}
@Override
public int index() {
return index;
}
}
}

View File

@ -47,13 +47,13 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.blob.Blob; import org.teavm.backend.wasm.blob.Blob;
import org.teavm.backend.wasm.debug.info.VariableType;
import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; import org.teavm.backend.wasm.dwarf.DwarfAbbreviation;
import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter;
import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.PrimitiveType; import org.teavm.model.PrimitiveType;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.util.VariableType;
public class DwarfClassGenerator { public class DwarfClassGenerator {
private static final ValueType objectType = ValueType.object("java.lang.Object"); private static final ValueType objectType = ValueType.object("java.lang.Object");

View File

@ -32,9 +32,9 @@ import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_SUBPROGRAM;
import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_VARIABLE; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_VARIABLE;
import org.teavm.backend.wasm.blob.Blob; import org.teavm.backend.wasm.blob.Blob;
import org.teavm.backend.wasm.blob.Marker; import org.teavm.backend.wasm.blob.Marker;
import org.teavm.backend.wasm.debug.info.VariableType;
import org.teavm.backend.wasm.dwarf.DwarfAbbreviation; import org.teavm.backend.wasm.dwarf.DwarfAbbreviation;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.model.util.VariableType;
public class DwarfFunctionGenerator { public class DwarfFunctionGenerator {
private DwarfClassGenerator classGen; private DwarfClassGenerator classGen;

View File

@ -21,6 +21,7 @@ import org.teavm.ast.VariableNode;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.BinaryWriter;
import org.teavm.backend.wasm.debug.info.VariableType;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
@ -87,7 +88,7 @@ public class WasmGenerator {
? WasmGeneratorUtil.mapType(variable.getType()) ? WasmGeneratorUtil.mapType(variable.getType())
: WasmType.INT32; : WasmType.INT32;
var local = new WasmLocal(type, variable.getName()); var local = new WasmLocal(type, variable.getName());
local.setJavaType(variable.getType()); local.setJavaType(mapType(variable.getType()));
function.add(local); function.add(local);
} }
@ -107,6 +108,21 @@ public class WasmGenerator {
return function; return function;
} }
private VariableType mapType(org.teavm.model.util.VariableType type) {
switch (type) {
case INT:
return VariableType.INT;
case LONG:
return VariableType.LONG;
case FLOAT:
return VariableType.FLOAT;
case DOUBLE:
return VariableType.DOUBLE;
default:
return VariableType.OBJECT;
}
}
public WasmFunction generateNative(MethodReference methodReference) { public WasmFunction generateNative(MethodReference methodReference) {
WasmFunction function = context.getFunction(names.forMethod(methodReference)); WasmFunction function = context.getFunction(names.forMethod(methodReference));

View File

@ -16,7 +16,7 @@
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
import java.util.Objects; import java.util.Objects;
import org.teavm.model.util.VariableType; import org.teavm.backend.wasm.debug.info.VariableType;
public class WasmLocal { public class WasmLocal {
WasmFunction function; WasmFunction function;

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.debug.DebugLines;
import org.teavm.backend.wasm.debug.DebugVariables;
import org.teavm.backend.wasm.generate.DwarfClassGenerator; import org.teavm.backend.wasm.generate.DwarfClassGenerator;
import org.teavm.backend.wasm.generate.DwarfFunctionGenerator; import org.teavm.backend.wasm.generate.DwarfFunctionGenerator;
import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator;
@ -59,15 +60,18 @@ public class WasmBinaryRenderer {
private DwarfGenerator dwarfGenerator; private DwarfGenerator dwarfGenerator;
private DwarfFunctionGenerator dwarfFunctionGen; private DwarfFunctionGenerator dwarfFunctionGen;
private DebugLines debugLines; private DebugLines debugLines;
private DebugVariables debugVariables;
public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated,
DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen, DebugLines debugLines) { DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen, DebugLines debugLines,
DebugVariables debugVariables) {
this.output = output; this.output = output;
this.version = version; this.version = version;
this.obfuscated = obfuscated; this.obfuscated = obfuscated;
this.dwarfGenerator = dwarfGenerator; this.dwarfGenerator = dwarfGenerator;
dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null; dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null;
this.debugLines = debugLines; this.debugLines = debugLines;
this.debugVariables = debugVariables;
} }
public void render(WasmModule module) { public void render(WasmModule module) {
@ -335,10 +339,24 @@ public class WasmBinaryRenderer {
if (dwarfFunctionGen != null) { if (dwarfFunctionGen != null) {
dwarfFunctionGen.end(code.getPosition()); dwarfFunctionGen.end(code.getPosition());
} }
if (debugVariables != null) {
writeDebugVariables(function, offset, code.getPosition());
}
return code.getData(); return code.getData();
} }
private void writeDebugVariables(WasmFunction function, int offset, int size) {
debugVariables.startSequence(offset);
for (var local : function.getLocalVariables()) {
if (local.getName() != null && local.getJavaType() != null) {
debugVariables.type(local.getName(), local.getJavaType());
debugVariables.range(local.getName(), offset, offset + size, local.getIndex());
}
}
debugVariables.endSequence();
}
private void renderInitializer(WasmBinaryWriter output, int value) { private void renderInitializer(WasmBinaryWriter output, int value) {
output.writeByte(0x41); output.writeByte(0x41);
output.writeLEB(value); output.writeLEB(value);

View File

@ -17,6 +17,7 @@ package org.teavm.debugging;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.debug.info.DebugInfo;
import org.teavm.common.Promise; import org.teavm.common.Promise;
import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.SourceLocation; import org.teavm.debugging.information.SourceLocation;
@ -31,14 +32,16 @@ public class CallFrame {
private MethodReference method; private MethodReference method;
private Promise<Map<String, Variable>> variables; private Promise<Map<String, Variable>> variables;
private DebugInformation debugInformation; private DebugInformation debugInformation;
private DebugInfo wasmDebugInfo;
CallFrame(Debugger debugger, JavaScriptCallFrame originalFrame, SourceLocation location, MethodReference method, CallFrame(Debugger debugger, JavaScriptCallFrame originalFrame, SourceLocation location, MethodReference method,
DebugInformation debugInformation) { DebugInformation debugInformation, DebugInfo wasmDebugInfo) {
this.debugger = debugger; this.debugger = debugger;
this.originalCallFrame = originalFrame; this.originalCallFrame = originalFrame;
this.location = location; this.location = location;
this.method = method; this.method = method;
this.debugInformation = debugInformation; this.debugInformation = debugInformation;
this.wasmDebugInfo = wasmDebugInfo;
} }
public Debugger getDebugger() { public Debugger getDebugger() {
@ -65,6 +68,8 @@ public class CallFrame {
if (variables == null) { if (variables == null) {
if (debugInformation != null) { if (debugInformation != null) {
variables = debugger.createVariables(originalCallFrame, debugInformation); variables = debugger.createVariables(originalCallFrame, debugInformation);
} else if (wasmDebugInfo != null) {
variables = debugger.createVariables(originalCallFrame, wasmDebugInfo);
} else { } else {
variables = Promise.of(Collections.emptyMap()); variables = Promise.of(Collections.emptyMap());
} }

View File

@ -403,6 +403,7 @@ public class Debugger {
for (var jsFrame : javaScriptDebugger.getCallStack()) { for (var jsFrame : javaScriptDebugger.getCallStack()) {
List<SourceLocationWithMethod> locations; List<SourceLocationWithMethod> locations;
DebugInformation debugInformation = null; DebugInformation debugInformation = null;
DebugInfo wasmDebugInfo = null;
switch (jsFrame.getLocation().getScript().getLanguage()) { switch (jsFrame.getLocation().getScript().getLanguage()) {
case JS: case JS:
debugInformation = debugInformationMap.get(jsFrame.getLocation().getScript()); debugInformation = debugInformationMap.get(jsFrame.getLocation().getScript());
@ -410,6 +411,9 @@ public class Debugger {
break; break;
case WASM: case WASM:
locations = mapWasmFrames(jsFrame); locations = mapWasmFrames(jsFrame);
if (!locations.isEmpty()) {
wasmDebugInfo = wasmDebugInfoMap.get(jsFrame.getLocation().getScript());
}
break; break;
default: default:
locations = Collections.emptyList(); locations = Collections.emptyList();
@ -419,7 +423,7 @@ public class Debugger {
var loc = locWithMethod.loc; var loc = locWithMethod.loc;
var method = locWithMethod.method; var method = locWithMethod.method;
if (!locWithMethod.empty || !wasEmpty) { if (!locWithMethod.empty || !wasEmpty) {
frames.add(new CallFrame(this, jsFrame, loc, method, debugInformation)); frames.add(new CallFrame(this, jsFrame, loc, method, debugInformation, wasmDebugInfo));
} }
wasEmpty = locWithMethod.empty; wasEmpty = locWithMethod.empty;
} }
@ -513,6 +517,36 @@ public class Debugger {
}); });
} }
Promise<Map<String, Variable>> createVariables(JavaScriptCallFrame jsFrame, DebugInfo debugInfo) {
return jsFrame.getVariables().thenAsync(jsVariables -> {
var vars = new HashMap<String, Variable>();
var variables = debugInfo.variables();
var promises = new ArrayList<Promise<Void>>();
if (variables != null) {
var address = jsFrame.getLocation().getColumn();
address -= debugInfo.offset();
for (var range : variables.find(address)) {
var propertiesPromise = jsVariables.get("$var" + range.index()).getValue().getProperties();
promises.add(propertiesPromise
.then(prop -> {
var variable = prop.get("value");
return variable != null ? variable.getValue() : null;
})
.thenAsync(value -> {
if (value != null) {
var varValue = new Value(this, debugInfo, value);
var variable = new Variable(range.variable().name(), varValue);
vars.put(variable.getName(), variable);
}
return Promise.VOID;
})
);
}
}
return Promise.allVoid(promises).then(x -> vars);
});
}
private void addScript(JavaScriptScript script) { private void addScript(JavaScriptScript script) {
Promise<Void> promise; Promise<Void> promise;
switch (script.getLanguage()) { switch (script.getLanguage()) {

View File

@ -17,6 +17,7 @@ package org.teavm.debugging;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.debug.info.DebugInfo;
import org.teavm.common.Promise; import org.teavm.common.Promise;
import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.javascript.JavaScriptValue; import org.teavm.debugging.javascript.JavaScriptValue;
@ -25,6 +26,7 @@ import org.teavm.debugging.javascript.JavaScriptVariable;
public class Value { public class Value {
private Debugger debugger; private Debugger debugger;
private DebugInformation debugInformation; private DebugInformation debugInformation;
private DebugInfo wasmDebugInfo;
private JavaScriptValue jsValue; private JavaScriptValue jsValue;
private Promise<Map<String, Variable>> properties; private Promise<Map<String, Variable>> properties;
private Promise<String> type; private Promise<String> type;
@ -35,6 +37,12 @@ public class Value {
this.jsValue = jsValue; this.jsValue = jsValue;
} }
Value(Debugger debugger, DebugInfo wasmDebugInfo, JavaScriptValue jsValue) {
this.debugger = debugger;
this.wasmDebugInfo = wasmDebugInfo;
this.jsValue = jsValue;
}
private static boolean isNumeric(String str) { private static boolean isNumeric(String str) {
for (int i = 0; i < str.length(); ++i) { for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i); char c = str.charAt(i);

View File

@ -86,7 +86,6 @@ public class ChromeRDPDebugger extends BaseChromeRDPDebugger implements JavaScri
protected void onDetach() { protected void onDetach() {
suspended = false; suspended = false;
callStack = null; callStack = null;
} }
private Promise<Void> injectFunctions(int contextId) { private Promise<Void> injectFunctions(int contextId) {