JS: split SourceWriter into interface and implementation part

This commit is contained in:
Alexey Andreev 2023-10-31 20:58:56 +01:00
parent 23ad999bbd
commit 8024d84ed5
8 changed files with 413 additions and 341 deletions

View File

@ -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());
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
Diagnostics diagnostics, RenderingContext context) {
public Renderer(OutputSourceWriter writer, Set<MethodReference> asyncMethods,
Set<MethodReference> asyncFamilyMethods, Diagnostics diagnostics, RenderingContext context) {
this.naming = context.getNaming();
this.writer = writer;
this.classSource = context.getClassSource();

View File

@ -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<MethodReference> virtualPredicate;
private final Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
private final Map<String, Integer> stringPoolMap = new HashMap<>();
private final List<String> stringPool = new ArrayList<>();
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
private final Map<MethodReference, InjectorHolder> 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));
}

View File

@ -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<String, String> 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("<clinit>", ValueType.VOID);
private VariableNameGenerator variableNameGenerator;
private final Deque<LocationStackEntry> 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;
}
}
}

View File

@ -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);