diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index 6db5d2c522..201694a292 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java @@ -1000,6 +1000,8 @@ private void optimizeJsLoop(Collection toInline) throws InterruptedExcep // Save the stats to print out after optimizers finish. allOptimizerStats.add(stats); + AstDumper.maybeDumpAST(jsProgram); + optimizeJsEvent.end(); if ((optimizationLevel < OptionOptimize.OPTIMIZE_LEVEL_MAX && counter > optimizationLevel) || !stats.didChange()) { diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java index 956a67832b..cea938408f 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java +++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java @@ -54,12 +54,12 @@ public void setSourceInfo(SourceInfo info) { public abstract void traverse(JVisitor visitor, Context ctx); // Causes source generation to delegate to the one visitor - // toSource should be customized in ToStringGenerationVisitor, do not remove final. + // toSource should be customized in SourceGenerationVisitor, do not remove final. public final String toSource() { DefaultTextOutput out = new DefaultTextOutput(false); SourceGenerationVisitor v = new SourceGenerationVisitor(out); v.accept(this); - return out.toString(); + return out.toString().trim(); } // Causes source generation to delegate to the one visitor. diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java index 6d250c3229..3c07165ff3 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java @@ -16,22 +16,42 @@ package com.google.gwt.dev.jjs.impl; +import com.google.gwt.dev.jjs.HasSourceInfo; +import com.google.gwt.dev.jjs.ast.Context; +import com.google.gwt.dev.jjs.ast.JClassType; +import com.google.gwt.dev.jjs.ast.JInterfaceType; +import com.google.gwt.dev.jjs.ast.JMethod; +import com.google.gwt.dev.jjs.ast.JNode; import com.google.gwt.dev.jjs.ast.JProgram; -import com.google.gwt.dev.util.AbstractTextOutput; -import com.google.gwt.dev.util.TextOutput; +import com.google.gwt.dev.jjs.ast.JVisitor; +import com.google.gwt.dev.js.JsSourceGenerationVisitor; +import com.google.gwt.dev.js.ast.JsBlock; +import com.google.gwt.dev.js.ast.JsContext; +import com.google.gwt.dev.js.ast.JsNode; +import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.js.ast.JsSuperVisitor; +import com.google.gwt.dev.js.ast.JsVisitor; +import com.google.gwt.dev.util.PrintWriterTextOutput; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; /** - * A simple utility to dump a JProgram to a temp file, which can be called - * sequentially during a compilation/optimization run, so intermediate steps can - * be compared. - * - * It uses the system property "gwt.jjs.dumpAst" to determine the name (or - * prefix) of the file to dump the AST to. + * A simple utility to dump a JProgram or JsProgram to a temp file, which can be called + * sequentially during a compilation/optimization run, so intermediate steps can be compared. + *

+ * Uses the system property {@code gwt.jjs.dumpAst} to determine the name (or prefix) of the file + * to dump the AST to. Output can be filtered to specific source files with the system property + * {@code gwt.jjs.dumpAst.filter}, which is a comma-separated list of source file names, optionally + * suffixed with {@code :{startLine}-{endLine}} to only include lines in that range. Those lines + * will then be printed, regardless of which method/type they are migrated to during the course of + * compilation. * + *

* TODO(jbrosenberg): Add proper logging and/or exception handling for the * potential IOException that might occur when writing the file. */ @@ -40,13 +60,19 @@ public class AstDumper { private static int autoVersionNumber = 0; /** - * Appends a new version of the AST at the end of the file, each time it's - * called. + * Appends a new version of the Java AST at the end of the file, each time its called. */ public static void maybeDumpAST(JProgram jprogram) { maybeDumpAST(jprogram, null, true); } + /** + * Appends a new version of the JS AST to the end of the file each time its called. + */ + public static void maybeDumpAST(JsProgram jsProgram) { + maybeDumpAST(jsProgram, null, true); + } + /** * Writes the AST to the file with a versioned extension, using an * auto-incrementing version number (starting from 1), each time it's called. @@ -80,25 +106,243 @@ public static void maybeDumpAST(JProgram jprogram, String versionString) { private static void maybeDumpAST(JProgram jprogram, String fileExtension, boolean append) { String dumpFile = System.getProperty("gwt.jjs.dumpAst"); - if (dumpFile != null) { - if (fileExtension != null) { - dumpFile += fileExtension; + if (dumpFile == null) { + return; + } + final Set files = getFilter(); + try (PrintWriterTextOutput out = createWriter(dumpFile, fileExtension, append)) { + JVisitor v = new SourceGenerationVisitor(out); + if (files != null) { + v = new JFilteredAstVisitor(v, files); + } + + v.accept(jprogram); + } catch (Exception e) { + System.out.println("Could not dump AST"); + e.printStackTrace(); + } + } + + private static void maybeDumpAST(JsProgram jsProgram, String fileExtension, boolean append) { + String dumpFile = System.getProperty("gwt.jjs.dumpAst"); + if (dumpFile == null) { + return; + } + final Set files = getFilter(); + try (PrintWriterTextOutput out = createWriter(dumpFile, fileExtension, append)) { + JsVisitor v = new JsSourceGenerationVisitor(out); + if (files != null) { + v = new JsFilteredAstVisitor(v, files); + } + + v.accept(jsProgram); + } catch (Exception e) { + System.out.println("Could not dump AST"); + e.printStackTrace(); + } + } + + private static Set getFilter() { + String dumpFilter = System.getProperty("gwt.jjs.dumpAst.filter"); + final Set files; + if (dumpFilter != null) { + files = Arrays.stream(dumpFilter.split(",")) + .map(String::trim) + .map(FilterRange::new) + .collect(Collectors.toSet()); + } else { + files = null; + } + return files; + } + + private static PrintWriterTextOutput createWriter(String dumpFile, String fileExtension, boolean append) throws IOException { + if (fileExtension != null) { + dumpFile += fileExtension; + } + final FileOutputStream os = new FileOutputStream(dumpFile, append); + final PrintWriter pw = new PrintWriter(os); + return new PrintWriterTextOutput(pw, false); + } + + private static class FilterRange { + private final String fileName; + private final int startLine; + private final int endLine; + + private FilterRange(String directive) { + if (directive.contains(":")) { + String[] parts = directive.split(":"); + this.fileName = parts[0]; + String[] lineParts = parts[1].split("-"); + this.startLine = Integer.parseInt(lineParts[0]); + if (lineParts.length > 1) { + this.endLine = Integer.parseInt(lineParts[1]); + } else { + this.endLine = this.startLine; + } + } else { + this.fileName = directive; + this.startLine = 0; + this.endLine = Integer.MAX_VALUE; + } + } + + public boolean matches(HasSourceInfo node) { + String nodeFile = node.getSourceInfo().getFileName(); + int nodeLine = node.getSourceInfo().getStartLine(); + return nodeFile.equals(fileName) && nodeLine >= startLine && nodeLine <= endLine; + } + } + + /** + * Filters the AST by type, method to determine what the delegate can see, by checking if any + * code is present from the requested files. Any class/interface that needs to be written will be + * visited directly (only via visit/endVisit, not accept/traverse), and only after at least one + * member is found that needs to be visited, and members will be then be handled by accept() to + * limit the type to only those members which match the specified filter. + */ + private static class JFilteredAstVisitor extends JVisitor { + private final JVisitor delegate; + private final Set sourceFiles; + + private JClassType currentClass = null; + private JInterfaceType currentInterface = null; + private boolean visitedCurrentType = false; + private boolean shouldVisitCurrentMethod = false; + + private JFilteredAstVisitor(JVisitor delegate, Set sourceFiles) { + this.delegate = delegate; + this.sourceFiles = sourceFiles; + } + + @Override + public boolean visit(JNode x, Context ctx) { + shouldVisitCurrentMethod |= sourceFiles.stream().anyMatch(s -> s.matches(x)); + + // If we're already looking at this method, no need to keep checking + return !shouldVisitCurrentMethod; + } + + @Override + public boolean visit(JInterfaceType x, Context ctx) { + currentInterface = x; + visitedCurrentType = false; + return true; + } + + @Override + public void endVisit(JInterfaceType x, Context ctx) { + if (visitedCurrentType) { + delegate.endVisit(x, ctx); + } + currentInterface = null; + } + + @Override + public boolean visit(JClassType x, Context ctx) { + currentClass = x; + visitedCurrentType = false; + return true; + } + + @Override + public void endVisit(JClassType x, Context ctx) { + if (visitedCurrentType) { + delegate.endVisit(x, ctx); + } + currentClass = null; + } + + @Override + public boolean visit(JMethod x, Context ctx) { + // Reset for each method + shouldVisitCurrentMethod = sourceFiles.stream().anyMatch(s -> s.matches(x)); + + // If we're already looking at this method, no need to keep checking + return !shouldVisitCurrentMethod; + } + + @Override + public void endVisit(JMethod x, Context ctx) { + if (shouldVisitCurrentMethod) { + if (!visitedCurrentType) { + if (currentClass != null) { + delegate.visit(currentClass, ctx); + } else { + assert currentInterface != null; + delegate.visit(currentInterface, ctx); + } + visitedCurrentType = true; + } + + delegate.accept(x); + shouldVisitCurrentMethod = false; + } + } + + @Override + public boolean visit(JProgram x, Context ctx) { + // As SourceGenerationVisitor visits all types (not just ones that are reference-only), + // we need to as well. + for (int i = 0; i < x.getDeclaredTypes().size(); i++) { + accept(x.getDeclaredTypes().get(i)); } - try { - FileOutputStream os = new FileOutputStream(dumpFile, append); - final PrintWriter pw = new PrintWriter(os); - TextOutput out = new AbstractTextOutput(false) { - { - setPrintWriter(pw); + return false; + } + } + + /** + * Filters the AST by top-level node to determine what the delegate can see, by checking if any + * code is present from the requested files. Top level nodes are usually functions are class + * declaration code. + */ + private static class JsFilteredAstVisitor extends JsSuperVisitor { + private final JsVisitor delegate; + private final Set sourceFiles; + + private boolean shouldVisitCurrentTopLevelStatement = false; + + + public JsFilteredAstVisitor(JsVisitor delegate, Set sourceFiles) { + this.delegate = delegate; + this.sourceFiles = sourceFiles; + } + + @Override + public boolean visit(JsNode x, JsContext ctx) { + return test(x); + } + + /** + * Tests if we should print the node and its surrounding top-level statement. Returns true + * if we should continue visiting children, false to skip them (because we already know we + * will print contents). + */ + private boolean test(JsNode node) { + shouldVisitCurrentTopLevelStatement |= sourceFiles.stream().anyMatch(s -> s.matches(node)); + + return !shouldVisitCurrentTopLevelStatement; + } + + @Override + public boolean visit(JsBlock x, JsContext ctx) { + if (x.isGlobalBlock()) { + // Iterate children directly, so we can track what is top-level or not + for (JsNode child : x.getStatements()) { + accept(child); + + // If any part of that node should be visited, pass to the delegate and reset + if (shouldVisitCurrentTopLevelStatement) { + delegate.accept(child); + + // Reset after each top-level statement + shouldVisitCurrentTopLevelStatement = false; } - }; - SourceGenerationVisitor v = new SourceGenerationVisitor(out); - v.accept(jprogram); - pw.close(); - } catch (IOException e) { - System.out.println("Could not dump AST"); - e.printStackTrace(); + } + return false; } + return test(x); } } } diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java index 7b353199d9..e408a9efde 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java @@ -18,11 +18,13 @@ import com.google.gwt.dev.jjs.ast.Context; import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JDeclaredType; +import com.google.gwt.dev.jjs.ast.JEnumType; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JInterfaceType; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JMethodBody; import com.google.gwt.dev.jjs.ast.JProgram; +import com.google.gwt.dev.jjs.ast.JRecordType; import com.google.gwt.dev.util.TextOutput; /** @@ -30,16 +32,9 @@ * relatively short toString() results, for easy viewing in a debugger. This * subclass delves into the bodies of classes, interfaces, and methods to * produce the whole source tree. - * - * The goal is not to generate the input source tree. Rather, the goal is to - * produce a set of classes that can be pasted into an enclosing class and - * compiled with a standard Java compiler. In practice, there are cases that - * require hand-editing to actually get a full compilation, due to Java's - * built-in reliance on particular built-in types. - * - * Known to be broken: Our generated String, Class, and Throwable are not - * compatible with the real ones, which breaks string literals, class literals, - * try/catch/throw, and overrides of Object methods. + *

+ * The goal is not to generate the input source tree, or even properly compilable sources. + * Instead, this provides a way to log the AST in a human-readable way. */ public class SourceGenerationVisitor extends ToStringGenerationVisitor { @@ -51,7 +46,14 @@ public SourceGenerationVisitor(TextOutput textOutput) { public boolean visit(JClassType x, Context ctx) { printAbstractFlag(x); printFinalFlag(x); - print(CHARS_CLASS); + if (x instanceof JEnumType) { + print(CHARS_ENUM); + } else if (x instanceof JRecordType) { + print(CHARS_RECORD); + } else { + print(CHARS_CLASS); + } + printTypeName(x); space(); if (x.getSuperClass() != null) { print(CHARS_EXTENDS); @@ -71,26 +73,40 @@ public boolean visit(JClassType x, Context ctx) { } openBlock(); - for (JField field : x.getFields()) { - accept(field); - newline(); - newline(); - } - for (JMethod method : x.getMethods()) { - if (JProgram.isClinit(method)) { - // Suppress empty clinit. - JMethodBody body = (JMethodBody) method.getBody(); - if (body.getBlock().getStatements().isEmpty()) { - continue; - } + return true; + } + + @Override + public void endVisit(JDeclaredType x, Context ctx) { + closeBlock(); + + newline(); + newline(); + } + + @Override + public void endVisit(JField x, Context ctx) { + semi(); + newline(); + newline(); + } + + @Override + public boolean visit(JMethod x, Context ctx) { + if (JProgram.isClinit(x)) { + // Suppress empty clinit. + JMethodBody body = (JMethodBody) x.getBody(); + if (body.getBlock().getStatements().isEmpty()) { + return false; } - accept(method); - newline(); - newline(); } + return super.visit(x, ctx); + } - closeBlock(); - return false; + @Override + public void endVisit(JMethod x, Context ctx) { + newline(); + newline(); } @Override @@ -112,26 +128,7 @@ public boolean visit(JInterfaceType x, Context ctx) { openBlock(); - for (JField field : x.getFields()) { - accept(field); - newline(); - newline(); - } - for (JMethod method : x.getMethods()) { - if (JProgram.isClinit(method)) { - // Suppress empty clinit. - JMethodBody body = (JMethodBody) method.getBody(); - if (body.getBlock().getStatements().isEmpty()) { - continue; - } - } - accept(method); - newline(); - newline(); - } - - closeBlock(); - return false; + return true; } @Override @@ -139,8 +136,6 @@ public boolean visit(JProgram x, Context ctx) { for (int i = 0; i < x.getDeclaredTypes().size(); ++i) { JDeclaredType type = x.getDeclaredTypes().get(i); accept(type); - newline(); - newline(); } return false; } diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java index 3edf170491..51b4ade037 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java @@ -15,6 +15,7 @@ */ package com.google.gwt.dev.jjs.impl; +import com.google.gwt.dev.jjs.ast.AccessModifier; import com.google.gwt.dev.jjs.ast.CanBeAbstract; import com.google.gwt.dev.jjs.ast.CanBeFinal; import com.google.gwt.dev.jjs.ast.CanBeStatic; @@ -34,6 +35,7 @@ import com.google.gwt.dev.jjs.ast.JCastOperation; import com.google.gwt.dev.jjs.ast.JCharLiteral; import com.google.gwt.dev.jjs.ast.JClassLiteral; +import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JConditional; import com.google.gwt.dev.jjs.ast.JConstructor; import com.google.gwt.dev.jjs.ast.JContinueStatement; @@ -81,6 +83,7 @@ import com.google.gwt.dev.jjs.ast.JThrowStatement; import com.google.gwt.dev.jjs.ast.JTryStatement; import com.google.gwt.dev.jjs.ast.JType; +import com.google.gwt.dev.jjs.ast.JUnsafeTypeCoercion; import com.google.gwt.dev.jjs.ast.JWhileStatement; import com.google.gwt.dev.jjs.ast.JYieldStatement; import com.google.gwt.dev.jjs.ast.js.JDebuggerStatement; @@ -114,6 +117,7 @@ public class ToStringGenerationVisitor extends TextOutputVisitor { protected static final char[] CHARS_DO = "do".toCharArray(); protected static final char[] CHARS_DOTCLASS = ".class".toCharArray(); protected static final char[] CHARS_ELSE = "else".toCharArray(); + protected static final char[] CHARS_ENUM = "enum ".toCharArray(); protected static final char[] CHARS_EXTENDS = "extends ".toCharArray(); protected static final char[] CHARS_FALSE = "false".toCharArray(); protected static final char[] CHARS_FINAL = "final ".toCharArray(); @@ -131,6 +135,7 @@ public class ToStringGenerationVisitor extends TextOutputVisitor { protected static final char[] CHARS_PRIVATE = "private ".toCharArray(); protected static final char[] CHARS_PROTECTED = "protected ".toCharArray(); protected static final char[] CHARS_PUBLIC = "public ".toCharArray(); + protected static final char[] CHARS_RECORD = "record ".toCharArray(); protected static final char[] CHARS_RETURN = "return".toCharArray(); protected static final char[] CHARS_RUNTIMETYPEREFERENCE = " JRuntimeTypeReference ".toCharArray(); @@ -141,8 +146,10 @@ public class ToStringGenerationVisitor extends TextOutputVisitor { protected static final char[] CHARS_SWITCH = "switch ".toCharArray(); protected static final char[] CHARS_THIS = "this".toCharArray(); protected static final char[] CHARS_THROW = "throw".toCharArray(); + protected static final char[] CHARS_THROWS = "throws ".toCharArray(); protected static final char[] CHARS_TRUE = "true".toCharArray(); protected static final char[] CHARS_TRY = "try ".toCharArray(); + protected static final char[] CHARS_UNCHECKED_CAST = "(/* @unchecked cast to ".toCharArray(); protected static final char[] CHARS_WHILE = "while ".toCharArray(); protected static final char[] CHARS_YIELD = "yield ".toCharArray(); @@ -283,6 +290,17 @@ public boolean visit(JCastOperation x, Context ctx) { return false; } + @Override + public boolean visit(JUnsafeTypeCoercion x, Context ctx) { + lparen(); + print(CHARS_UNCHECKED_CAST); + printType(x); + space(); + accept(x.getExpression()); + rparen(); + return false; + } + @Override public boolean visit(JCastMap x, Context ctx) { print('['); @@ -363,11 +381,7 @@ public boolean visit(JConditional x, Context ctx) { @Override public boolean visit(JConstructor x, Context ctx) { // Modifiers - if (x.isPrivate()) { - print(CHARS_PRIVATE); - } else { - print(CHARS_PUBLIC); - } + printAccess(x.getAccess()); printName(x); // Parameters @@ -401,11 +415,12 @@ public boolean visit(JDebuggerStatement x, Context ctx) { @Override public boolean visit(JDeclarationStatement x, Context ctx) { - if (!suppressType) { - accept(x.getVariableRef().getTarget()); - } else { - accept(x.getVariableRef()); + if (!(x.getVariableRef().getTarget() instanceof JField)) { + printFinalFlag(x.getVariableRef().getTarget()); + printType(x.getVariableRef().getTarget()); + space(); } + printName(x.getVariableRef().getTarget()); JExpression initializer = x.getInitializer(); if (initializer != null) { print(" = "); @@ -1024,7 +1039,9 @@ protected void printChar(char c) { print("\\\\"); break; default: - if (Character.isISOControl(c)) { + if (' ' <= c && c <= '~') { + print(c); + } else { print("\\u"); if (c < 0x1000) { print('0'); @@ -1038,8 +1055,6 @@ protected void printChar(char c) { print('0'); } print(Integer.toHexString(c)); - } else { - print(c); } } } @@ -1067,7 +1082,31 @@ protected void printLongLiteral(long value) { protected void printMethodHeader(JMethod x) { // Modifiers - switch (x.getAccess()) { + printAccess(x.getAccess()); + printStaticFlag(x); + printAbstractFlag(x); + printNativeFlag(x); + printFinalFlag(x); + printType(x); + space(); + printName(x); + + // Parameters + printParameterList(x); + + // Declared exceptions + if (!x.getThrownExceptions().isEmpty()) { + space(); + print(CHARS_THROWS); + for (JClassType thrownException : x.getThrownExceptions()) { + printTypeName(thrownException); + space(); + } + } + } + + private void printAccess(AccessModifier access) { + switch (access) { case PUBLIC: print(CHARS_PUBLIC); break; @@ -1080,16 +1119,6 @@ protected void printMethodHeader(JMethod x) { case DEFAULT: break; } - printStaticFlag(x); - printAbstractFlag(x); - printNativeFlag(x); - printFinalFlag(x); - printType(x); - space(); - printName(x); - - // Parameters - printParameterList(x); } protected void printName(HasName x) { @@ -1097,7 +1126,7 @@ protected void printName(HasName x) { } protected void printNativeFlag(JMethod x) { - if (x.isJsniMethod()) { + if (x.isJsniMethod() || x.isJsNative()) { print(CHARS_NATIVE); } } diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java index bbb2912809..00c21f0615 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java @@ -255,7 +255,7 @@ public void endVisit(JField x, Context ctx) { if (!x.hasInitializer() || canUninitializedValueBeObserved.apply(x)) { addAssignment(x, x.getType().getDefaultValue()); } - currentMethod = null; + assert currentMethod == null; } @Override diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsSuperVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSuperVisitor.java index 7904916738..90e9a09388 100644 --- a/dev/core/src/com/google/gwt/dev/js/ast/JsSuperVisitor.java +++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSuperVisitor.java @@ -393,7 +393,6 @@ public boolean visit(JsNew x, JsContext ctx) { return visit((JsExpression) x, ctx); } - @SuppressWarnings("unused") public boolean visit(JsNode x, JsContext ctx) { return true; } diff --git a/dev/core/src/com/google/gwt/dev/util/DefaultTextOutput.java b/dev/core/src/com/google/gwt/dev/util/DefaultTextOutput.java index e62c411878..cc51f453e5 100644 --- a/dev/core/src/com/google/gwt/dev/util/DefaultTextOutput.java +++ b/dev/core/src/com/google/gwt/dev/util/DefaultTextOutput.java @@ -34,10 +34,6 @@ public DefaultTextOutput(boolean compact) { @Override public String toString() { out.flush(); - if (sw != null) { - return sw.toString(); - } else { - return super.toString(); - } + return sw.toString(); } } diff --git a/dev/core/src/com/google/gwt/dev/util/PrintWriterTextOutput.java b/dev/core/src/com/google/gwt/dev/util/PrintWriterTextOutput.java new file mode 100644 index 0000000000..66bdfe2ad1 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/PrintWriterTextOutput.java @@ -0,0 +1,36 @@ +/* + * Copyright 2025 GWT Project Authors + * + * 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 com.google.gwt.dev.util; + +import java.io.PrintWriter; + +/** + * TextOutput implementation that writes to a provided PrintWriter. + */ +public class PrintWriterTextOutput extends AbstractTextOutput implements AutoCloseable { + private final PrintWriter out; + + public PrintWriterTextOutput(PrintWriter out, boolean compact) { + super(compact); + this.out = out; + setPrintWriter(out); + } + + @Override + public void close() throws Exception { + out.close(); + } +} diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/CompileTimeConstantsReplacerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/CompileTimeConstantsReplacerTest.java index 7bb06c1776..b3a6388327 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/CompileTimeConstantsReplacerTest.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/CompileTimeConstantsReplacerTest.java @@ -62,7 +62,7 @@ public void testReplacement_inLValue() throws Exception { result.into("int i = 1; int[] a = new int[] {1, 2, 3}; a[1] = 1;"); JDeclaredType bType = result.findClass("test.EntryPoint.B"); assertTrue("f1 not found as l-value of declaration statement", - findMethod(bType, "$clinit").getBody().toString().contains("final static int f1 = 1")); + findMethod(bType, "$clinit").getBody().toString().contains("f1 = 1")); assertTrue("f1 in condition was not replaced", findMethod(bType, "m").getBody().toString().contains("1 == 1")); assertEquals("incorrect modifiers for f1", diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/Java10AstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/Java10AstTest.java index ddb12331fd..d28c81ba9b 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/Java10AstTest.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/Java10AstTest.java @@ -71,7 +71,7 @@ public void testLocalVarType_ForLoop() throws Exception { public void testLocalVarType_EnhancedForLoopArray() throws Exception { assertEqualBlock( - "for(final String[] s$array=new String[]{},s$index=0,s$max=s$array.length;" + "for(final String[] s$array=new String[]{},int s$index=0,final int s$max=s$array.length;" + " s$index