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.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<String, Integer> fileNameMap;
String[] classNames;
Map<String, Integer> classNameMap;
String[] methods;
Map<String, Integer> 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<GeneratedLocation> 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.<GeneratedLocation>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<GeneratedLocation> 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> T componentByKey(GeneratedLocation[] keys, int[] valueIndexes, T[] values,
GeneratedLocation location) {
int keyIndex = indexByKey(keys, location);
int valueIndex = keyIndex >= 0 ? valueIndexes[keyIndex] : -1;
private <T> 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<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 {
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<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 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<String> fileNames = new ArrayList<>();
private Map<String, Integer> fileNameMap = new HashMap<>();
private List<Entry> fileNameEntries = new ArrayList<>();
private List<Entry> 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<FileDescriptionProto> 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;
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<GeneratedLocation> locations = fileDescProto.generatedLocations.get(index);
fileDesc.generatedLocations[i] = locations != null ?
locations.toArray(new GeneratedLocation[0]) : null;
}
}
debugInformation.rebuildFileDescriptions();
}
return debugInformation;
}
static class FileDescriptionProto {
List<List<GeneratedLocation>> generatedLocations = new ArrayList<>();
List<MethodReference> 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.<List<GeneratedLocation>>nCopies(
line - generatedLocations.size() + 1, null));
}
List<GeneratedLocation> 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.<MethodReference>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<String> list = new ArrayList<>();
private Map<String, Integer> 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<String, Integer> getIndexes() {
return new HashMap<>(map);
}
}
}

View File

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

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.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);
writeMapping(debugInfo.fileMapping);
writeMapping(debugInfo.lineMapping);
writeMapping(debugInfo.classMapping);
writeMapping(debugInfo.methodMapping);
}
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]);
}
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<MethodReference, IntegerArray> methodLineMap = new HashMap<>();
for (int i = 0; i < fileDesc.methodMap.length; ++i) {
MethodReference method = fileDesc.methodMap[i];
if (method == null) {
continue;
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;
}
IntegerArray lines = methodLineMap.get(method);
if (lines == null) {
lines = new IntegerArray(1);
methodLineMap.put(method, lines);
writeRle(lines);
resetRelativeNumber();
for (int i = 0; i < mapping.columns.length; ++i) {
writeRelativeNumber(mapping.columns[i]);
}
lines.add(i);
}
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);
resetRelativeNumber();
for (int i = 0; i < mapping.values.length; ++i) {
writeRelativeNumber(mapping.values[i]);
}
}
private void writeNumber(int number) throws IOException {
do {
if (number < 0) {
number = (-number << 1) | 1;
} else {
number = number << 1;
writeUnsignedNumber(convertToSigned(number));
}
private int convertToSigned(int number) {
return number < 0 ? (-number << 1) | 1 : number << 1;
}
private void writeUnsignedNumber(int number) throws IOException {
do {
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);
}
}

View File

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

View File

@ -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

View File

@ -44,4 +44,30 @@ public class GeneratedLocation implements Comparable<GeneratedLocation> {
}
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 {
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<MethodNode> methods)
throws NamingException, IOException {
if (methods.isEmpty()) {
return;
}
for (MethodNode method : methods) {
MethodReference ref = method.getReference();
if (ref.getDescriptor().getName().equals("<init>")) {
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("<init>")) {
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 {