mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 07:54:11 -08:00
Refactoring of debug information
This commit is contained in:
parent
88c47095a6
commit
45c336ebb8
|
@ -57,6 +57,18 @@ public class RecordArray {
|
|||
return arraysPerRecord;
|
||||
}
|
||||
|
||||
public int[] cut(int index) {
|
||||
if (index < 0 || index >= recordSize) {
|
||||
throw new IndexOutOfBoundsException("Index " + index + " is outside of [0; " + recordSize + ")");
|
||||
}
|
||||
int[] result = new int[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
result[i] = data[index];
|
||||
index += recordSize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public class Record {
|
||||
int offset;
|
||||
int arrayOffset;
|
||||
|
|
|
@ -110,6 +110,13 @@ public class RecordArrayBuilder {
|
|||
return data.get(index + offset);
|
||||
}
|
||||
|
||||
public void set(int index, int value) {
|
||||
if (index >= recordSize) {
|
||||
throw new IndexOutOfBoundsException("Index out of bounds: " + index + " of " + recordSize);
|
||||
}
|
||||
data.set(index + offset, value);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return recordSize;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
@ -5,5 +20,40 @@ package org.teavm.debugging;
|
|||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class ClassNameIterator {
|
||||
private DebugInformation debugInformation;
|
||||
private int index;
|
||||
|
||||
ClassNameIterator(DebugInformation debugInformation) {
|
||||
this.debugInformation = debugInformation;
|
||||
}
|
||||
|
||||
public boolean isEndReached() {
|
||||
return index < debugInformation.classMapping.size();
|
||||
}
|
||||
|
||||
public GeneratedLocation getLocation() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return DebugInformation.key(debugInformation.classMapping.get(index));
|
||||
}
|
||||
|
||||
public int getClassNameId() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return debugInformation.classMapping.get(index).get(2);
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
int classNameId = getClassNameId();
|
||||
return classNameId >= 0 ? debugInformation.getClassName(classNameId) : null;
|
||||
}
|
||||
|
||||
public void next() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class DebugInformation {
|
|||
RecordArray methodMapping;
|
||||
RecordArray lineMapping;
|
||||
RecordArray callSiteMapping;
|
||||
MultiMapping[] variableMappings;
|
||||
RecordArray[] variableMappings;
|
||||
RecordArray[] lineCallSites;
|
||||
CFG[] controlFlowGraphs;
|
||||
List<ClassMetadata> classesMetadata;
|
||||
|
@ -73,6 +73,71 @@ public class DebugInformation {
|
|||
return fileNames[fileNameId];
|
||||
}
|
||||
|
||||
public String[] getClassNames() {
|
||||
return classNames.clone();
|
||||
}
|
||||
|
||||
public String getClassName(int classNameId) {
|
||||
return classNames[classNameId];
|
||||
}
|
||||
|
||||
public MethodDescriptor[] getMethods() {
|
||||
MethodDescriptor[] descriptors = new MethodDescriptor[methods.length];
|
||||
for (int i = 0; i < descriptors.length; ++i) {
|
||||
descriptors[i] = MethodDescriptor.parse(methods[i]);
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public MethodDescriptor getMethod(int methodId) {
|
||||
return MethodDescriptor.parse(methods[methodId]);
|
||||
}
|
||||
|
||||
public MethodReference[] getExactMethods() {
|
||||
MethodReference[] result = new MethodReference[exactMethods.length];
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i] = getExactMethod(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Integer getExactMethodIndex(MethodReference methodRef) {
|
||||
Integer classIndex = classNameMap.get(methodRef.getClassName());
|
||||
if (classIndex == null) {
|
||||
return null;
|
||||
}
|
||||
Integer methodIndex = methodMap.get(methodRef.getDescriptor().toString());
|
||||
if (methodIndex == null) {
|
||||
return null;
|
||||
}
|
||||
return getExactMethodIndex(classIndex, methodIndex);
|
||||
}
|
||||
|
||||
public MethodReference getExactMethod(int index) {
|
||||
long item = exactMethods[index];
|
||||
int classIndex = (int)(item >>> 32);
|
||||
int methodIndex = (int)item;
|
||||
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
|
||||
}
|
||||
|
||||
public int getExactMethodId(int classNameId, int methodId) {
|
||||
long full = ((long)classNameId << 32) | methodId;
|
||||
Integer id = exactMethodMap.get(full);
|
||||
return id != null ? id : -1;
|
||||
}
|
||||
|
||||
public ClassNameIterator iterateOverClassNames() {
|
||||
return new ClassNameIterator(this);
|
||||
}
|
||||
|
||||
public MethodIterator iterateOverMethods() {
|
||||
return new MethodIterator(this);
|
||||
}
|
||||
|
||||
public ExactMethodIterator iterateOverExactMethods() {
|
||||
return new ExactMethodIterator(this);
|
||||
}
|
||||
|
||||
public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) {
|
||||
Integer fileIndex = fileNameMap.get(fileName);
|
||||
if (fileIndex == null) {
|
||||
|
@ -139,11 +204,11 @@ public class DebugInformation {
|
|||
if (varIndex == null) {
|
||||
return new String[0];
|
||||
}
|
||||
MultiMapping mapping = variableMappings[varIndex];
|
||||
RecordArray mapping = variableMappings[varIndex];
|
||||
if (mapping == null) {
|
||||
return new String[0];
|
||||
}
|
||||
return componentByKey(mapping, variableNames, location);
|
||||
return componentSetByKey(mapping, variableNames, location);
|
||||
}
|
||||
|
||||
public SourceLocation[] getFollowingLines(SourceLocation location) {
|
||||
|
@ -212,7 +277,7 @@ public class DebugInformation {
|
|||
case DebuggerCallSite.STATIC:
|
||||
return new DebuggerStaticCallSite(getExactMethod(data[0]));
|
||||
case DebuggerCallSite.VIRTUAL:
|
||||
return new DebuggerVirtualCallSite(getExactMethod(data[0]), data[1], variableNames[data[1]]);
|
||||
return new DebuggerVirtualCallSite(getExactMethod(data[0]));
|
||||
default:
|
||||
throw new AssertionError("Unrecognized call site type: " + type);
|
||||
}
|
||||
|
@ -227,26 +292,12 @@ public class DebugInformation {
|
|||
if (index == null) {
|
||||
return new GeneratedLocation[0];
|
||||
}
|
||||
return methodEntrances.getEntrances(index);
|
||||
}
|
||||
|
||||
private Integer getExactMethodIndex(MethodReference methodRef) {
|
||||
Integer classIndex = classNameMap.get(methodRef.getClassName());
|
||||
if (classIndex == null) {
|
||||
return null;
|
||||
int[] data = methodEntrances.get(0).getArray(0);
|
||||
GeneratedLocation[] entrances = new GeneratedLocation[data.length / 2];
|
||||
for (int i = 0; i < entrances.length; ++i) {
|
||||
entrances[i] = new GeneratedLocation(data[i * 2], data[i * 2 + 1]);
|
||||
}
|
||||
Integer methodIndex = methodMap.get(methodRef.getDescriptor().toString());
|
||||
if (methodIndex == null) {
|
||||
return null;
|
||||
}
|
||||
return getExactMethodIndex(classIndex, methodIndex);
|
||||
}
|
||||
|
||||
public MethodReference getExactMethod(int index) {
|
||||
long item = exactMethods[index];
|
||||
int classIndex = (int)(item >>> 32);
|
||||
int methodIndex = (int)item;
|
||||
return new MethodReference(classNames[classIndex], MethodDescriptor.parse(methods[methodIndex]));
|
||||
return entrances;
|
||||
}
|
||||
|
||||
public MethodReference[] getDirectOverridingMethods(MethodReference methodRef) {
|
||||
|
@ -300,16 +351,15 @@ public class DebugInformation {
|
|||
return valueIndex >= 0 ? values[valueIndex] : null;
|
||||
}
|
||||
|
||||
private String[] componentByKey(MultiMapping mapping, String[] values, GeneratedLocation location) {
|
||||
private String[] componentSetByKey(RecordArray mapping, String[] values, GeneratedLocation location) {
|
||||
int keyIndex = indexByKey(mapping, location);
|
||||
if (keyIndex < 0) {
|
||||
return new String[0];
|
||||
}
|
||||
int start = mapping.offsets[keyIndex];
|
||||
int end = mapping.offsets[keyIndex + 1];
|
||||
String[] result = new String[end - start];
|
||||
int[] valueIndexes = mapping.get(keyIndex).getArray(0);
|
||||
String[] result = new String[valueIndexes.length];
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i] = values[mapping.data[i + start]];
|
||||
result[i] = values[valueIndexes[i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -324,11 +374,6 @@ public class DebugInformation {
|
|||
return index >= 0 ? mapping.get(index).get(2) : -1;
|
||||
}
|
||||
|
||||
private int indexByKey(MultiMapping mapping, GeneratedLocation location) {
|
||||
int index = Collections.binarySearch(mapping.keyList(), location);
|
||||
return index >= 0 ? index : -index - 2;
|
||||
}
|
||||
|
||||
public void write(OutputStream output) throws IOException {
|
||||
DebugInformationWriter writer = new DebugInformationWriter(new DataOutputStream(output));
|
||||
writer.write(this);
|
||||
|
@ -372,48 +417,56 @@ public class DebugInformation {
|
|||
}
|
||||
|
||||
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()) {
|
||||
RecordArray.Record fileRec = fileMapping.get(fileIndex);
|
||||
RecordArray.Record lineRec = lineMapping.get(lineIndex);
|
||||
GeneratedLocation fileLoc = key(fileRec);
|
||||
GeneratedLocation lineLoc = key(lineRec);
|
||||
int cmp = fileLoc.compareTo(lineLoc);
|
||||
if (cmp < 0) {
|
||||
currentFile = fileRec.get(2);
|
||||
fileIndex++;
|
||||
} else if (cmp > 0){
|
||||
currentLine = lineRec.get(2);
|
||||
lineIndex++;
|
||||
} else {
|
||||
currentFile = fileRec.get(2);
|
||||
currentLine = lineRec.get(2);
|
||||
fileIndex++;
|
||||
lineIndex++;
|
||||
RecordArrayBuilder[] builders = new RecordArrayBuilder[fileNames.length];
|
||||
for (int i = 0; i < builders.length; ++i) {
|
||||
builders[i] = new RecordArrayBuilder(0, 1);
|
||||
}
|
||||
for (SourceLocationIterator iter = iterateOverSourceLocations(); !iter.isEndReached(); iter.next()) {
|
||||
if (iter.getFileNameId() >= 0 && iter.getLine() >= 0) {
|
||||
RecordArrayBuilder builder = builders[iter.getFileNameId()];
|
||||
while (builder.size() <= iter.getLine()) {
|
||||
builder.add();
|
||||
}
|
||||
GeneratedLocation loc = iter.getLocation();
|
||||
RecordArrayBuilder.RecordSubArray array = builder.get(iter.getLine()).getArray(0);
|
||||
array.add(loc.getLine());
|
||||
array.add(loc.getColumn());
|
||||
}
|
||||
builder.emit(fileLoc.getLine(), fileLoc.getColumn(), currentFile, currentLine);
|
||||
}
|
||||
while (fileIndex < fileMapping.size()) {
|
||||
RecordArray.Record fileRec = fileMapping.get(fileIndex++);
|
||||
builder.emit(fileRec.get(0), fileRec.get(1), fileRec.get(2), currentLine);
|
||||
fileDescriptions = new RecordArray[builders.length];
|
||||
for (int i = 0; i < fileDescriptions.length; ++i) {
|
||||
fileDescriptions[i] = builders[i].build();
|
||||
}
|
||||
while (lineIndex < lineMapping.size()) {
|
||||
RecordArray.Record lineRec = lineMapping.get(lineIndex++);
|
||||
builder.emit(lineRec.get(0), lineRec.get(1), currentFile, lineRec.get(2));
|
||||
}
|
||||
fileDescriptions = builder.build();
|
||||
}
|
||||
|
||||
void rebuildEntrances() {
|
||||
RecordArrayBuilder builder = new RecordArrayBuilder(0, 1);
|
||||
for (SourceLocationIterator iter = iterateOverSourceLocations(); !iter.isEndReached(); iter.next()) {
|
||||
iter.getLocation();
|
||||
for (int i = 0; i < exactMethods.length; ++i) {
|
||||
builder.add();
|
||||
}
|
||||
GeneratedLocation prevLocation = new GeneratedLocation(0, 0);
|
||||
MethodReference prevMethod = null;
|
||||
for (ExactMethodIterator iter = iterateOverExactMethods(); !iter.isEndReached(); iter.next()) {
|
||||
int id = iter.getExactMethodId();
|
||||
if (prevMethod != null) {
|
||||
int lineIndex = Math.max(0, indexByKey(lineMapping, prevLocation));
|
||||
while (lineIndex < 0) {
|
||||
if (key(lineMapping.get(lineIndex)).compareTo(iter.getLocation()) >= 0) {
|
||||
break;
|
||||
}
|
||||
int line = lineMapping.get(0).get(2);
|
||||
if (line >= 0) {
|
||||
GeneratedLocation firstLineLoc = key(lineMapping.get(lineIndex));
|
||||
RecordArrayBuilder.RecordSubArray array = builder.get(id).getArray(0);
|
||||
array.add(firstLineLoc.getLine());
|
||||
array.add(firstLineLoc.getColumn());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prevMethod = iter.getExactMethod();
|
||||
prevLocation = iter.getLocation();
|
||||
}
|
||||
methodEntrances = new MethodEntrancesBuilder().build();
|
||||
}
|
||||
|
||||
void rebuildMethodTree() {
|
||||
|
@ -516,188 +569,10 @@ public class DebugInformation {
|
|||
}
|
||||
}
|
||||
|
||||
class MethodEntrancesBuilder {
|
||||
int[] start;
|
||||
IntegerArray data;
|
||||
IntegerArray next;
|
||||
int methodIndex;
|
||||
int classIndex;
|
||||
|
||||
public MethodEntrances build() {
|
||||
methodIndex = -1;
|
||||
classIndex = -1;
|
||||
start = new int[exactMethods.length];
|
||||
Arrays.fill(start, -1);
|
||||
data = new IntegerArray(0);
|
||||
next = new IntegerArray(0);
|
||||
int methodMappingIndex = 0;
|
||||
int classMappingIndex = 0;
|
||||
GeneratedLocation previousLocation = new GeneratedLocation(0, 0);
|
||||
while (methodMappingIndex < methodMapping.lines.length &&
|
||||
classMappingIndex < classMapping.lines.length) {
|
||||
GeneratedLocation methodLoc = new GeneratedLocation(methodMapping.lines[methodMappingIndex],
|
||||
methodMapping.columns[methodMappingIndex]);
|
||||
GeneratedLocation classLoc = new GeneratedLocation(classMapping.lines[classMappingIndex],
|
||||
classMapping.columns[classMappingIndex]);
|
||||
int cmp = methodLoc.compareTo(classLoc);
|
||||
if (cmp < 0) {
|
||||
addMethodEntrance(previousLocation, methodLoc);
|
||||
previousLocation = methodLoc;
|
||||
methodIndex = methodMapping.values[methodMappingIndex++];
|
||||
} else if (cmp > 0) {
|
||||
addMethodEntrance(previousLocation, classLoc);
|
||||
previousLocation = classLoc;
|
||||
classIndex = classMapping.values[classMappingIndex++];
|
||||
} else {
|
||||
addMethodEntrance(previousLocation, classLoc);
|
||||
previousLocation = classLoc;
|
||||
methodIndex = methodMapping.values[methodMappingIndex++];
|
||||
classIndex = classMapping.values[classMappingIndex++];
|
||||
}
|
||||
}
|
||||
while (methodMappingIndex < methodMapping.lines.length) {
|
||||
GeneratedLocation methodLoc = new GeneratedLocation(methodMapping.lines[methodMappingIndex],
|
||||
methodMapping.columns[methodMappingIndex]);
|
||||
addMethodEntrance(previousLocation, methodLoc);
|
||||
previousLocation = methodLoc;
|
||||
methodIndex = methodMapping.values[methodMappingIndex++];
|
||||
}
|
||||
while (classMappingIndex < classMapping.lines.length) {
|
||||
GeneratedLocation classLoc = new GeneratedLocation(classMapping.lines[classMappingIndex],
|
||||
classMapping.columns[classMappingIndex]);
|
||||
addMethodEntrance(previousLocation, classLoc);
|
||||
previousLocation = classLoc;
|
||||
classIndex = classMapping.values[classMappingIndex++];
|
||||
}
|
||||
return assemble();
|
||||
}
|
||||
|
||||
private void addMethodEntrance(GeneratedLocation location, GeneratedLocation limit) {
|
||||
if (classIndex < 0 || methodIndex < 0) {
|
||||
return;
|
||||
}
|
||||
long exactMethod = ((long)classIndex << 32) | methodIndex;
|
||||
Integer exactMethodIndex = exactMethodMap.get(exactMethod);
|
||||
if (exactMethodIndex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int lineIndex = indexByKey(lineMapping, location);
|
||||
if (lineIndex < 0) {
|
||||
lineIndex = 0;
|
||||
}
|
||||
int line = -1;
|
||||
while (lineIndex < lineMapping.values.length) {
|
||||
location = new GeneratedLocation(lineMapping.lines[lineIndex], lineMapping.columns[lineIndex]);
|
||||
if (location.compareTo(limit) >= 0) {
|
||||
break;
|
||||
}
|
||||
if (lineMapping.values[lineIndex] >= 0) {
|
||||
line = lineMapping.values[lineIndex];
|
||||
break;
|
||||
}
|
||||
++lineIndex;
|
||||
}
|
||||
if (line == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ptr = start[exactMethodIndex];
|
||||
start[exactMethodIndex] = data.size();
|
||||
next.add(ptr);
|
||||
ptr = data.size();
|
||||
data.add(location.getColumn());
|
||||
next.add(ptr);
|
||||
start[exactMethodIndex] = data.size();
|
||||
data.add(location.getLine());
|
||||
}
|
||||
|
||||
private MethodEntrances assemble() {
|
||||
MethodEntrances entrances = new MethodEntrances();
|
||||
entrances.offsets = new int[start.length + 1];
|
||||
entrances.data = new int[data.size()];
|
||||
int index = 0;
|
||||
for (int i = 0; i < start.length; ++i) {
|
||||
int ptr = start[i];
|
||||
while (ptr != -1) {
|
||||
entrances.data[index++] = data.get(ptr);
|
||||
ptr = next.get(ptr);
|
||||
}
|
||||
entrances.offsets[i + 1] = index;
|
||||
}
|
||||
return entrances;
|
||||
}
|
||||
}
|
||||
|
||||
static class FileDescriptionBuilder {
|
||||
RecordArrayBuilder[] files;
|
||||
int lastFileIndex = -1;
|
||||
int lastSourceLine = -1;
|
||||
|
||||
public FileDescriptionBuilder(int size) {
|
||||
files = new RecordArrayBuilder[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
files[i] = new RecordArrayBuilder(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void emit(int line, int column, int fileIndex, int sourceLine) {
|
||||
if (sourceLine == -1 || fileIndex == -1) {
|
||||
return;
|
||||
}
|
||||
if (lastFileIndex == fileIndex && lastSourceLine == sourceLine) {
|
||||
return;
|
||||
}
|
||||
lastFileIndex = fileIndex;
|
||||
lastSourceLine = sourceLine;
|
||||
RecordArrayBuilder proto = files[fileIndex];
|
||||
while (proto.size() <= sourceLine) {
|
||||
proto.add();
|
||||
}
|
||||
RecordArrayBuilder.RecordSubArray array = proto.get(sourceLine).getArray(0);
|
||||
array.add(line);
|
||||
array.add(column);
|
||||
}
|
||||
|
||||
public RecordArray[] build() {
|
||||
RecordArray[] descriptions = new RecordArray[files.length];
|
||||
for (int i = 0; i < files.length; ++i) {
|
||||
descriptions[i] = files[i].build();
|
||||
}
|
||||
return descriptions;
|
||||
}
|
||||
}
|
||||
|
||||
static GeneratedLocation key(RecordArray.Record record) {
|
||||
return new GeneratedLocation(record.get(0), record.get(1));
|
||||
}
|
||||
|
||||
static class MultiMapping {
|
||||
int[] lines;
|
||||
int[] columns;
|
||||
int[] offsets;
|
||||
int[] data;
|
||||
|
||||
public MultiMapping(int[] lines, int[] columns, int[] offsets, int[] data) {
|
||||
this.lines = lines;
|
||||
this.columns = columns;
|
||||
this.offsets = offsets;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
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 RecordArray recordArray;
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.teavm.debugging;
|
|||
import java.util.*;
|
||||
import org.teavm.codegen.LocationProvider;
|
||||
import org.teavm.common.IntegerArray;
|
||||
import org.teavm.common.RecordArray;
|
||||
import org.teavm.common.RecordArrayBuilder;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
|
@ -35,12 +37,12 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
private MappedList variableNames = new MappedList();
|
||||
private List<Long> exactMethods = new ArrayList<>();
|
||||
private Map<Long, Integer> exactMethodMap = new HashMap<>();
|
||||
private Mapping fileMapping = new Mapping();
|
||||
private Mapping lineMapping = new Mapping();
|
||||
private Mapping classMapping = new Mapping();
|
||||
private Mapping methodMapping = new Mapping();
|
||||
private Mapping callSiteMapping = new Mapping();
|
||||
private Map<Integer, MultiMapping> variableMappings = new HashMap<>();
|
||||
private RecordArrayBuilder fileMapping = new RecordArrayBuilder(3, 0);
|
||||
private RecordArrayBuilder lineMapping = new RecordArrayBuilder(3, 0);
|
||||
private RecordArrayBuilder classMapping = new RecordArrayBuilder(3, 0);
|
||||
private RecordArrayBuilder methodMapping = new RecordArrayBuilder(3, 0);
|
||||
private RecordArrayBuilder callSiteMapping = new RecordArrayBuilder(3, 1);
|
||||
private Map<Integer, RecordArrayBuilder> variableMappings = new HashMap<>();
|
||||
private MethodDescriptor currentMethod;
|
||||
private String currentClass;
|
||||
private String currentFileName;
|
||||
|
@ -63,21 +65,40 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
debugInformation = null;
|
||||
int fileIndex = files.index(fileName);
|
||||
if (!Objects.equals(currentFileName, fileName)) {
|
||||
fileMapping.add(locationProvider, fileIndex, true);
|
||||
add(fileMapping, fileIndex);
|
||||
currentFileName = fileName;
|
||||
}
|
||||
if (currentLine != line) {
|
||||
lineMapping.add(locationProvider, line, true);
|
||||
add(lineMapping, line);
|
||||
currentLine = line;
|
||||
}
|
||||
}
|
||||
|
||||
private RecordArrayBuilder.Record add(RecordArrayBuilder builder) {
|
||||
if (builder.size() > 1) {
|
||||
RecordArrayBuilder.Record lastRecord = builder.get(builder.size() - 1);
|
||||
if (lastRecord.get(0) == locationProvider.getLine() && lastRecord.get(1) == locationProvider.getColumn()) {
|
||||
return lastRecord;
|
||||
}
|
||||
}
|
||||
RecordArrayBuilder.Record record = builder.add();
|
||||
record.set(0, locationProvider.getLine());
|
||||
record.set(1, locationProvider.getColumn());
|
||||
return record;
|
||||
}
|
||||
|
||||
private RecordArrayBuilder.Record add(RecordArrayBuilder builder, int value) {
|
||||
RecordArrayBuilder.Record record = add(builder);
|
||||
record.set(2, value);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitClass(String className) {
|
||||
debugInformation = null;
|
||||
int classIndex = classes.index(className);
|
||||
if (!Objects.equals(className, currentClass)) {
|
||||
classMapping.add(locationProvider, classIndex, true);
|
||||
add(classMapping, classIndex);
|
||||
currentClass = className;
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +108,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
debugInformation = null;
|
||||
int methodIndex = methods.index(method != null ? method.toString() : null);
|
||||
if (!Objects.equals(method, currentMethod)) {
|
||||
methodMapping.add(locationProvider, methodIndex, true);
|
||||
add(methodMapping, methodIndex);
|
||||
currentMethod = method;
|
||||
}
|
||||
if (currentClass != null) {
|
||||
|
@ -108,19 +129,42 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
Arrays.sort(sourceIndexes);
|
||||
int generatedIndex = variableNames.index(generatedName);
|
||||
MultiMapping mapping = variableMappings.get(generatedIndex);
|
||||
RecordArrayBuilder mapping = variableMappings.get(generatedIndex);
|
||||
if (mapping == null) {
|
||||
mapping = new MultiMapping();
|
||||
mapping = new RecordArrayBuilder(2, 1);
|
||||
variableMappings.put(generatedIndex, mapping);
|
||||
}
|
||||
mapping.add(locationProvider, sourceIndexes);
|
||||
|
||||
RecordArrayBuilder.Record record = add(mapping);
|
||||
RecordArrayBuilder.RecordSubArray array = record.getArray(0);
|
||||
for (int sourceIndex : sourceIndexes) {
|
||||
array.add(sourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeferredCallSite emitCallSite() {
|
||||
final RecordArrayBuilder.Record record = add(callSiteMapping, DebuggerCallSite.NONE);
|
||||
DeferredCallSite callSite = new DeferredCallSite() {
|
||||
int index = callSiteMapping.values.size();
|
||||
@Override public void setMethod(MethodReference method) {
|
||||
@Override
|
||||
public void setVirtualMethod(MethodReference method) {
|
||||
record.set(2, DebuggerCallSite.VIRTUAL);
|
||||
RecordArrayBuilder.RecordSubArray array = record.getArray(0);
|
||||
array.clear();
|
||||
array.add(getExactMethodIndex(method));
|
||||
}
|
||||
@Override
|
||||
public void setStaticMethod(MethodReference method) {
|
||||
record.set(2, DebuggerCallSite.STATIC);
|
||||
RecordArrayBuilder.RecordSubArray array = record.getArray(0);
|
||||
array.clear();
|
||||
array.add(getExactMethodIndex(method));
|
||||
}
|
||||
@Override
|
||||
public void clean() {
|
||||
record.set(2, DebuggerCallSite.NONE);
|
||||
}
|
||||
private int getExactMethodIndex(MethodReference method) {
|
||||
int methodIndex = methods.index(method.getDescriptor().toString());
|
||||
int classIndex = classes.index(method.getClassName());
|
||||
long fullIndex = ((long)classIndex << 32) | methodIndex;
|
||||
|
@ -130,18 +174,12 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
exactMethodMap.put(fullIndex, exactMethodIndex);
|
||||
exactMethods.add(fullIndex);
|
||||
}
|
||||
callSiteMapping.values.set(index, exactMethodIndex);
|
||||
return exactMethodIndex;
|
||||
}
|
||||
};
|
||||
callSiteMapping.add(locationProvider, -1, false);
|
||||
return callSite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitEmptyCallSite() {
|
||||
callSiteMapping.add(locationProvider, -1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClass(String className, String parentName) {
|
||||
int classIndex = classes.index(className);
|
||||
|
@ -181,6 +219,43 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
private RecordArrayBuilder compress(RecordArrayBuilder builder) {
|
||||
int lastValue = 0;
|
||||
RecordArrayBuilder compressed = new RecordArrayBuilder(builder.getRecordSize(), builder.getArraysPerRecord());
|
||||
for (int i = 0; i < builder.size(); ++i) {
|
||||
RecordArrayBuilder.Record record = builder.get(i);
|
||||
if (i == 0 || lastValue != record.get(2)) {
|
||||
RecordArrayBuilder.Record compressedRecord = compressed.add();
|
||||
for (int j = 0; j < builder.getRecordSize(); ++j) {
|
||||
compressedRecord.set(j, record.get(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
return compressed;
|
||||
}
|
||||
|
||||
private void compressAndSortArrays(RecordArrayBuilder builder) {
|
||||
for (int i = 0; i < builder.size(); ++i) {
|
||||
RecordArrayBuilder.Record record = builder.get(i);
|
||||
for (int j = 0; j < builder.getArraysPerRecord(); ++j) {
|
||||
RecordArrayBuilder.RecordSubArray array = record.getArray(j);
|
||||
int[] data = array.getData();
|
||||
Arrays.sort(data);
|
||||
array.clear();
|
||||
if (data.length > 0) {
|
||||
int last = data[0];
|
||||
array.add(last);
|
||||
for (int k = 1; k < data.length; ++k) {
|
||||
if (data[k] != last) {
|
||||
last = data[k];
|
||||
array.add(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DebugInformation getDebugInformation() {
|
||||
if (debugInformation == null) {
|
||||
debugInformation = new DebugInformation();
|
||||
|
@ -196,14 +271,15 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
}
|
||||
debugInformation.exactMethodMap = new HashMap<>(exactMethodMap);
|
||||
|
||||
debugInformation.fileMapping = fileMapping.build();
|
||||
debugInformation.lineMapping = lineMapping.build();
|
||||
debugInformation.classMapping = classMapping.build();
|
||||
debugInformation.methodMapping = methodMapping.build();
|
||||
debugInformation.fileMapping = compress(fileMapping).build();
|
||||
debugInformation.lineMapping = compress(lineMapping).build();
|
||||
debugInformation.classMapping = compress(classMapping).build();
|
||||
debugInformation.methodMapping = compress(methodMapping).build();
|
||||
debugInformation.callSiteMapping = callSiteMapping.build();
|
||||
debugInformation.variableMappings = new DebugInformation.MultiMapping[variableNames.list.size()];
|
||||
debugInformation.variableMappings = new RecordArray[variableNames.list.size()];
|
||||
for (int var : variableMappings.keySet()) {
|
||||
MultiMapping mapping = variableMappings.get(var);
|
||||
RecordArrayBuilder mapping = variableMappings.get(var);
|
||||
compressAndSortArrays(mapping);
|
||||
debugInformation.variableMappings[var] = mapping.build();
|
||||
}
|
||||
|
||||
|
@ -233,107 +309,6 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
|||
return debugInformation;
|
||||
}
|
||||
|
||||
static class Mapping {
|
||||
IntegerArray lines = new IntegerArray(1);
|
||||
IntegerArray columns = new IntegerArray(1);
|
||||
IntegerArray values = new IntegerArray(1);
|
||||
|
||||
public void add(LocationProvider location, int value, boolean merge) {
|
||||
if (merge && lines.size() > 1) {
|
||||
int last = lines.size() - 1;
|
||||
if (lines.get(last) == location.getLine() && columns.get(last) == location.getColumn()) {
|
||||
values.set(last, value);
|
||||
// TODO: check why this gives an invalid result
|
||||
/*if (values.get(last) == values.get(last - 1)) {
|
||||
values.remove(last);
|
||||
lines.remove(last);
|
||||
columns.remove(last);
|
||||
}*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
lines.add(location.getLine());
|
||||
columns.add(location.getColumn());
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
DebugInformation.Mapping build() {
|
||||
return new DebugInformation.Mapping(lines.getAll(), columns.getAll(), values.getAll());
|
||||
}
|
||||
}
|
||||
|
||||
static class MultiMapping {
|
||||
IntegerArray lines = new IntegerArray(1);
|
||||
IntegerArray columns = new IntegerArray(1);
|
||||
IntegerArray offsets = new IntegerArray(1);
|
||||
IntegerArray data = new IntegerArray(1);
|
||||
|
||||
public MultiMapping() {
|
||||
offsets.add(0);
|
||||
}
|
||||
|
||||
public void add(LocationProvider location, int[] values) {
|
||||
if (lines.size() > 1) {
|
||||
int last = lines.size() - 1;
|
||||
if (lines.get(last) == location.getLine() && columns.get(last) == location.getColumn()) {
|
||||
addToLast(values);
|
||||
return;
|
||||
}
|
||||
}
|
||||
lines.add(location.getLine());
|
||||
columns.add(location.getColumn());
|
||||
data.addAll(values);
|
||||
offsets.add(data.size());
|
||||
}
|
||||
|
||||
private void addToLast(int[] values) {
|
||||
int start = offsets.get(offsets.size() - 2);
|
||||
int end = offsets.get(offsets.size() - 1);
|
||||
int[] existing = data.getRange(start, end);
|
||||
values = merge(existing, values);
|
||||
if (values.length == existing.length) {
|
||||
return;
|
||||
}
|
||||
data.remove(start, end - start);
|
||||
data.addAll(values);
|
||||
offsets.set(offsets.size() - 1, data.size());
|
||||
}
|
||||
|
||||
private int[] merge(int[] a, int[] b) {
|
||||
int[] result = new int[a.length + b.length];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
while (i < a.length && j < b.length) {
|
||||
int p = a[i];
|
||||
int q = b[j];
|
||||
if (p == q) {
|
||||
result[k++] = p;
|
||||
++i;
|
||||
++j;
|
||||
} else if (p < q) {
|
||||
result[k++] = p;
|
||||
++i;
|
||||
} else {
|
||||
result[k++] = q;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
while (i < a.length) {
|
||||
result[k++] = a[i++];
|
||||
}
|
||||
while (j < b.length) {
|
||||
result[k++] = b[j++];
|
||||
}
|
||||
return k < result.length ? Arrays.copyOf(result, k) : result;
|
||||
}
|
||||
|
||||
public DebugInformation.MultiMapping build() {
|
||||
return new DebugInformation.MultiMapping(lines.getAll(), columns.getAll(), offsets.getAll(),
|
||||
data.getAll());
|
||||
}
|
||||
}
|
||||
|
||||
static class MappedList {
|
||||
private List<String> list = new ArrayList<>();
|
||||
private Map<String, Integer> map = new HashMap<>();
|
||||
|
|
|
@ -35,8 +35,6 @@ public interface DebugInformationEmitter {
|
|||
|
||||
DeferredCallSite emitCallSite();
|
||||
|
||||
void emitEmptyCallSite();
|
||||
|
||||
void addClass(String className, String parentName);
|
||||
|
||||
void addField(String fieldName, String jsName);
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.common.RecordArray;
|
||||
import org.teavm.debugging.DebugInformation.ClassMetadata;
|
||||
|
||||
/**
|
||||
|
@ -57,13 +58,13 @@ class DebugInformationWriter {
|
|||
int lastVar = 0;
|
||||
writeUnsignedNumber(nonNullVariableMappings(debugInfo));
|
||||
for (int i = 0; i < debugInfo.variableMappings.length; ++i) {
|
||||
DebugInformation.MultiMapping mapping = debugInfo.variableMappings[i];
|
||||
RecordArray mapping = debugInfo.variableMappings[i];
|
||||
if (mapping == null) {
|
||||
continue;
|
||||
}
|
||||
writeUnsignedNumber(i - lastVar);
|
||||
lastVar = i;
|
||||
writeMapping(mapping);
|
||||
writeMultiMapping(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,8 +115,8 @@ class DebugInformationWriter {
|
|||
}
|
||||
}
|
||||
|
||||
private void writeMapping(DebugInformation.MultiMapping mapping) throws IOException {
|
||||
int[] lines = mapping.lines.clone();
|
||||
private void writeLinesAndColumns(RecordArray mapping) throws IOException {
|
||||
int[] lines = mapping.cut(0);
|
||||
int last = 0;
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
int next = lines[i];
|
||||
|
@ -124,36 +125,36 @@ class DebugInformationWriter {
|
|||
}
|
||||
writeRle(lines);
|
||||
resetRelativeNumber();
|
||||
for (int i = 0; i < mapping.columns.length; ++i) {
|
||||
writeRelativeNumber(mapping.columns[i]);
|
||||
}
|
||||
int lastOffset = 0;
|
||||
for (int i = 1; i < mapping.offsets.length; ++i) {
|
||||
writeUnsignedNumber(mapping.offsets[i] - lastOffset);
|
||||
lastOffset = mapping.offsets[i];
|
||||
}
|
||||
resetRelativeNumber();
|
||||
for (int i = 0; i < mapping.data.length; ++i) {
|
||||
writeRelativeNumber(mapping.data[i]);
|
||||
int[] columns = mapping.cut(1);
|
||||
int lastLine = -1;
|
||||
for (int i = 0; i < columns.length; ++i) {
|
||||
if (lastLine != mapping.get(i).get(0)) {
|
||||
resetRelativeNumber();
|
||||
lastLine = mapping.get(i).get(0);
|
||||
}
|
||||
writeRelativeNumber(columns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMapping(DebugInformation.Mapping mapping) throws IOException {
|
||||
int[] lines = mapping.lines.clone();
|
||||
int last = 0;
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
int next = lines[i];
|
||||
lines[i] -= last;
|
||||
last = next;
|
||||
private void writeMultiMapping(RecordArray mapping) throws IOException {
|
||||
writeLinesAndColumns(mapping);
|
||||
for (int i = 0; i < mapping.size(); ++i) {
|
||||
int[] array = mapping.get(0).getArray(0);
|
||||
writeUnsignedNumber(array.length);
|
||||
int lastNumber = 0;
|
||||
for (int elem : array) {
|
||||
writeUnsignedNumber(elem - lastNumber);
|
||||
lastNumber = elem;
|
||||
}
|
||||
}
|
||||
writeRle(lines);
|
||||
}
|
||||
|
||||
private void writeMapping(RecordArray mapping) throws IOException {
|
||||
writeLinesAndColumns(mapping);
|
||||
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]);
|
||||
int[] values = mapping.cut(2);
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
writeRelativeNumber(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,16 +101,14 @@ public class Debugger {
|
|||
DebugInformation debugInfo = debugInformationMap.get(script);
|
||||
if (frame.getLocation() != null && frame.getLocation().getFileName() != null &&
|
||||
frame.getLocation().getLine() >= 0 && debugInfo != null) {
|
||||
MethodReference[] callMethods = debugInfo.getCallSites(frame.getLocation());
|
||||
exits = addFollowing(debugInfo, frame.getLocation(), script, new HashSet<SourceLocation>(),
|
||||
successors);
|
||||
if (enterMethod) {
|
||||
for (MethodReference callMethod : callMethods) {
|
||||
for (MethodReference potentialMethod : debugInfo.getOverridingMethods(callMethod)) {
|
||||
for (GeneratedLocation loc : debugInfo.getMethodEntrances(potentialMethod)) {
|
||||
successors.add(new JavaScriptLocation(script, loc.getLine(), loc.getColumn()));
|
||||
}
|
||||
}
|
||||
CallSiteSuccessorFinder successorFinder = new CallSiteSuccessorFinder(debugInfo, script,
|
||||
successors);
|
||||
DebuggerCallSite[] callSites = debugInfo.getCallSites(frame.getLocation());
|
||||
for (DebuggerCallSite callSite : callSites) {
|
||||
callSite.acceptVisitor(successorFinder);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -126,6 +124,34 @@ public class Debugger {
|
|||
javaScriptDebugger.resume();
|
||||
}
|
||||
|
||||
private static class CallSiteSuccessorFinder implements DebuggerCallSiteVisitor {
|
||||
private DebugInformation debugInfo;
|
||||
private String script;
|
||||
Set<JavaScriptLocation> locations;
|
||||
|
||||
public CallSiteSuccessorFinder(DebugInformation debugInfo, String script, Set<JavaScriptLocation> locations) {
|
||||
this.debugInfo = debugInfo;
|
||||
this.script = script;
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(DebuggerVirtualCallSite callSite) {
|
||||
for (MethodReference potentialMethod : debugInfo.getOverridingMethods(callSite.getMethod())) {
|
||||
for (GeneratedLocation loc : debugInfo.getMethodEntrances(potentialMethod)) {
|
||||
locations.add(new JavaScriptLocation(script, loc.getLine(), loc.getColumn()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(DebuggerStaticCallSite callSite) {
|
||||
for (GeneratedLocation loc : debugInfo.getMethodEntrances(callSite.getMethod())) {
|
||||
locations.add(new JavaScriptLocation(script, loc.getLine(), loc.getColumn()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addFollowing(DebugInformation debugInfo, SourceLocation location, String script,
|
||||
Set<SourceLocation> visited, Set<JavaScriptLocation> successors) {
|
||||
if (!visited.add(location)) {
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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 org.teavm.model.MethodReference;
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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 org.teavm.model.MethodReference;
|
||||
|
@ -8,13 +23,9 @@ import org.teavm.model.MethodReference;
|
|||
*/
|
||||
public class DebuggerVirtualCallSite extends DebuggerCallSite {
|
||||
private MethodReference method;
|
||||
private int variableId;
|
||||
private String variableName;
|
||||
|
||||
DebuggerVirtualCallSite(MethodReference method, int variableId, String variableName) {
|
||||
DebuggerVirtualCallSite(MethodReference method) {
|
||||
this.method = method;
|
||||
this.variableId = variableId;
|
||||
this.variableName = variableName;
|
||||
}
|
||||
|
||||
public MethodReference getMethod() {
|
||||
|
@ -25,22 +36,6 @@ public class DebuggerVirtualCallSite extends DebuggerCallSite {
|
|||
this.method = method;
|
||||
}
|
||||
|
||||
public int getVariableId() {
|
||||
return variableId;
|
||||
}
|
||||
|
||||
public void setVariableId(int variableId) {
|
||||
this.variableId = variableId;
|
||||
}
|
||||
|
||||
public String getVariableName() {
|
||||
return variableName;
|
||||
}
|
||||
|
||||
public void setVariableName(String variableName) {
|
||||
this.variableName = variableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(DebuggerCallSiteVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
|
|
|
@ -22,5 +22,9 @@ import org.teavm.model.MethodReference;
|
|||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface DeferredCallSite {
|
||||
void setMethod(MethodReference method);
|
||||
void setVirtualMethod(MethodReference method);
|
||||
|
||||
void setStaticMethod(MethodReference method);
|
||||
|
||||
void clean();
|
||||
}
|
||||
|
|
|
@ -44,15 +44,12 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter {
|
|||
@Override
|
||||
public DeferredCallSite emitCallSite() {
|
||||
return new DeferredCallSite() {
|
||||
@Override public void setMethod(MethodReference method) {
|
||||
}
|
||||
@Override public void setVirtualMethod(MethodReference method) { }
|
||||
@Override public void setStaticMethod(MethodReference method) { }
|
||||
@Override public void clean() { }
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitEmptyCallSite() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocationProvider(LocationProvider locationProvider) {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 org.teavm.common.RecordArray;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class ExactMethodIterator {
|
||||
private DebugInformation debugInformation;
|
||||
private GeneratedLocation location;
|
||||
private int classIndex;
|
||||
private int methodIndex;
|
||||
private int classId = -1;
|
||||
private int methodId = -1;
|
||||
private boolean endReached;
|
||||
|
||||
ExactMethodIterator(DebugInformation debugInformation) {
|
||||
this.debugInformation = debugInformation;
|
||||
read();
|
||||
}
|
||||
|
||||
public boolean isEndReached() {
|
||||
return endReached;
|
||||
}
|
||||
|
||||
private void read() {
|
||||
if (classIndex >= debugInformation.classMapping.size()) {
|
||||
nextClassRecord();
|
||||
} else if (methodIndex >= debugInformation.methodMapping.size()) {
|
||||
nextMethodRecord();
|
||||
} else if (classIndex < debugInformation.classMapping.size() &&
|
||||
methodIndex < debugInformation.methodMapping.size()) {
|
||||
RecordArray.Record classRecord = debugInformation.classMapping.get(classIndex++);
|
||||
RecordArray.Record methodRecord = debugInformation.methodMapping.get(methodIndex++);
|
||||
GeneratedLocation classLoc = DebugInformation.key(classRecord);
|
||||
GeneratedLocation methodLoc = DebugInformation.key(methodRecord);
|
||||
int cmp = classLoc.compareTo(methodLoc);
|
||||
if (cmp < 0) {
|
||||
nextClassRecord();
|
||||
} else if (cmp > 0) {
|
||||
nextMethodRecord();
|
||||
} else {
|
||||
nextClassRecord();
|
||||
nextMethodRecord();
|
||||
}
|
||||
} else {
|
||||
endReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void nextClassRecord() {
|
||||
RecordArray.Record record = debugInformation.classMapping.get(classIndex++);
|
||||
classId = record.get(2);
|
||||
location = DebugInformation.key(record);
|
||||
}
|
||||
|
||||
private void nextMethodRecord() {
|
||||
RecordArray.Record record = debugInformation.methodMapping.get(methodIndex++);
|
||||
methodId = record.get(2);
|
||||
location = DebugInformation.key(record);
|
||||
}
|
||||
|
||||
public void next() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
read();
|
||||
}
|
||||
|
||||
public int getClassNameId() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return classId;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
int classId = getClassNameId();
|
||||
return classId >= 0 ? debugInformation.getClassName(classId) : null;
|
||||
}
|
||||
|
||||
public int getMethodId() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return methodId;
|
||||
}
|
||||
|
||||
public MethodDescriptor getMethod() {
|
||||
int methodId = getMethodId();
|
||||
return methodId >= 0 ? debugInformation.getMethod(methodId) : null;
|
||||
}
|
||||
|
||||
public int getExactMethodId() {
|
||||
return debugInformation.getExactMethodId(classId, methodId);
|
||||
}
|
||||
|
||||
public MethodReference getExactMethod() {
|
||||
return new MethodReference(getClassName(), getMethod());
|
||||
}
|
||||
|
||||
public GeneratedLocation getLocation() {
|
||||
return location;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 org.teavm.model.MethodDescriptor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class MethodIterator {
|
||||
private DebugInformation debugInformation;
|
||||
private int index;
|
||||
|
||||
MethodIterator(DebugInformation debugInformation) {
|
||||
this.debugInformation = debugInformation;
|
||||
}
|
||||
|
||||
public boolean isEndReached() {
|
||||
return index < debugInformation.methodMapping.size();
|
||||
}
|
||||
|
||||
public GeneratedLocation getLocation() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return DebugInformation.key(debugInformation.methodMapping.get(index));
|
||||
}
|
||||
|
||||
public int getMethodId() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
return debugInformation.methodMapping.get(index).get(2);
|
||||
}
|
||||
|
||||
public MethodDescriptor getMethod() {
|
||||
int methodId = getMethodId();
|
||||
return methodId >= 0 ? debugInformation.getMethod(methodId) : null;
|
||||
}
|
||||
|
||||
public void next() {
|
||||
if (isEndReached()) {
|
||||
throw new IllegalStateException("End already reached");
|
||||
}
|
||||
++index;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* 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 org.teavm.common.RecordArray;
|
||||
|
|
|
@ -27,9 +27,7 @@ class SourceMapsWriter {
|
|||
private Writer output;
|
||||
private int lastLine;
|
||||
private int lastColumn;
|
||||
private int sourceLine;
|
||||
private int lastSourceLine;
|
||||
private int sourceFile;
|
||||
private int lastSourceFile;
|
||||
private boolean first;
|
||||
|
||||
|
@ -58,31 +56,10 @@ class SourceMapsWriter {
|
|||
first = true;
|
||||
lastLine = 0;
|
||||
lastColumn = 0;
|
||||
sourceLine = -1;
|
||||
sourceFile = -1;
|
||||
lastSourceFile = 0;
|
||||
lastSourceLine = 0;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < debugInfo.lineMapping.lines.length && j < debugInfo.fileMapping.lines.length) {
|
||||
GeneratedLocation a = debugInfo.lineMapping.key(i);
|
||||
GeneratedLocation b = debugInfo.fileMapping.key(j);
|
||||
int cmp = a.compareTo(b);
|
||||
if (cmp < 0) {
|
||||
writeSegment(a, sourceFile, debugInfo.lineMapping.values[i++]);
|
||||
} else if (cmp > 0) {
|
||||
writeSegment(b, debugInfo.fileMapping.values[j++], sourceLine);
|
||||
} else {
|
||||
writeSegment(a, debugInfo.fileMapping.values[j++], debugInfo.lineMapping.values[i++] - 1);
|
||||
}
|
||||
}
|
||||
while (i < debugInfo.lineMapping.lines.length) {
|
||||
GeneratedLocation a = debugInfo.lineMapping.key(i);
|
||||
writeSegment(a, sourceFile, debugInfo.lineMapping.values[i++]);
|
||||
}
|
||||
while (j < debugInfo.fileMapping.lines.length) {
|
||||
GeneratedLocation b = debugInfo.fileMapping.key(j);
|
||||
writeSegment(b, debugInfo.fileMapping.values[j++], sourceLine);
|
||||
for (SourceLocationIterator iter = debugInfo.iterateOverSourceLocations(); !iter.isEndReached(); iter.next()) {
|
||||
writeSegment(iter.getLocation(), iter.getFileNameId(), iter.getLine());
|
||||
}
|
||||
output.write("\"}");
|
||||
}
|
||||
|
@ -106,8 +83,6 @@ class SourceMapsWriter {
|
|||
lastSourceLine = sourceLine;
|
||||
}
|
||||
lastColumn = loc.getColumn();
|
||||
this.sourceFile = sourceFile;
|
||||
this.sourceLine = sourceLine;
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -608,7 +608,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
writer.ws().append("=").ws();
|
||||
}
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
writer.append(";").softNewLine();
|
||||
if (statement.getLocation() != null) {
|
||||
popLocation();
|
||||
|
@ -640,7 +640,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
prevCallSite = debugEmitter.emitCallSite();
|
||||
writer.append("if").ws().append("(");
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
writer.append(")").ws().append("{").softNewLine().indent();
|
||||
if (statement.getCondition().getLocation() != null) {
|
||||
popLocation();
|
||||
|
@ -678,7 +678,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
prevCallSite = debugEmitter.emitCallSite();
|
||||
writer.append("switch").ws().append("(");
|
||||
statement.getValue().acceptVisitor(this);
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
writer.append(")").ws().append("{").softNewLine().indent();
|
||||
for (SwitchClause clause : statement.getClauses()) {
|
||||
for (int condition : clause.getConditions()) {
|
||||
|
@ -713,7 +713,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
if (statement.getCondition() != null) {
|
||||
prevCallSite = debugEmitter.emitCallSite();
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
} else {
|
||||
writer.append("true");
|
||||
}
|
||||
|
@ -794,7 +794,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
writer.append(' ');
|
||||
prevCallSite = debugEmitter.emitCallSite();
|
||||
statement.getResult().acceptVisitor(this);
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
}
|
||||
writer.append(";").softNewLine();
|
||||
if (statement.getLocation() != null) {
|
||||
|
@ -815,7 +815,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
prevCallSite = debugEmitter.emitCallSite();
|
||||
statement.getException().acceptVisitor(this);
|
||||
writer.append(");").softNewLine();
|
||||
debugEmitter.emitEmptyCallSite();
|
||||
debugEmitter.emitCallSite();
|
||||
if (statement.getLocation() != null) {
|
||||
popLocation();
|
||||
}
|
||||
|
@ -1317,6 +1317,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
if (lastCallSite == null) {
|
||||
lastCallSite = callSite;
|
||||
}
|
||||
boolean virtual = false;
|
||||
switch (expr.getType()) {
|
||||
case STATIC:
|
||||
writer.append(fullName).append("(");
|
||||
|
@ -1350,6 +1351,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
}
|
||||
writer.append(')');
|
||||
virtual = true;
|
||||
break;
|
||||
case CONSTRUCTOR:
|
||||
writer.append(className).append(".").append(name).append("(");
|
||||
|
@ -1364,7 +1366,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
break;
|
||||
}
|
||||
if (lastCallSite != null) {
|
||||
lastCallSite.setMethod(expr.getMethod());
|
||||
if (virtual) {
|
||||
lastCallSite.setVirtualMethod(expr.getMethod());
|
||||
} else {
|
||||
lastCallSite.setStaticMethod(expr.getMethod());
|
||||
}
|
||||
lastCallSite = callSite;
|
||||
}
|
||||
if (shouldEraseCallSite) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user