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 140405c7a..c9415ac21 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -17,11 +17,11 @@ package org.teavm.debugging; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; +import java.util.*; +import org.teavm.common.IntegerArray; +import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; /** @@ -31,12 +31,15 @@ import org.teavm.model.MethodReference; public class DebugInformation { String[] fileNames; Map fileNameMap; + String[] classNames; + Map classNameMap; + String[] methods; + Map methodMap; FileDescription[] fileDescriptions; - // TODO: for less memory consumption replace with two arrays + custom binary search - GeneratedLocation[] fileNameKeys; - int[] fileNameValues; - GeneratedLocation[] lineNumberKeys; - int[] lineNumberValues; + Mapping fileMapping; + Mapping classMapping; + Mapping methodMapping; + Mapping lineMapping; public Collection getGeneratedLocations(String fileName, int line) { Integer fileIndex = fileNameMap.get(fileName); @@ -45,11 +48,19 @@ public class DebugInformation { } FileDescription description = fileIndex >= 0 ? fileDescriptions[fileIndex] : null; if (description == null) { - return null; + return Collections.emptyList(); } - GeneratedLocation[] locations = line < description.generatedLocations.length ? - description.generatedLocations[line] : null; - return locations != null ? Arrays.asList(locations) : Collections.emptyList(); + if (line > description.generatedLocationStart.length) { + return Collections.emptyList(); + } + int start = description.generatedLocationStart[line]; + int end = description.generatedLocationStart[line + 1]; + GeneratedLocation[] resultArray = new GeneratedLocation[(end - start) / 2]; + for (int i = 0; i < resultArray.length; ++i) { + resultArray[i] = new GeneratedLocation(description.generatedLocationData[i * 2], + description.generatedLocationData[i * 2 + 1]); + } + return Arrays.asList(resultArray); } public Collection getGeneratedLocations(SourceLocation sourceLocation) { @@ -61,40 +72,32 @@ public class DebugInformation { } public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) { - String fileName = componentByKey(fileNameKeys, fileNameValues, fileNames, generatedLocation); - int lineNumberIndex = indexByKey(lineNumberKeys, generatedLocation); - int lineNumber = lineNumberIndex >= 0 ? lineNumberValues[lineNumberIndex] : -1; + String fileName = componentByKey(fileMapping, fileNames, generatedLocation); + int lineNumberIndex = indexByKey(lineMapping, generatedLocation); + int lineNumber = lineNumberIndex >= 0 ? lineMapping.values[lineNumberIndex] : -1; return new SourceLocation(fileName, lineNumber); } - public MethodReference getMethodAt(String fileName, int line) { - if (line < 0) { + public MethodReference getMethodAt(GeneratedLocation generatedLocation) { + String className = componentByKey(classMapping, classNames, generatedLocation); + if (className == null) { return null; } - Integer fileIndex = fileNameMap.get(fileName); - if (fileIndex == null) { + String method = componentByKey(methodMapping, methods, generatedLocation); + if (method == null) { return null; } - FileDescription description = fileDescriptions[fileIndex]; - if (description == null) { - return null; - } - return line < description.methodMap.length ? description.methodMap[line] : null; + return new MethodReference(className, MethodDescriptor.parse(method)); } - 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; + private T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) { + int keyIndex = indexByKey(mapping, location); + int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1; return valueIndex >= 0 ? values[valueIndex] : null; } - private int indexByKey(GeneratedLocation[] keys, GeneratedLocation location) { - int index = Arrays.binarySearch(keys, location); + private int indexByKey(Mapping mapping, GeneratedLocation location) { + int index = Collections.binarySearch(mapping.keyList(), location); return index >= 0 ? index : -index - 2; } @@ -103,8 +106,179 @@ public class DebugInformation { writer.write(this); } + public DebugInformation read(InputStream input) throws IOException { + DebugInformationReader reader = new DebugInformationReader(input); + return reader.read(); + } + + void rebuildMaps() { + fileNameMap = mapArray(fileNames); + classNameMap = mapArray(classNames); + methodMap = mapArray(methods); + } + + private Map mapArray(String[] array) { + Map map = new HashMap<>(); + for (int i = 0; i < array.length; ++i) { + map.put(array[i], i); + } + return map; + } + + void rebuildFileDescriptions() { + FileDescriptionBuilder builder = new FileDescriptionBuilder(fileNames.length); + int fileIndex = 0; + int lineIndex = 0; + int currentFile = -1; + int currentLine = -1; + while (fileIndex < fileMapping.size() && lineIndex < lineMapping.size()) { + GeneratedLocation fileLoc = fileMapping.key(fileIndex); + GeneratedLocation lineLoc = lineMapping.key(lineIndex); + int cmp = fileLoc.compareTo(lineLoc); + if (cmp < 0) { + currentFile = fileMapping.values[fileIndex++]; + } else if (cmp > 0){ + currentLine = lineMapping.values[lineIndex++]; + } else { + currentFile = fileMapping.values[fileIndex++]; + currentLine = lineMapping.values[lineIndex++]; + } + builder.emit(fileLoc.getLine(), fileLoc.getColumn(), currentFile, currentLine); + } + while (fileIndex < fileMapping.size()) { + builder.emit(fileMapping.lines[fileIndex], fileMapping.columns[fileIndex], + fileMapping.values[fileIndex], currentLine); + ++fileIndex; + } + while (lineIndex < lineMapping.size()) { + builder.emit(lineMapping.lines[lineIndex], lineMapping.columns[lineIndex], currentFile, + lineMapping.values[lineIndex]); + ++lineIndex; + } + fileDescriptions = builder.build(); + } + + static class FileDescriptionBuilder { + FileDescriptionProto[] files; + + public FileDescriptionBuilder(int size) { + files = new FileDescriptionProto[size]; + for (int i = 0; i < size; ++i) { + files[i] = new FileDescriptionProto(); + } + } + + void emit(int line, int column, int fileIndex, int sourceLine) { + if (sourceLine == -1 || fileIndex == -1) { + return; + } + FileDescriptionProto proto = files[fileIndex]; + proto.addLocation(sourceLine, line, column); + } + + public FileDescription[] build() { + FileDescription[] descriptions = new FileDescription[files.length]; + for (int i = 0; i < files.length; ++i) { + descriptions[i] = files[i].build(); + } + return descriptions; + } + } + + static class FileDescriptionProto { + IntegerArray generatedLocationData = new IntegerArray(1); + IntegerArray generatedLocationPointers = new IntegerArray(1); + IntegerArray generatedLocationStart = new IntegerArray(1); + IntegerArray generatedLocationSize = new IntegerArray(1); + + public void addLocation(int sourceLine, int line, int column) { + ensureLine(sourceLine); + generatedLocationSize.set(sourceLine, generatedLocationSize.get(sourceLine) + 1); + int slot = generatedLocationStart.get(sourceLine); + slot = addData(slot, column); + slot = addData(slot, line); + generatedLocationStart.set(sourceLine, slot); + } + + int addData(int slot, int value) { + int result = generatedLocationData.size(); + generatedLocationData.add(value); + generatedLocationPointers.add(slot); + return result; + } + + void ensureLine(int sourceLine) { + while (sourceLine >= generatedLocationSize.size()) { + generatedLocationSize.add(0); + generatedLocationStart.add(-1); + } + } + + FileDescription build() { + FileDescription description = new FileDescription(); + description.generatedLocationData = new int[generatedLocationData.size()]; + description.generatedLocationStart = new int[generatedLocationStart.size()]; + int current = 0; + for (int i = 0; i < generatedLocationStart.size(); ++i) { + description.generatedLocationStart[i] = current; + current += generatedLocationSize.get(i) * 2; + int j = current; + int ptr = generatedLocationStart.get(i); + while (ptr >= 0) { + description.generatedLocationData[--j] = generatedLocationData.get(ptr); + ptr = generatedLocationPointers.get(ptr); + } + } + return description; + } + } + static class FileDescription { - GeneratedLocation[][] generatedLocations; - MethodReference[] methodMap; + int[] generatedLocationData; + int[] generatedLocationStart; + } + + static class Mapping { + int[] lines; + int[] columns; + int[] values; + + public Mapping(int[] lines, int[] columns, int[] values) { + this.lines = lines; + this.columns = columns; + this.values = values; + } + + public LocationList keyList() { + return new LocationList(lines, columns); + } + + public int size() { + return lines.length; + } + + public GeneratedLocation key(int index) { + return new GeneratedLocation(lines[index], columns[index]); + } + } + + static class LocationList extends AbstractList { + private int[] lines; + private int[] columns; + + public LocationList(int[] lines, int[] columns) { + this.lines = lines; + this.columns = columns; + } + + @Override + public GeneratedLocation get(int index) { + return new GeneratedLocation(lines[index], columns[index]); + } + + @Override + public int size() { + return lines.length; + } } } 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 bc8baaa99..5f132ff5f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -17,7 +17,8 @@ package org.teavm.debugging; import java.util.*; import org.teavm.codegen.LocationProvider; -import org.teavm.model.MethodReference; +import org.teavm.common.IntegerArray; +import org.teavm.model.MethodDescriptor; /** * @@ -26,14 +27,17 @@ 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 MappedList files = new MappedList(); + private MappedList classes = new MappedList(); + private MappedList methods = new MappedList(); + private Mapping fileMapping = new Mapping(); + private Mapping lineMapping = new Mapping(); + private Mapping classMapping = new Mapping(); + private Mapping methodMapping = new Mapping(); + private MethodDescriptor currentMethod; + private String currentClass; private String currentFileName; private int currentLine; - private List fileDescriptions = new ArrayList<>(); public LocationProvider getLocationProvider() { return locationProvider; @@ -44,118 +48,100 @@ public class DebugInformationBuilder implements DebugInformationEmitter { this.locationProvider = locationProvider; } - private GeneratedLocation getGeneratedLocation() { - return new GeneratedLocation(locationProvider.getLine(), locationProvider.getColumn()); - } - @Override public void emitLocation(String fileName, int line) { debugInformation = null; - 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)); + int fileIndex = files.index(fileName); + if (!Objects.equals(currentFileName, fileName)) { + fileMapping.add(locationProvider, fileIndex); + currentFileName = fileName; } 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()); + lineMapping.add(locationProvider, line); + currentLine = line; } } @Override - public void emitMethod(MethodReference method) { + public void emitClass(String className) { debugInformation = null; - currentMethod = method; + int classIndex = classes.index(className); + if (!Objects.equals(className, currentClass)) { + classMapping.add(locationProvider, classIndex); + currentClass = className; + } + } + + @Override + public void emitMethod(MethodDescriptor method) { + debugInformation = null; + int methodIndex = methods.index(method != null ? method.toString() : null); + if (!Objects.equals(method, currentMethod)) { + methodMapping.add(locationProvider, methodIndex); + currentMethod = method; + } } public DebugInformation getDebugInformation() { if (debugInformation == null) { debugInformation = new DebugInformation(); - debugInformation.fileNames = fileNames.toArray(new String[0]); - debugInformation.fileNameMap = new HashMap<>(fileNameMap); + debugInformation.fileNames = files.getItems(); + debugInformation.fileNameMap = files.getIndexes(); + debugInformation.classNames = classes.getItems(); + debugInformation.classNameMap = classes.getIndexes(); + debugInformation.methods = methods.getItems(); + debugInformation.methodMap = methods.getIndexes(); - 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.fileMapping = fileMapping.build(); + debugInformation.lineMapping = lineMapping.build(); + debugInformation.classMapping = classMapping.build(); + debugInformation.methodMapping = methodMapping.build(); - 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) { - List locations = fileDescProto.generatedLocations.get(index); - fileDesc.generatedLocations[i] = locations != null ? - locations.toArray(new GeneratedLocation[0]) : null; - } - } + debugInformation.rebuildFileDescriptions(); } return debugInformation; } - static class FileDescriptionProto { - List> generatedLocations = new ArrayList<>(); - List methodMap = new ArrayList<>(); + static class Mapping { + IntegerArray lines = new IntegerArray(1); + IntegerArray columns = new IntegerArray(1); + IntegerArray values = new IntegerArray(1); - void addGeneratedLocation(int line, GeneratedLocation location) { - if (line >= generatedLocations.size()) { - generatedLocations.addAll(Collections.>nCopies( - line - generatedLocations.size() + 1, null)); - } - List existingLocations = generatedLocations.get(line); - if (existingLocations == null) { - existingLocations = new ArrayList<>(); - generatedLocations.set(line, existingLocations); - } - existingLocations.add(location); + public void add(LocationProvider location, int value) { + lines.add(location.getLine()); + columns.add(location.getColumn()); + values.add(value); } - void setMethod(int line, MethodReference method) { - if (line >= methodMap.size()) { - methodMap.addAll(Collections.nCopies(line - methodMap.size() + 1, null)); - } - methodMap.set(line, method); + DebugInformation.Mapping build() { + return new DebugInformation.Mapping(lines.getAll(), columns.getAll(), values.getAll()); } } - static class Entry { - GeneratedLocation key; - int value; + static class MappedList { + private List list = new ArrayList<>(); + private Map map = new HashMap<>(); - public Entry(GeneratedLocation key, int value) { - this.key = key; - this.value = value; + public int index(String item) { + if (item == null) { + return -1; + } + Integer index = map.get(item); + if (index == null) { + index = list.size(); + list.add(item); + map.put(item, index); + } + return index; + } + + public String[] getItems() { + return list.toArray(new String[list.size()]); + } + + public Map getIndexes() { + return new HashMap<>(map); } } } 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 df2d3f678..b88ad22b0 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -16,7 +16,7 @@ package org.teavm.debugging; import org.teavm.codegen.LocationProvider; -import org.teavm.model.MethodReference; +import org.teavm.model.MethodDescriptor; /** * @@ -27,5 +27,7 @@ public interface DebugInformationEmitter { void emitLocation(String fileName, int line); - void emitMethod(MethodReference method); + void emitMethod(MethodDescriptor method); + + void emitClass(String className); } \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java new file mode 100644 index 000000000..c22e36851 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java @@ -0,0 +1,138 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.debugging; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author Alexey Andreev + */ +class DebugInformationReader { + private InputStream input; + private int lastNumber; + + public DebugInformationReader(InputStream input) { + this.input = input; + } + + public DebugInformation read() throws IOException { + DebugInformation debugInfo = new DebugInformation(); + debugInfo.fileNames = readStrings(); + debugInfo.classNames = readStrings(); + debugInfo.methods = readStrings(); + debugInfo.fileMapping = readMapping(); + debugInfo.lineMapping = readMapping(); + debugInfo.classMapping = readMapping(); + debugInfo.methodMapping = readMapping(); + debugInfo.rebuildFileDescriptions(); + return debugInfo; + } + + private int processSign(int number) { + boolean negative = (number & 1) == 1; + number >>>= 1; + return !negative ? number : -number; + } + + private DebugInformation.Mapping readMapping() throws IOException { + int[] lines = readRle(); + int last = 0; + for (int i = 0; i < lines.length; ++i) { + last += lines[i]; + lines[i] = last; + } + int[] columns = new int[readUnsignedNumber()]; + resetRelativeNumber(); + for (int i = 0; i < columns.length; ++i) { + columns[i] = readRelativeNumber(); + } + int[] values = new int[readUnsignedNumber()]; + resetRelativeNumber(); + for (int i = 0; i < values.length; ++i) { + values[i] = readRelativeNumber(); + } + return new DebugInformation.Mapping(lines, columns, values); + } + + private String[] readStrings() throws IOException { + String[] array = new String[readUnsignedNumber()]; + for (int i = 0; i < array.length; ++i) { + array[i] = readString(); + } + return array; + } + + private int[] readRle() throws IOException { + int[] array = new int[readUnsignedNumber()]; + for (int i = 0; i < array.length;) { + int n = readUnsignedNumber(); + int count = 1; + if ((n & 1) != 0) { + count = readUnsignedNumber(); + } + n = processSign(n >>> 1); + while (count-- > 0) { + array[i] = n; + } + } + return array; + } + + private int readNumber() throws IOException { + return processSign(readUnsignedNumber()); + } + + private int readUnsignedNumber() throws IOException { + int number = 0; + while (true) { + int r = input.read(); + if (r < 0) { + throw new EOFException(); + } + byte b = (byte)r; + number = (number << 7) | (b & 0x7F); + if ((b & 0x80) == 0) { + break; + } + } + return number; + } + + private int readRelativeNumber() throws IOException { + lastNumber += readNumber(); + return lastNumber; + } + + private void resetRelativeNumber() { + lastNumber = 0; + } + + private String readString() throws IOException { + byte[] bytes = new byte[readUnsignedNumber()]; + int pos = 0; + while (pos < bytes.length) { + int read = input.read(bytes, pos, bytes.length - pos); + if (read == -1) { + throw new EOFException(); + } + pos += read; + } + return new String(bytes, "UTF-8"); + } +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java index f4b83a6db..aaab23d4e 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -17,12 +17,6 @@ package org.teavm.debugging; import java.io.DataOutput; import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.teavm.common.IntegerArray; -import org.teavm.debugging.DebugInformation.FileDescription; -import org.teavm.model.MethodReference; /** * @@ -37,84 +31,52 @@ class DebugInformationWriter { } public void write(DebugInformation debugInfo) throws IOException { - writeNumber(debugInfo.fileNames.length); - for (int i = 0; i < debugInfo.fileNames.length; ++i) { - String fileName = debugInfo.fileNames[i]; - writeString(fileName); - writeMethods(debugInfo.fileDescriptions[i]); - } + writeStringArray(debugInfo.fileNames); + writeStringArray(debugInfo.classNames); + writeStringArray(debugInfo.methods); - writeNumber(debugInfo.fileNameKeys.length); - resetRelativeNumber(); - for (int i = 0; i < debugInfo.fileNameKeys.length; ++i) { - writeRelativeNumber(debugInfo.fileNameKeys[i].getLine()); - } - resetRelativeNumber(); - for (int i = 0; i < debugInfo.fileNameKeys.length; ++i) { - writeRelativeNumber(debugInfo.fileNameKeys[i].getColumn()); - } - resetRelativeNumber(); - for (int i = 0; i < debugInfo.fileNameValues.length; ++i) { - writeRelativeNumber(debugInfo.fileNameValues[i]); - } + writeMapping(debugInfo.fileMapping); + writeMapping(debugInfo.lineMapping); + writeMapping(debugInfo.classMapping); + writeMapping(debugInfo.methodMapping); + } - writeNumber(debugInfo.lineNumberKeys.length); - resetRelativeNumber(); - resetRelativeNumber(); - for (int i = 0; i < debugInfo.lineNumberKeys.length; ++i) { - writeRelativeNumber(debugInfo.lineNumberKeys[i].getLine()); - } - resetRelativeNumber(); - for (int i = 0; i < debugInfo.lineNumberKeys.length; ++i) { - writeRelativeNumber(debugInfo.lineNumberKeys[i].getColumn()); - } - resetRelativeNumber(); - for (int i = 0; i < debugInfo.fileNameValues.length; ++i) { - writeRelativeNumber(debugInfo.lineNumberValues[i]); + private void writeStringArray(String[] array) throws IOException { + writeUnsignedNumber(array.length); + for (int i = 0; i < array.length; ++i) { + writeString(array[i]); } } - private void writeMethods(FileDescription fileDesc) throws IOException { - Map methodLineMap = new HashMap<>(); - for (int i = 0; i < fileDesc.methodMap.length; ++i) { - MethodReference method = fileDesc.methodMap[i]; - if (method == null) { - continue; - } - IntegerArray lines = methodLineMap.get(method); - if (lines == null) { - lines = new IntegerArray(1); - methodLineMap.put(method, lines); - } - lines.add(i); + private void writeMapping(DebugInformation.Mapping mapping) throws IOException { + writeUnsignedNumber(mapping.lines.length); + int[] lines = mapping.lines.clone(); + int last = 0; + for (int i = 0; i < lines.length; ++i) { + last = lines[i]; + lines[i] -= last; } - writeNumber(methodLineMap.size()); - for (MethodReference method : methodLineMap.keySet()) { - writeString(method.toString()); - int[] lines = methodLineMap.get(method).getAll(); - Arrays.sort(lines); - for (int i = 0; i < lines.length;) { - writeRelativeNumber(i); - int j = i; - int last = lines[i]; - ++i; - while (i < lines.length && lines[i] == last + 1) { - ++i; - ++last; - } - writeNumber(i - j); - } - writeRelativeNumber(-1); + writeRle(lines); + resetRelativeNumber(); + for (int i = 0; i < mapping.columns.length; ++i) { + writeRelativeNumber(mapping.columns[i]); + } + resetRelativeNumber(); + for (int i = 0; i < mapping.values.length; ++i) { + writeRelativeNumber(mapping.values[i]); } } private void writeNumber(int number) throws IOException { + writeUnsignedNumber(convertToSigned(number)); + } + + private int convertToSigned(int number) { + return number < 0 ? (-number << 1) | 1 : number << 1; + } + + private void writeUnsignedNumber(int number) throws IOException { do { - if (number < 0) { - number = (-number << 1) | 1; - } else { - number = number << 1; - } byte b = (byte)(number & 0x7F); if ((number & 0xFFFFFF80) != 0) { b |= 0x80; @@ -124,6 +86,25 @@ class DebugInformationWriter { } while (number != 0); } + private void writeRle(int[] array) throws IOException { + writeUnsignedNumber(array.length); + for (int i = 0; i < array.length;) { + int e = array[i]; + int count = 1; + ++i; + while (i < array.length && array[i] == e) { + ++count; + ++i; + } + if (count > 1) { + writeUnsignedNumber((convertToSigned(e) << 1) | 1); + writeUnsignedNumber(count); + } else { + writeUnsignedNumber(convertToSigned(e) << 1); + } + } + } + private void writeRelativeNumber(int number) throws IOException { writeNumber(number - lastNumber); lastNumber = number; @@ -134,7 +115,8 @@ class DebugInformationWriter { } private void writeString(String str) throws IOException { - writeNumber(str.length()); - output.write(str.getBytes("UTF-8")); + byte[] bytes = str.getBytes("UTF-8"); + writeUnsignedNumber(bytes.length); + output.write(bytes); } } 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 a080373c8..e77a77920 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -119,7 +119,7 @@ public class Debugger { for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) { SourceLocation loc = debugInformation.getSourceLocation(jsFrame.getLocation()); boolean empty = loc == null || (loc.getFileName() == null && loc.getLine() < 0); - MethodReference method = !empty ? debugInformation.getMethodAt(loc) : null; + MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation()) : null; if (!empty || !wasEmpty) { frames.add(new CallFrame(loc, method)); } 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 1bfa50782..49d6e2c98 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -16,7 +16,7 @@ package org.teavm.debugging; import org.teavm.codegen.LocationProvider; -import org.teavm.model.MethodReference; +import org.teavm.model.MethodDescriptor; /** @@ -29,7 +29,11 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter { } @Override - public void emitMethod(MethodReference method) { + public void emitMethod(MethodDescriptor method) { + } + + @Override + public void emitClass(String className) { } @Override diff --git a/teavm-core/src/main/java/org/teavm/debugging/GeneratedLocation.java b/teavm-core/src/main/java/org/teavm/debugging/GeneratedLocation.java index 250c26b5c..7ab80442e 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/GeneratedLocation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/GeneratedLocation.java @@ -44,4 +44,30 @@ public class GeneratedLocation implements Comparable { } return r; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + column; + result = prime * result + line; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + GeneratedLocation other = (GeneratedLocation)obj; + return line == other.line && column == other.column; + } + + @Override + public String toString() { + return "line: " + line + ", column: " + column; + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 6f04762af..40f496781 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -242,6 +242,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } public void render(ClassNode cls) throws RenderingException { + debugEmitter.emitClass(cls.getName()); try { writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{") .indent().softNewLine(); @@ -354,6 +355,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext } catch (IOException e) { throw new RenderingException("IO error occured", e); } + debugEmitter.emitClass(null); } private static Object getDefaultValue(ValueType type) { @@ -383,6 +385,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext private void renderInitializer(MethodNode method) throws IOException { MethodReference ref = method.getReference(); + debugEmitter.emitMethod(ref.getDescriptor()); writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function("); for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1) { @@ -403,21 +406,23 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(");").softNewLine(); writer.append("return result;").softNewLine(); writer.outdent().append("}").newLine(); + debugEmitter.emitMethod(null); } private void renderVirtualDeclarations(String className, List methods) throws NamingException, IOException { + if (methods.isEmpty()) { + return; + } for (MethodNode method : methods) { MethodReference ref = method.getReference(); if (ref.getDescriptor().getName().equals("")) { renderInitializer(method); } } - if (methods.isEmpty()) { - return; - } writer.append("$rt_virtualMethods(").appendClass(className).indent(); for (MethodNode method : methods) { + debugEmitter.emitMethod(method.getReference().getDescriptor()); MethodReference ref = method.getReference(); writer.append(",").newLine(); if (method.isOriginalNamePreserved()) { @@ -443,12 +448,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.append(",").ws().append(variableName(i)); } writer.append(");").ws().append("}"); + debugEmitter.emitMethod(null); } writer.append(");").newLine().outdent(); } private void renderStaticDeclaration(MethodNode method) throws NamingException, IOException { MethodReference ref = method.getReference(); + debugEmitter.emitMethod(ref.getDescriptor()); if (ref.getDescriptor().getName().equals("")) { renderInitializer(method); } @@ -470,11 +477,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext writer.appendClass(ref.getClassName()).append(".").append(ref.getName()).ws().append("=") .ws().appendClass(ref.getClassName()).append(".").appendMethod(ref).append(';').newLine(); } + debugEmitter.emitMethod(null); } public void renderBody(MethodNode method, boolean inner) throws IOException { MethodReference ref = method.getReference(); - debugEmitter.emitMethod(ref); + debugEmitter.emitMethod(ref.getDescriptor()); if (inner) { writer.appendMethodBody(ref).ws().append("=").ws().append("function("); } else {