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 {
stmt = Statement.assign(null, invocationExpr);
}
invocationExpr.setLocation(currentLocation);
stmt.setLocation(currentLocation);
stmt.setAsync(async);
async = false;

View File

@ -25,6 +25,7 @@ import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate;
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.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.InliningInfo;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
@ -62,6 +64,7 @@ public class RenderingContext {
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
private boolean minifying;
private ClassInitializerInfo classInitializerInfo;
private TextLocation lastEmittedLocation = TextLocation.EMPTY;
public RenderingContext(DebugInformationEmitter debugEmitter,
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
@ -128,11 +131,11 @@ public class RenderingContext {
LocationStackEntry prevEntry = locationStack.peek();
if (location != null) {
if (prevEntry == null || !location.equals(prevEntry.location)) {
debugEmitter.emitLocation(location.getFileName(), location.getLine());
emitLocation(location);
}
} else {
if (prevEntry != null) {
debugEmitter.emitLocation(null, -1);
emitLocation(TextLocation.EMPTY);
}
}
locationStack.push(new LocationStackEntry(location));
@ -143,13 +146,64 @@ public class RenderingContext {
LocationStackEntry entry = locationStack.peek();
if (entry != null) {
if (!entry.location.equals(prevEntry.location)) {
debugEmitter.emitLocation(entry.location.getFileName(), entry.location.getLine());
emitLocation(entry.location);
}
} 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() {
return minifying;
}

View File

@ -250,47 +250,63 @@ public class AstIO {
return;
}
InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = lastLocation.getInliningPath();
InliningInfo[] newPath = location.getInliningPath();
int pathIndex = 0;
while (pathIndex < prevPath.length && pathIndex < newPath.length
&& prevPath[pathIndex].equals(newPath[pathIndex])) {
lastCommonInlining = prevPath[pathIndex++];
}
String fileName = lastLocation.getFileName();
int lineNumber = lastLocation.getLine();
InliningInfo prevInlining = lastLocation.getInlining();
while (prevInlining != lastCommonInlining) {
output.writeUnsigned(124);
prevInlining = prevInlining.getParent();
}
if (location.getInlining() != lastLocation.getInlining()) {
InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = lastLocation.getInliningPath();
InliningInfo[] newPath = location.getInliningPath();
int pathIndex = 0;
while (pathIndex < prevPath.length && pathIndex < newPath.length
&& prevPath[pathIndex].equals(newPath[pathIndex])) {
lastCommonInlining = prevPath[pathIndex++];
}
while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++];
MethodReference method = inlining.getMethod();
output.writeUnsigned(inlining.isEmpty() ? 122 : 123);
output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
if (!inlining.isEmpty()) {
output.writeUnsigned(fileTable.lookup(inlining.getFileName()));
output.writeUnsigned(inlining.getLine());
InliningInfo prevInlining = location.getInlining();
while (prevInlining != lastCommonInlining) {
output.writeUnsigned(123);
fileName = prevInlining.getFileName();
lineNumber = prevInlining.getLine();
prevInlining = prevInlining.getParent();
}
while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++];
writeSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
fileName = null;
lineNumber = -1;
output.writeUnsigned(124);
MethodReference method = inlining.getMethod();
output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
}
}
if (location.isEmpty()) {
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());
}
writeSimpleLocation(fileName, lineNumber, location.getFileName(), location.getLine());
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 {
output.writeUnsigned(sequence.size());
for (Statement part : sequence) {
@ -741,25 +757,17 @@ public class AstIO {
lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned(),
lastReadInlining);
break;
case 122:
case 123: {
case 124: {
String className = symbolTable.at(input.readUnsigned());
MethodDescriptor methodDescriptor = MethodDescriptor.parse(symbolTable.at(input.readUnsigned()));
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),
fileName, lineNumber, lastReadInlining);
lastReadLocation.getFileName(), lastReadLocation.getLine(), lastReadInlining);
lastReadLocation = new TextLocation(null, -1, lastReadInlining);
break;
}
case 124:
case 123:
lastReadLocation = new TextLocation(lastReadInlining.getFileName(), lastReadInlining.getLine());
lastReadInlining = lastReadInlining.getParent();
break;
default:

View File

@ -19,6 +19,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;
import org.teavm.model.BasicBlock;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.FieldReference;
@ -208,7 +209,7 @@ public class ProgramIO {
}
InliningInfo inliningInfo = null;
TextLocation location = null;
TextLocation location = TextLocation.EMPTY;
insnLoop: while (true) {
int insnType = data.readUnsigned();
switch (insnType) {
@ -228,24 +229,16 @@ public class ProgramIO {
location = new TextLocation(location.getFileName(), line, inliningInfo);
break;
}
case 124:
case 125: {
String className = 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),
fileName, lineNumber, inliningInfo);
location.getFileName(), location.getLine(), inliningInfo);
location = new TextLocation(null, -1, inliningInfo);
break;
}
case 126:
location = new TextLocation(inliningInfo.getFileName(), inliningInfo.getLine());
inliningInfo = inliningInfo.getParent();
break;
default: {
@ -276,51 +269,65 @@ public class ProgramIO {
newLocation = TextLocation.EMPTY;
}
InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = location.getInliningPath();
InliningInfo[] newPath = newLocation.getInliningPath();
int pathIndex = 0;
while (pathIndex < prevPath.length && pathIndex < newPath.length
&& prevPath[pathIndex].equals(newPath[pathIndex])) {
lastCommonInlining = prevPath[pathIndex++];
}
String fileName = location.getFileName();
int lineNumber = location.getLine();
InliningInfo prevInlining = location.getInlining();
while (prevInlining != lastCommonInlining) {
output.writeUnsigned(126);
prevInlining = prevInlining.getParent();
}
if (newLocation.getInlining() != location.getInlining()) {
InliningInfo lastCommonInlining = null;
InliningInfo[] prevPath = location.getInliningPath();
InliningInfo[] newPath = newLocation.getInliningPath();
int pathIndex = 0;
while (pathIndex < prevPath.length && pathIndex < newPath.length
&& prevPath[pathIndex].equals(newPath[pathIndex])) {
lastCommonInlining = prevPath[pathIndex++];
}
while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++];
MethodReference method = inlining.getMethod();
output.writeUnsigned(inlining.isEmpty() ? 124 : 125);
output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
if (!inlining.isEmpty()) {
output.writeUnsigned(fileTable.lookup(inlining.getFileName()));
output.writeUnsigned(inlining.getLine());
InliningInfo prevInlining = location.getInlining();
while (prevInlining != lastCommonInlining) {
output.writeUnsigned(126);
fileName = prevInlining.getFileName();
lineNumber = prevInlining.getLine();
prevInlining = prevInlining.getParent();
}
while (pathIndex < newPath.length) {
InliningInfo inlining = newPath[pathIndex++];
writeSimpleLocation(fileName, lineNumber, inlining.getFileName(), inlining.getLine());
fileName = null;
lineNumber = -1;
output.writeUnsigned(125);
MethodReference method = inlining.getMethod();
output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
}
}
if (newLocation.isEmpty()) {
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());
}
}
writeSimpleLocation(fileName, lineNumber, newLocation.getFileName(), newLocation.getLine());
location = newLocation;
} catch (IOException 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
public void nop() {
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;
Map<Long, Integer> exactMethodMap;
RecordArray[] fileDescriptions;
RecordArray fileMapping;
RecordArray classMapping;
RecordArray methodMapping;
RecordArray lineMapping;
Layer[] layers;
RecordArray callSiteMapping;
RecordArray statementStartMapping;
RecordArray[] variableMappings;
@ -69,14 +66,6 @@ public class DebugInformation {
return variableNames.clone();
}
public LineNumberIterator iterateOverLineNumbers() {
return new LineNumberIterator(this);
}
public FileNameIterator iterateOverFileNames() {
return new FileNameIterator(this);
}
public String getFileName(int fileNameId) {
return fileNames[fileNameId];
}
@ -135,16 +124,12 @@ public class DebugInformation {
return id != null ? id : -1;
}
public ClassNameIterator iterateOverClassNames() {
return new ClassNameIterator(this);
public int layerCount() {
return layers.length;
}
public MethodIterator iterateOverMethods() {
return new MethodIterator(this);
}
public ExactMethodIterator iterateOverExactMethods() {
return new ExactMethodIterator(this);
public ExactMethodIterator iterateOverExactMethods(int layerIndex) {
return new ExactMethodIterator(this, layers[layerIndex]);
}
public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) {
@ -177,29 +162,67 @@ public class DebugInformation {
return new SourceLocationIterator(this);
}
private LayerSourceLocationIterator iterateOverSourceLocations(int layer) {
return new LayerSourceLocationIterator(this, layers[layer]);
}
public SourceLocation getSourceLocation(int line, int 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) {
String fileName = componentByKey(fileMapping, fileNames, generatedLocation);
int lineNumberIndex = indexByKey(lineMapping, generatedLocation);
int lineNumber = lineNumberIndex >= 0 ? lineMapping.get(lineNumberIndex).get(2) : -1;
return getSourceLocation(generatedLocation, autodetectLayer(generatedLocation));
}
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);
}
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) {
return null;
}
String method = componentByKey(methodMapping, methods, generatedLocation);
String method = componentByKey(layer.methodMapping, methods, generatedLocation);
if (method == null) {
return null;
}
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) {
return getMethodAt(new GeneratedLocation(line, column));
}
@ -460,16 +483,19 @@ public class DebugInformation {
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();
for (int layer = 0; layer < layers.length; ++layer) {
for (LayerSourceLocationIterator iter = iterateOverSourceLocations(layer);
!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.SubArray array = builder.get(iter.getLine()).getArray(0);
array.add(loc.getLine());
array.add(loc.getColumn());
}
GeneratedLocation loc = iter.getLocation();
RecordArrayBuilder.SubArray array = builder.get(iter.getLine()).getArray(0);
array.add(loc.getLine());
array.add(loc.getColumn());
}
}
fileDescriptions = new RecordArray[builders.length];
@ -486,7 +512,8 @@ public class DebugInformation {
GeneratedLocation prevLocation = new GeneratedLocation(0, 0);
MethodReference prevMethod = null;
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();
if (prevMethod != null) {
int lineIndex = Math.max(0, indexByKey(lineMapping, prevLocation));
@ -596,8 +623,9 @@ public class DebugInformation {
GeneratedLocation loc = key(callSiteRec);
int callSiteType = callSiteRec.get(2);
if (callSiteType != DebuggerCallSite.NONE) {
int line = valueByKey(lineMapping, loc);
int fileId = valueByKey(fileMapping, loc);
int layerIndex = autodetectLayer(loc);
int line = valueByKey(layers[layerIndex].lineMapping, loc);
int fileId = valueByKey(layers[layerIndex].fileMapping, loc);
if (fileId >= 0 && line >= 0) {
RecordArrayBuilder builder = builders[fileId];
while (builder.size() <= line) {
@ -699,4 +727,11 @@ public class DebugInformation {
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 Map<Long, Integer> exactMethodMap = new HashMap<>();
private RecordArrayBuilder statementStartMapping = new RecordArrayBuilder(2, 0);
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 List<LayerBuilder> layers = new ArrayList<>();
private LayerBuilder currentLayer;
private int currentLayerIndex;
private RecordArrayBuilder callSiteMapping = new RecordArrayBuilder(4, 0);
private Map<Integer, RecordArrayBuilder> variableMappings = new HashMap<>();
private MethodDescriptor currentMethod;
private String currentClass;
private String currentFileName;
private int currentClassMetadata = -1;
private List<ClassMetadata> classesMetadata = new ArrayList<>();
private List<RecordArrayBuilder> cfgs = new ArrayList<>();
private int currentLine;
private ReferenceCache referenceCache;
public DebugInformationBuilder(ReferenceCache referenceCache) {
this.referenceCache = referenceCache;
currentLayer = new LayerBuilder();
layers.add(currentLayer);
}
public LocationProvider getLocationProvider() {
@ -66,16 +63,32 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
public void emitLocation(String fileName, int line) {
debugInformation = null;
int fileIndex = files.index(fileName);
if (!Objects.equals(currentFileName, fileName)) {
add(fileMapping, fileIndex);
currentFileName = fileName;
if (!Objects.equals(currentLayer.currentFileName, fileName)) {
add(currentLayer.fileMapping, fileIndex);
currentLayer.currentFileName = fileName;
}
if (currentLine != line) {
add(lineMapping, line);
currentLine = line;
if (currentLayer.currentLine != line) {
add(currentLayer.lineMapping, 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) {
if (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) {
debugInformation = null;
int classIndex = classes.index(className);
if (!Objects.equals(className, currentClass)) {
add(classMapping, classIndex);
currentClass = className;
if (!Objects.equals(className, currentLayer.currentClass)) {
add(currentLayer.classMapping, classIndex);
currentLayer.currentClass = className;
}
}
@ -109,12 +122,12 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
public void emitMethod(MethodDescriptor method) {
debugInformation = null;
int methodIndex = methods.index(method != null ? method.toString() : null);
if (!Objects.equals(method, currentMethod)) {
add(methodMapping, methodIndex);
currentMethod = method;
if (!Objects.equals(method, currentLayer.currentMethod)) {
add(currentLayer.methodMapping, methodIndex);
currentLayer.currentMethod = method;
}
if (currentClass != null) {
int classIndex = classes.index(currentClass);
if (currentLayer.currentClass != null) {
int classIndex = classes.index(currentLayer.currentClass);
long fullIndex = ((long) classIndex << 32) | methodIndex;
if (!exactMethodMap.containsKey(fullIndex)) {
exactMethodMap.put(fullIndex, exactMethods.size());
@ -283,10 +296,19 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
debugInformation.exactMethodMap = new HashMap<>(exactMethodMap);
debugInformation.statementStartMapping = statementStartMapping.build();
debugInformation.fileMapping = compress(fileMapping).build();
debugInformation.lineMapping = compress(lineMapping).build();
debugInformation.classMapping = compress(classMapping).build();
debugInformation.methodMapping = compress(methodMapping).build();
DebugInformation.Layer[] builtLayers = new DebugInformation.Layer[layers.size()];
for (int i = 0; i < builtLayers.length; ++i) {
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.variableMappings = new RecordArray[variableNames.list.size()];
for (int var : variableMappings.keySet()) {
@ -339,7 +361,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
}
public String[] getItems() {
return list.toArray(new String[list.size()]);
return list.toArray(new String[0]);
}
public Map<String, Integer> getIndexes() {
@ -352,4 +374,15 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
String jsName;
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 enterLocation();
void exitLocation();
void emitStatementStart();
void emitMethod(MethodDescriptor method);

View File

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

View File

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

View File

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

View File

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

View File

@ -15,85 +15,77 @@
*/
package org.teavm.debugging.information;
import org.teavm.common.RecordArray;
public class SourceLocationIterator {
private DebugInformation debugInformation;
private int lineIndex;
private int fileIndex;
private GeneratedLocation location;
private int fileId = -1;
private int line = -1;
private LayerIterator layerIterator;
private LayerInfo[] layerSourceIterators;
private boolean endReached;
private int currentLayer;
private GeneratedLocation lastLocation;
SourceLocationIterator(DebugInformation debugInformation) {
public SourceLocationIterator(DebugInformation debugInformation) {
this.debugInformation = debugInformation;
if (!isEndReached()) {
read();
layerIterator = new LayerIterator(debugInformation);
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() {
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() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
if (endReached) {
throw new IllegalStateException();
}
read();
}
public GeneratedLocation getLocation() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
LayerInfo currentIterator = layerSourceIterators[currentLayer];
if (currentLayer == 0 && currentIterator.iterator.isEndReached()) {
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() {
if (isEndReached()) {
throw new IllegalStateException("End already reached");
}
return fileId;
return layerSourceIterators[currentLayer].lastFileId;
}
public String getFileName() {
@ -105,6 +97,27 @@ public class SourceLocationIterator {
if (isEndReached()) {
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("\"}");
}
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) {
output.write(';');
++lastLine;

View File

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

View File

@ -61,7 +61,7 @@ class LocationGraphBuilder {
while (!stack.isEmpty()) {
Step step = stack.pop();
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)));
}
continue;
@ -83,14 +83,14 @@ class LocationGraphBuilder {
if (blockLocations[step.block] == null) {
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());
}
location = insn.getLocation();
}
}
if (graph.outgoingEdgesCount(step.block) == 0) {
if (location != null) {
if (location != null && !location.isEmpty()) {
addEdge(location, new TextLocation(null, -1));
}
} else {

View File

@ -17,8 +17,11 @@ package org.teavm.devserver.deobfuscate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.GeneratedLocation;
import org.teavm.debugging.information.SourceLocation;
import org.teavm.jso.JSBody;
import org.teavm.jso.ajax.XMLHttpRequest;
@ -71,11 +74,12 @@ public final class Deobfuscator {
String fileName = groups.get(2).stringValue();
int lineNumber = Integer.parseInt(groups.get(3).stringValue());
int columnNumber = Integer.parseInt(groups.get(4).stringValue());
Frame frame = deobfuscateFrame(debugInformation, classesFileName, fileName, lineNumber, columnNumber);
if (frame == null) {
frame = createDefaultFrame(fileName, functionName, lineNumber);
List<Frame> framesPerLine = deobfuscateFrames(debugInformation, classesFileName, fileName,
lineNumber, columnNumber);
if (framesPerLine == null) {
framesPerLine = Arrays.asList(createDefaultFrame(fileName, functionName, lineNumber));
}
frames.add(frame);
frames.addAll(framesPerLine);
}
return frames.toArray(new Frame[0]);
});
@ -85,32 +89,42 @@ 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) {
if (!fileName.equals(classesFileName)) {
return null;
}
MethodReference method = debugInformation.getMethodAt(lineNumber - 1, columnNumber - 1);
if (method == null) {
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) {
break;
}
SourceLocation location = debugInformation.getSourceLocation(jsLocation, layer);
String decodedFileName = location != null ? location.getFileName() : null;
if (decodedFileName != null) {
decodedFileName = decodedFileName.substring(decodedFileName.lastIndexOf('/') + 1);
}
Frame frame = createEmptyFrame();
frame.setClassName(method.getClassName());
frame.setMethodName(method.getName());
frame.setFileName(decodedFileName);
if (location != null) {
frame.setLineNumber(location.getLine());
}
}
if (result.isEmpty()) {
return null;
}
SourceLocation location = debugInformation.getSourceLocation(lineNumber - 1, columnNumber - 1);
String decodedFileName = location != null ? location.getFileName() : null;
if (decodedFileName != null) {
decodedFileName = decodedFileName.substring(decodedFileName.lastIndexOf('/') + 1);
}
Frame frame = createEmptyFrame();
frame.setClassName(method.getClassName());
frame.setMethodName(method.getName());
frame.setFileName(decodedFileName);
if (location != null) {
frame.setLineNumber(location.getLine());
}
return frame;
Collections.reverse(result);
return result;
}
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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.GeneratedLocation;
import org.teavm.debugging.information.SourceLocation;
import org.teavm.model.MethodReference;
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 DebugInformation debugInformation;
private String[] script;
@ -70,8 +72,7 @@ final class RhinoResultParser {
String stack = result.get("stack", result).toString();
StackTraceElement[] decodedStack = null;
if (debugInformation != null) {
List<StackTraceElement> elements = new ArrayList<>();
elements.addAll(Arrays.asList(decodeStack(stack)));
List<StackTraceElement> elements = new ArrayList<>(Arrays.asList(decodeStack(stack)));
List<StackTraceElement> currentElements = Arrays.asList(Thread.currentThread().getStackTrace());
elements.addAll(currentElements.subList(2, currentElements.size()));
decodedStack = elements.toArray(new StackTraceElement[0]);
@ -104,34 +105,49 @@ final class RhinoResultParser {
}
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];
int column = firstNonSpace(scriptLine);
MethodReference method = debugInformation.getMethodAt(lineNumber, column);
String className;
String methodName;
String scriptLine = script[jsLineNumber];
int jsColumn = firstNonSpace(scriptLine);
if (method != null) {
className = method.getClassName();
methodName = method.getName();
} else {
className = "<JS>";
methodName = functionName != null ? functionName : "<unknown_function>";
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 methodName;
if (method != null) {
className = method.getClassName();
methodName = method.getName();
} else if (layer > 0) {
break;
} else {
className = "<JS>";
methodName = functionName != null ? functionName : "<unknown_function>";
}
String fileName;
SourceLocation location = debugInformation.getSourceLocation(jsLocation, layer);
if (location != null && location.getFileName() != null) {
fileName = location.getFileName();
fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
lineNumber = location.getLine();
} else {
fileName = "test.js";
lineNumber++;
}
elementsByLine.add(new StackTraceElement(className, methodName, fileName, lineNumber));
++layer;
}
String fileName;
SourceLocation location = debugInformation.getSourceLocation(lineNumber, column);
if (location != null && location.getFileName() != null) {
fileName = location.getFileName();
fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
lineNumber = location.getLine();
} else {
fileName = "test.js";
lineNumber++;
}
elements.add(new StackTraceElement(className, methodName, fileName, lineNumber));
Collections.reverse(elementsByLine);
elements.addAll(elementsByLine);
}
return elements.toArray(new StackTraceElement[0]);

View File

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