mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-09 08:24:10 -08:00
Adds line CFG to debug information
This commit is contained in:
parent
fa1a620fdb
commit
b05d1a7524
|
@ -45,6 +45,7 @@ public class DebugInformation {
|
||||||
Mapping methodMapping;
|
Mapping methodMapping;
|
||||||
Mapping lineMapping;
|
Mapping lineMapping;
|
||||||
MultiMapping[] variableMappings;
|
MultiMapping[] variableMappings;
|
||||||
|
CFG[] controlFlowGraphs;
|
||||||
List<ClassMetadata> classesMetadata;
|
List<ClassMetadata> classesMetadata;
|
||||||
|
|
||||||
public String[] getCoveredSourceFiles() {
|
public String[] getCoveredSourceFiles() {
|
||||||
|
@ -121,6 +122,34 @@ public class DebugInformation {
|
||||||
return componentByKey(mapping, variableNames, location);
|
return componentByKey(mapping, variableNames, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SourceLocation[] getFollowingLines(SourceLocation location) {
|
||||||
|
Integer fileIndex = fileNameMap.get(location.getFileName());
|
||||||
|
if (fileIndex == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CFG cfg = controlFlowGraphs[fileIndex];
|
||||||
|
if (cfg == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int start = cfg.offsets[location.getLine()];
|
||||||
|
int end = cfg.offsets[location.getLine() + 1];
|
||||||
|
if (end - start == 1 && cfg.offsets[start] == -1) {
|
||||||
|
return new SourceLocation[0];
|
||||||
|
} else if (start == end) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
SourceLocation[] result = new SourceLocation[end - start];
|
||||||
|
for (int i = 0; i < result.length; ++i) {
|
||||||
|
int line = cfg.lines[i + start];
|
||||||
|
if (line >= 0) {
|
||||||
|
result[i] = new SourceLocation(fileNames[cfg.files[i + start]], line);
|
||||||
|
} else {
|
||||||
|
result[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFieldMeaning(String className, String jsName) {
|
public String getFieldMeaning(String className, String jsName) {
|
||||||
Integer classIndex = classNameMap.get(className);
|
Integer classIndex = classNameMap.get(className);
|
||||||
if (classIndex == null) {
|
if (classIndex == null) {
|
||||||
|
@ -391,4 +420,10 @@ public class DebugInformation {
|
||||||
Integer parentId;
|
Integer parentId;
|
||||||
Map<Integer, Integer> fieldMap = new HashMap<>();
|
Map<Integer, Integer> fieldMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CFG {
|
||||||
|
int[] lines;
|
||||||
|
int[] files;
|
||||||
|
int[] offsets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
private String currentFileName;
|
private String currentFileName;
|
||||||
private int currentClassMetadata = -1;
|
private int currentClassMetadata = -1;
|
||||||
private List<ClassMetadata> classesMetadata = new ArrayList<>();
|
private List<ClassMetadata> classesMetadata = new ArrayList<>();
|
||||||
|
private List<CFG> cfgs = new ArrayList<>();
|
||||||
private int currentLine;
|
private int currentLine;
|
||||||
|
|
||||||
public LocationProvider getLocationProvider() {
|
public LocationProvider getLocationProvider() {
|
||||||
|
@ -122,6 +123,26 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
metadata.fieldMap.put(jsIndex, fieldIndex);
|
metadata.fieldMap.put(jsIndex, fieldIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addSuccessors(SourceLocation location, SourceLocation[] successors) {
|
||||||
|
int fileIndex = files.index(location.getFileName());
|
||||||
|
if (cfgs.size() <= fileIndex) {
|
||||||
|
cfgs.addAll(Collections.<CFG>nCopies(fileIndex - cfgs.size() + 1, null));
|
||||||
|
}
|
||||||
|
CFG cfg = cfgs.get(fileIndex);
|
||||||
|
if (cfg == null) {
|
||||||
|
cfg = new CFG();
|
||||||
|
cfgs.set(fileIndex, cfg);
|
||||||
|
}
|
||||||
|
for (SourceLocation succ : successors) {
|
||||||
|
if (succ == null) {
|
||||||
|
cfg.add(location.getLine(), fileIndex, -1);
|
||||||
|
} else {
|
||||||
|
cfg.add(location.getLine(), files.index(succ.getFileName()), succ.getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DebugInformation getDebugInformation() {
|
public DebugInformation getDebugInformation() {
|
||||||
if (debugInformation == null) {
|
if (debugInformation == null) {
|
||||||
debugInformation = new DebugInformation();
|
debugInformation = new DebugInformation();
|
||||||
|
@ -156,6 +177,13 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
}
|
}
|
||||||
debugInformation.classesMetadata = builtMetadata;
|
debugInformation.classesMetadata = builtMetadata;
|
||||||
|
|
||||||
|
DebugInformation.CFG[] cfgs = new DebugInformation.CFG[files.list.size()];
|
||||||
|
for (int i = 0; i < this.cfgs.size(); ++i) {
|
||||||
|
if (this.cfgs.get(i) != null) {
|
||||||
|
cfgs[i] = this.cfgs.get(i).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debugInformation.rebuildFileDescriptions();
|
debugInformation.rebuildFileDescriptions();
|
||||||
debugInformation.rebuildMaps();
|
debugInformation.rebuildMaps();
|
||||||
}
|
}
|
||||||
|
@ -293,4 +321,59 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
int parentIndex;
|
int parentIndex;
|
||||||
Map<Integer, Integer> fieldMap = new HashMap<>();
|
Map<Integer, Integer> fieldMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CFG {
|
||||||
|
IntegerArray start = new IntegerArray(1);
|
||||||
|
IntegerArray next = new IntegerArray(1);
|
||||||
|
IntegerArray lines = new IntegerArray(1);
|
||||||
|
IntegerArray files = new IntegerArray(1);
|
||||||
|
|
||||||
|
public void add(int line, int succLine, int succFile) {
|
||||||
|
while (start.size() <= line) {
|
||||||
|
start.add(-1);
|
||||||
|
}
|
||||||
|
int ptr = start.get(line);
|
||||||
|
start.set(line, lines.size());
|
||||||
|
next.add(ptr);
|
||||||
|
lines.add(succLine);
|
||||||
|
files.add(succFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugInformation.CFG build() {
|
||||||
|
int[] offsets = new int[start.size() + 1];
|
||||||
|
IntegerArray linesData = new IntegerArray(1);
|
||||||
|
IntegerArray filesData = new IntegerArray(1);
|
||||||
|
for (int i = 0; i < start.size(); ++i) {
|
||||||
|
IntegerArray linesChunk = new IntegerArray(1);
|
||||||
|
IntegerArray filesChunk = new IntegerArray(1);
|
||||||
|
int ptr = start.get(i);
|
||||||
|
while (ptr > 0) {
|
||||||
|
linesChunk.add(lines.get(ptr));
|
||||||
|
filesChunk.add(files.get(ptr));
|
||||||
|
ptr = next.get(ptr);
|
||||||
|
}
|
||||||
|
long[] pairs = new long[linesChunk.size()];
|
||||||
|
for (int j = 0; j < pairs.length; ++j) {
|
||||||
|
pairs[j] = (filesChunk.get(j) << 32) | linesChunk.get(j);
|
||||||
|
}
|
||||||
|
Arrays.sort(pairs);
|
||||||
|
int distinctSize = 0;
|
||||||
|
for (int j = 0; j < pairs.length; ++j) {
|
||||||
|
long pair = pairs[j];
|
||||||
|
if (distinctSize == 0 || pair != pairs[distinctSize]) {
|
||||||
|
pairs[distinctSize++] = pair;
|
||||||
|
linesData.add((int)(pair >>> 32));
|
||||||
|
filesData.add((int)pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsets[i + 1] = linesData.size();
|
||||||
|
}
|
||||||
|
DebugInformation.CFG cfg = new DebugInformation.CFG();
|
||||||
|
cfg.offsets = offsets;
|
||||||
|
cfg.lines = lines.getAll();
|
||||||
|
cfg.files = files.getAll();
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,6 @@ public interface DebugInformationEmitter {
|
||||||
void addClass(String className, String parentName);
|
void addClass(String className, String parentName);
|
||||||
|
|
||||||
void addField(String fieldName, String jsName);
|
void addField(String fieldName, String jsName);
|
||||||
|
|
||||||
|
void addSuccessors(SourceLocation location, SourceLocation[] successors);
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.teavm.common.IntegerArray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -46,6 +47,7 @@ class DebugInformationReader {
|
||||||
debugInfo.methodMapping = readMapping();
|
debugInfo.methodMapping = readMapping();
|
||||||
debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length);
|
debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length);
|
||||||
debugInfo.classesMetadata = readClassesMetadata(debugInfo.classNames.length);
|
debugInfo.classesMetadata = readClassesMetadata(debugInfo.classNames.length);
|
||||||
|
debugInfo.controlFlowGraphs = readCFGs(debugInfo.fileNames.length);
|
||||||
debugInfo.rebuildFileDescriptions();
|
debugInfo.rebuildFileDescriptions();
|
||||||
debugInfo.rebuildMaps();
|
debugInfo.rebuildMaps();
|
||||||
return debugInfo;
|
return debugInfo;
|
||||||
|
@ -82,6 +84,47 @@ class DebugInformationReader {
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DebugInformation.CFG[] readCFGs(int count) throws IOException {
|
||||||
|
DebugInformation.CFG[] cfgs = new DebugInformation.CFG[count];
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
cfgs[i] = readCFG(i);
|
||||||
|
}
|
||||||
|
return cfgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DebugInformation.CFG readCFG(int index) throws IOException {
|
||||||
|
int[] offsets = new int[readUnsignedNumber() + 1];
|
||||||
|
IntegerArray lines = new IntegerArray(1);
|
||||||
|
IntegerArray files = new IntegerArray(1);
|
||||||
|
for (int i = 0; i < offsets.length - 1; ++i) {
|
||||||
|
offsets[i] = lines.size();
|
||||||
|
int sz = readUnsignedNumber();
|
||||||
|
if (sz == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (sz == 1) {
|
||||||
|
lines.add(-1);
|
||||||
|
files.add(-1);
|
||||||
|
} else if (sz == 2) {
|
||||||
|
lines.add(i + 1);
|
||||||
|
files.add(index);
|
||||||
|
} else {
|
||||||
|
sz -= 2;
|
||||||
|
int last = i;
|
||||||
|
for (int j = 0; j < sz; ++j) {
|
||||||
|
last += readNumber();
|
||||||
|
lines.add(last);
|
||||||
|
files.add(index + readNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsets[offsets.length - 1] = lines.size();
|
||||||
|
DebugInformation.CFG cfg = new DebugInformation.CFG();
|
||||||
|
cfg.offsets = offsets;
|
||||||
|
cfg.lines = lines.getAll();
|
||||||
|
cfg.files = files.getAll();
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
private int processSign(int number) {
|
private int processSign(int number) {
|
||||||
boolean negative = (number & 1) != 0;
|
boolean negative = (number & 1) != 0;
|
||||||
number >>>= 1;
|
number >>>= 1;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.debugging;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.debugging.DebugInformation.ClassMetadata;
|
import org.teavm.debugging.DebugInformation.ClassMetadata;
|
||||||
|
@ -47,6 +48,7 @@ class DebugInformationWriter {
|
||||||
writeMapping(debugInfo.methodMapping);
|
writeMapping(debugInfo.methodMapping);
|
||||||
writeVariableMappings(debugInfo);
|
writeVariableMappings(debugInfo);
|
||||||
writeClassMetadata(debugInfo.classesMetadata);
|
writeClassMetadata(debugInfo.classesMetadata);
|
||||||
|
writeCFGs(debugInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeVariableMappings(DebugInformation debugInfo) throws IOException {
|
private void writeVariableMappings(DebugInformation debugInfo) throws IOException {
|
||||||
|
@ -138,6 +140,38 @@ class DebugInformationWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeCFGs(DebugInformation debugInfo) throws IOException {
|
||||||
|
for (int i = 0; i < debugInfo.controlFlowGraphs.length; ++i) {
|
||||||
|
writeCFG(debugInfo.controlFlowGraphs[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCFG(DebugInformation.CFG mapping, int fileIndex) throws IOException {
|
||||||
|
writeUnsignedNumber(mapping.offsets.length - 1);
|
||||||
|
for (int i = 0; i < mapping.offsets.length - 1; ++i) {
|
||||||
|
int start = mapping.offsets[i];
|
||||||
|
int sz = mapping.offsets[i + 1] - start;
|
||||||
|
if (sz == 0) {
|
||||||
|
writeUnsignedNumber(0);
|
||||||
|
} else if (sz == 1 && mapping.lines[start] == -1) {
|
||||||
|
writeUnsignedNumber(1);
|
||||||
|
} else if (sz == 1 && mapping.lines[start] == i + 1 && mapping.files[start] == fileIndex) {
|
||||||
|
writeUnsignedNumber(2);
|
||||||
|
} else {
|
||||||
|
writeUnsignedNumber(2 + sz);
|
||||||
|
int[] lines = Arrays.copyOfRange(mapping.lines, start, start + sz);
|
||||||
|
int[] files = Arrays.copyOfRange(mapping.files, start, start + sz);
|
||||||
|
int last = i;
|
||||||
|
for (int j = 0; j < sz; ++j) {
|
||||||
|
int succ = lines[j];
|
||||||
|
writeNumber(succ - last);
|
||||||
|
writeNumber(files[j] - fileIndex);
|
||||||
|
last = succ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void writeNumber(int number) throws IOException {
|
private void writeNumber(int number) throws IOException {
|
||||||
writeUnsignedNumber(convertToSigned(number));
|
writeUnsignedNumber(convertToSigned(number));
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,4 +51,8 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter {
|
||||||
@Override
|
@Override
|
||||||
public void addField(String fieldName, String jsName) {
|
public void addField(String fieldName, String jsName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addSuccessors(SourceLocation location, SourceLocation[] successors) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
|
@ -35,4 +37,25 @@ public class InstructionLocation {
|
||||||
public int getLine() {
|
public int getLine() {
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (fileName == null ? 0 : fileName.hashCode());
|
||||||
|
result = prime * result + line;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof InstructionLocation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
InstructionLocation other = (InstructionLocation)obj;
|
||||||
|
return Objects.equals(fileName, other.fileName) && line == other.line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.util;
|
package org.teavm.model.util;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphBuilder;
|
import org.teavm.common.GraphBuilder;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
|
@ -68,6 +68,67 @@ public final class ProgramUtils {
|
||||||
return graphBuilder.build();
|
return graphBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<InstructionLocation, InstructionLocation[]> getLocationCFG(Program program) {
|
||||||
|
Graph graph = buildControlFlowGraph(program);
|
||||||
|
class Step {
|
||||||
|
InstructionLocation location;
|
||||||
|
int block;
|
||||||
|
}
|
||||||
|
Deque<Step> stack = new ArrayDeque<>();
|
||||||
|
for (int i = 0; i < graph.size(); ++i) {
|
||||||
|
if (graph.incomingEdgesCount(i) == 0) {
|
||||||
|
Step step = new Step();
|
||||||
|
step.block = i;
|
||||||
|
step.location = null;
|
||||||
|
stack.push(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean[] visited = new boolean[graph.size()];
|
||||||
|
Map<InstructionLocation, Set<InstructionLocation>> locationGraphBuilder = new HashMap<>();
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
Step step = stack.pop();
|
||||||
|
if (visited[step.block]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited[step.block] = true;
|
||||||
|
BasicBlock block = program.basicBlockAt(step.block);
|
||||||
|
InstructionLocation location = step.location;
|
||||||
|
for (Instruction insn : block.getInstructions()) {
|
||||||
|
if (insn.getLocation() != null) {
|
||||||
|
if (location != null) {
|
||||||
|
Set<InstructionLocation> successors = locationGraphBuilder.get(location);
|
||||||
|
if (successors == null) {
|
||||||
|
successors = new HashSet<>();
|
||||||
|
locationGraphBuilder.put(location, successors);
|
||||||
|
}
|
||||||
|
successors.add(insn.getLocation());
|
||||||
|
}
|
||||||
|
location = insn.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (graph.outgoingEdgesCount(step.block) == 0) {
|
||||||
|
Set<InstructionLocation> successors = locationGraphBuilder.get(location);
|
||||||
|
if (successors == null) {
|
||||||
|
successors = new HashSet<>();
|
||||||
|
locationGraphBuilder.put(location, successors);
|
||||||
|
}
|
||||||
|
successors.add(new InstructionLocation(null, -1));
|
||||||
|
} else {
|
||||||
|
for (int next : graph.outgoingEdges(step.block)) {
|
||||||
|
step = new Step();
|
||||||
|
step.location = location;
|
||||||
|
step.block = next;
|
||||||
|
stack.push(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<InstructionLocation, InstructionLocation[]> locationGraph = new HashMap<>();
|
||||||
|
for (Map.Entry<InstructionLocation, Set<InstructionLocation>> entry : locationGraphBuilder.entrySet()) {
|
||||||
|
locationGraph.put(entry.getKey(), entry.getValue().toArray(new InstructionLocation[0]));
|
||||||
|
}
|
||||||
|
return locationGraph;
|
||||||
|
}
|
||||||
|
|
||||||
public static Program copy(ProgramReader program) {
|
public static Program copy(ProgramReader program) {
|
||||||
Program copy = new Program();
|
Program copy = new Program();
|
||||||
InstructionCopyReader insnCopier = new InstructionCopyReader();
|
InstructionCopyReader insnCopier = new InstructionCopyReader();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user