mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-10 08:54:11 -08:00
Implements step into and fixes bugs
This commit is contained in:
parent
2c8902e94a
commit
0c708868a5
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user