mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Refactoring of DebugInformation. Reduce memory consumption and debug
file size. Add debug information reader
This commit is contained in:
parent
62281f696c
commit
444c599b17
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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<MethodReference, IntegerArray> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user