Refactoring of DebugInformation. Reduce memory consumption and debug

file size. Add debug information reader
This commit is contained in:
Alexey Andreev 2014-07-28 16:17:21 +04:00
parent 62281f696c
commit 444c599b17
9 changed files with 531 additions and 211 deletions

View File

@ -17,11 +17,11 @@ package org.teavm.debugging;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.*;
import java.util.Collection; import org.teavm.common.IntegerArray;
import java.util.Collections; import org.teavm.model.MethodDescriptor;
import java.util.Map;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/** /**
@ -31,12 +31,15 @@ import org.teavm.model.MethodReference;
public class DebugInformation { public class DebugInformation {
String[] fileNames; String[] fileNames;
Map<String, Integer> fileNameMap; Map<String, Integer> fileNameMap;
String[] classNames;
Map<String, Integer> classNameMap;
String[] methods;
Map<String, Integer> methodMap;
FileDescription[] fileDescriptions; FileDescription[] fileDescriptions;
// TODO: for less memory consumption replace with two arrays + custom binary search Mapping fileMapping;
GeneratedLocation[] fileNameKeys; Mapping classMapping;
int[] fileNameValues; Mapping methodMapping;
GeneratedLocation[] lineNumberKeys; Mapping lineMapping;
int[] lineNumberValues;
public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) { public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) {
Integer fileIndex = fileNameMap.get(fileName); Integer fileIndex = fileNameMap.get(fileName);
@ -45,11 +48,19 @@ public class DebugInformation {
} }
FileDescription description = fileIndex >= 0 ? fileDescriptions[fileIndex] : null; FileDescription description = fileIndex >= 0 ? fileDescriptions[fileIndex] : null;
if (description == null) { if (description == null) {
return null; return Collections.emptyList();
} }
GeneratedLocation[] locations = line < description.generatedLocations.length ? if (line > description.generatedLocationStart.length) {
description.generatedLocations[line] : null; return Collections.emptyList();
return locations != null ? Arrays.asList(locations) : Collections.<GeneratedLocation>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<GeneratedLocation> getGeneratedLocations(SourceLocation sourceLocation) { public Collection<GeneratedLocation> getGeneratedLocations(SourceLocation sourceLocation) {
@ -61,40 +72,32 @@ public class DebugInformation {
} }
public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) { public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) {
String fileName = componentByKey(fileNameKeys, fileNameValues, fileNames, generatedLocation); String fileName = componentByKey(fileMapping, fileNames, generatedLocation);
int lineNumberIndex = indexByKey(lineNumberKeys, generatedLocation); int lineNumberIndex = indexByKey(lineMapping, generatedLocation);
int lineNumber = lineNumberIndex >= 0 ? lineNumberValues[lineNumberIndex] : -1; int lineNumber = lineNumberIndex >= 0 ? lineMapping.values[lineNumberIndex] : -1;
return new SourceLocation(fileName, lineNumber); return new SourceLocation(fileName, lineNumber);
} }
public MethodReference getMethodAt(String fileName, int line) { public MethodReference getMethodAt(GeneratedLocation generatedLocation) {
if (line < 0) { String className = componentByKey(classMapping, classNames, generatedLocation);
if (className == null) {
return null; return null;
} }
Integer fileIndex = fileNameMap.get(fileName); String method = componentByKey(methodMapping, methods, generatedLocation);
if (fileIndex == null) { if (method == null) {
return null; return null;
} }
FileDescription description = fileDescriptions[fileIndex]; return new MethodReference(className, MethodDescriptor.parse(method));
if (description == null) {
return null;
}
return line < description.methodMap.length ? description.methodMap[line] : null;
} }
public MethodReference getMethodAt(SourceLocation sourceLocation) { private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) {
return getMethodAt(sourceLocation.getFileName(), sourceLocation.getLine()); int keyIndex = indexByKey(mapping, location);
} int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1;
private <T> 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[valueIndex] : null; return valueIndex >= 0 ? values[valueIndex] : null;
} }
private int indexByKey(GeneratedLocation[] keys, GeneratedLocation location) { private int indexByKey(Mapping mapping, GeneratedLocation location) {
int index = Arrays.binarySearch(keys, location); int index = Collections.binarySearch(mapping.keyList(), location);
return index >= 0 ? index : -index - 2; return index >= 0 ? index : -index - 2;
} }
@ -103,8 +106,179 @@ public class DebugInformation {
writer.write(this); 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<String, Integer> mapArray(String[] array) {
Map<String, Integer> 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 { static class FileDescription {
GeneratedLocation[][] generatedLocations; int[] generatedLocationData;
MethodReference[] methodMap; 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<GeneratedLocation> {
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;
}
} }
} }

View File

@ -17,7 +17,8 @@ package org.teavm.debugging;
import java.util.*; import java.util.*;
import org.teavm.codegen.LocationProvider; 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 { public class DebugInformationBuilder implements DebugInformationEmitter {
private LocationProvider locationProvider; private LocationProvider locationProvider;
private DebugInformation debugInformation; private DebugInformation debugInformation;
private List<String> fileNames = new ArrayList<>(); private MappedList files = new MappedList();
private Map<String, Integer> fileNameMap = new HashMap<>(); private MappedList classes = new MappedList();
private List<Entry> fileNameEntries = new ArrayList<>(); private MappedList methods = new MappedList();
private List<Entry> lineNumberEntries = new ArrayList<>(); private Mapping fileMapping = new Mapping();
private MethodReference currentMethod; 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 String currentFileName;
private int currentLine; private int currentLine;
private List<FileDescriptionProto> fileDescriptions = new ArrayList<>();
public LocationProvider getLocationProvider() { public LocationProvider getLocationProvider() {
return locationProvider; return locationProvider;
@ -44,118 +48,100 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
this.locationProvider = locationProvider; this.locationProvider = locationProvider;
} }
private GeneratedLocation getGeneratedLocation() {
return new GeneratedLocation(locationProvider.getLine(), locationProvider.getColumn());
}
@Override @Override
public void emitLocation(String fileName, int line) { public void emitLocation(String fileName, int line) {
debugInformation = null; debugInformation = null;
Integer fileIndex; int fileIndex = files.index(fileName);
if (fileName != null) { if (!Objects.equals(currentFileName, fileName)) {
fileIndex = fileNameMap.get(fileName); fileMapping.add(locationProvider, fileIndex);
if (fileIndex == null) { currentFileName = fileName;
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) { if (currentLine != line) {
lineNumberEntries.add(new Entry(getGeneratedLocation(), line)); lineMapping.add(locationProvider, line);
} currentLine = line;
if (fileName != null && line >= 0 && (currentFileName != fileName || currentLine != line)) {
FileDescriptionProto fileDesc = fileDescriptions.get(fileIndex);
fileDesc.setMethod(line, currentMethod);
fileDesc.addGeneratedLocation(line, getGeneratedLocation());
} }
} }
@Override @Override
public void emitMethod(MethodReference method) { public void emitClass(String className) {
debugInformation = null; 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() { public DebugInformation getDebugInformation() {
if (debugInformation == null) { if (debugInformation == null) {
debugInformation = new DebugInformation(); debugInformation = new DebugInformation();
debugInformation.fileNames = fileNames.toArray(new String[0]); debugInformation.fileNames = files.getItems();
debugInformation.fileNameMap = new HashMap<>(fileNameMap); 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.fileMapping = fileMapping.build();
debugInformation.fileNameValues = new int[fileNameEntries.size()]; debugInformation.lineMapping = lineMapping.build();
int index = 0; debugInformation.classMapping = classMapping.build();
for (Entry entry : fileNameEntries) { debugInformation.methodMapping = methodMapping.build();
debugInformation.fileNameKeys[index] = entry.key;
debugInformation.fileNameValues[index] = entry.value;
index++;
}
debugInformation.lineNumberKeys = new GeneratedLocation[lineNumberEntries.size()]; debugInformation.rebuildFileDescriptions();
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<GeneratedLocation> locations = fileDescProto.generatedLocations.get(index);
fileDesc.generatedLocations[i] = locations != null ?
locations.toArray(new GeneratedLocation[0]) : null;
}
}
} }
return debugInformation; return debugInformation;
} }
static class FileDescriptionProto { static class Mapping {
List<List<GeneratedLocation>> generatedLocations = new ArrayList<>(); IntegerArray lines = new IntegerArray(1);
List<MethodReference> methodMap = new ArrayList<>(); IntegerArray columns = new IntegerArray(1);
IntegerArray values = new IntegerArray(1);
void addGeneratedLocation(int line, GeneratedLocation location) { public void add(LocationProvider location, int value) {
if (line >= generatedLocations.size()) { lines.add(location.getLine());
generatedLocations.addAll(Collections.<List<GeneratedLocation>>nCopies( columns.add(location.getColumn());
line - generatedLocations.size() + 1, null)); values.add(value);
}
List<GeneratedLocation> existingLocations = generatedLocations.get(line);
if (existingLocations == null) {
existingLocations = new ArrayList<>();
generatedLocations.set(line, existingLocations);
}
existingLocations.add(location);
} }
void setMethod(int line, MethodReference method) { DebugInformation.Mapping build() {
if (line >= methodMap.size()) { return new DebugInformation.Mapping(lines.getAll(), columns.getAll(), values.getAll());
methodMap.addAll(Collections.<MethodReference>nCopies(line - methodMap.size() + 1, null));
}
methodMap.set(line, method);
} }
} }
static class Entry { static class MappedList {
GeneratedLocation key; private List<String> list = new ArrayList<>();
int value; private Map<String, Integer> map = new HashMap<>();
public Entry(GeneratedLocation key, int value) { public int index(String item) {
this.key = key; if (item == null) {
this.value = value; 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<String, Integer> getIndexes() {
return new HashMap<>(map);
} }
} }
} }

View File

@ -16,7 +16,7 @@
package org.teavm.debugging; package org.teavm.debugging;
import org.teavm.codegen.LocationProvider; 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 emitLocation(String fileName, int line);
void emitMethod(MethodReference method); void emitMethod(MethodDescriptor method);
void emitClass(String className);
} }

View File

@ -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");
}
}

View File

@ -17,12 +17,6 @@ package org.teavm.debugging;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; 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 { public void write(DebugInformation debugInfo) throws IOException {
writeNumber(debugInfo.fileNames.length); writeStringArray(debugInfo.fileNames);
for (int i = 0; i < debugInfo.fileNames.length; ++i) { writeStringArray(debugInfo.classNames);
String fileName = debugInfo.fileNames[i]; writeStringArray(debugInfo.methods);
writeString(fileName);
writeMethods(debugInfo.fileDescriptions[i]);
}
writeNumber(debugInfo.fileNameKeys.length); writeMapping(debugInfo.fileMapping);
resetRelativeNumber(); writeMapping(debugInfo.lineMapping);
for (int i = 0; i < debugInfo.fileNameKeys.length; ++i) { writeMapping(debugInfo.classMapping);
writeRelativeNumber(debugInfo.fileNameKeys[i].getLine()); writeMapping(debugInfo.methodMapping);
} }
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]);
}
writeNumber(debugInfo.lineNumberKeys.length); private void writeStringArray(String[] array) throws IOException {
resetRelativeNumber(); writeUnsignedNumber(array.length);
resetRelativeNumber(); for (int i = 0; i < array.length; ++i) {
for (int i = 0; i < debugInfo.lineNumberKeys.length; ++i) { writeString(array[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 writeMethods(FileDescription fileDesc) throws IOException { private void writeMapping(DebugInformation.Mapping mapping) throws IOException {
Map<MethodReference, IntegerArray> methodLineMap = new HashMap<>(); writeUnsignedNumber(mapping.lines.length);
for (int i = 0; i < fileDesc.methodMap.length; ++i) { int[] lines = mapping.lines.clone();
MethodReference method = fileDesc.methodMap[i]; int last = 0;
if (method == null) { for (int i = 0; i < lines.length; ++i) {
continue; last = lines[i];
} lines[i] -= last;
IntegerArray lines = methodLineMap.get(method);
if (lines == null) {
lines = new IntegerArray(1);
methodLineMap.put(method, lines);
}
lines.add(i);
} }
writeNumber(methodLineMap.size()); writeRle(lines);
for (MethodReference method : methodLineMap.keySet()) { resetRelativeNumber();
writeString(method.toString()); for (int i = 0; i < mapping.columns.length; ++i) {
int[] lines = methodLineMap.get(method).getAll(); writeRelativeNumber(mapping.columns[i]);
Arrays.sort(lines); }
for (int i = 0; i < lines.length;) { resetRelativeNumber();
writeRelativeNumber(i); for (int i = 0; i < mapping.values.length; ++i) {
int j = i; writeRelativeNumber(mapping.values[i]);
int last = lines[i];
++i;
while (i < lines.length && lines[i] == last + 1) {
++i;
++last;
}
writeNumber(i - j);
}
writeRelativeNumber(-1);
} }
} }
private void writeNumber(int number) throws IOException { 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 { do {
if (number < 0) {
number = (-number << 1) | 1;
} else {
number = number << 1;
}
byte b = (byte)(number & 0x7F); byte b = (byte)(number & 0x7F);
if ((number & 0xFFFFFF80) != 0) { if ((number & 0xFFFFFF80) != 0) {
b |= 0x80; b |= 0x80;
@ -124,6 +86,25 @@ class DebugInformationWriter {
} while (number != 0); } 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 { private void writeRelativeNumber(int number) throws IOException {
writeNumber(number - lastNumber); writeNumber(number - lastNumber);
lastNumber = number; lastNumber = number;
@ -134,7 +115,8 @@ class DebugInformationWriter {
} }
private void writeString(String str) throws IOException { private void writeString(String str) throws IOException {
writeNumber(str.length()); byte[] bytes = str.getBytes("UTF-8");
output.write(str.getBytes("UTF-8")); writeUnsignedNumber(bytes.length);
output.write(bytes);
} }
} }

View File

@ -119,7 +119,7 @@ public class Debugger {
for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) { for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) {
SourceLocation loc = debugInformation.getSourceLocation(jsFrame.getLocation()); SourceLocation loc = debugInformation.getSourceLocation(jsFrame.getLocation());
boolean empty = loc == null || (loc.getFileName() == null && loc.getLine() < 0); 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) { if (!empty || !wasEmpty) {
frames.add(new CallFrame(loc, method)); frames.add(new CallFrame(loc, method));
} }

View File

@ -16,7 +16,7 @@
package org.teavm.debugging; package org.teavm.debugging;
import org.teavm.codegen.LocationProvider; 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 @Override
public void emitMethod(MethodReference method) { public void emitMethod(MethodDescriptor method) {
}
@Override
public void emitClass(String className) {
} }
@Override @Override

View File

@ -44,4 +44,30 @@ public class GeneratedLocation implements Comparable<GeneratedLocation> {
} }
return r; 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;
}
} }

View File

@ -242,6 +242,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
public void render(ClassNode cls) throws RenderingException { public void render(ClassNode cls) throws RenderingException {
debugEmitter.emitClass(cls.getName());
try { try {
writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{") writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{")
.indent().softNewLine(); .indent().softNewLine();
@ -354,6 +355,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occured", e); throw new RenderingException("IO error occured", e);
} }
debugEmitter.emitClass(null);
} }
private static Object getDefaultValue(ValueType type) { private static Object getDefaultValue(ValueType type) {
@ -383,6 +385,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private void renderInitializer(MethodNode method) throws IOException { private void renderInitializer(MethodNode method) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor());
writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function("); writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).ws().append("=").ws().append("function(");
for (int i = 1; i <= ref.parameterCount(); ++i) { for (int i = 1; i <= ref.parameterCount(); ++i) {
if (i > 1) { if (i > 1) {
@ -403,21 +406,23 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(");").softNewLine(); writer.append(");").softNewLine();
writer.append("return result;").softNewLine(); writer.append("return result;").softNewLine();
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
debugEmitter.emitMethod(null);
} }
private void renderVirtualDeclarations(String className, List<MethodNode> methods) private void renderVirtualDeclarations(String className, List<MethodNode> methods)
throws NamingException, IOException { throws NamingException, IOException {
if (methods.isEmpty()) {
return;
}
for (MethodNode method : methods) { for (MethodNode method : methods) {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
if (ref.getDescriptor().getName().equals("<init>")) { if (ref.getDescriptor().getName().equals("<init>")) {
renderInitializer(method); renderInitializer(method);
} }
} }
if (methods.isEmpty()) {
return;
}
writer.append("$rt_virtualMethods(").appendClass(className).indent(); writer.append("$rt_virtualMethods(").appendClass(className).indent();
for (MethodNode method : methods) { for (MethodNode method : methods) {
debugEmitter.emitMethod(method.getReference().getDescriptor());
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
writer.append(",").newLine(); writer.append(",").newLine();
if (method.isOriginalNamePreserved()) { if (method.isOriginalNamePreserved()) {
@ -443,12 +448,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(",").ws().append(variableName(i)); writer.append(",").ws().append(variableName(i));
} }
writer.append(");").ws().append("}"); writer.append(");").ws().append("}");
debugEmitter.emitMethod(null);
} }
writer.append(");").newLine().outdent(); writer.append(");").newLine().outdent();
} }
private void renderStaticDeclaration(MethodNode method) throws NamingException, IOException { private void renderStaticDeclaration(MethodNode method) throws NamingException, IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref.getDescriptor());
if (ref.getDescriptor().getName().equals("<init>")) { if (ref.getDescriptor().getName().equals("<init>")) {
renderInitializer(method); renderInitializer(method);
} }
@ -470,11 +477,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.appendClass(ref.getClassName()).append(".").append(ref.getName()).ws().append("=") writer.appendClass(ref.getClassName()).append(".").append(ref.getName()).ws().append("=")
.ws().appendClass(ref.getClassName()).append(".").appendMethod(ref).append(';').newLine(); .ws().appendClass(ref.getClassName()).append(".").appendMethod(ref).append(';').newLine();
} }
debugEmitter.emitMethod(null);
} }
public void renderBody(MethodNode method, boolean inner) throws IOException { public void renderBody(MethodNode method, boolean inner) throws IOException {
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
debugEmitter.emitMethod(ref); debugEmitter.emitMethod(ref.getDescriptor());
if (inner) { if (inner) {
writer.appendMethodBody(ref).ws().append("=").ws().append("function("); writer.appendMethodBody(ref).ws().append("=").ws().append("function(");
} else { } else {