mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Wasm: support local variables in debugger
This commit is contained in:
parent
7e3197429d
commit
6808d9e517
|
@ -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)) {
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user