From 0c708868a5a6cbf85ceecbfea405813d9b1ec16b Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Tue, 19 Aug 2014 17:53:28 +0400 Subject: [PATCH] Implements step into and fixes bugs --- .../java/org/teavm/common/IntegerArray.java | 4 + .../java/org/teavm/debugging/CallFrame.java | 5 +- .../org/teavm/debugging/DebugInformation.java | 148 ++++++++++++++++-- .../debugging/DebugInformationReader.java | 7 +- .../java/org/teavm/debugging/Debugger.java | 44 ++++-- 5 files changed, 179 insertions(+), 29 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/common/IntegerArray.java b/teavm-core/src/main/java/org/teavm/common/IntegerArray.java index 4c128c38a..c0f9579f7 100644 --- a/teavm-core/src/main/java/org/teavm/common/IntegerArray.java +++ b/teavm-core/src/main/java/org/teavm/common/IntegerArray.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java b/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java index 5fdc7a473..a27177582 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java +++ b/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java @@ -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 variables; - CallFrame(SourceLocation location, MethodReference method, Map variables) { + CallFrame(JavaScriptLocation originalLocation, SourceLocation location, MethodReference method, + Map variables) { + this.originalLocation = originalLocation; this.location = location; this.method = method; this.variables = Collections.unmodifiableMap(variables); diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java index 5dfde44a1..6899a05f3 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -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 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 overridingMethods = new HashSet<>(); + getOverridingMethods(methodRef, overridingMethods); + return overridingMethods.toArray(new MethodReference[0]); + } + + private void getOverridingMethods(MethodReference methodRef, Set overridingMethods) { + if (overridingMethods.add(methodRef)) { + for (MethodReference overridingMethod : getDirectOverridingMethods(methodRef)) { + getOverridingMethods(overridingMethod, overridingMethods); + } + } + } + private 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 fieldMap = new HashMap<>(); + int[] methods; } static class CFG { diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java index 7aedb87e6..68173ff9f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java @@ -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(); diff --git a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java index 0b3a4aa32..e2eb4709f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -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 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 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; }