From 45c336ebb89801f5b2df00202679c199daec890e Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Thu, 28 Aug 2014 17:54:02 +0400 Subject: [PATCH] Refactoring of debug information --- .../java/org/teavm/common/RecordArray.java | 12 + .../org/teavm/common/RecordArrayBuilder.java | 7 + .../teavm/debugging/ClassNameIterator.java | 50 +++ .../org/teavm/debugging/DebugInformation.java | 365 ++++++------------ .../debugging/DebugInformationBuilder.java | 233 +++++------ .../debugging/DebugInformationEmitter.java | 2 - .../debugging/DebugInformationWriter.java | 59 +-- .../java/org/teavm/debugging/Debugger.java | 40 +- .../org/teavm/debugging/DebuggerCallSite.java | 15 + .../debugging/DebuggerCallSiteVisitor.java | 15 + .../debugging/DebuggerStaticCallSite.java | 15 + .../debugging/DebuggerVirtualCallSite.java | 37 +- .../org/teavm/debugging/DeferredCallSite.java | 6 +- .../DummyDebugInformationEmitter.java | 9 +- .../teavm/debugging/ExactMethodIterator.java | 123 ++++++ .../org/teavm/debugging/FileNameIterator.java | 15 + .../teavm/debugging/LineNumberIterator.java | 15 + .../org/teavm/debugging/MethodIterator.java | 61 +++ .../debugging/SourceLocationIterator.java | 15 + .../org/teavm/debugging/SourceMapsWriter.java | 29 +- .../java/org/teavm/javascript/Renderer.java | 20 +- 21 files changed, 669 insertions(+), 474 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/debugging/ExactMethodIterator.java create mode 100644 teavm-core/src/main/java/org/teavm/debugging/MethodIterator.java diff --git a/teavm-core/src/main/java/org/teavm/common/RecordArray.java b/teavm-core/src/main/java/org/teavm/common/RecordArray.java index 5298b3907..b3fa0365d 100644 --- a/teavm-core/src/main/java/org/teavm/common/RecordArray.java +++ b/teavm-core/src/main/java/org/teavm/common/RecordArray.java @@ -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; diff --git a/teavm-core/src/main/java/org/teavm/common/RecordArrayBuilder.java b/teavm-core/src/main/java/org/teavm/common/RecordArrayBuilder.java index 62481da7d..6173375f6 100644 --- a/teavm-core/src/main/java/org/teavm/common/RecordArrayBuilder.java +++ b/teavm-core/src/main/java/org/teavm/common/RecordArrayBuilder.java @@ -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; } diff --git a/teavm-core/src/main/java/org/teavm/debugging/ClassNameIterator.java b/teavm-core/src/main/java/org/teavm/debugging/ClassNameIterator.java index e1c7ef818..5bf47e8ac 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/ClassNameIterator.java +++ b/teavm-core/src/main/java/org/teavm/debugging/ClassNameIterator.java @@ -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 */ 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; + } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java index 8a2a664e1..d4335f0bd 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -46,7 +46,7 @@ public class DebugInformation { RecordArray methodMapping; RecordArray lineMapping; RecordArray callSiteMapping; - MultiMapping[] variableMappings; + RecordArray[] variableMappings; RecordArray[] lineCallSites; CFG[] controlFlowGraphs; List 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 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 { private RecordArray recordArray; diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java index 01ad4c6be..00dde319e 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -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 exactMethods = new ArrayList<>(); private Map 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 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 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 list = new ArrayList<>(); private Map map = new HashMap<>(); diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java index c83358782..cff999d6a 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -35,8 +35,6 @@ public interface DebugInformationEmitter { DeferredCallSite emitCallSite(); - void emitEmptyCallSite(); - void addClass(String className, String parentName); void addField(String fieldName, String jsName); diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java index 290146af2..f3257b117 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -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]); } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java index e3fd78053..14e1819b3 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -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(), 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 locations; + + public CallSiteSuccessorFinder(DebugInformation debugInfo, String script, Set 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 visited, Set successors) { if (!visited.add(location)) { diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSite.java b/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSite.java index 6c574acbc..3a8b33b2e 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSite.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSite.java @@ -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; /** diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSiteVisitor.java b/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSiteVisitor.java index c393a6abb..85218179f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSiteVisitor.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebuggerCallSiteVisitor.java @@ -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; /** diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebuggerStaticCallSite.java b/teavm-core/src/main/java/org/teavm/debugging/DebuggerStaticCallSite.java index 8a0bd5ace..469f1f68f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebuggerStaticCallSite.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebuggerStaticCallSite.java @@ -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; diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebuggerVirtualCallSite.java b/teavm-core/src/main/java/org/teavm/debugging/DebuggerVirtualCallSite.java index 86e18551e..294b3baff 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebuggerVirtualCallSite.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebuggerVirtualCallSite.java @@ -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); diff --git a/teavm-core/src/main/java/org/teavm/debugging/DeferredCallSite.java b/teavm-core/src/main/java/org/teavm/debugging/DeferredCallSite.java index c6b14dae4..d82ccc849 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DeferredCallSite.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DeferredCallSite.java @@ -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(); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java index 58b3d601b..4684491b9 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -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) { } diff --git a/teavm-core/src/main/java/org/teavm/debugging/ExactMethodIterator.java b/teavm-core/src/main/java/org/teavm/debugging/ExactMethodIterator.java new file mode 100644 index 000000000..bdf6cbb6b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/ExactMethodIterator.java @@ -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; + } +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/FileNameIterator.java b/teavm-core/src/main/java/org/teavm/debugging/FileNameIterator.java index 0ae22eb20..12b542918 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/FileNameIterator.java +++ b/teavm-core/src/main/java/org/teavm/debugging/FileNameIterator.java @@ -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; /** diff --git a/teavm-core/src/main/java/org/teavm/debugging/LineNumberIterator.java b/teavm-core/src/main/java/org/teavm/debugging/LineNumberIterator.java index cf67bba51..677e33a76 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/LineNumberIterator.java +++ b/teavm-core/src/main/java/org/teavm/debugging/LineNumberIterator.java @@ -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; /** diff --git a/teavm-core/src/main/java/org/teavm/debugging/MethodIterator.java b/teavm-core/src/main/java/org/teavm/debugging/MethodIterator.java new file mode 100644 index 000000000..3b67e604a --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/MethodIterator.java @@ -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; + } +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/SourceLocationIterator.java b/teavm-core/src/main/java/org/teavm/debugging/SourceLocationIterator.java index 24fc604ca..eb13a3350 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/SourceLocationIterator.java +++ b/teavm-core/src/main/java/org/teavm/debugging/SourceLocationIterator.java @@ -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; diff --git a/teavm-core/src/main/java/org/teavm/debugging/SourceMapsWriter.java b/teavm-core/src/main/java/org/teavm/debugging/SourceMapsWriter.java index 0ee05ba35..c1fd88f96 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/SourceMapsWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/SourceMapsWriter.java @@ -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; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 3a192a373..34cee3244 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -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) {