JS: preserve stack trace items when agressive inlining enabled

This commit is contained in:
Alexey Andreev 2019-10-10 18:41:15 +03:00
parent abf90e8b5f
commit 4ef231c7fa
24 changed files with 739 additions and 466 deletions

View File

@ -497,6 +497,7 @@ class StatementGenerator implements InstructionVisitor {
} else { } else {
stmt = Statement.assign(null, invocationExpr); stmt = Statement.assign(null, invocationExpr);
} }
invocationExpr.setLocation(currentLocation);
stmt.setLocation(currentLocation); stmt.setLocation(currentLocation);
stmt.setAsync(async); stmt.setAsync(async);
async = false; async = false;

View File

@ -25,6 +25,7 @@ import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.NamingStrategy;
@ -38,6 +39,7 @@ import org.teavm.interop.PlatformMarker;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.InliningInfo;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -62,6 +64,7 @@ public class RenderingContext {
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>(); private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
private boolean minifying; private boolean minifying;
private ClassInitializerInfo classInitializerInfo; private ClassInitializerInfo classInitializerInfo;
private TextLocation lastEmittedLocation = TextLocation.EMPTY;
public RenderingContext(DebugInformationEmitter debugEmitter, public RenderingContext(DebugInformationEmitter debugEmitter,
ClassReaderSource initialClassSource, ListableClassReaderSource classSource, ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
@ -128,11 +131,11 @@ public class RenderingContext {
LocationStackEntry prevEntry = locationStack.peek(); LocationStackEntry prevEntry = locationStack.peek();
if (location != null) { if (location != null) {
if (prevEntry == null || !location.equals(prevEntry.location)) { if (prevEntry == null || !location.equals(prevEntry.location)) {
debugEmitter.emitLocation(location.getFileName(), location.getLine()); emitLocation(location);
} }
} else { } else {
if (prevEntry != null) { if (prevEntry != null) {
debugEmitter.emitLocation(null, -1); emitLocation(TextLocation.EMPTY);
} }
} }
locationStack.push(new LocationStackEntry(location)); locationStack.push(new LocationStackEntry(location));
@ -143,13 +146,64 @@ public class RenderingContext {
LocationStackEntry entry = locationStack.peek(); LocationStackEntry entry = locationStack.peek();
if (entry != null) { if (entry != null) {
if (!entry.location.equals(prevEntry.location)) { if (!entry.location.equals(prevEntry.location)) {
debugEmitter.emitLocation(entry.location.getFileName(), entry.location.getLine()); emitLocation(entry.location);
} }
} else { } else {
debugEmitter.emitLocation(null, -1); emitLocation(TextLocation.EMPTY);
} }
} }
private void emitLocation(TextLocation location) {
if (lastEmittedLocation.equals(location)) {
return;
}
String fileName = lastEmittedLocation.getFileName();
int lineNumber = lastEmittedLocation.getLine();
if (lastEmittedLocation.getInlining() != location.getInlining()) {
InliningInfo[] newPath = location.getInliningPath();
InliningInfo[] prevPath = lastEmittedLocation.getInliningPath();
InliningInfo lastCommonInlining = null;
int pathIndex = 0;
while (pathIndex < prevPath.length && pathIndex < newPath.length
&& prevPath[pathIndex].equals(newPath[pathIndex])) {
lastCommonInlining = prevPath[pathIndex++];
}
InliningInfo prevInlining = lastEmittedLocation.getInlining();
while (prevInlining != lastCommonInlining) {
debugEmitter.exitLocation();
fileName = prevInlining.getFileName();
lineNumber = prevInlining.getLine();
prevInlining = prevInlining.getParent();
}
while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++];
emitSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
fileName = null;
lineNumber = -1;
debugEmitter.enterLocation();
debugEmitter.emitClass(inlining.getMethod().getClassName());
debugEmitter.emitMethod(inlining.getMethod().getDescriptor());
}
}
emitSimpleLocation(fileName, lineNumber, location.getFileName(), location.getLine());
lastEmittedLocation = location;
}
private void emitSimpleLocation(String fileName, int lineNumber, String newFileName, int newLineNumber) {
if (Objects.equals(fileName, newFileName) && lineNumber == newLineNumber) {
return;
}
debugEmitter.emitLocation(newFileName, newLineNumber);
}
public boolean isMinifying() { public boolean isMinifying() {
return minifying; return minifying;
} }

View File

@ -250,6 +250,10 @@ public class AstIO {
return; return;
} }
String fileName = lastLocation.getFileName();
int lineNumber = lastLocation.getLine();
if (location.getInlining() != lastLocation.getInlining()) {
InliningInfo lastCommonInlining = null; InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = lastLocation.getInliningPath(); InliningInfo[] prevPath = lastLocation.getInliningPath();
InliningInfo[] newPath = location.getInliningPath(); InliningInfo[] newPath = location.getInliningPath();
@ -259,38 +263,50 @@ public class AstIO {
lastCommonInlining = prevPath[pathIndex++]; lastCommonInlining = prevPath[pathIndex++];
} }
InliningInfo prevInlining = lastLocation.getInlining(); InliningInfo prevInlining = location.getInlining();
while (prevInlining != lastCommonInlining) { while (prevInlining != lastCommonInlining) {
output.writeUnsigned(124); output.writeUnsigned(123);
fileName = prevInlining.getFileName();
lineNumber = prevInlining.getLine();
prevInlining = prevInlining.getParent(); prevInlining = prevInlining.getParent();
} }
while (pathIndex < newPath.length) { while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++]; InliningInfo inlining = newPath[pathIndex++];
writeSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
fileName = null;
lineNumber = -1;
output.writeUnsigned(124);
MethodReference method = inlining.getMethod(); MethodReference method = inlining.getMethod();
output.writeUnsigned(inlining.isEmpty() ? 122 : 123);
output.writeUnsigned(symbolTable.lookup(method.getClassName())); output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString())); output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
if (!inlining.isEmpty()) {
output.writeUnsigned(fileTable.lookup(inlining.getFileName()));
output.writeUnsigned(inlining.getLine());
} }
} }
if (location.isEmpty()) { writeSimpleLocation(fileName, lineNumber, location.getFileName(), location.getLine());
output.writeUnsigned(127);
} else if (!lastLocation.isEmpty() && lastLocation.getFileName().equals(location.getFileName())) {
output.writeUnsigned(126);
output.writeSigned(location.getLine() - lastLocation.getLine());
} else {
output.writeUnsigned(125);
output.writeUnsigned(fileTable.lookup(location.getFileName()));
output.writeUnsigned(location.getLine());
}
lastLocation = location; lastLocation = location;
} }
private void writeSimpleLocation(String fileName, int lineNumber, String newFileName, int newLineNumber)
throws IOException {
if (Objects.equals(fileName, newFileName) && lineNumber == newLineNumber) {
return;
}
if (newFileName == null) {
output.writeUnsigned(127);
} else if (fileName != null && fileName.equals(newFileName)) {
output.writeUnsigned(126);
output.writeSigned(newLineNumber - lineNumber);
} else {
output.writeUnsigned(125);
output.writeUnsigned(fileTable.lookup(newFileName));
output.writeUnsigned(newLineNumber);
}
}
private void writeSequence(List<Statement> sequence) throws IOException { private void writeSequence(List<Statement> sequence) throws IOException {
output.writeUnsigned(sequence.size()); output.writeUnsigned(sequence.size());
for (Statement part : sequence) { for (Statement part : sequence) {
@ -741,25 +757,17 @@ public class AstIO {
lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned(), lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned(),
lastReadInlining); lastReadInlining);
break; break;
case 122: case 124: {
case 123: {
String className = symbolTable.at(input.readUnsigned()); String className = symbolTable.at(input.readUnsigned());
MethodDescriptor methodDescriptor = MethodDescriptor.parse(symbolTable.at(input.readUnsigned())); MethodDescriptor methodDescriptor = MethodDescriptor.parse(symbolTable.at(input.readUnsigned()));
methodDescriptor = referenceCache.getCached(methodDescriptor); methodDescriptor = referenceCache.getCached(methodDescriptor);
String fileName;
int lineNumber;
if (type == 122) {
fileName = fileTable.at(input.readUnsigned());
lineNumber = input.readUnsigned();
} else {
fileName = null;
lineNumber = -1;
}
lastReadInlining = new InliningInfo(referenceCache.getCached(className, methodDescriptor), lastReadInlining = new InliningInfo(referenceCache.getCached(className, methodDescriptor),
fileName, lineNumber, lastReadInlining); lastReadLocation.getFileName(), lastReadLocation.getLine(), lastReadInlining);
lastReadLocation = new TextLocation(null, -1, lastReadInlining);
break; break;
} }
case 124: case 123:
lastReadLocation = new TextLocation(lastReadInlining.getFileName(), lastReadInlining.getLine());
lastReadInlining = lastReadInlining.getParent(); lastReadInlining = lastReadInlining.getParent();
break; break;
default: default:

View File

@ -19,6 +19,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import java.util.Objects;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.BasicBlockReader; import org.teavm.model.BasicBlockReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
@ -208,7 +209,7 @@ public class ProgramIO {
} }
InliningInfo inliningInfo = null; InliningInfo inliningInfo = null;
TextLocation location = null; TextLocation location = TextLocation.EMPTY;
insnLoop: while (true) { insnLoop: while (true) {
int insnType = data.readUnsigned(); int insnType = data.readUnsigned();
switch (insnType) { switch (insnType) {
@ -228,24 +229,16 @@ public class ProgramIO {
location = new TextLocation(location.getFileName(), line, inliningInfo); location = new TextLocation(location.getFileName(), line, inliningInfo);
break; break;
} }
case 124:
case 125: { case 125: {
String className = symbolTable.at(data.readUnsigned()); String className = symbolTable.at(data.readUnsigned());
MethodDescriptor methodDescriptor = parseMethodDescriptor(symbolTable.at(data.readUnsigned())); MethodDescriptor methodDescriptor = parseMethodDescriptor(symbolTable.at(data.readUnsigned()));
String fileName;
int lineNumber;
if (insnType == 125) {
fileName = fileTable.at(data.readUnsigned());
lineNumber = data.readUnsigned();
} else {
fileName = null;
lineNumber = -1;
}
inliningInfo = new InliningInfo(createMethodReference(className, methodDescriptor), inliningInfo = new InliningInfo(createMethodReference(className, methodDescriptor),
fileName, lineNumber, inliningInfo); location.getFileName(), location.getLine(), inliningInfo);
location = new TextLocation(null, -1, inliningInfo);
break; break;
} }
case 126: case 126:
location = new TextLocation(inliningInfo.getFileName(), inliningInfo.getLine());
inliningInfo = inliningInfo.getParent(); inliningInfo = inliningInfo.getParent();
break; break;
default: { default: {
@ -276,6 +269,10 @@ public class ProgramIO {
newLocation = TextLocation.EMPTY; newLocation = TextLocation.EMPTY;
} }
String fileName = location.getFileName();
int lineNumber = location.getLine();
if (newLocation.getInlining() != location.getInlining()) {
InliningInfo lastCommonInlining = null; InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = location.getInliningPath(); InliningInfo[] prevPath = location.getInliningPath();
InliningInfo[] newPath = newLocation.getInliningPath(); InliningInfo[] newPath = newLocation.getInliningPath();
@ -288,39 +285,49 @@ public class ProgramIO {
InliningInfo prevInlining = location.getInlining(); InliningInfo prevInlining = location.getInlining();
while (prevInlining != lastCommonInlining) { while (prevInlining != lastCommonInlining) {
output.writeUnsigned(126); output.writeUnsigned(126);
fileName = prevInlining.getFileName();
lineNumber = prevInlining.getLine();
prevInlining = prevInlining.getParent(); prevInlining = prevInlining.getParent();
} }
while (pathIndex < newPath.length) { while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++]; InliningInfo inlining = newPath[pathIndex++];
writeSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
fileName = null;
lineNumber = -1;
output.writeUnsigned(125);
MethodReference method = inlining.getMethod(); MethodReference method = inlining.getMethod();
output.writeUnsigned(inlining.isEmpty() ? 124 : 125);
output.writeUnsigned(symbolTable.lookup(method.getClassName())); output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString())); output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
if (!inlining.isEmpty()) {
output.writeUnsigned(fileTable.lookup(inlining.getFileName()));
output.writeUnsigned(inlining.getLine());
} }
} }
if (newLocation.isEmpty()) { writeSimpleLocation(fileName, lineNumber, newLocation.getFileName(), newLocation.getLine());
output.writeUnsigned(1);
} else {
if (!location.isEmpty() && location.getFileName().equals(newLocation.getFileName())) {
output.writeUnsigned(127);
output.writeSigned(newLocation.getLine() - location.getLine());
} else {
output.writeUnsigned(2);
output.writeUnsigned(fileTable.lookup(newLocation.getFileName()));
output.writeUnsigned(newLocation.getLine());
}
}
location = newLocation; location = newLocation;
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
private void writeSimpleLocation(String fileName, int lineNumber, String newFileName, int newLineNumber)
throws IOException {
if (Objects.equals(fileName, newFileName) && lineNumber == newLineNumber) {
return;
}
if (newFileName == null) {
output.writeUnsigned(1);
} else if (fileName != null && fileName.equals(newFileName)) {
output.writeUnsigned(127);
output.writeSigned(newLineNumber - lineNumber);
} else {
output.writeUnsigned(2);
output.writeUnsigned(fileTable.lookup(newFileName));
output.writeUnsigned(newLineNumber);
}
}
@Override @Override
public void nop() { public void nop() {
try { try {

View File

@ -1,55 +0,0 @@
/*
* 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.information;
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;
}
}

View File

@ -38,10 +38,7 @@ public class DebugInformation {
long[] exactMethods; long[] exactMethods;
Map<Long, Integer> exactMethodMap; Map<Long, Integer> exactMethodMap;
RecordArray[] fileDescriptions; RecordArray[] fileDescriptions;
RecordArray fileMapping; Layer[] layers;
RecordArray classMapping;
RecordArray methodMapping;
RecordArray lineMapping;
RecordArray callSiteMapping; RecordArray callSiteMapping;
RecordArray statementStartMapping; RecordArray statementStartMapping;
RecordArray[] variableMappings; RecordArray[] variableMappings;
@ -69,14 +66,6 @@ public class DebugInformation {
return variableNames.clone(); return variableNames.clone();
} }
public LineNumberIterator iterateOverLineNumbers() {
return new LineNumberIterator(this);
}
public FileNameIterator iterateOverFileNames() {
return new FileNameIterator(this);
}
public String getFileName(int fileNameId) { public String getFileName(int fileNameId) {
return fileNames[fileNameId]; return fileNames[fileNameId];
} }
@ -135,16 +124,12 @@ public class DebugInformation {
return id != null ? id : -1; return id != null ? id : -1;
} }
public ClassNameIterator iterateOverClassNames() { public int layerCount() {
return new ClassNameIterator(this); return layers.length;
} }
public MethodIterator iterateOverMethods() { public ExactMethodIterator iterateOverExactMethods(int layerIndex) {
return new MethodIterator(this); return new ExactMethodIterator(this, layers[layerIndex]);
}
public ExactMethodIterator iterateOverExactMethods() {
return new ExactMethodIterator(this);
} }
public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) { public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) {
@ -177,29 +162,67 @@ public class DebugInformation {
return new SourceLocationIterator(this); return new SourceLocationIterator(this);
} }
private LayerSourceLocationIterator iterateOverSourceLocations(int layer) {
return new LayerSourceLocationIterator(this, layers[layer]);
}
public SourceLocation getSourceLocation(int line, int column) { public SourceLocation getSourceLocation(int line, int column) {
return getSourceLocation(new GeneratedLocation(line, column)); return getSourceLocation(new GeneratedLocation(line, column));
} }
public SourceLocation getSourceLocation(int line, int column, int layerIndex) {
return getSourceLocation(new GeneratedLocation(line, column), layerIndex);
}
public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) { public SourceLocation getSourceLocation(GeneratedLocation generatedLocation) {
String fileName = componentByKey(fileMapping, fileNames, generatedLocation); return getSourceLocation(generatedLocation, autodetectLayer(generatedLocation));
int lineNumberIndex = indexByKey(lineMapping, generatedLocation); }
int lineNumber = lineNumberIndex >= 0 ? lineMapping.get(lineNumberIndex).get(2) : -1;
public SourceLocation getSourceLocation(GeneratedLocation generatedLocation, int layerIndex) {
if (layerIndex < 0 || layerIndex >= layers.length) {
return null;
}
Layer layer = layers[layerIndex];
String fileName = componentByKey(layer.fileMapping, fileNames, generatedLocation);
int lineNumberIndex = indexByKey(layer.lineMapping, generatedLocation);
int lineNumber = lineNumberIndex >= 0 ? layer.lineMapping.get(lineNumberIndex).get(2) : -1;
return new SourceLocation(fileName, lineNumber); return new SourceLocation(fileName, lineNumber);
} }
public MethodReference getMethodAt(GeneratedLocation generatedLocation) { public MethodReference getMethodAt(GeneratedLocation generatedLocation) {
String className = componentByKey(classMapping, classNames, generatedLocation); return getMethodAt(generatedLocation, autodetectLayer(generatedLocation));
}
public MethodReference getMethodAt(GeneratedLocation generatedLocation, int layerIndex) {
if (layerIndex < 0 || layerIndex >= layers.length) {
return null;
}
Layer layer = layers[layerIndex];
String className = componentByKey(layer.classMapping, classNames, generatedLocation);
if (className == null) { if (className == null) {
return null; return null;
} }
String method = componentByKey(methodMapping, methods, generatedLocation); String method = componentByKey(layer.methodMapping, methods, generatedLocation);
if (method == null) { if (method == null) {
return null; return null;
} }
return referenceCache.getCached(className, referenceCache.parseDescriptorCached(method)); return referenceCache.getCached(className, referenceCache.parseDescriptorCached(method));
} }
private int autodetectLayer(GeneratedLocation generatedLocation) {
int layerIndex = 0;
for (int i = 1; i < layers.length; ++i) {
if (componentByKey(layers[i].classMapping, classNames, generatedLocation) == null) {
break;
}
layerIndex = i;
}
return layerIndex;
}
public MethodReference getMethodAt(int line, int column) { public MethodReference getMethodAt(int line, int column) {
return getMethodAt(new GeneratedLocation(line, column)); return getMethodAt(new GeneratedLocation(line, column));
} }
@ -460,7 +483,9 @@ public class DebugInformation {
for (int i = 0; i < builders.length; ++i) { for (int i = 0; i < builders.length; ++i) {
builders[i] = new RecordArrayBuilder(0, 1); builders[i] = new RecordArrayBuilder(0, 1);
} }
for (SourceLocationIterator iter = iterateOverSourceLocations(); !iter.isEndReached(); iter.next()) { for (int layer = 0; layer < layers.length; ++layer) {
for (LayerSourceLocationIterator iter = iterateOverSourceLocations(layer);
!iter.isEndReached(); iter.next()) {
if (iter.getFileNameId() >= 0 && iter.getLine() >= 0) { if (iter.getFileNameId() >= 0 && iter.getLine() >= 0) {
RecordArrayBuilder builder = builders[iter.getFileNameId()]; RecordArrayBuilder builder = builders[iter.getFileNameId()];
while (builder.size() <= iter.getLine()) { while (builder.size() <= iter.getLine()) {
@ -472,6 +497,7 @@ public class DebugInformation {
array.add(loc.getColumn()); array.add(loc.getColumn());
} }
} }
}
fileDescriptions = new RecordArray[builders.length]; fileDescriptions = new RecordArray[builders.length];
for (int i = 0; i < fileDescriptions.length; ++i) { for (int i = 0; i < fileDescriptions.length; ++i) {
fileDescriptions[i] = builders[i].build(); fileDescriptions[i] = builders[i].build();
@ -486,7 +512,8 @@ public class DebugInformation {
GeneratedLocation prevLocation = new GeneratedLocation(0, 0); GeneratedLocation prevLocation = new GeneratedLocation(0, 0);
MethodReference prevMethod = null; MethodReference prevMethod = null;
int prevMethodId = -1; int prevMethodId = -1;
for (ExactMethodIterator iter = iterateOverExactMethods(); !iter.isEndReached(); iter.next()) { RecordArray lineMapping = layers[0].lineMapping;
for (ExactMethodIterator iter = iterateOverExactMethods(0); !iter.isEndReached(); iter.next()) {
int id = iter.getExactMethodId(); int id = iter.getExactMethodId();
if (prevMethod != null) { if (prevMethod != null) {
int lineIndex = Math.max(0, indexByKey(lineMapping, prevLocation)); int lineIndex = Math.max(0, indexByKey(lineMapping, prevLocation));
@ -596,8 +623,9 @@ public class DebugInformation {
GeneratedLocation loc = key(callSiteRec); GeneratedLocation loc = key(callSiteRec);
int callSiteType = callSiteRec.get(2); int callSiteType = callSiteRec.get(2);
if (callSiteType != DebuggerCallSite.NONE) { if (callSiteType != DebuggerCallSite.NONE) {
int line = valueByKey(lineMapping, loc); int layerIndex = autodetectLayer(loc);
int fileId = valueByKey(fileMapping, loc); int line = valueByKey(layers[layerIndex].lineMapping, loc);
int fileId = valueByKey(layers[layerIndex].fileMapping, loc);
if (fileId >= 0 && line >= 0) { if (fileId >= 0 && line >= 0) {
RecordArrayBuilder builder = builders[fileId]; RecordArrayBuilder builder = builders[fileId];
while (builder.size() <= line) { while (builder.size() <= line) {
@ -699,4 +727,11 @@ public class DebugInformation {
return references; return references;
} }
} }
static class Layer {
RecordArray fileMapping;
RecordArray classMapping;
RecordArray methodMapping;
RecordArray lineMapping;
}
} }

View File

@ -34,23 +34,20 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
private List<Long> exactMethods = new ArrayList<>(); private List<Long> exactMethods = new ArrayList<>();
private Map<Long, Integer> exactMethodMap = new HashMap<>(); private Map<Long, Integer> exactMethodMap = new HashMap<>();
private RecordArrayBuilder statementStartMapping = new RecordArrayBuilder(2, 0); private RecordArrayBuilder statementStartMapping = new RecordArrayBuilder(2, 0);
private RecordArrayBuilder fileMapping = new RecordArrayBuilder(3, 0); private List<LayerBuilder> layers = new ArrayList<>();
private RecordArrayBuilder lineMapping = new RecordArrayBuilder(3, 0); private LayerBuilder currentLayer;
private RecordArrayBuilder classMapping = new RecordArrayBuilder(3, 0); private int currentLayerIndex;
private RecordArrayBuilder methodMapping = new RecordArrayBuilder(3, 0);
private RecordArrayBuilder callSiteMapping = new RecordArrayBuilder(4, 0); private RecordArrayBuilder callSiteMapping = new RecordArrayBuilder(4, 0);
private Map<Integer, RecordArrayBuilder> variableMappings = new HashMap<>(); private Map<Integer, RecordArrayBuilder> variableMappings = new HashMap<>();
private MethodDescriptor currentMethod;
private String currentClass;
private String currentFileName;
private int currentClassMetadata = -1; private int currentClassMetadata = -1;
private List<ClassMetadata> classesMetadata = new ArrayList<>(); private List<ClassMetadata> classesMetadata = new ArrayList<>();
private List<RecordArrayBuilder> cfgs = new ArrayList<>(); private List<RecordArrayBuilder> cfgs = new ArrayList<>();
private int currentLine;
private ReferenceCache referenceCache; private ReferenceCache referenceCache;
public DebugInformationBuilder(ReferenceCache referenceCache) { public DebugInformationBuilder(ReferenceCache referenceCache) {
this.referenceCache = referenceCache; this.referenceCache = referenceCache;
currentLayer = new LayerBuilder();
layers.add(currentLayer);
} }
public LocationProvider getLocationProvider() { public LocationProvider getLocationProvider() {
@ -66,16 +63,32 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
public void emitLocation(String fileName, int line) { public void emitLocation(String fileName, int line) {
debugInformation = null; debugInformation = null;
int fileIndex = files.index(fileName); int fileIndex = files.index(fileName);
if (!Objects.equals(currentFileName, fileName)) { if (!Objects.equals(currentLayer.currentFileName, fileName)) {
add(fileMapping, fileIndex); add(currentLayer.fileMapping, fileIndex);
currentFileName = fileName; currentLayer.currentFileName = fileName;
} }
if (currentLine != line) { if (currentLayer.currentLine != line) {
add(lineMapping, line); add(currentLayer.lineMapping, line);
currentLine = line; currentLayer.currentLine = line;
} }
} }
@Override
public void enterLocation() {
if (++currentLayerIndex >= layers.size()) {
layers.add(new LayerBuilder());
}
currentLayer = layers.get(currentLayerIndex);
}
@Override
public void exitLocation() {
emitClass(null);
emitMethod(null);
emitLocation(null, -1);
currentLayer = layers.get(--currentLayerIndex);
}
private RecordArrayBuilder.Record add(RecordArrayBuilder builder) { private RecordArrayBuilder.Record add(RecordArrayBuilder builder) {
if (builder.size() > 1) { if (builder.size() > 1) {
RecordArrayBuilder.Record lastRecord = builder.get(builder.size() - 1); RecordArrayBuilder.Record lastRecord = builder.get(builder.size() - 1);
@ -99,9 +112,9 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
public void emitClass(String className) { public void emitClass(String className) {
debugInformation = null; debugInformation = null;
int classIndex = classes.index(className); int classIndex = classes.index(className);
if (!Objects.equals(className, currentClass)) { if (!Objects.equals(className, currentLayer.currentClass)) {
add(classMapping, classIndex); add(currentLayer.classMapping, classIndex);
currentClass = className; currentLayer.currentClass = className;
} }
} }
@ -109,12 +122,12 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
public void emitMethod(MethodDescriptor method) { public void emitMethod(MethodDescriptor method) {
debugInformation = null; debugInformation = null;
int methodIndex = methods.index(method != null ? method.toString() : null); int methodIndex = methods.index(method != null ? method.toString() : null);
if (!Objects.equals(method, currentMethod)) { if (!Objects.equals(method, currentLayer.currentMethod)) {
add(methodMapping, methodIndex); add(currentLayer.methodMapping, methodIndex);
currentMethod = method; currentLayer.currentMethod = method;
} }
if (currentClass != null) { if (currentLayer.currentClass != null) {
int classIndex = classes.index(currentClass); int classIndex = classes.index(currentLayer.currentClass);
long fullIndex = ((long) classIndex << 32) | methodIndex; long fullIndex = ((long) classIndex << 32) | methodIndex;
if (!exactMethodMap.containsKey(fullIndex)) { if (!exactMethodMap.containsKey(fullIndex)) {
exactMethodMap.put(fullIndex, exactMethods.size()); exactMethodMap.put(fullIndex, exactMethods.size());
@ -283,10 +296,19 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
debugInformation.exactMethodMap = new HashMap<>(exactMethodMap); debugInformation.exactMethodMap = new HashMap<>(exactMethodMap);
debugInformation.statementStartMapping = statementStartMapping.build(); debugInformation.statementStartMapping = statementStartMapping.build();
debugInformation.fileMapping = compress(fileMapping).build();
debugInformation.lineMapping = compress(lineMapping).build(); DebugInformation.Layer[] builtLayers = new DebugInformation.Layer[layers.size()];
debugInformation.classMapping = compress(classMapping).build(); for (int i = 0; i < builtLayers.length; ++i) {
debugInformation.methodMapping = compress(methodMapping).build(); LayerBuilder layerBuilder = layers.get(i);
DebugInformation.Layer builtLayer = new DebugInformation.Layer();
builtLayer.fileMapping = compress(layerBuilder.fileMapping).build();
builtLayer.lineMapping = compress(layerBuilder.lineMapping).build();
builtLayer.classMapping = compress(layerBuilder.classMapping).build();
builtLayer.methodMapping = compress(layerBuilder.methodMapping).build();
builtLayers[i] = builtLayer;
}
debugInformation.layers = builtLayers;
debugInformation.callSiteMapping = callSiteMapping.build(); debugInformation.callSiteMapping = callSiteMapping.build();
debugInformation.variableMappings = new RecordArray[variableNames.list.size()]; debugInformation.variableMappings = new RecordArray[variableNames.list.size()];
for (int var : variableMappings.keySet()) { for (int var : variableMappings.keySet()) {
@ -339,7 +361,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
} }
public String[] getItems() { public String[] getItems() {
return list.toArray(new String[list.size()]); return list.toArray(new String[0]);
} }
public Map<String, Integer> getIndexes() { public Map<String, Integer> getIndexes() {
@ -352,4 +374,15 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
String jsName; String jsName;
Map<Integer, Integer> fieldMap = new HashMap<>(); Map<Integer, Integer> fieldMap = new HashMap<>();
} }
static class LayerBuilder {
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 MethodDescriptor currentMethod;
private String currentClass;
private String currentFileName;
private int currentLine;
}
} }

View File

@ -23,6 +23,10 @@ public interface DebugInformationEmitter {
void emitLocation(String fileName, int line); void emitLocation(String fileName, int line);
void enterLocation();
void exitLocation();
void emitStatementStart(); void emitStatementStart();
void emitMethod(MethodDescriptor method); void emitMethod(MethodDescriptor method);

View File

@ -43,10 +43,15 @@ class DebugInformationReader {
debugInfo.methods = readStrings(); debugInfo.methods = readStrings();
debugInfo.variableNames = readStrings(); debugInfo.variableNames = readStrings();
debugInfo.exactMethods = readExactMethods(); debugInfo.exactMethods = readExactMethods();
debugInfo.fileMapping = readMapping(); debugInfo.layers = new DebugInformation.Layer[input.read()];
debugInfo.lineMapping = readMapping(); for (int i = 0; i < debugInfo.layers.length; ++i) {
debugInfo.classMapping = readMapping(); DebugInformation.Layer layer = new DebugInformation.Layer();
debugInfo.methodMapping = readMapping(); layer.fileMapping = readMapping();
layer.lineMapping = readMapping();
layer.classMapping = readMapping();
layer.methodMapping = readMapping();
debugInfo.layers[i] = layer;
}
debugInfo.statementStartMapping = readBooleanMapping(); debugInfo.statementStartMapping = readBooleanMapping();
debugInfo.callSiteMapping = readCallSiteMapping(); debugInfo.callSiteMapping = readCallSiteMapping();
debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length); debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length);

View File

@ -42,10 +42,13 @@ class DebugInformationWriter {
writeStringArray(debugInfo.variableNames); writeStringArray(debugInfo.variableNames);
writeExactMethods(debugInfo.exactMethods); writeExactMethods(debugInfo.exactMethods);
writeMapping(debugInfo.fileMapping); output.write(debugInfo.layers.length);
writeMapping(debugInfo.lineMapping); for (DebugInformation.Layer layer : debugInfo.layers) {
writeMapping(debugInfo.classMapping); writeMapping(layer.fileMapping);
writeMapping(debugInfo.methodMapping); writeMapping(layer.lineMapping);
writeMapping(layer.classMapping);
writeMapping(layer.methodMapping);
}
writeLinesAndColumns(debugInfo.statementStartMapping); writeLinesAndColumns(debugInfo.statementStartMapping);
writeCallSiteMapping(debugInfo.callSiteMapping); writeCallSiteMapping(debugInfo.callSiteMapping);
writeVariableMappings(debugInfo); writeVariableMappings(debugInfo);

View File

@ -24,6 +24,16 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter {
public void emitLocation(String fileName, int line) { public void emitLocation(String fileName, int line) {
} }
@Override
public void enterLocation() {
}
@Override
public void exitLocation() {
}
@Override @Override
public void emitMethod(MethodDescriptor method) { public void emitMethod(MethodDescriptor method) {
} }

View File

@ -21,29 +21,30 @@ import org.teavm.model.MethodReference;
public class ExactMethodIterator { public class ExactMethodIterator {
private DebugInformation debugInformation; private DebugInformation debugInformation;
private DebugInformation.Layer layer;
private GeneratedLocation location; private GeneratedLocation location;
private int classIndex; private int classIndex;
private int methodIndex; private int methodIndex;
private int classId = -1; private int classId = -1;
private int methodId = -1; private int methodId = -1;
ExactMethodIterator(DebugInformation debugInformation) { ExactMethodIterator(DebugInformation debugInformation, DebugInformation.Layer layer) {
this.debugInformation = debugInformation; this.debugInformation = debugInformation;
this.layer = layer;
if (!isEndReached()) { if (!isEndReached()) {
read(); read();
} }
} }
public boolean isEndReached() { public boolean isEndReached() {
return methodIndex >= debugInformation.methodMapping.size() return methodIndex >= layer.methodMapping.size() && classIndex >= layer.classMapping.size();
&& classIndex >= debugInformation.classMapping.size();
} }
private void read() { private void read() {
if (classIndex < debugInformation.classMapping.size() if (classIndex < layer.classMapping.size()
&& methodIndex < debugInformation.methodMapping.size()) { && methodIndex < layer.methodMapping.size()) {
RecordArray.Record classRecord = debugInformation.classMapping.get(classIndex); RecordArray.Record classRecord = layer.classMapping.get(classIndex);
RecordArray.Record methodRecord = debugInformation.methodMapping.get(methodIndex); RecordArray.Record methodRecord = layer.methodMapping.get(methodIndex);
GeneratedLocation classLoc = DebugInformation.key(classRecord); GeneratedLocation classLoc = DebugInformation.key(classRecord);
GeneratedLocation methodLoc = DebugInformation.key(methodRecord); GeneratedLocation methodLoc = DebugInformation.key(methodRecord);
int cmp = classLoc.compareTo(methodLoc); int cmp = classLoc.compareTo(methodLoc);
@ -55,9 +56,9 @@ public class ExactMethodIterator {
nextClassRecord(); nextClassRecord();
nextMethodRecord(); nextMethodRecord();
} }
} else if (classIndex < debugInformation.classMapping.size()) { } else if (classIndex < layer.classMapping.size()) {
nextClassRecord(); nextClassRecord();
} else if (methodIndex < debugInformation.methodMapping.size()) { } else if (methodIndex < layer.methodMapping.size()) {
nextMethodRecord(); nextMethodRecord();
} else { } else {
throw new IllegalStateException("End already reached"); throw new IllegalStateException("End already reached");
@ -65,13 +66,13 @@ public class ExactMethodIterator {
} }
private void nextClassRecord() { private void nextClassRecord() {
RecordArray.Record record = debugInformation.classMapping.get(classIndex++); RecordArray.Record record = layer.classMapping.get(classIndex++);
classId = record.get(2); classId = record.get(2);
location = DebugInformation.key(record); location = DebugInformation.key(record);
} }
private void nextMethodRecord() { private void nextMethodRecord() {
RecordArray.Record record = debugInformation.methodMapping.get(methodIndex++); RecordArray.Record record = layer.methodMapping.get(methodIndex++);
methodId = record.get(2); methodId = record.get(2);
location = DebugInformation.key(record); location = DebugInformation.key(record);
} }

View File

@ -1,55 +0,0 @@
/*
* 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.information;
public class FileNameIterator {
private DebugInformation debugInformation;
private int index;
FileNameIterator(DebugInformation debugInformation) {
this.debugInformation = debugInformation;
}
public boolean isEndReached() {
return index < debugInformation.fileMapping.size();
}
public GeneratedLocation getLocation() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return DebugInformation.key(debugInformation.fileMapping.get(index));
}
public int getFileNameId() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return debugInformation.fileMapping.get(index).get(2);
}
public String getFileName() {
int fileNameId = getFileNameId();
return fileNameId >= 0 ? debugInformation.getFileName(fileNameId) : null;
}
public void next() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
++index;
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2019 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.information;
class LayerIterator {
private int currentLayer;
private MethodIterator[] methodIterators;
private GeneratedLocation currentLocation;
LayerIterator(DebugInformation debugInformation) {
methodIterators = new MethodIterator[debugInformation.layerCount()];
for (int i = 0; i < methodIterators.length; ++i) {
methodIterators[i] = new MethodIterator(debugInformation.layers[i]);
}
nextImpl();
}
public boolean isEndReached() {
return currentLayer == 0 && methodIterators[currentLayer].isEndReached();
}
public int getLayer() {
if (isEndReached()) {
throw new IllegalStateException();
}
return currentLayer;
}
public GeneratedLocation getLocation() {
if (isEndReached()) {
throw new IllegalStateException();
}
return currentLocation;
}
public void next() {
if (isEndReached()) {
throw new IllegalStateException();
}
int previous = currentLayer;
do {
nextImpl();
} while (!isEndReached() && previous == currentLayer);
}
private void nextImpl() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
if (canEnterLayer()) {
++currentLayer;
while (canEnterLayer()) {
++currentLayer;
}
currentLocation = methodIterators[currentLayer].getLocation();
methodIterators[currentLayer].next();
} else if (canExitLayer()) {
GeneratedLocation limitLocation = methodIterators[currentLayer].getLocation();
moveToNextNotEmpty();
while (true) {
--currentLayer;
moveToLocation(limitLocation);
if (currentLayer <= 0 || methodIterators[currentLayer].getMethodId() >= 0
|| !methodIterators[currentLayer].getLocation().equals(limitLocation)) {
break;
}
moveToNextNotEmpty();
}
currentLocation = limitLocation;
} else {
currentLocation = methodIterators[currentLayer].getLocation();
methodIterators[currentLayer].next();
}
}
private boolean canEnterLayer() {
return currentLayer < methodIterators.length - 1
&& !methodIterators[currentLayer + 1].isEndReached()
&& !methodIterators[currentLayer].isEndReached()
&& isLocationLessOrEqual(currentLayer + 1, currentLayer);
}
private boolean canExitLayer() {
if (currentLayer == 0) {
return false;
}
return methodIterators[currentLayer].getMethodId() < 0;
}
private void moveToNextNotEmpty() {
while (!methodIterators[currentLayer].isEndReached() && methodIterators[currentLayer].getMethodId() < 0) {
methodIterators[currentLayer].next();
}
}
private void moveToLocation(GeneratedLocation location) {
while (true) {
GeneratedLocation nextLocation = methodIterators[currentLayer].getLocation();
if (nextLocation.compareTo(location) >= 0) {
break;
}
currentLocation = location;
methodIterators[currentLayer].next();
}
}
private boolean isLocationLessOrEqual(int firstLayer, int secondLayer) {
return methodIterators[firstLayer].getLocation().compareTo(methodIterators[secondLayer].getLocation()) <= 0;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2019 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.information;
import org.teavm.common.RecordArray;
class LayerSourceLocationIterator {
private DebugInformation debugInformation;
private int lineIndex;
private int fileIndex;
private GeneratedLocation location;
private int fileId = -1;
private int line = -1;
private boolean endReached;
private DebugInformation.Layer layer;
LayerSourceLocationIterator(DebugInformation debugInformation, DebugInformation.Layer layer) {
this.debugInformation = debugInformation;
this.layer = layer;
read();
}
public boolean isEndReached() {
return endReached;
}
private void read() {
if (fileIndex < layer.fileMapping.size() && lineIndex < layer.lineMapping.size()) {
RecordArray.Record fileRecord = layer.fileMapping.get(fileIndex);
RecordArray.Record lineRecord = layer.lineMapping.get(lineIndex);
GeneratedLocation fileLoc = DebugInformation.key(fileRecord);
GeneratedLocation lineLoc = DebugInformation.key(lineRecord);
int cmp = fileLoc.compareTo(lineLoc);
if (cmp < 0) {
nextFileRecord();
} else if (cmp > 0) {
nextLineRecord();
} else {
nextFileRecord();
nextLineRecord();
}
} else if (fileIndex < layer.fileMapping.size()) {
nextFileRecord();
} else if (lineIndex < layer.lineMapping.size()) {
nextLineRecord();
} else if (endReached) {
throw new IllegalStateException("End already reached");
} else {
endReached = true;
}
}
private void nextFileRecord() {
RecordArray.Record record = layer.fileMapping.get(fileIndex++);
location = DebugInformation.key(record);
fileId = record.get(2);
}
private void nextLineRecord() {
RecordArray.Record record = layer.lineMapping.get(lineIndex++);
location = DebugInformation.key(record);
line = record.get(2);
}
public void next() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
read();
}
public GeneratedLocation getLocation() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return location;
}
public int getFileNameId() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return fileId;
}
public String getFileName() {
int fileId = getFileNameId();
return fileId >= 0 ? debugInformation.getFileName(fileId) : null;
}
public int getLine() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return line;
}
}

View File

@ -1,50 +0,0 @@
/*
* 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.information;
public class LineNumberIterator {
private DebugInformation debugInformation;
private int index;
LineNumberIterator(DebugInformation debugInformation) {
this.debugInformation = debugInformation;
}
public boolean isEndReached() {
return index < debugInformation.lineMapping.size();
}
public GeneratedLocation getLocation() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return DebugInformation.key(debugInformation.lineMapping.get(index));
}
public int getLineNumber() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return debugInformation.lineMapping.get(index).get(2);
}
public void next() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
++index;
}
}

View File

@ -15,37 +15,30 @@
*/ */
package org.teavm.debugging.information; package org.teavm.debugging.information;
import org.teavm.model.MethodDescriptor; class MethodIterator {
private DebugInformation.Layer layer;
public class MethodIterator {
private DebugInformation debugInformation;
private int index; private int index;
MethodIterator(DebugInformation debugInformation) { MethodIterator(DebugInformation.Layer layer) {
this.debugInformation = debugInformation; this.layer = layer;
} }
public boolean isEndReached() { public boolean isEndReached() {
return index < debugInformation.methodMapping.size(); return index >= layer.methodMapping.size();
} }
public GeneratedLocation getLocation() { public GeneratedLocation getLocation() {
if (isEndReached()) { if (isEndReached()) {
throw new IllegalStateException("End already reached"); throw new IllegalStateException("End already reached");
} }
return DebugInformation.key(debugInformation.methodMapping.get(index)); return DebugInformation.key(layer.methodMapping.get(index));
} }
public int getMethodId() { public int getMethodId() {
if (isEndReached()) { if (isEndReached()) {
throw new IllegalStateException("End already reached"); throw new IllegalStateException("End already reached");
} }
return debugInformation.methodMapping.get(index).get(2); return layer.methodMapping.get(index).get(2);
}
public MethodDescriptor getMethod() {
int methodId = getMethodId();
return methodId >= 0 ? debugInformation.getMethod(methodId) : null;
} }
public void next() { public void next() {

View File

@ -15,85 +15,77 @@
*/ */
package org.teavm.debugging.information; package org.teavm.debugging.information;
import org.teavm.common.RecordArray;
public class SourceLocationIterator { public class SourceLocationIterator {
private DebugInformation debugInformation; private DebugInformation debugInformation;
private int lineIndex; private LayerIterator layerIterator;
private int fileIndex; private LayerInfo[] layerSourceIterators;
private GeneratedLocation location;
private int fileId = -1;
private int line = -1;
private boolean endReached; private boolean endReached;
private int currentLayer;
private GeneratedLocation lastLocation;
SourceLocationIterator(DebugInformation debugInformation) { public SourceLocationIterator(DebugInformation debugInformation) {
this.debugInformation = debugInformation; this.debugInformation = debugInformation;
if (!isEndReached()) { layerIterator = new LayerIterator(debugInformation);
read(); layerSourceIterators = new LayerInfo[debugInformation.layerCount()];
for (int i = 0; i < layerSourceIterators.length; ++i) {
layerSourceIterators[i] = new LayerInfo(new LayerSourceLocationIterator(
debugInformation, debugInformation.layers[i]));
} }
if (!layerIterator.isEndReached()) {
currentLayer = layerIterator.getLayer();
layerIterator.next();
} else {
currentLayer = 0;
}
lastLocation = layerSourceIterators[currentLayer].lastLocation;
if (lastLocation == null) {
endReached = true;
}
}
public GeneratedLocation getLocation() {
return lastLocation;
} }
public boolean isEndReached() { public boolean isEndReached() {
return endReached; return endReached;
} }
private void read() {
if (fileIndex < debugInformation.fileMapping.size() && lineIndex < debugInformation.lineMapping.size()) {
RecordArray.Record fileRecord = debugInformation.fileMapping.get(fileIndex);
RecordArray.Record lineRecord = debugInformation.lineMapping.get(lineIndex);
GeneratedLocation fileLoc = DebugInformation.key(fileRecord);
GeneratedLocation lineLoc = DebugInformation.key(lineRecord);
int cmp = fileLoc.compareTo(lineLoc);
if (cmp < 0) {
nextFileRecord();
} else if (cmp > 0) {
nextLineRecord();
} else {
nextFileRecord();
nextLineRecord();
}
} else if (fileIndex < debugInformation.fileMapping.size()) {
nextFileRecord();
} else if (lineIndex < debugInformation.lineMapping.size()) {
nextLineRecord();
} else if (endReached) {
throw new IllegalStateException("End already reached");
} else {
endReached = true;
}
}
private void nextFileRecord() {
RecordArray.Record record = debugInformation.fileMapping.get(fileIndex++);
location = DebugInformation.key(record);
fileId = record.get(2);
}
private void nextLineRecord() {
RecordArray.Record record = debugInformation.lineMapping.get(lineIndex++);
location = DebugInformation.key(record);
line = record.get(2);
}
public void next() { public void next() {
if (isEndReached()) { if (endReached) {
throw new IllegalStateException("End already reached"); throw new IllegalStateException();
}
read();
} }
public GeneratedLocation getLocation() { LayerInfo currentIterator = layerSourceIterators[currentLayer];
if (isEndReached()) { if (currentLayer == 0 && currentIterator.iterator.isEndReached()) {
throw new IllegalStateException("End already reached"); endReached = true;
return;
}
if (currentIterator.iterator.isEndReached() || (!layerIterator.isEndReached()
&& currentIterator.iterator.getLocation().compareTo(layerIterator.getLocation()) >= 0)) {
currentLayer = layerIterator.getLayer();
lastLocation = layerIterator.getLocation();
layerIterator.next();
currentIterator = layerSourceIterators[currentLayer];
while (!currentIterator.iterator.isEndReached()
&& currentIterator.iterator.getLocation().compareTo(lastLocation) <= 0) {
currentIterator.next();
}
} else {
currentIterator.next();
lastLocation = currentIterator.lastLocation;
} }
return location;
} }
public int getFileNameId() { public int getFileNameId() {
if (isEndReached()) { if (isEndReached()) {
throw new IllegalStateException("End already reached"); throw new IllegalStateException("End already reached");
} }
return fileId; return layerSourceIterators[currentLayer].lastFileId;
} }
public String getFileName() { public String getFileName() {
@ -105,6 +97,27 @@ public class SourceLocationIterator {
if (isEndReached()) { if (isEndReached()) {
throw new IllegalStateException("End already reached"); throw new IllegalStateException("End already reached");
} }
return line; return layerSourceIterators[currentLayer].lastLine;
}
static class LayerInfo {
LayerSourceLocationIterator iterator;
int lastFileId;
int lastLine;
GeneratedLocation lastLocation;
LayerInfo(LayerSourceLocationIterator iterator) {
this.iterator = iterator;
if (!iterator.isEndReached()) {
next();
}
}
void next() {
this.lastFileId = iterator.getFileNameId();
this.lastLine = iterator.getLine();
this.lastLocation = iterator.getLocation();
iterator.next();
}
} }
} }

View File

@ -62,7 +62,8 @@ class SourceMapsWriter {
output.write("\"}"); output.write("\"}");
} }
private void writeSegment(GeneratedLocation loc, int sourceFile, int sourceLine) throws IOException { private void writeSegment(GeneratedLocation loc, int sourceFile, int sourceLine)
throws IOException {
while (loc.getLine() > lastLine) { while (loc.getLine() > lastLine) {
output.write(';'); output.write(';');
++lastLine; ++lastLine;

View File

@ -398,7 +398,7 @@ public class Inlining {
entry.targetInstruction = insn; entry.targetInstruction = insn;
entry.program = invokedProgram; entry.program = invokedProgram;
entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1, innerStep, invokedMethod.getReference(), entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1, innerStep, invokedMethod.getReference(),
inliningInfo)); innerInliningInfo));
entry.depth = depth; entry.depth = depth;
entry.method = invokedMethod.getReference(); entry.method = invokedMethod.getReference();
entry.locationInfo = innerInliningInfo; entry.locationInfo = innerInliningInfo;

View File

@ -61,7 +61,7 @@ class LocationGraphBuilder {
while (!stack.isEmpty()) { while (!stack.isEmpty()) {
Step step = stack.pop(); Step step = stack.pop();
if (visited[step.block]) { if (visited[step.block]) {
if (step.location != null) { if (step.location != null && !step.location.isEmpty()) {
additionalConnections.add(new AdditionalConnection(step.location, startLocations.get(step.block))); additionalConnections.add(new AdditionalConnection(step.location, startLocations.get(step.block)));
} }
continue; continue;
@ -83,14 +83,14 @@ class LocationGraphBuilder {
if (blockLocations[step.block] == null) { if (blockLocations[step.block] == null) {
blockLocations[step.block] = insn.getLocation(); blockLocations[step.block] = insn.getLocation();
} }
if (location != null && !Objects.equals(location, insn.getLocation())) { if (location != null && !location.isEmpty() && !Objects.equals(location, insn.getLocation())) {
addEdge(location, insn.getLocation()); addEdge(location, insn.getLocation());
} }
location = insn.getLocation(); location = insn.getLocation();
} }
} }
if (graph.outgoingEdgesCount(step.block) == 0) { if (graph.outgoingEdgesCount(step.block) == 0) {
if (location != null) { if (location != null && !location.isEmpty()) {
addEdge(location, new TextLocation(null, -1)); addEdge(location, new TextLocation(null, -1));
} }
} else { } else {

View File

@ -17,8 +17,11 @@ package org.teavm.devserver.deobfuscate;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.GeneratedLocation;
import org.teavm.debugging.information.SourceLocation; import org.teavm.debugging.information.SourceLocation;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
import org.teavm.jso.ajax.XMLHttpRequest; import org.teavm.jso.ajax.XMLHttpRequest;
@ -71,11 +74,12 @@ public final class Deobfuscator {
String fileName = groups.get(2).stringValue(); String fileName = groups.get(2).stringValue();
int lineNumber = Integer.parseInt(groups.get(3).stringValue()); int lineNumber = Integer.parseInt(groups.get(3).stringValue());
int columnNumber = Integer.parseInt(groups.get(4).stringValue()); int columnNumber = Integer.parseInt(groups.get(4).stringValue());
Frame frame = deobfuscateFrame(debugInformation, classesFileName, fileName, lineNumber, columnNumber); List<Frame> framesPerLine = deobfuscateFrames(debugInformation, classesFileName, fileName,
if (frame == null) { lineNumber, columnNumber);
frame = createDefaultFrame(fileName, functionName, lineNumber); if (framesPerLine == null) {
framesPerLine = Arrays.asList(createDefaultFrame(fileName, functionName, lineNumber));
} }
frames.add(frame); frames.addAll(framesPerLine);
} }
return frames.toArray(new Frame[0]); return frames.toArray(new Frame[0]);
}); });
@ -85,18 +89,22 @@ public final class Deobfuscator {
} }
} }
private static Frame deobfuscateFrame(DebugInformation debugInformation, String classesFileName, private static List<Frame> deobfuscateFrames(DebugInformation debugInformation, String classesFileName,
String fileName, int lineNumber, int columnNumber) { String fileName, int lineNumber, int columnNumber) {
if (!fileName.equals(classesFileName)) { if (!fileName.equals(classesFileName)) {
return null; return null;
} }
MethodReference method = debugInformation.getMethodAt(lineNumber - 1, columnNumber - 1); List<Frame> result = new ArrayList<>();
for (int layer = 0; layer < debugInformation.layerCount(); ++layer) {
GeneratedLocation jsLocation = new GeneratedLocation(lineNumber - 1, columnNumber - 1);
MethodReference method = debugInformation.getMethodAt(jsLocation, layer);
if (method == null) { if (method == null) {
return null; break;
} }
SourceLocation location = debugInformation.getSourceLocation(lineNumber - 1, columnNumber - 1); SourceLocation location = debugInformation.getSourceLocation(jsLocation, layer);
String decodedFileName = location != null ? location.getFileName() : null; String decodedFileName = location != null ? location.getFileName() : null;
if (decodedFileName != null) { if (decodedFileName != null) {
@ -110,7 +118,13 @@ public final class Deobfuscator {
if (location != null) { if (location != null) {
frame.setLineNumber(location.getLine()); frame.setLineNumber(location.getLine());
} }
return frame; }
if (result.isEmpty()) {
return null;
}
Collections.reverse(result);
return result;
} }
private static Frame createDefaultFrame(String fileName, String functionName, int lineNumber) { private static Frame createDefaultFrame(String fileName, String functionName, int lineNumber) {

View File

@ -25,16 +25,18 @@ import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable; import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.GeneratedLocation;
import org.teavm.debugging.information.SourceLocation; import org.teavm.debugging.information.SourceLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
final class RhinoResultParser { final class RhinoResultParser {
private static Pattern pattern = Pattern.compile("(([A-Za-z_$]+)\\(\\))?@.+:([0-9]+)"); private static Pattern pattern = Pattern.compile("(([A-Za-z_$][A-Za-z0-9_$]*)\\(\\))?@.+:([0-9]+)");
private static Pattern lineSeparator = Pattern.compile("\\r\\n|\r|\n"); private static Pattern lineSeparator = Pattern.compile("\\r\\n|\r|\n");
private DebugInformation debugInformation; private DebugInformation debugInformation;
private String[] script; private String[] script;
@ -70,8 +72,7 @@ final class RhinoResultParser {
String stack = result.get("stack", result).toString(); String stack = result.get("stack", result).toString();
StackTraceElement[] decodedStack = null; StackTraceElement[] decodedStack = null;
if (debugInformation != null) { if (debugInformation != null) {
List<StackTraceElement> elements = new ArrayList<>(); List<StackTraceElement> elements = new ArrayList<>(Arrays.asList(decodeStack(stack)));
elements.addAll(Arrays.asList(decodeStack(stack)));
List<StackTraceElement> currentElements = Arrays.asList(Thread.currentThread().getStackTrace()); List<StackTraceElement> currentElements = Arrays.asList(Thread.currentThread().getStackTrace());
elements.addAll(currentElements.subList(2, currentElements.size())); elements.addAll(currentElements.subList(2, currentElements.size()));
decodedStack = elements.toArray(new StackTraceElement[0]); decodedStack = elements.toArray(new StackTraceElement[0]);
@ -104,24 +105,33 @@ final class RhinoResultParser {
} }
String functionName = matcher.group(2); String functionName = matcher.group(2);
int lineNumber = Integer.parseInt(matcher.group(3)) - 1; int jsLineNumber = Integer.parseInt(matcher.group(3)) - 1;
String scriptLine = script[lineNumber]; String scriptLine = script[jsLineNumber];
int column = firstNonSpace(scriptLine); int jsColumn = firstNonSpace(scriptLine);
MethodReference method = debugInformation.getMethodAt(lineNumber, column);
int layer = 0;
List<StackTraceElement> elementsByLine = new ArrayList<>();
GeneratedLocation jsLocation = new GeneratedLocation(jsLineNumber, jsColumn);
while (true) {
int lineNumber = jsLineNumber;
MethodReference method = debugInformation.getMethodAt(jsLocation, layer);
String className; String className;
String methodName; String methodName;
if (method != null) { if (method != null) {
className = method.getClassName(); className = method.getClassName();
methodName = method.getName(); methodName = method.getName();
} else if (layer > 0) {
break;
} else { } else {
className = "<JS>"; className = "<JS>";
methodName = functionName != null ? functionName : "<unknown_function>"; methodName = functionName != null ? functionName : "<unknown_function>";
} }
String fileName; String fileName;
SourceLocation location = debugInformation.getSourceLocation(lineNumber, column); SourceLocation location = debugInformation.getSourceLocation(jsLocation, layer);
if (location != null && location.getFileName() != null) { if (location != null && location.getFileName() != null) {
fileName = location.getFileName(); fileName = location.getFileName();
fileName = fileName.substring(fileName.lastIndexOf('/') + 1); fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
@ -131,7 +141,13 @@ final class RhinoResultParser {
lineNumber++; lineNumber++;
} }
elements.add(new StackTraceElement(className, methodName, fileName, lineNumber)); elementsByLine.add(new StackTraceElement(className, methodName, fileName, lineNumber));
++layer;
}
Collections.reverse(elementsByLine);
elements.addAll(elementsByLine);
} }
return elements.toArray(new StackTraceElement[0]); return elements.toArray(new StackTraceElement[0]);

View File

@ -37,7 +37,7 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
@Override @Override
public void apply(TeaVM vm) { public void apply(TeaVM vm) {
vm.setOptimizationLevel(TeaVMOptimizationLevel.FULL); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
} }
@Override @Override