From 6d347961b2bbcf62d9123f3b621c5be22863a2a9 Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Tue, 28 Oct 2025 10:58:01 -0700 Subject: [PATCH] [dart2js] Minify statement labels. Also simplify use of JavaScriptPrintingOptions. Change-Id: Icdb603edd76b71dbc4a5d913407b96e5e9589265 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452223 Reviewed-by: Mayank Patke Commit-Queue: Stephen Adams --- pkg/compiler/lib/src/js/js.dart | 9 +-- pkg/compiler/lib/src/js/js_debug.dart | 3 +- .../test/js/js_parser_statements_test.dart | 2 +- pkg/compiler/test/js/js_parser_test.dart | 2 +- .../sourcemaps/helpers/sourcemap_helper.dart | 2 +- pkg/js_ast/lib/src/printer.dart | 81 +++++++++++++++---- pkg/js_ast/test/print_helper.dart | 6 +- 7 files changed, 73 insertions(+), 32 deletions(-) diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart index 1ac621b84d6..a396ceaeb6b 100644 --- a/pkg/compiler/lib/src/js/js.dart +++ b/pkg/compiler/lib/src/js/js.dart @@ -25,13 +25,11 @@ export 'js_debug.dart'; String prettyPrint( Node node, { bool enableMinification = false, - bool allowVariableMinification = true, bool preferSemicolonToNewlineInMinifiedOutput = false, }) { // TODO(johnniwinther): Do we need all the options here? JavaScriptPrintingOptions options = JavaScriptPrintingOptions( - shouldCompressOutput: enableMinification, - minifyLocalVariables: allowVariableMinification, + minify: enableMinification, preferSemicolonToNewlineInMinifiedOutput: preferSemicolonToNewlineInMinifiedOutput, ); @@ -48,13 +46,12 @@ CodeBuffer createCodeBuffer( DumpInfoJsAstRegistry? monitor, JavaScriptAnnotationMonitor annotationMonitor = const JavaScriptAnnotationMonitor(), - bool allowVariableMinification = true, List listeners = const [], }) { + bool enableMinification = compilerOptions.enableMinification; JavaScriptPrintingOptions options = JavaScriptPrintingOptions( utf8: compilerOptions.features.writeUtf8.isEnabled, - shouldCompressOutput: compilerOptions.enableMinification, - minifyLocalVariables: allowVariableMinification, + minify: enableMinification, ); CodeBuffer outBuffer = CodeBuffer(listeners); SourceInformationProcessor sourceInformationProcessor = diff --git a/pkg/compiler/lib/src/js/js_debug.dart b/pkg/compiler/lib/src/js/js_debug.dart index b45179c11f9..09f3035556b 100644 --- a/pkg/compiler/lib/src/js/js_debug.dart +++ b/pkg/compiler/lib/src/js/js_debug.dart @@ -12,8 +12,7 @@ import 'package:kernel/text/indentation.dart' show Indentation, Tagging; /// Unparse the JavaScript [node]. String nodeToString(Node node, {bool pretty = false}) { JavaScriptPrintingOptions options = JavaScriptPrintingOptions( - shouldCompressOutput: !pretty, - preferSemicolonToNewlineInMinifiedOutput: !pretty, + minify: !pretty, ); LenientPrintingContext printingContext = LenientPrintingContext(); Printer(options, printingContext).visit(node); diff --git a/pkg/compiler/test/js/js_parser_statements_test.dart b/pkg/compiler/test/js/js_parser_statements_test.dart index d69f6a45f6c..1fc0d3f5274 100644 --- a/pkg/compiler/test/js/js_parser_statements_test.dart +++ b/pkg/compiler/test/js/js_parser_statements_test.dart @@ -9,7 +9,7 @@ import 'package:compiler/src/js/js.dart' show js; testStatement(String statement, arguments, String expect) { jsAst.Node node = js.statement(statement, arguments); - String jsText = jsAst.prettyPrint(node, allowVariableMinification: false); + String jsText = jsAst.prettyPrint(node); Expect.stringEquals( expect.trim(), jsText.trim(), diff --git a/pkg/compiler/test/js/js_parser_test.dart b/pkg/compiler/test/js/js_parser_test.dart index 0757adca72f..70aa7c0e434 100644 --- a/pkg/compiler/test/js/js_parser_test.dart +++ b/pkg/compiler/test/js/js_parser_test.dart @@ -8,7 +8,7 @@ import 'package:compiler/src/js/js.dart' show js; testExpression(String expression, [String expect = ""]) { jsAst.Node node = js(expression); - String jsText = jsAst.prettyPrint(node, allowVariableMinification: false); + String jsText = jsAst.prettyPrint(node); if (expect == "") { Expect.stringEquals(expression, jsText); } else { diff --git a/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart b/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart index c5e257bf964..67ccc09ee88 100644 --- a/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart +++ b/pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart @@ -696,7 +696,7 @@ class CodePointComputer extends TraceListener { String nodeToString(js.Node node) { js.JavaScriptPrintingOptions options = js.JavaScriptPrintingOptions( - shouldCompressOutput: true, + minify: true, preferSemicolonToNewlineInMinifiedOutput: true, ); LenientPrintingContext printingContext = LenientPrintingContext(); diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart index 35314e45c5a..61e783443e6 100644 --- a/pkg/js_ast/lib/src/printer.dart +++ b/pkg/js_ast/lib/src/printer.dart @@ -11,14 +11,16 @@ class JavaScriptPrintingOptions { final bool utf8; final bool shouldCompressOutput; final bool minifyLocalVariables; + final bool minifyStatementLabels; final bool preferSemicolonToNewlineInMinifiedOutput; const JavaScriptPrintingOptions({ this.utf8 = false, - this.shouldCompressOutput = false, - this.minifyLocalVariables = false, + bool minify = false, this.preferSemicolonToNewlineInMinifiedOutput = false, - }); + }) : shouldCompressOutput = minify, + minifyLocalVariables = minify, + minifyStatementLabels = minify; } /// An environment in which JavaScript printing is done. Provides emitting of @@ -89,6 +91,7 @@ class Printer implements NodeVisitor { final bool shouldCompressOutput; final DanglingElseVisitor danglingElseVisitor; final LocalNamer localNamer; + final _LabelNamer _labelNamer; final bool isDebugContext; int _charCount = 0; @@ -140,10 +143,10 @@ class Printer implements NodeVisitor { : isDebugContext = context.isDebugContext, shouldCompressOutput = options.shouldCompressOutput, danglingElseVisitor = DanglingElseVisitor(context), - localNamer = determineRenamer( - options.shouldCompressOutput, - options.minifyLocalVariables, - ); + localNamer = options.minifyLocalVariables + ? MinifyRenamer() + : IdentityNamer(), + _labelNamer = _LabelNamer(options.minifyStatementLabels); static LocalNamer determineRenamer( bool shouldCompressOutput, @@ -577,7 +580,7 @@ class Printer implements NodeVisitor { if (node.targetLabel == null) { outIndent('continue'); } else { - outIndent('continue ${node.targetLabel}'); + outIndent('continue ${_labelNamer.mapLabelName(node.targetLabel!)}'); } outSemicolonLn(); } @@ -587,7 +590,7 @@ class Printer implements NodeVisitor { if (node.targetLabel == null) { outIndent('break'); } else { - outIndent('break ${node.targetLabel}'); + outIndent('break ${_labelNamer.mapLabelName(node.targetLabel!)}'); } outSemicolonLn(); } @@ -724,7 +727,7 @@ class Printer implements NodeVisitor { @override void visitLabeledStatement(LabeledStatement node) { - outIndent('${node.label}:'); + outIndent('${_labelNamer.mapLabelName(node.label)}:'); blockBody(node.body, needsSeparation: false, needsNewline: true); } @@ -740,7 +743,7 @@ class Printer implements NodeVisitor { newAtStatementBegin: false, ); } - localNamer.enterScope(vars); + _enterFunctionScope(vars); out('('); visitCommaSeparated( fun.params, @@ -771,10 +774,20 @@ class Printer implements NodeVisitor { shouldIndent: false, needsNewline: false, ); - localNamer.leaveScope(); + _exitFunctionScope(); return closingPosition; } + void _enterFunctionScope(VarCollector vars) { + localNamer.enterScope(vars); + _labelNamer.enterFunction(); + } + + void _exitFunctionScope() { + _labelNamer.exitFunction(); + localNamer.leaveScope(); + } + @override void visitFunctionDeclaration(FunctionDeclaration declaration) { VarCollector vars = VarCollector(); @@ -1393,7 +1406,7 @@ class Printer implements NodeVisitor { int arrowFunctionOut(ArrowFunction fun, VarCollector vars) { // TODO: support static, get/set, async, and generators. - localNamer.enterScope(vars); + _enterFunctionScope(vars); final List params = fun.params; if (params.length == 1 && _isIdentifierParameter(params.first)) { visitNestedExpression( @@ -1438,7 +1451,7 @@ class Printer implements NodeVisitor { if (needsParens) out(')'); closingPosition = _charCount; } - localNamer.leaveScope(); + _exitFunctionScope(); return closingPosition; } @@ -1637,7 +1650,7 @@ class Printer implements NodeVisitor { int methodOut(MethodDefinition node, VarCollector vars) { // TODO: support static, get/set, async, and generators. Fun fun = node.function; - localNamer.enterScope(vars); + _enterFunctionScope(vars); out('('); visitCommaSeparated( fun.params, @@ -1652,7 +1665,7 @@ class Printer implements NodeVisitor { shouldIndent: false, needsNewline: false, ); - localNamer.leaveScope(); + _exitFunctionScope(); return closingPosition; } @@ -2139,6 +2152,42 @@ class MinifyRenamer implements LocalNamer { } } +class _LabelNamer { + final bool renameLabels; + + Map _renamings = {}; + + final List> _outerScopes = []; + + _LabelNamer(this.renameLabels); + + String mapLabelName(String name) { + if (!renameLabels) return name; + return _renamings[name] ??= _newLabelName(_renamings, name); + } + + static String _newLabelName(Map renamings, String name) { + assert(!renamings.containsKey(name)); + int index = renamings.length; + if (index < 26) return String.fromCharCode(index + 'A'.codeUnitAt(0)); + index -= 26; + if (index < 26) return String.fromCharCode(index + 'a'.codeUnitAt(0)); + index -= 26; + return 'L$index'; + } + + void enterFunction() { + if (!renameLabels) return; + _outerScopes.add(_renamings); + _renamings = {}; + } + + void exitFunction() { + if (!renameLabels) return; + _renamings = _outerScopes.removeLast(); + } +} + /// Information pertaining the enter and exit callbacks for [node]. class EnterExitNode { final EnterExitNode? parent; diff --git a/pkg/js_ast/test/print_helper.dart b/pkg/js_ast/test/print_helper.dart index 2a9603c2632..43913ece6dd 100644 --- a/pkg/js_ast/test/print_helper.dart +++ b/pkg/js_ast/test/print_helper.dart @@ -56,11 +56,7 @@ Node _test( } String prettyPrint(Node node) { - JavaScriptPrintingOptions options = JavaScriptPrintingOptions( - shouldCompressOutput: false, - minifyLocalVariables: false, - preferSemicolonToNewlineInMinifiedOutput: false, - ); + JavaScriptPrintingOptions options = JavaScriptPrintingOptions(); SimpleJavaScriptPrintingContext context = SimpleJavaScriptPrintingContext(); Printer printer = Printer(options, context); printer.visit(node);