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 59d55fc3f..20edbe8c4 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java +++ b/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java @@ -15,18 +15,26 @@ */ package org.teavm.debugging; +import org.teavm.model.MethodReference; + /** * * @author Alexey Andreev */ public class CallFrame { private SourceLocation location; + private MethodReference method; - CallFrame(SourceLocation location) { + CallFrame(SourceLocation location, MethodReference method) { this.location = location; + this.method = method; } public SourceLocation getLocation() { return location; } + + public MethodReference getMethod() { + return method; + } } 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 3d207156b..a1a4ba273 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -16,7 +16,6 @@ package org.teavm.debugging; import java.util.*; -import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; /** @@ -24,31 +23,31 @@ import org.teavm.model.MethodReference; * @author Alexey Andreev */ public class DebugInformation { - List fileNames = new ArrayList<>(); - Map fileNameMap = new HashMap<>(); - List classNames = new ArrayList<>(); - Map classNameMap = new HashMap<>(); - List methods = new ArrayList<>(); - Map methodMap = new HashMap<>(); - List fileDescriptions = new ArrayList<>(); + String[] fileNames; + Map fileNameMap; + FileDescription[] fileDescriptions; + // TODO: for less memory consumption replace with two arrays + custom binary search GeneratedLocation[] fileNameKeys; int[] fileNameValues; - GeneratedLocation[] classKeys; - int[] classValues; - GeneratedLocation[] methodKeys; - int[] methodValues; GeneratedLocation[] lineNumberKeys; int[] lineNumberValues; - public Collection getGeneratedLocations(String fileName, int lineNumber) { + public Collection getGeneratedLocations(String fileName, int line) { Integer fileIndex = fileNameMap.get(fileName); if (fileIndex == null) { return Collections.emptyList(); } - FileDescription description = fileDescriptions.get(lineNumber); - return lineNumber < description.generatedLocations.length ? - Arrays.asList(description.generatedLocations[lineNumber]) : - Collections.emptyList(); + FileDescription description = fileIndex >= 0 ? fileDescriptions[fileIndex] : null; + if (description == null) { + return null; + } + GeneratedLocation[] locations = line < description.generatedLocations.length ? + description.generatedLocations[line] : null; + return locations != null ? Arrays.asList(locations) : Collections.emptyList(); + } + + public Collection getGeneratedLocations(SourceLocation sourceLocation) { + return getGeneratedLocations(sourceLocation.getFileName(), sourceLocation.getLine()); } public SourceLocation getSourceLocation(int line, int column) { @@ -57,18 +56,35 @@ public class DebugInformation { public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) { String fileName = componentByKey(fileNameKeys, fileNameValues, fileNames, generatedLocation); - String className = componentByKey(classKeys, classValues, classNames, generatedLocation); - MethodDescriptor method = componentByKey(methodKeys, methodValues, methods, generatedLocation); int lineNumberIndex = indexByKey(lineNumberKeys, generatedLocation); int lineNumber = lineNumberIndex >= 0 ? lineNumberValues[lineNumberIndex] : -1; - return new SourceLocation(fileName, lineNumber, new MethodReference(className, method)); + return new SourceLocation(fileName, lineNumber); } - private T componentByKey(GeneratedLocation[] keys, int[] valueIndexes, List values, + public MethodReference getMethodAt(String fileName, int line) { + if (line < 0) { + return null; + } + Integer fileIndex = fileNameMap.get(fileName); + if (fileIndex == null) { + return null; + } + FileDescription description = fileDescriptions[fileIndex]; + if (description == null) { + return null; + } + return line < description.methodMap.length ? description.methodMap[line] : null; + } + + public MethodReference getMethodAt(SourceLocation sourceLocation) { + return getMethodAt(sourceLocation.getFileName(), sourceLocation.getLine()); + } + + private T componentByKey(GeneratedLocation[] keys, int[] valueIndexes, T[] values, GeneratedLocation location) { int keyIndex = indexByKey(keys, location); int valueIndex = keyIndex >= 0 ? valueIndexes[keyIndex] : -1; - return valueIndex >= 0 ? values.get(valueIndex) : null; + return valueIndex >= 0 ? values[valueIndex] : null; } private int indexByKey(GeneratedLocation[] keys, GeneratedLocation location) { @@ -76,7 +92,8 @@ public class DebugInformation { return index >= 0 ? index : -index - 2; } - class FileDescription { + static class FileDescription { GeneratedLocation[][] generatedLocations; + MethodReference[] methodMap; } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java index c43f18799..9abfd8ccf 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -15,6 +15,7 @@ */ package org.teavm.debugging; +import java.util.*; import org.teavm.codegen.LocationProvider; import org.teavm.model.MethodReference; @@ -24,6 +25,15 @@ import org.teavm.model.MethodReference; */ public class DebugInformationBuilder implements DebugInformationEmitter { private LocationProvider locationProvider; + private DebugInformation debugInformation; + private List fileNames = new ArrayList<>(); + private Map fileNameMap = new HashMap<>(); + private List fileNameEntries = new ArrayList<>(); + private List lineNumberEntries = new ArrayList<>(); + private MethodReference currentMethod; + private String currentFileName; + private int currentLine; + private List fileDescriptions = new ArrayList<>(); public LocationProvider getLocationProvider() { return locationProvider; @@ -33,18 +43,111 @@ public class DebugInformationBuilder implements DebugInformationEmitter { this.locationProvider = locationProvider; } - @Override - public void emitFile(String fileName) { + private GeneratedLocation getGeneratedLocation() { + return new GeneratedLocation(locationProvider.getLine(), locationProvider.getColumn()); + } + @Override + public void emitLocation(String fileName, int line) { + Integer fileIndex; + if (fileName != null) { + fileIndex = fileNameMap.get(fileName); + if (fileIndex == null) { + fileIndex = fileNames.size(); + fileNames.add(fileName); + fileNameMap.put(fileName, fileIndex); + fileDescriptions.add(new FileDescriptionProto()); + } + } else { + fileIndex = -1; + } + if (currentFileName != fileName) { + fileNameEntries.add(new Entry(getGeneratedLocation(), fileIndex)); + } + if (currentLine != line) { + lineNumberEntries.add(new Entry(getGeneratedLocation(), line)); + } + if (fileName != null && line >= 0 && (currentFileName != fileName || currentLine != line)) { + FileDescriptionProto fileDesc = fileDescriptions.get(fileIndex); + fileDesc.setMethod(line, currentMethod); + fileDesc.addGeneratedLocation(line, getGeneratedLocation()); + } } @Override public void emitMethod(MethodReference method) { - + currentMethod = method; } - @Override - public void emitLineNumber(String lineNumber) { + public DebugInformation getDebugInformation() { + if (debugInformation == null) { + debugInformation = new DebugInformation(); + debugInformation.fileNames = fileNames.toArray(new String[0]); + debugInformation.fileNameMap = new HashMap<>(fileNameMap); + + debugInformation.fileNameKeys = new GeneratedLocation[fileNameEntries.size()]; + debugInformation.fileNameValues = new int[fileNameEntries.size()]; + int index = 0; + for (Entry entry : fileNameEntries) { + debugInformation.fileNameKeys[index] = entry.key; + debugInformation.fileNameValues[index] = entry.value; + index++; + } + + debugInformation.lineNumberKeys = new GeneratedLocation[lineNumberEntries.size()]; + debugInformation.lineNumberValues = new int[lineNumberEntries.size()]; + index = 0; + for (Entry entry : lineNumberEntries) { + debugInformation.lineNumberKeys[index] = entry.key; + debugInformation.lineNumberValues[index] = entry.value; + index++; + } + + debugInformation.fileDescriptions = new DebugInformation.FileDescription[fileDescriptions.size()]; + index = 0; + for (FileDescriptionProto fileDescProto : fileDescriptions) { + DebugInformation.FileDescription fileDesc = new DebugInformation.FileDescription(); + debugInformation.fileDescriptions[index++] = fileDesc; + fileDesc.methodMap = fileDescProto.methodMap.toArray(new MethodReference[0]); + fileDesc.generatedLocations = new GeneratedLocation[fileDescProto.generatedLocations.size()][]; + for (int i = 0; i < fileDescProto.generatedLocations.size(); ++i) { + fileDesc.generatedLocations[i] = fileDescProto.generatedLocations.get(index) + .toArray(new GeneratedLocation[0]); + } + } + } + return debugInformation; + } + + static class FileDescriptionProto { + List> generatedLocations = new ArrayList<>(); + List methodMap = new ArrayList<>(); + + void addGeneratedLocation(int line, GeneratedLocation location) { + if (line >= generatedLocations.size()) { + generatedLocations.addAll(Collections.>nCopies( + line - generatedLocations.size() + 1, null)); + } + List existingLocations = generatedLocations.get(line); + existingLocations.add(location); + } + + void setMethod(int line, MethodReference method) { + if (line >= methodMap.size()) { + methodMap.addAll(Collections.nCopies(line - methodMap.size() + 1, null)); + } + methodMap.set(line, method); + } + } + + static class Entry { + GeneratedLocation key; + int value; + + public Entry(GeneratedLocation key, int value) { + this.key = key; + this.value = value; + } } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java index 718a48b5d..c22a846a4 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -22,9 +22,7 @@ import org.teavm.model.MethodReference; * @author Alexey Andreev */ public interface DebugInformationEmitter { - void emitFile(String fileName); + void emitLocation(String fileName, int line); void emitMethod(MethodReference method); - - void emitLineNumber(String lineNumber); } \ No newline at end of file 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 dbcaf7132..a080373c8 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -16,6 +16,7 @@ package org.teavm.debugging; import java.util.*; +import org.teavm.model.MethodReference; /** * @@ -65,12 +66,14 @@ public class Debugger { return; } // TODO: lookup all locations that are "out of" the current location and create temporary breakpoints + javaScriptDebugger.stepOut(); } public void stepOver() { if (!javaScriptDebugger.isSuspended()) { return; } + javaScriptDebugger.stepOver(); // TODO: lookup all locations that are "after" the current location and create temporary breakpoints } @@ -115,9 +118,10 @@ public class Debugger { boolean wasEmpty = false; for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) { SourceLocation loc = debugInformation.getSourceLocation(jsFrame.getLocation()); - boolean empty = loc.getFileName() != null || loc.getMethod() != null || loc.getLine() < 0; + boolean empty = loc == null || (loc.getFileName() == null && loc.getLine() < 0); + MethodReference method = !empty ? debugInformation.getMethodAt(loc) : null; if (!empty || !wasEmpty) { - frames.add(new CallFrame(loc)); + frames.add(new CallFrame(loc, method)); } wasEmpty = empty; } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java index 43c6b20b1..38cff54b4 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -24,14 +24,10 @@ import org.teavm.model.MethodReference; */ public class DummyDebugInformationEmitter implements DebugInformationEmitter { @Override - public void emitFile(String fileName) { + public void emitLocation(String fileName, int line) { } @Override public void emitMethod(MethodReference method) { } - - @Override - public void emitLineNumber(String lineNumber) { - } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/SourceLocation.java b/teavm-core/src/main/java/org/teavm/debugging/SourceLocation.java index 9be482085..28625420f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/SourceLocation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/SourceLocation.java @@ -15,7 +15,6 @@ */ package org.teavm.debugging; -import org.teavm.model.MethodReference; /** * @@ -24,12 +23,10 @@ import org.teavm.model.MethodReference; public class SourceLocation { private String fileName; private int line; - private MethodReference method; - public SourceLocation(String fileName, int line, MethodReference method) { + public SourceLocation(String fileName, int line) { this.fileName = fileName; this.line = line; - this.method = method; } public String getFileName() { @@ -39,8 +36,4 @@ public class SourceLocation { public int getLine() { return line; } - - public MethodReference getMethod() { - return method; - } }