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 acb976669..118924665 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -50,8 +50,8 @@ import org.teavm.backend.javascript.codegen.AliasProvider; import org.teavm.backend.javascript.codegen.DefaultAliasProvider; import org.teavm.backend.javascript.codegen.DefaultNamingStrategy; import org.teavm.backend.javascript.codegen.MinifyingAliasProvider; +import org.teavm.backend.javascript.codegen.OutputSourceWriterBuilder; import org.teavm.backend.javascript.codegen.SourceWriter; -import org.teavm.backend.javascript.codegen.SourceWriterBuilder; import org.teavm.backend.javascript.decompile.PreparedClass; import org.teavm.backend.javascript.decompile.PreparedMethod; import org.teavm.backend.javascript.intrinsics.ref.ReferenceQueueGenerator; @@ -414,14 +414,16 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { ? new MinifyingAliasProvider(topLevelNameLimit) : new DefaultAliasProvider(topLevelNameLimit); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource()); - SourceWriterBuilder builder = new SourceWriterBuilder(naming); + OutputSourceWriterBuilder builder = new OutputSourceWriterBuilder(naming); builder.setMinified(obfuscated); - SourceWriter sourceWriter = builder.build(writer); + var sourceWriter = builder.build(writer); DebugInformationEmitter debugEmitterToUse = debugEmitter; if (debugEmitterToUse == null) { debugEmitterToUse = new DummyDebugInformationEmitter(); } + sourceWriter.setDebugInformationEmitter(debugEmitterToUse); + var virtualMethodContributorContext = new VirtualMethodContributorContextImpl(classes); RenderingContext renderingContext = new RenderingContext(debugEmitterToUse, controller.getUnprocessedClassSource(), classes, @@ -454,7 +456,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { } renderer.setDebugEmitter(debugEmitter); } - renderer.getDebugEmitter().setLocationProvider(sourceWriter); for (var entry : methodInjectors.entrySet()) { renderingContext.addInjector(entry.getKey(), entry.getValue()); } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java new file mode 100644 index 000000000..88a00e66b --- /dev/null +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriter.java @@ -0,0 +1,279 @@ +/* + * Copyright 2023 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.backend.javascript.codegen; + +import java.io.IOException; +import org.teavm.debugging.information.DebugInformationEmitter; +import org.teavm.debugging.information.DummyDebugInformationEmitter; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; + +public class OutputSourceWriter extends SourceWriter implements LocationProvider { + private final Appendable innerWriter; + private int indentSize; + private final NamingStrategy naming; + private boolean lineStart; + private boolean minified; + private final int lineWidth; + private int column; + private int line; + private int offset; + private DebugInformationEmitter debugInformationEmitter = new DummyDebugInformationEmitter(); + + OutputSourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) { + this.naming = naming; + this.innerWriter = innerWriter; + this.lineWidth = lineWidth; + } + + public void setDebugInformationEmitter(DebugInformationEmitter debugInformationEmitter) { + this.debugInformationEmitter = debugInformationEmitter; + debugInformationEmitter.setLocationProvider(this); + } + + void setMinified(boolean minified) { + this.minified = minified; + } + + @Override + public SourceWriter append(char value) { + appendIndent(); + try { + innerWriter.append(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (value == '\n') { + newLine(); + } else { + column++; + offset++; + } + return this; + } + + @Override + public SourceWriter append(CharSequence csq, int start, int end) { + int last = start; + for (int i = start; i < end; ++i) { + if (csq.charAt(i) == '\n') { + appendSingleLine(csq, last, i); + newLine(); + last = i + 1; + } + } + appendSingleLine(csq, last, end); + return this; + } + + private void appendSingleLine(CharSequence csq, int start, int end) { + if (start == end) { + return; + } + appendIndent(); + column += end - start; + offset += end - start; + try { + innerWriter.append(csq, start, end); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public SourceWriter appendClass(String cls) { + return appendName(naming.getNameFor(cls)); + } + + @Override + public SourceWriter appendField(FieldReference field) { + return append(naming.getNameFor(field)); + } + + @Override + public SourceWriter appendStaticField(FieldReference field) { + return appendName(naming.getFullNameFor(field)); + } + + @Override + public SourceWriter appendMethod(MethodDescriptor method) { + return append(naming.getNameFor(method)); + } + + @Override + public SourceWriter appendMethodBody(MethodReference method) { + return appendName(naming.getFullNameFor(method)); + } + + @Override + public SourceWriter appendFunction(String name) { + return append(naming.getNameForFunction(name)); + } + + @Override + public SourceWriter appendInit(MethodReference method) { + return appendName(naming.getNameForInit(method)); + } + + @Override + public SourceWriter appendClassInit(String className) { + return appendName(naming.getNameForClassInit(className)); + } + + private SourceWriter appendName(ScopedName name) { + if (name.scoped) { + append(naming.getScopeName()).append("."); + } + append(name.value); + return this; + } + + private void appendIndent() { + if (minified) { + return; + } + if (lineStart) { + try { + for (int i = 0; i < indentSize; ++i) { + innerWriter.append(" "); + column += 4; + offset += 4; + } + lineStart = false; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public SourceWriter newLine() { + try { + innerWriter.append('\n'); + } catch (IOException e) { + throw new RuntimeException(e); + } + column = 0; + ++line; + ++offset; + lineStart = true; + return this; + } + + @Override + public SourceWriter ws() { + if (column >= lineWidth) { + newLine(); + } else { + if (!minified) { + try { + innerWriter.append(' '); + } catch (IOException e) { + throw new RuntimeException(e); + } + column++; + offset++; + } + } + return this; + } + + @Override + public SourceWriter tokenBoundary() { + if (column >= lineWidth) { + newLine(); + } + return this; + } + + @Override + public SourceWriter softNewLine() { + if (!minified) { + try { + innerWriter.append('\n'); + } catch (IOException e) { + throw new RuntimeException(e); + } + column = 0; + ++offset; + ++line; + lineStart = true; + } + return this; + } + + @Override + public SourceWriter indent() { + ++indentSize; + return this; + } + + @Override + public SourceWriter outdent() { + --indentSize; + return this; + } + + @Override + public SourceWriter emitLocation(String fileName, int line) { + debugInformationEmitter.emitLocation(fileName, line); + return this; + } + + @Override + public SourceWriter enterLocation() { + debugInformationEmitter.enterLocation(); + return this; + } + + @Override + public SourceWriter exitLocation() { + debugInformationEmitter.exitLocation(); + return this; + } + + @Override + public SourceWriter emitStatementStart() { + debugInformationEmitter.emitStatementStart(); + return this; + } + + @Override + public void emitMethod(MethodDescriptor method) { + debugInformationEmitter.emitMethod(method); + } + + @Override + public void emitClass(String className) { + debugInformationEmitter.emitClass(className); + } + + @Override + public int getLine() { + return line; + } + + @Override + public int getColumn() { + return column; + } + + @Override + public int getOffset() { + return offset; + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriterBuilder.java similarity index 82% rename from core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java rename to core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriterBuilder.java index 8ecdd12ee..dca53c50f 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriterBuilder.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/OutputSourceWriterBuilder.java @@ -15,12 +15,12 @@ */ package org.teavm.backend.javascript.codegen; -public class SourceWriterBuilder { +public class OutputSourceWriterBuilder { private NamingStrategy naming; private boolean minified; private int lineWidth = 512; - public SourceWriterBuilder(NamingStrategy naming) { + public OutputSourceWriterBuilder(NamingStrategy naming) { this.naming = naming; } @@ -36,8 +36,8 @@ public class SourceWriterBuilder { this.lineWidth = lineWidth; } - public SourceWriter build(Appendable innerWriter) { - SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth); + public OutputSourceWriter build(Appendable innerWriter) { + var writer = new OutputSourceWriter(naming, innerWriter, lineWidth); writer.setMinified(minified); return writer; } diff --git a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java index f72c5e74b..7e1d856f7 100644 --- a/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/codegen/SourceWriter.java @@ -15,33 +15,12 @@ */ package org.teavm.backend.javascript.codegen; -import java.io.IOException; import org.teavm.model.FieldReference; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; -public class SourceWriter implements Appendable, LocationProvider { - private final Appendable innerWriter; - private int indentSize; - private final NamingStrategy naming; - private boolean lineStart; - private boolean minified; - private final int lineWidth; - private int column; - private int line; - private int offset; - - SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) { - this.naming = naming; - this.innerWriter = innerWriter; - this.lineWidth = lineWidth; - } - - void setMinified(boolean minified) { - this.minified = minified; - } - +public abstract class SourceWriter implements Appendable { public SourceWriter append(String value) { append((CharSequence) value); return this; @@ -72,21 +51,7 @@ public class SourceWriter implements Appendable, LocationProvider { } @Override - public SourceWriter append(char value) { - appendIndent(); - try { - innerWriter.append(value); - } catch (IOException e) { - throw new RuntimeException(e); - } - if (value == '\n') { - newLine(); - } else { - column++; - offset++; - } - return this; - } + public abstract SourceWriter append(char value); @Override public SourceWriter append(CharSequence csq) { @@ -95,60 +60,25 @@ public class SourceWriter implements Appendable, LocationProvider { } @Override - public SourceWriter append(CharSequence csq, int start, int end) { - int last = start; - for (int i = start; i < end; ++i) { - if (csq.charAt(i) == '\n') { - appendSingleLine(csq, last, i); - newLine(); - last = i + 1; - } - } - appendSingleLine(csq, last, end); - return this; - } + public abstract SourceWriter append(CharSequence csq, int start, int end); - private void appendSingleLine(CharSequence csq, int start, int end) { - if (start == end) { - return; - } - appendIndent(); - column += end - start; - offset += end - start; - try { - innerWriter.append(csq, start, end); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public SourceWriter appendClass(String cls) { - return appendName(naming.getNameFor(cls)); - } + public abstract SourceWriter appendClass(String cls); public SourceWriter appendClass(Class cls) { return appendClass(cls.getName()); } - public SourceWriter appendField(FieldReference field) { - return append(naming.getNameFor(field)); - } + public abstract SourceWriter appendField(FieldReference field); - public SourceWriter appendStaticField(FieldReference field) { - return appendName(naming.getFullNameFor(field)); - } + public abstract SourceWriter appendStaticField(FieldReference field); - public SourceWriter appendMethod(MethodDescriptor method) { - return append(naming.getNameFor(method)); - } + public abstract SourceWriter appendMethod(MethodDescriptor method); public SourceWriter appendMethod(String name, Class... params) { - return append(naming.getNameFor(new MethodDescriptor(name, params))); + return appendMethod(new MethodDescriptor(name, params)); } - public SourceWriter appendMethodBody(MethodReference method) { - return appendName(naming.getFullNameFor(method)); - } + public abstract SourceWriter appendMethodBody(MethodReference method); public SourceWriter appendMethodBody(String className, String name, ValueType... params) { return appendMethodBody(new MethodReference(className, new MethodDescriptor(name, params))); @@ -158,122 +88,33 @@ public class SourceWriter implements Appendable, LocationProvider { return appendMethodBody(new MethodReference(cls, name, params)); } - public SourceWriter appendFunction(String name) { - return append(naming.getNameForFunction(name)); - } + public abstract SourceWriter appendFunction(String name); - public SourceWriter appendInit(MethodReference method) { - return appendName(naming.getNameForInit(method)); - } + public abstract SourceWriter appendInit(MethodReference method); - public SourceWriter appendClassInit(String className) { - return appendName(naming.getNameForClassInit(className)); - } + public abstract SourceWriter appendClassInit(String className); - private SourceWriter appendName(ScopedName name) { - if (name.scoped) { - append(naming.getScopeName()).append("."); - } - append(name.value); - return this; - } + public abstract SourceWriter newLine(); - private void appendIndent() { - if (minified) { - return; - } - if (lineStart) { - try { - for (int i = 0; i < indentSize; ++i) { - innerWriter.append(" "); - column += 4; - offset += 4; - } - lineStart = false; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } + public abstract SourceWriter ws(); - public SourceWriter newLine() { - try { - innerWriter.append('\n'); - } catch (IOException e) { - throw new RuntimeException(e); - } - column = 0; - ++line; - ++offset; - lineStart = true; - return this; - } + public abstract SourceWriter tokenBoundary(); - public SourceWriter ws() { - if (column >= lineWidth) { - newLine(); - } else { - if (!minified) { - try { - innerWriter.append(' '); - } catch (IOException e) { - throw new RuntimeException(e); - } - column++; - offset++; - } - } - return this; - } + public abstract SourceWriter softNewLine(); - public SourceWriter tokenBoundary() { - if (column >= lineWidth) { - newLine(); - } - return this; - } + public abstract SourceWriter indent(); - public SourceWriter softNewLine() { - if (!minified) { - try { - innerWriter.append('\n'); - } catch (IOException e) { - throw new RuntimeException(e); - } - column = 0; - ++offset; - ++line; - lineStart = true; - } - return this; - } + public abstract SourceWriter outdent(); - public SourceWriter indent() { - ++indentSize; - return this; - } + public abstract SourceWriter emitLocation(String fileName, int line); - public SourceWriter outdent() { - --indentSize; - return this; - } + public abstract SourceWriter enterLocation(); - public NamingStrategy getNaming() { - return naming; - } + public abstract SourceWriter exitLocation(); - @Override - public int getColumn() { - return column; - } + public abstract SourceWriter emitStatementStart(); - @Override - public int getLine() { - return line; - } + public abstract void emitMethod(MethodDescriptor method); - @Override - public int getOffset() { - return offset; - } + public abstract void emitClass(String className); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index 2936ce304..6cebd29b4 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -37,6 +37,7 @@ import org.teavm.ast.Statement; import org.teavm.ast.VariableNode; import org.teavm.backend.javascript.codegen.NamingOrderer; import org.teavm.backend.javascript.codegen.NamingStrategy; +import org.teavm.backend.javascript.codegen.OutputSourceWriter; import org.teavm.backend.javascript.codegen.ScopedName; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.decompile.PreparedClass; @@ -64,7 +65,7 @@ import org.teavm.vm.TeaVMProgressFeedback; public class Renderer implements RenderingManager { private final NamingStrategy naming; - private final SourceWriter writer; + private final OutputSourceWriter writer; private final ListableClassReaderSource classSource; private final ClassLoader classLoader; private boolean minifying; @@ -86,8 +87,8 @@ public class Renderer implements RenderingManager { private boolean longLibraryUsed; private boolean threadLibraryUsed; - public Renderer(SourceWriter writer, Set asyncMethods, Set asyncFamilyMethods, - Diagnostics diagnostics, RenderingContext context) { + public Renderer(OutputSourceWriter writer, Set asyncMethods, + Set asyncFamilyMethods, Diagnostics diagnostics, RenderingContext context) { this.naming = context.getNaming(); this.writer = writer; this.classSource = context.getClassSource(); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index e405ffd66..aecb9649c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -17,14 +17,11 @@ package org.teavm.backend.javascript.rendering; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; -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,11 +35,9 @@ 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; -import org.teavm.model.TextLocation; import org.teavm.model.ValueType; import org.teavm.model.analysis.ClassInitializerInfo; @@ -56,14 +51,12 @@ public abstract class RenderingContext { private NamingStrategy naming; private DependencyInfo dependencyInfo; private Predicate virtualPredicate; - private final Deque locationStack = new ArrayDeque<>(); private final Map stringPoolMap = new HashMap<>(); private final List stringPool = new ArrayList<>(); private final List readonlyStringPool = Collections.unmodifiableList(stringPool); private final Map injectorMap = new HashMap<>(); private boolean minifying; private ClassInitializerInfo classInitializerInfo; - private TextLocation lastEmittedLocation = TextLocation.EMPTY; private boolean strict; public RenderingContext(DebugInformationEmitter debugEmitter, @@ -129,83 +122,6 @@ public abstract class RenderingContext { return classInitializerInfo.isDynamicInitializer(className); } - public void pushLocation(TextLocation location) { - LocationStackEntry prevEntry = locationStack.peek(); - if (location != null) { - if (prevEntry == null || !location.equals(prevEntry.location)) { - emitLocation(location); - } - } else { - if (prevEntry != null) { - emitLocation(TextLocation.EMPTY); - } - } - locationStack.push(new LocationStackEntry(location)); - } - - public void popLocation() { - LocationStackEntry prevEntry = locationStack.pop(); - LocationStackEntry entry = locationStack.peek(); - if (entry != null) { - if (!entry.location.equals(prevEntry.location)) { - emitLocation(entry.location); - } - } else { - 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; } @@ -367,14 +283,6 @@ public abstract class RenderingContext { return minifying ? "$T" : "$thread"; } - private static class LocationStackEntry { - final TextLocation location; - - LocationStackEntry(TextLocation location) { - this.location = location; - } - } - public void addInjector(MethodReference method, Injector injector) { injectorMap.put(method, new InjectorHolder(injector)); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 35f537fc8..ae14a4dec 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -17,11 +17,14 @@ package org.teavm.backend.javascript.rendering; import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntIndexedContainer; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +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 org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.AssignmentStatement; @@ -68,11 +71,10 @@ import org.teavm.backend.javascript.codegen.NamingStrategy; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Injector; import org.teavm.backend.javascript.spi.InjectorContext; -import org.teavm.debugging.information.DebugInformationEmitter; -import org.teavm.debugging.information.DeferredCallSite; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; +import org.teavm.model.InliningInfo; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; @@ -88,10 +90,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { private boolean async; private boolean minifying; private Precedence precedence; - private DebugInformationEmitter debugEmitter; private NamingStrategy naming; - private DeferredCallSite lastCallSite; - private DeferredCallSite prevCallSite; private boolean end; private final Map blockIdMap = new HashMap<>(); private int currentPart; @@ -100,6 +99,8 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { private boolean longLibraryUsed; private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("", ValueType.VOID); private VariableNameGenerator variableNameGenerator; + private final Deque locationStack = new ArrayDeque<>(); + private TextLocation lastEmittedLocation = TextLocation.EMPTY; public StatementRenderer(RenderingContext context, SourceWriter writer) { this.context = context; @@ -107,7 +108,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { this.classSource = context.getClassSource(); this.minifying = context.isMinifying(); this.naming = context.getNaming(); - this.debugEmitter = context.getDebugEmitter(); variableNameGenerator = new VariableNameGenerator(minifying); } @@ -135,21 +135,88 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { this.end = end; } - private void pushLocation(TextLocation location) { - context.pushLocation(location); + public void pushLocation(TextLocation location) { + var prevEntry = locationStack.peek(); + if (location != null) { + if (prevEntry == null || !location.equals(prevEntry.location)) { + emitLocation(location); + } + } else { + if (prevEntry != null) { + emitLocation(TextLocation.EMPTY); + } + } + locationStack.push(new LocationStackEntry(location)); } - private void popLocation() { - context.popLocation(); + public void popLocation() { + var prevEntry = locationStack.pop(); + var entry = locationStack.peek(); + if (entry != null) { + if (!entry.location.equals(prevEntry.location)) { + emitLocation(entry.location); + } + } else { + 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) { + writer.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; + + writer.enterLocation(); + writer.emitClass(inlining.getMethod().getClassName()); + writer.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; + } + + writer.emitLocation(newFileName, newLineNumber); } @Override public void visit(AssignmentStatement statement) throws RenderingException { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } - prevCallSite = debugEmitter.emitCallSite(); if (statement.getLeftValue() != null) { if (statement.isAsync()) { writer.append(context.tempVarName()); @@ -161,7 +228,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { } precedence = Precedence.COMMA; statement.getRightValue().acceptVisitor(this); - debugEmitter.emitCallSite(); writer.append(";").softNewLine(); if (statement.isAsync()) { emitSuspendChecker(); @@ -185,18 +251,16 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { public void visit(ConditionalStatement statement) { boolean needClosingBracket; while (true) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getCondition().getLocation() != null) { pushLocation(statement.getCondition().getLocation()); } - prevCallSite = debugEmitter.emitCallSite(); writer.append("if").ws().append("("); precedence = Precedence.COMMA; statement.getCondition().acceptVisitor(this); if (statement.getCondition().getLocation() != null) { popLocation(); } - debugEmitter.emitCallSite(); writer.append(")"); if (isSimpleIfContent(statement.getConsequent())) { needClosingBracket = false; @@ -250,21 +314,19 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(SwitchStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getValue().getLocation() != null) { pushLocation(statement.getValue().getLocation()); } if (statement.getId() != null) { writer.append(mapBlockId(statement.getId())).append(":").ws(); } - prevCallSite = debugEmitter.emitCallSite(); writer.append("switch").ws().append("("); precedence = Precedence.min(); statement.getValue().acceptVisitor(this); if (statement.getValue().getLocation() != null) { popLocation(); } - debugEmitter.emitCallSite(); writer.append(")").ws().append("{").softNewLine().indent(); for (SwitchClause clause : statement.getClauses()) { for (int condition : clause.getConditions()) { @@ -294,17 +356,15 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(WhileStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getId() != null) { writer.append(mapBlockId(statement.getId())).append(":").ws(); } writer.append("while"); writer.ws().append("("); if (statement.getCondition() != null) { - prevCallSite = debugEmitter.emitCallSite(); precedence = Precedence.min(); statement.getCondition().acceptVisitor(this); - debugEmitter.emitCallSite(); } else { writer.append("true"); } @@ -351,7 +411,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(BreakStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } @@ -367,7 +427,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(ContinueStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } @@ -383,17 +443,15 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(ReturnStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } writer.append("return"); if (statement.getResult() != null) { writer.append(' '); - prevCallSite = debugEmitter.emitCallSite(); precedence = Precedence.min(); statement.getResult().acceptVisitor(this); - debugEmitter.emitCallSite(); } writer.append(";").softNewLine(); if (statement.getLocation() != null) { @@ -403,16 +461,14 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { @Override public void visit(ThrowStatement statement) { - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } writer.appendFunction("$rt_throw").append("("); - prevCallSite = debugEmitter.emitCallSite(); precedence = Precedence.min(); statement.getException().acceptVisitor(this); writer.append(");").softNewLine(); - debugEmitter.emitCallSite(); if (statement.getLocation() != null) { popLocation(); } @@ -428,7 +484,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (method == null) { return; } - debugEmitter.emitStatementStart(); + writer.emitStatementStart(); if (statement.getLocation() != null) { pushLocation(statement.getLocation()); } @@ -1040,16 +1096,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { } MethodReference method = expr.getMethod(); String name = naming.getNameFor(method.getDescriptor()); - DeferredCallSite callSite = prevCallSite; - boolean shouldEraseCallSite = lastCallSite == null; - if (lastCallSite == null) { - lastCallSite = callSite; - } - boolean virtual = false; switch (expr.getType()) { case STATIC: writer.appendMethodBody(method).append("("); - prevCallSite = debugEmitter.emitCallSite(); for (int i = 0; i < expr.getArguments().size(); ++i) { if (i > 0) { writer.append(",").ws(); @@ -1060,7 +1109,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; case SPECIAL: writer.appendMethodBody(method).append("("); - prevCallSite = debugEmitter.emitCallSite(); precedence = Precedence.min(); expr.getArguments().get(0).acceptVisitor(this); for (int i = 1; i < expr.getArguments().size(); ++i) { @@ -1071,7 +1119,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; case DYNAMIC: writer.append(".").append(name).append("("); - prevCallSite = debugEmitter.emitCallSite(); for (int i = 1; i < expr.getArguments().size(); ++i) { if (i > 1) { writer.append(",").ws(); @@ -1079,11 +1126,9 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { precedence = Precedence.min(); expr.getArguments().get(i).acceptVisitor(this); } - virtual = true; break; case CONSTRUCTOR: writer.appendInit(expr.getMethod()).append("("); - prevCallSite = debugEmitter.emitCallSite(); for (int i = 0; i < expr.getArguments().size(); ++i) { if (i > 0) { writer.append(",").ws(); @@ -1094,17 +1139,6 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; } writer.append(')'); - if (lastCallSite != null) { - if (virtual) { - lastCallSite.setVirtualMethod(expr.getMethod()); - } else { - lastCallSite.setStaticMethod(expr.getMethod()); - } - lastCallSite = callSite; - } - if (shouldEraseCallSite) { - lastCallSite = null; - } if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { writer.append(')'); @@ -1612,4 +1646,12 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { return context.importModule(name); } } + + private static class LocationStackEntry { + final TextLocation location; + + LocationStackEntry(TextLocation location) { + this.location = location; + } + } } diff --git a/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java b/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java index 579305192..51627d2e7 100644 --- a/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java +++ b/core/src/test/java/org/teavm/backend/javascript/rendering/AstWriterTest.java @@ -21,8 +21,8 @@ import java.io.StringReader; import org.junit.Test; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; +import org.teavm.backend.javascript.codegen.OutputSourceWriterBuilder; import org.teavm.backend.javascript.codegen.SourceWriter; -import org.teavm.backend.javascript.codegen.SourceWriterBuilder; public class AstWriterTest { private StringBuilder sb = new StringBuilder(); @@ -31,7 +31,7 @@ public class AstWriterTest { private AstWriter writerWithGlobals; public AstWriterTest() { - var builder = new SourceWriterBuilder(null); + var builder = new OutputSourceWriterBuilder(null); builder.setMinified(true); sourceWriter = builder.build(sb); writer = new AstWriter(sourceWriter, null);