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;
}
public void clear() {
sz = 0;
}
public void optimize() {
if (sz > data.length) {
data = Arrays.copyOf(data, sz);

View File

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

View File

@ -136,6 +136,9 @@ public class DebugInformation {
if (cfg == null) {
return null;
}
if (location.getLine() >= cfg.offsets.length - 1) {
return null;
}
int start = cfg.offsets[location.getLine()];
int end = cfg.offsets[location.getLine() + 1];
if (end - start == 1 && cfg.offsets[start] == -1) {
@ -184,33 +187,80 @@ public class DebugInformation {
if (valueIndex < 0) {
return null;
}
long item = exactMethods[valueIndex];
int classIndex = (int)(item >> 32);
int methodIndex = (int)item;
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
return getExactMethod(valueIndex);
}
public MethodReference getCallSite(int line, int 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) {
Integer classIndex = classNameMap.get(methodRef.getClassName());
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);
Integer index = getExactMethodIndex(methodRef);
if (index == null) {
return new GeneratedLocation[0];
}
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) {
int keyIndex = indexByKey(mapping, location);
int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1;
@ -255,6 +305,7 @@ public class DebugInformation {
rebuildMaps();
rebuildFileDescriptions();
rebuildEntrances();
rebuildMethodTree();
}
void rebuildMaps() {
@ -315,10 +366,76 @@ public class DebugInformation {
}
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) {
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 {
@ -571,6 +688,7 @@ public class DebugInformation {
static class ClassMetadata {
Integer parentId;
Map<Integer, Integer> fieldMap = new HashMap<>();
int[] methods;
}
static class CFG {

View File

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

View File

@ -63,7 +63,7 @@ public class Debugger {
}
public void stepInto() {
javaScriptDebugger.stepInto();
step(true);
}
public void stepOut() {
@ -71,32 +71,54 @@ public class Debugger {
}
public void stepOver() {
step(false);
}
private void step(boolean enterMethod) {
CallFrame[] callStack = getCallStack();
if (callStack == null || callStack.length == 0) {
javaScriptDebugger.stepOver();
if (enterMethod) {
javaScriptDebugger.stepInto();
} else {
javaScriptDebugger.stepOver();
}
return;
}
CallFrame recentFrame = callStack[0];
if (recentFrame.getLocation() == null || recentFrame.getLocation().getFileName() == null ||
recentFrame.getLocation().getLine() < 0) {
javaScriptDebugger.stepOver();
if (enterMethod) {
javaScriptDebugger.stepInto();
} else {
javaScriptDebugger.stepOver();
}
return;
}
Set<JavaScriptLocation> successors = new HashSet<>();
for (int i = 0; i < callStack.length; ++i) {
CallFrame frame = callStack[i];
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()) {
DebugInformation debugInfo = entry.getValue();
SourceLocation[] following = debugInfo.getFollowingLines(frame.getLocation());
if (following == null) {
continue;
if (following != null) {
for (SourceLocation successor : following) {
if (successor == null) {
exits = true;
} else {
for (GeneratedLocation loc : debugInfo.getGeneratedLocations(successor)) {
successors.add(new JavaScriptLocation(entry.getKey(), loc.getLine(), loc.getColumn()));
}
}
}
}
for (SourceLocation successor : debugInfo.getFollowingLines(frame.getLocation())) {
if (successor == null) {
exits = true;
} else {
for (GeneratedLocation loc : debugInfo.getGeneratedLocations(successor)) {
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()));
}
}
@ -228,7 +250,7 @@ public class Debugger {
jsFrame.getLocation().getColumn()) : null;
if (!empty || !wasEmpty) {
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;
}