diff --git a/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java b/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java index ab09546c2..8693a5ced 100644 --- a/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java +++ b/core/src/main/java/org/teavm/ast/analysis/LocationGraphBuilder.java @@ -65,8 +65,7 @@ public final class LocationGraphBuilder { for (int terminal : visitor.nodes) { visitor.terminalNodes.set(terminal); } - TextLocation[][] locations = propagate(visitor.locations.toArray(new TextLocation[0]), graph - ); + TextLocation[][] locations = propagate(visitor.locations.toArray(new TextLocation[0]), graph); Map> builder = new LinkedHashMap<>(); for (int i = 0; i < graph.size(); ++i) { @@ -353,7 +352,7 @@ public final class LocationGraphBuilder { } private void setLocation(TextLocation location) { - if (location == null) { + if (location == null || location.isEmpty()) { return; } int node = createNode(location); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index c3adb96b5..fb0ce2900 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -746,7 +746,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } private static SourceLocation map(TextLocation location) { - if (location == null) { + if (location == null || location.isEmpty()) { return null; } return new SourceLocation(location.getFileName(), location.getLine()); diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java index e7ea7e108..d33307469 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/DefaultNamingStrategy.java @@ -153,6 +153,9 @@ public class DefaultNamingStrategy implements NamingStrategy { String cls = fieldRef.getClassName(); while (cls != null) { ClassReader clsReader = classSource.get(cls); + if (clsReader == null) { + break; + } if (clsReader != null) { FieldReader fieldReader = clsReader.getField(fieldRef.getFieldName()); if (fieldReader != null) { @@ -164,7 +167,7 @@ public class DefaultNamingStrategy implements NamingStrategy { return fieldRef; } - private final class Key { + static final class Key { final MethodReference data; int hash; final byte classifier; diff --git a/core/src/main/java/org/teavm/cache/AstIO.java b/core/src/main/java/org/teavm/cache/AstIO.java index 37b21323e..39c5ee4bb 100644 --- a/core/src/main/java/org/teavm/cache/AstIO.java +++ b/core/src/main/java/org/teavm/cache/AstIO.java @@ -71,6 +71,7 @@ import org.teavm.ast.VariableNode; import org.teavm.ast.WhileStatement; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; +import org.teavm.model.InliningInfo; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ReferenceCache; @@ -89,6 +90,7 @@ public class AstIO { private ReferenceCache referenceCache; private TextLocation lastWrittenLocation; private TextLocation lastReadLocation; + private InliningInfo lastReadInlining; public AstIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable, SymbolTable variableTable) { @@ -99,7 +101,7 @@ public class AstIO { } public void write(VarDataOutput output, ControlFlowEntry[] cfg) throws IOException { - lastWrittenLocation = null; + lastWrittenLocation = TextLocation.EMPTY; output.writeUnsigned(cfg.length); for (ControlFlowEntry entry : cfg) { writeLocation(output, entry.from); @@ -130,7 +132,7 @@ public class AstIO { } public ControlFlowEntry[] readControlFlow(VarDataInput input) throws IOException { - lastReadLocation = null; + lastReadLocation = TextLocation.EMPTY; int size = input.readUnsigned(); ControlFlowEntry[] result = new ControlFlowEntry[size]; for (int i = 0; i < size; ++i) { @@ -152,7 +154,7 @@ public class AstIO { for (int i = 0; i < varCount; ++i) { node.getVariables().add(readVariable(input)); } - lastReadLocation = null; + lastReadLocation = TextLocation.EMPTY; node.setBody(readStatement(input)); return node; } @@ -211,23 +213,24 @@ public class AstIO { } private void writeLocation(VarDataOutput output, TextLocation location) throws IOException { - if (location == null || location.getFileName() == null) { + if (location == null) { + location = TextLocation.EMPTY; + } + if (location.isEmpty()) { output.writeUnsigned(0); - lastWrittenLocation = null; - } else if (lastWrittenLocation != null && lastWrittenLocation.getFileName().equals(location.getFileName())) { + } else if (!lastWrittenLocation.isEmpty() && lastWrittenLocation.getFileName().equals(location.getFileName())) { output.writeUnsigned(1); output.writeSigned(location.getLine() - lastWrittenLocation.getLine()); - lastWrittenLocation = location; } else { output.writeUnsigned(fileTable.lookup(location.getFileName()) + 2); output.writeUnsigned(location.getLine()); - lastWrittenLocation = location; } + lastWrittenLocation = location; } private class NodeWriter implements ExprVisitor, StatementVisitor { private final VarDataOutput output; - private TextLocation lastLocation; + private TextLocation lastLocation = TextLocation.EMPTY; NodeWriter(VarDataOutput output) { super(); @@ -240,22 +243,52 @@ public class AstIO { } private void writeLocation(TextLocation location) throws IOException { + if (location == null) { + location = TextLocation.EMPTY; + } if (Objects.equals(location, lastLocation)) { return; } - if (location == null || location.getFileName() == null) { + + 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++]; + } + + InliningInfo prevInlining = lastLocation.getInlining(); + while (prevInlining != lastCommonInlining) { + output.writeUnsigned(124); + prevInlining = prevInlining.getParent(); + } + + 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()); + } + } + + if (location.isEmpty()) { output.writeUnsigned(127); - lastLocation = null; - } else if (lastLocation != null && lastLocation.getFileName().equals(location.getFileName())) { + } else if (!lastLocation.isEmpty() && lastLocation.getFileName().equals(location.getFileName())) { output.writeUnsigned(126); output.writeSigned(location.getLine() - lastLocation.getLine()); - lastLocation = location; } else { output.writeUnsigned(125); output.writeUnsigned(fileTable.lookup(location.getFileName())); output.writeUnsigned(location.getLine()); - lastLocation = location; } + + lastLocation = location; } private void writeSequence(List sequence) throws IOException { @@ -702,10 +735,32 @@ public class AstIO { break; case 126: lastReadLocation = new TextLocation(lastReadLocation.getFileName(), - lastReadLocation.getLine() + input.readSigned()); + lastReadLocation.getLine() + input.readSigned(), lastReadInlining); break; case 125: - lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned()); + lastReadLocation = new TextLocation(fileTable.at(input.readUnsigned()), input.readUnsigned(), + lastReadInlining); + break; + case 122: + case 123: { + 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); + break; + } + case 124: + lastReadInlining = lastReadInlining.getParent(); break; default: return type; diff --git a/core/src/main/java/org/teavm/cache/ProgramIO.java b/core/src/main/java/org/teavm/cache/ProgramIO.java index 995cb725d..5b4a73a2a 100644 --- a/core/src/main/java/org/teavm/cache/ProgramIO.java +++ b/core/src/main/java/org/teavm/cache/ProgramIO.java @@ -24,6 +24,7 @@ import org.teavm.model.BasicBlockReader; import org.teavm.model.FieldReference; import org.teavm.model.Incoming; import org.teavm.model.IncomingReader; +import org.teavm.model.InliningInfo; import org.teavm.model.Instruction; import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.MethodDescriptor; @@ -206,6 +207,7 @@ public class ProgramIO { block.getTryCatchBlocks().add(tryCatch); } + InliningInfo inliningInfo = null; TextLocation location = null; insnLoop: while (true) { int insnType = data.readUnsigned(); @@ -213,19 +215,39 @@ public class ProgramIO { case 0: break insnLoop; case 1: - location = null; + location = new TextLocation(null, -1, inliningInfo); break; case 2: { String file = fileTable.at(data.readUnsigned()); int line = data.readUnsigned(); - location = new TextLocation(file, line); + location = new TextLocation(file, line, inliningInfo); break; } case 127: { int line = location.getLine() + data.readSigned(); - location = new TextLocation(location.getFileName(), line); + 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); + break; + } + case 126: + inliningInfo = inliningInfo.getParent(); + break; default: { Instruction insn = readInstruction(insnType, program, data); insn.setLocation(location); @@ -241,7 +263,7 @@ public class ProgramIO { private class InstructionWriter implements InstructionReader { private VarDataOutput output; - TextLocation location; + TextLocation location = TextLocation.EMPTY; InstructionWriter(VarDataOutput output) { this.output = output; @@ -250,11 +272,41 @@ public class ProgramIO { @Override public void location(TextLocation newLocation) { try { - if (newLocation == null || newLocation.getFileName() == null || newLocation.getLine() < 0) { + if (newLocation == null) { + 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++]; + } + + InliningInfo prevInlining = location.getInlining(); + while (prevInlining != lastCommonInlining) { + output.writeUnsigned(126); + prevInlining = prevInlining.getParent(); + } + + 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()); + } + } + + if (newLocation.isEmpty()) { output.writeUnsigned(1); - location = null; } else { - if (location != null && location.getFileName().equals(newLocation.getFileName())) { + if (!location.isEmpty() && location.getFileName().equals(newLocation.getFileName())) { output.writeUnsigned(127); output.writeSigned(newLocation.getLine() - location.getLine()); } else { @@ -262,8 +314,8 @@ public class ProgramIO { output.writeUnsigned(fileTable.lookup(newLocation.getFileName())); output.writeUnsigned(newLocation.getLine()); } - location = newLocation; } + location = newLocation; } catch (IOException e) { throw new IOExceptionWrapper(e); } diff --git a/core/src/main/java/org/teavm/debugging/information/DebugInformationBuilder.java b/core/src/main/java/org/teavm/debugging/information/DebugInformationBuilder.java index ffcd071a7..d41fad85b 100644 --- a/core/src/main/java/org/teavm/debugging/information/DebugInformationBuilder.java +++ b/core/src/main/java/org/teavm/debugging/information/DebugInformationBuilder.java @@ -348,7 +348,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter { } static class ClassMetadata { - int parentIndex; + int parentIndex = -1; String jsName; Map fieldMap = new HashMap<>(); } diff --git a/core/src/main/java/org/teavm/model/InliningInfo.java b/core/src/main/java/org/teavm/model/InliningInfo.java new file mode 100644 index 000000000..a99b7db47 --- /dev/null +++ b/core/src/main/java/org/teavm/model/InliningInfo.java @@ -0,0 +1,84 @@ +/* + * 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.model; + +import java.io.Serializable; +import java.util.Objects; + +public class InliningInfo implements Serializable { + private MethodReference method; + private String fileName; + private int line; + private InliningInfo parent; + private transient int hash; + + public InliningInfo(MethodReference method, String fileName, int line, InliningInfo parent) { + this.method = method; + this.fileName = fileName; + this.line = line; + this.parent = parent; + } + + public MethodReference getMethod() { + return method; + } + + public String getFileName() { + return fileName; + } + + public int getLine() { + return line; + } + + public InliningInfo getParent() { + return parent; + } + + public boolean isEmpty() { + return fileName == null && line < 0; + } + + @Override + public int hashCode() { + int result = hash; + if (result == 0) { + final int prime = 31; + result = 1; + result = prime * result + method.hashCode(); + result = prime * result + (fileName == null ? 0 : fileName.hashCode()); + result = prime * result + line; + result = prime * result + (parent != null ? parent.hashCode() : 0); + hash = result; + } + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof InliningInfo)) { + return false; + } + InliningInfo that = (InliningInfo) obj; + return Objects.equals(method, that.method) + && Objects.equals(fileName, that.fileName) + && line == that.line + && Objects.equals(parent, that.parent); + } +} diff --git a/core/src/main/java/org/teavm/model/TextLocation.java b/core/src/main/java/org/teavm/model/TextLocation.java index 73675ef98..a713b1438 100644 --- a/core/src/main/java/org/teavm/model/TextLocation.java +++ b/core/src/main/java/org/teavm/model/TextLocation.java @@ -19,12 +19,22 @@ import java.io.Serializable; import java.util.Objects; public class TextLocation implements Serializable { + public static final TextLocation EMPTY = new TextLocation(null, -1); + private static final InliningInfo[] EMPTY_ARRAY = new InliningInfo[0]; + private String fileName; - private int line = -1; + private int line; + private InliningInfo inlining; + private transient int hash; public TextLocation(String fileName, int line) { + this(fileName, line, null); + } + + public TextLocation(String fileName, int line, InliningInfo inlining) { this.fileName = fileName; this.line = line; + this.inlining = inlining; } public String getFileName() { @@ -35,12 +45,47 @@ public class TextLocation implements Serializable { return line; } + public InliningInfo getInlining() { + return inlining; + } + + public InliningInfo[] getInliningPath() { + if (inlining == null) { + return EMPTY_ARRAY; + } + + InliningInfo inlining = this.inlining; + int sz = 0; + while (inlining != null) { + sz++; + inlining = inlining.getParent(); + } + + InliningInfo[] result = new InliningInfo[sz]; + inlining = this.inlining; + while (inlining != null) { + result[--sz] = inlining; + inlining = inlining.getParent(); + } + + return result; + } + + public boolean isEmpty() { + return fileName == null && line < 0; + } + @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (fileName == null ? 0 : fileName.hashCode()); - result = prime * result + line; + int result = hash; + if (result == 0) { + final int prime = 31; + result = 1; + result = prime * result + (fileName == null ? 0 : fileName.hashCode()); + result = prime * result + line; + result = prime * result + (inlining != null ? inlining.hashCode() : 0); + hash = result; + } return result; } @@ -53,11 +98,29 @@ public class TextLocation implements Serializable { return false; } TextLocation other = (TextLocation) obj; - return Objects.equals(fileName, other.fileName) && line == other.line; + return Objects.equals(fileName, other.fileName) && line == other.line + && Objects.equals(inlining, other.inlining); } @Override public String toString() { - return fileName + ":" + line; + StringBuilder sb = new StringBuilder(); + sb.append(fileName).append(':').append(line); + InliningInfo inlining = this.inlining; + if (inlining != null) { + sb.append('['); + boolean first = true; + while (inlining != null) { + if (!first) { + sb.append("->"); + } + first = false; + sb.append(inlining.getMethod()).append("@") + .append(inlining.getFileName()).append(':').append(inlining.getLine()); + inlining = inlining.getParent(); + } + sb.append(']'); + } + return sb.toString(); } } diff --git a/core/src/main/java/org/teavm/model/optimization/Inlining.java b/core/src/main/java/org/teavm/model/optimization/Inlining.java index f696ede36..d00300003 100644 --- a/core/src/main/java/org/teavm/model/optimization/Inlining.java +++ b/core/src/main/java/org/teavm/model/optimization/Inlining.java @@ -37,6 +37,7 @@ import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; import org.teavm.model.Incoming; +import org.teavm.model.InliningInfo; import org.teavm.model.Instruction; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReader; @@ -44,6 +45,7 @@ import org.teavm.model.MethodReference; import org.teavm.model.Phi; import org.teavm.model.Program; import org.teavm.model.ProgramReader; +import org.teavm.model.TextLocation; import org.teavm.model.TryCatchBlock; import org.teavm.model.VariableReader; import org.teavm.model.analysis.ClassInference; @@ -178,7 +180,7 @@ public class Inlining { if (step == null) { return false; } - List plan = buildPlan(program, -1, step, method); + List plan = buildPlan(program, -1, step, method, null); if (plan.isEmpty()) { return false; } @@ -227,6 +229,8 @@ public class Inlining { jumpToInlinedProgram.setTarget(firstInlineBlock); block.add(jumpToInlinedProgram); + InliningInfoMerger inliningInfoMerger = new InliningInfoMerger(planEntry.locationInfo); + for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) { BasicBlock blockToInline = inlineProgram.basicBlockAt(i); BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i); @@ -244,6 +248,14 @@ public class Inlining { } } } + + TextLocation location = insn.getLocation(); + if (location == null) { + location = TextLocation.EMPTY; + } + location = new TextLocation(location.getFileName(), location.getLine(), + inliningInfoMerger.merge(location.getInlining())); + insn.setLocation(location); } List phis = new ArrayList<>(blockToInline.getPhis()); @@ -324,7 +336,8 @@ public class Inlining { execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex()); } - private List buildPlan(Program program, int depth, InliningStep step, MethodReference method) { + private List buildPlan(Program program, int depth, InliningStep step, MethodReference method, + InliningInfo inliningInfo) { List plan = new ArrayList<>(); int originalDepth = depth; @@ -373,13 +386,22 @@ public class Inlining { } Program invokedProgram = ProgramUtils.copy(invokedMethod.getProgram()); + TextLocation location = insn.getLocation(); + InliningInfo innerInliningInfo = new InliningInfo( + invoke.getMethod(), + location != null ? location.getFileName() : null, + location != null ? location.getLine() : -1, + inliningInfo); + PlanEntry entry = new PlanEntry(); entry.targetBlock = block.getIndex(); entry.targetInstruction = insn; entry.program = invokedProgram; - entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1, innerStep, invokedMethod.getReference())); + entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1, innerStep, invokedMethod.getReference(), + inliningInfo)); entry.depth = depth; entry.method = invokedMethod.getReference(); + entry.locationInfo = innerInliningInfo; plan.add(entry); } } @@ -441,6 +463,7 @@ public class Inlining { Program program; int depth; final List innerPlan = new ArrayList<>(); + InliningInfo locationInfo; } static class MethodUsageCounter extends AbstractInstructionReader { diff --git a/core/src/main/java/org/teavm/model/optimization/InliningInfoMerger.java b/core/src/main/java/org/teavm/model/optimization/InliningInfoMerger.java new file mode 100644 index 000000000..79bd18c2f --- /dev/null +++ b/core/src/main/java/org/teavm/model/optimization/InliningInfoMerger.java @@ -0,0 +1,43 @@ +/* + * 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.model.optimization; + +import java.util.HashMap; +import java.util.Map; +import org.teavm.model.InliningInfo; + +class InliningInfoMerger { + private InliningInfo parent; + Map inliningInfoCache = new HashMap<>(); + + InliningInfoMerger(InliningInfo parent) { + this.parent = parent; + } + + InliningInfo merge(InliningInfo inliningInfo) { + if (inliningInfo == null) { + return parent; + } + + InliningInfo result = inliningInfoCache.get(inliningInfo); + if (result == null) { + result = new InliningInfo(inliningInfo.getMethod(), inliningInfo.getFileName(), inliningInfo.getLine(), + merge(inliningInfo.getParent())); + inliningInfoCache.put(inliningInfo, result); + } + return result; + } +} diff --git a/core/src/main/java/org/teavm/model/text/InstructionStringifier.java b/core/src/main/java/org/teavm/model/text/InstructionStringifier.java index 5001a670d..0dc8b3fd5 100644 --- a/core/src/main/java/org/teavm/model/text/InstructionStringifier.java +++ b/core/src/main/java/org/teavm/model/text/InstructionStringifier.java @@ -158,6 +158,11 @@ class InstructionStringifier implements InstructionReader { } private InstructionStringifier escapeIdentifierIfNeeded(String s) { + escapeIdentifierIfNeeded(sb, s); + return this; + } + + static void escapeIdentifierIfNeeded(StringBuilder sb, String s) { boolean needsEscaping = false; if (s.isEmpty()) { needsEscaping = true; @@ -176,8 +181,6 @@ class InstructionStringifier implements InstructionReader { } else { sb.append(s); } - - return this; } @Override diff --git a/core/src/main/java/org/teavm/model/text/ListingBuilder.java b/core/src/main/java/org/teavm/model/text/ListingBuilder.java index 79521afd4..deccea182 100644 --- a/core/src/main/java/org/teavm/model/text/ListingBuilder.java +++ b/core/src/main/java/org/teavm/model/text/ListingBuilder.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Objects; import org.teavm.model.BasicBlockReader; import org.teavm.model.IncomingReader; +import org.teavm.model.InliningInfo; import org.teavm.model.InstructionIterator; import org.teavm.model.PhiReader; import org.teavm.model.ProgramReader; @@ -76,13 +77,24 @@ public class ListingBuilder { if (!Objects.equals(location, stringifier.getLocation())) { location = stringifier.getLocation(); sb.append(prefix).append(" at "); - if (location == null) { + if (location == null || location.isEmpty()) { sb.append("unknown location"); } else { sb.append("'"); InstructionStringifier.escapeStringLiteral(location.getFileName(), sb); sb.append("' " + location.getLine()); } + if (location != null) { + InliningInfo inliningInfo = location.getInlining(); + while (inliningInfo != null) { + sb.append(" at "); + InstructionStringifier.escapeIdentifierIfNeeded(sb, inliningInfo.getMethod().toString()); + sb.append(" '"); + InstructionStringifier.escapeStringLiteral(inliningInfo.getFileName(), sb); + sb.append("' " + inliningInfo.getLine()); + inliningInfo = inliningInfo.getParent(); + } + } sb.append('\n'); } sb.append(prefix).append(" ").append(insnSb).append("\n"); diff --git a/core/src/main/java/org/teavm/parsing/ProgramParser.java b/core/src/main/java/org/teavm/parsing/ProgramParser.java index 4cf178a7c..85da08443 100644 --- a/core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -332,7 +332,7 @@ public class ProgramParser { BasicBlock basicBlock = null; Map accumulatedDebugNames = new HashMap<>(); Integer lastLineNumber = null; - TextLocation lastLocation = null; + TextLocation lastLocation = TextLocation.EMPTY; for (int i = 0; i < basicBlocks.size(); ++i) { BasicBlock newBasicBlock = basicBlocks.get(i); if (newBasicBlock != null) { diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java index 59459088c..b80e1260f 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestConfiguration.java @@ -37,7 +37,7 @@ interface TeaVMTestConfiguration { @Override public void apply(TeaVM vm) { - vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); + vm.setOptimizationLevel(TeaVMOptimizationLevel.FULL); } @Override