Implements step into and fixes bugs

This commit is contained in:
konsoletyper 2014-08-19 17:53:28 +04:00
parent 2c8902e94a
commit 0c708868a5
5 changed files with 179 additions and 29 deletions

View File

@ -40,6 +40,10 @@ public class IntegerArray {
return array; return array;
} }
public void clear() {
sz = 0;
}
public void optimize() { public void optimize() {
if (sz > data.length) { if (sz > data.length) {
data = Arrays.copyOf(data, sz); data = Arrays.copyOf(data, sz);

View File

@ -24,11 +24,14 @@ import org.teavm.model.MethodReference;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class CallFrame { public class CallFrame {
JavaScriptLocation originalLocation;
private SourceLocation location; private SourceLocation location;
private MethodReference method; private MethodReference method;
private Map<String, Variable> variables; private Map<String, Variable> variables;
CallFrame(SourceLocation location, MethodReference method, Map<String, Variable> variables) { CallFrame(JavaScriptLocation originalLocation, SourceLocation location, MethodReference method,
Map<String, Variable> variables) {
this.originalLocation = originalLocation;
this.location = location; this.location = location;
this.method = method; this.method = method;
this.variables = Collections.unmodifiableMap(variables); this.variables = Collections.unmodifiableMap(variables);

View File

@ -136,6 +136,9 @@ public class DebugInformation {
if (cfg == null) { if (cfg == null) {
return null; return null;
} }
if (location.getLine() >= cfg.offsets.length - 1) {
return null;
}
int start = cfg.offsets[location.getLine()]; int start = cfg.offsets[location.getLine()];
int end = cfg.offsets[location.getLine() + 1]; int end = cfg.offsets[location.getLine() + 1];
if (end - start == 1 && cfg.offsets[start] == -1) { if (end - start == 1 && cfg.offsets[start] == -1) {
@ -184,33 +187,80 @@ public class DebugInformation {
if (valueIndex < 0) { if (valueIndex < 0) {
return null; return null;
} }
long item = exactMethods[valueIndex]; return getExactMethod(valueIndex);
int classIndex = (int)(item >> 32);
int methodIndex = (int)item;
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
} }
public MethodReference getCallSite(int line, int column) { public MethodReference getCallSite(int line, int column) {
return getCallSite(new GeneratedLocation(line, column)); return getCallSite(new GeneratedLocation(line, column));
} }
public GeneratedLocation[] getCallSiteEntrances(GeneratedLocation location) {
MethodReference method = getCallSite(location);
if (method == null) {
return null;
}
Set<GeneratedLocation> locations = new HashSet<>();
for (MethodReference overriding : getOverridingMethods(method)) {
locations.addAll(Arrays.asList(getMethodEntrances(overriding)));
}
return locations.toArray(new GeneratedLocation[0]);
}
public GeneratedLocation[] getMethodEntrances(MethodReference methodRef) { public GeneratedLocation[] getMethodEntrances(MethodReference methodRef) {
Integer classIndex = classNameMap.get(methodRef.getClassName()); Integer index = getExactMethodIndex(methodRef);
if (classIndex == null) {
return new GeneratedLocation[0];
}
Integer methodIndex = methodMap.get(methodRef.getDescriptor().toString());
if (methodIndex == null) {
return new GeneratedLocation[0];
}
long exact = ((long)classIndex << 32) | methodIndex;
Integer index = exactMethodMap.get(exact);
if (index == null) { if (index == null) {
return new GeneratedLocation[0]; return new GeneratedLocation[0];
} }
return methodEntrances.getEntrances(index); return methodEntrances.getEntrances(index);
} }
private Integer getExactMethodIndex(MethodReference methodRef) {
Integer classIndex = classNameMap.get(methodRef.getClassName());
if (classIndex == null) {
return null;
}
Integer methodIndex = methodMap.get(methodRef.getDescriptor().toString());
if (methodIndex == null) {
return null;
}
return getExactMethodIndex(classIndex, methodIndex);
}
public MethodReference getExactMethod(int index) {
long item = exactMethods[index];
int classIndex = (int)(item >> 32);
int methodIndex = (int)item;
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
}
public MethodReference[] getDirectOverridingMethods(MethodReference methodRef) {
Integer methodIndex = getExactMethodIndex(methodRef);
if (methodIndex == null) {
return new MethodReference[0];
}
int start = methodTree.offsets[methodIndex];
int end = methodTree.offsets[methodIndex + 1];
MethodReference[] result = new MethodReference[end - start];
for (int i = 0; i < result.length; ++i) {
result[i] = getExactMethod(methodTree.data[i]);
}
return result;
}
public MethodReference[] getOverridingMethods(MethodReference methodRef) {
Set<MethodReference> overridingMethods = new HashSet<>();
getOverridingMethods(methodRef, overridingMethods);
return overridingMethods.toArray(new MethodReference[0]);
}
private void getOverridingMethods(MethodReference methodRef, Set<MethodReference> overridingMethods) {
if (overridingMethods.add(methodRef)) {
for (MethodReference overridingMethod : getDirectOverridingMethods(methodRef)) {
getOverridingMethods(overridingMethod, overridingMethods);
}
}
}
private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) { private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) {
int keyIndex = indexByKey(mapping, location); int keyIndex = indexByKey(mapping, location);
int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1; int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1;
@ -255,6 +305,7 @@ public class DebugInformation {
rebuildMaps(); rebuildMaps();
rebuildFileDescriptions(); rebuildFileDescriptions();
rebuildEntrances(); rebuildEntrances();
rebuildMethodTree();
} }
void rebuildMaps() { void rebuildMaps() {
@ -315,10 +366,76 @@ public class DebugInformation {
} }
void rebuildMethodTree() { void rebuildMethodTree() {
long[] exactMethods = this.exactMethods.clone();
Arrays.sort(exactMethods);
IntegerArray methods = new IntegerArray(1);
int lastClass = -1;
for (int i = 0; i < exactMethods.length; ++i) {
long exactMethod = exactMethods[i];
int classIndex = (int)(exactMethod >> 32);
if (classIndex != lastClass) {
if (lastClass >= 0) {
ClassMetadata clsData = classesMetadata.get(lastClass);
clsData.methods = methods.getAll();
methods.clear();
}
lastClass = classIndex;
}
int methodIndex = (int)exactMethod;
methods.add(methodIndex);
}
if (lastClass >= 0) {
ClassMetadata clsData = classesMetadata.get(lastClass);
clsData.methods = methods.getAll();
Arrays.sort(clsData.methods);
}
int[] start = new int[exactMethods.length];
Arrays.fill(start, -1);
IntegerArray data = new IntegerArray(1);
IntegerArray next = new IntegerArray(1);
for (int i = 0; i < classesMetadata.size(); ++i) { for (int i = 0; i < classesMetadata.size(); ++i) {
ClassMetadata clsData = classesMetadata.get(i); ClassMetadata clsData = classesMetadata.get(i);
clsData.parentId; if (clsData.parentId == null || clsData.methods == null) {
continue;
} }
for (int methodIndex : clsData.methods) {
ClassMetadata superclsData = classesMetadata.get(clsData.parentId);
Integer parentId = clsData.parentId;
while (superclsData != null) {
if (Arrays.binarySearch(superclsData.methods, methodIndex) >= 0) {
int childMethod = getExactMethodIndex(i, methodIndex);
int parentMethod = getExactMethodIndex(parentId, methodIndex);
int ptr = start[parentMethod];
start[parentMethod] = data.size();
data.add(childMethod);
next.add(ptr);
break;
}
parentId = superclsData.parentId;
superclsData = parentId != null ? classesMetadata.get(parentId) : null;
}
}
}
MethodTree methodTree = new MethodTree();
methodTree.offsets = new int[start.length + 1];
methodTree.data = new int[data.size()];
int index = 0;
for (int i = 0; i < start.length; ++i) {
int ptr = start[i];
while (ptr != -1) {
methodTree.data[index++] = data.get(ptr);
ptr = next.get(ptr);
}
methodTree.offsets[i + 1] = index;
}
this.methodTree = methodTree;
}
private Integer getExactMethodIndex(int classIndex, int methodIndex) {
long entry = ((long)classIndex << 32) | methodIndex;
return exactMethodMap.get(entry);
} }
class MethodEntrancesBuilder { class MethodEntrancesBuilder {
@ -571,6 +688,7 @@ public class DebugInformation {
static class ClassMetadata { static class ClassMetadata {
Integer parentId; Integer parentId;
Map<Integer, Integer> fieldMap = new HashMap<>(); Map<Integer, Integer> fieldMap = new HashMap<>();
int[] methods;
} }
static class CFG { static class CFG {

View File

@ -101,7 +101,8 @@ class DebugInformationReader {
int line = 0; int line = 0;
offsets.add(0); offsets.add(0);
while (i < lines.length) { while (i < lines.length) {
line += readUnsignedNumber(); int passedLines = readUnsignedNumber();
line += passedLines;
int sz = readUnsignedNumber(); int sz = readUnsignedNumber();
if (sz == 0) { if (sz == 0) {
lines[i] = -1; lines[i] = -1;
@ -117,8 +118,10 @@ class DebugInformationReader {
lines[i] = last; lines[i] = last;
files[i++] = index + readNumber(); files[i++] = index + readNumber();
} }
for (int j = 0; j < passedLines; ++j) {
offsets.add(i); offsets.add(i);
} }
}
DebugInformation.CFG cfg = new DebugInformation.CFG(); DebugInformation.CFG cfg = new DebugInformation.CFG();
cfg.offsets = offsets.getAll(); cfg.offsets = offsets.getAll();
cfg.lines = lines; cfg.lines = lines;

View File

@ -63,7 +63,7 @@ public class Debugger {
} }
public void stepInto() { public void stepInto() {
javaScriptDebugger.stepInto(); step(true);
} }
public void stepOut() { public void stepOut() {
@ -71,28 +71,42 @@ public class Debugger {
} }
public void stepOver() { public void stepOver() {
step(false);
}
private void step(boolean enterMethod) {
CallFrame[] callStack = getCallStack(); CallFrame[] callStack = getCallStack();
if (callStack == null || callStack.length == 0) { if (callStack == null || callStack.length == 0) {
if (enterMethod) {
javaScriptDebugger.stepInto();
} else {
javaScriptDebugger.stepOver(); javaScriptDebugger.stepOver();
}
return; return;
} }
CallFrame recentFrame = callStack[0]; CallFrame recentFrame = callStack[0];
if (recentFrame.getLocation() == null || recentFrame.getLocation().getFileName() == null || if (recentFrame.getLocation() == null || recentFrame.getLocation().getFileName() == null ||
recentFrame.getLocation().getLine() < 0) { recentFrame.getLocation().getLine() < 0) {
if (enterMethod) {
javaScriptDebugger.stepInto();
} else {
javaScriptDebugger.stepOver(); javaScriptDebugger.stepOver();
}
return; return;
} }
Set<JavaScriptLocation> successors = new HashSet<>(); Set<JavaScriptLocation> successors = new HashSet<>();
for (int i = 0; i < callStack.length; ++i) { for (int i = 0; i < callStack.length; ++i) {
CallFrame frame = callStack[i]; CallFrame frame = callStack[i];
boolean exits = false; boolean exits = false;
DebugInformation mainDebugInfo = debugInformationMap.get(frame.originalLocation.getScript());
GeneratedLocation genLoc = new GeneratedLocation(frame.originalLocation.getLine(),
frame.originalLocation.getColumn());
MethodReference callMethod = mainDebugInfo != null ? mainDebugInfo.getCallSite(genLoc) : null;
for (Map.Entry<String, DebugInformation> entry : debugInformationMap.entrySet()) { for (Map.Entry<String, DebugInformation> entry : debugInformationMap.entrySet()) {
DebugInformation debugInfo = entry.getValue(); DebugInformation debugInfo = entry.getValue();
SourceLocation[] following = debugInfo.getFollowingLines(frame.getLocation()); SourceLocation[] following = debugInfo.getFollowingLines(frame.getLocation());
if (following == null) { if (following != null) {
continue; for (SourceLocation successor : following) {
}
for (SourceLocation successor : debugInfo.getFollowingLines(frame.getLocation())) {
if (successor == null) { if (successor == null) {
exits = true; exits = true;
} else { } else {
@ -102,6 +116,14 @@ public class Debugger {
} }
} }
} }
if (enterMethod && callMethod != null) {
for (MethodReference potentialMethod : debugInfo.getOverridingMethods(callMethod)) {
for (GeneratedLocation loc : debugInfo.getMethodEntrances(potentialMethod)) {
successors.add(new JavaScriptLocation(entry.getKey(), loc.getLine(), loc.getColumn()));
}
}
}
}
if (!exits) { if (!exits) {
break; break;
} }
@ -228,7 +250,7 @@ public class Debugger {
jsFrame.getLocation().getColumn()) : null; jsFrame.getLocation().getColumn()) : null;
if (!empty || !wasEmpty) { if (!empty || !wasEmpty) {
VariableMap vars = new VariableMap(jsFrame.getVariables(), this, jsFrame.getLocation()); VariableMap vars = new VariableMap(jsFrame.getVariables(), this, jsFrame.getLocation());
frames.add(new CallFrame(loc, method, vars)); frames.add(new CallFrame(jsFrame.getLocation(), loc, method, vars));
} }
wasEmpty = empty; wasEmpty = empty;
} }