mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 07:54:11 -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 lineMapping;
|
||||
MultiMapping[] variableMappings;
|
||||
CFG[] controlFlowGraphs;
|
||||
List<ClassMetadata> classesMetadata;
|
||||
|
||||
public String[] getCoveredSourceFiles() {
|
||||
|
@ -121,6 +122,34 @@ public class DebugInformation {
|
|||
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) {
|
||||
Integer classIndex = classNameMap.get(className);
|
||||
if (classIndex == null) {
|
||||
|
@ -391,4 +420,10 @@ public class DebugInformation {
|
|||
Integer parentId;
|
||||
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 int currentClassMetadata = -1;
|
||||
private List<ClassMetadata> classesMetadata = new ArrayList<>();
|
||||
private List<CFG> cfgs = new ArrayList<>();
|
||||
private int currentLine;
|
||||
|
||||
public LocationProvider getLocationProvider() {
|
||||
|
@ -122,6 +123,26 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
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() {
|
||||
if (debugInformation == null) {
|
||||
debugInformation = new DebugInformation();
|
||||
|
@ -156,6 +177,13 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
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.rebuildMaps();
|
||||
}
|
||||
|
@ -293,4 +321,59 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
int parentIndex;
|
||||
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 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.common.IntegerArray;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -46,6 +47,7 @@ class DebugInformationReader {
|
|||
debugInfo.methodMapping = readMapping();
|
||||
debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length);
|
||||
debugInfo.classesMetadata = readClassesMetadata(debugInfo.classNames.length);
|
||||
debugInfo.controlFlowGraphs = readCFGs(debugInfo.fileNames.length);
|
||||
debugInfo.rebuildFileDescriptions();
|
||||
debugInfo.rebuildMaps();
|
||||
return debugInfo;
|
||||
|
@ -82,6 +84,47 @@ class DebugInformationReader {
|
|||
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) {
|
||||
boolean negative = (number & 1) != 0;
|
||||
number >>>= 1;
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.debugging;
|
|||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.debugging.DebugInformation.ClassMetadata;
|
||||
|
@ -47,6 +48,7 @@ class DebugInformationWriter {
|
|||
writeMapping(debugInfo.methodMapping);
|
||||
writeVariableMappings(debugInfo);
|
||||
writeClassMetadata(debugInfo.classesMetadata);
|
||||
writeCFGs(debugInfo);
|
||||
}
|
||||
|
||||
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 {
|
||||
writeUnsignedNumber(convertToSigned(number));
|
||||
}
|
||||
|
|
|
@ -51,4 +51,8 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter {
|
|||
@Override
|
||||
public void addField(String fieldName, String jsName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSuccessors(SourceLocation location, SourceLocation[] successors) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
|
@ -35,4 +37,25 @@ public class InstructionLocation {
|
|||
public int getLine() {
|
||||
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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphBuilder;
|
||||
import org.teavm.model.*;
|
||||
|
@ -68,6 +68,67 @@ public final class ProgramUtils {
|
|||
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) {
|
||||
Program copy = new Program();
|
||||
InstructionCopyReader insnCopier = new InstructionCopyReader();
|
||||
|
|
Loading…
Reference in New Issue
Block a user