[dart2js] Use DeferredStatement in existing Finalizers.

Change-Id: Iadc4b2d6f1fa0bebb57ac25012571078b9bfcdb4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195189
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/string_reference.dart b/pkg/compiler/lib/src/js_backend/string_reference.dart
index b8c41aa..05e1b8f 100644
--- a/pkg/compiler/lib/src/js_backend/string_reference.dart
+++ b/pkg/compiler/lib/src/js_backend/string_reference.dart
@@ -145,58 +145,55 @@
   Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
 }
 
-/// A [StringReferenceResource] is a deferred JavaScript expression determined
+/// A [StringReferenceResource] is a deferred JavaScript statement determined
 /// by the finalization of string references. It is the injection point for data
 /// or code to support string references. For example, if the
 /// [StringReferenceFinalizer] decides that a string should be referred to via a
 /// variable, the [StringReferenceResource] would be set to code that declares
 /// and initializes the variable.
-class StringReferenceResource extends js.DeferredExpression
+class StringReferenceResource extends js.DeferredStatement
     implements js.AstContainer {
-  js.Expression _value;
+  js.Statement _statement;
 
   @override
   final js.JavaScriptNodeSourceInformation sourceInformation;
 
   StringReferenceResource() : sourceInformation = null;
-  StringReferenceResource._(this._value, this.sourceInformation);
+  StringReferenceResource._(this._statement, this.sourceInformation);
 
-  set value(js.Expression value) {
-    assert(!isFinalized && value != null);
-    _value = value;
+  set statement(js.Statement statement) {
+    assert(!isFinalized && statement != null);
+    _statement = statement;
   }
 
   @override
-  js.Expression get value {
+  js.Statement get statement {
     assert(isFinalized, 'StringReferenceResource is unassigned');
-    return _value;
+    return _statement;
   }
 
   @override
-  int get precedenceLevel => value.precedenceLevel;
-
-  @override
-  bool get isFinalized => _value != null;
+  bool get isFinalized => _statement != null;
 
   @override
   StringReferenceResource withSourceInformation(
       js.JavaScriptNodeSourceInformation newSourceInformation) {
     if (newSourceInformation == sourceInformation) return this;
     if (newSourceInformation == null) return this;
-    return StringReferenceResource._(_value, newSourceInformation);
+    return StringReferenceResource._(_statement, newSourceInformation);
   }
 
   @override
-  Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
+  Iterable<js.Node> get containedNodes => isFinalized ? [_statement] : const [];
 
   @override
   void visitChildren<T>(js.NodeVisitor<T> visitor) {
-    _value?.accept<T>(visitor);
+    _statement?.accept<T>(visitor);
   }
 
   @override
   void visitChildren1<R, A>(js.NodeVisitor1<R, A> visitor, A arg) {
-    _value?.accept1<R, A>(visitor, arg);
+    _statement?.accept1<R, A>(visitor, arg);
   }
 }
 
@@ -287,14 +284,11 @@
     }
 
     if (properties.isEmpty) {
-      // We don't have a deferred statement sequence. "0;" is the smallest we
-      // can do with an expression statement.
-      // TODO(sra): Add deferred expression statement sequences.
-      _resource.value = js.js('0');
+      _resource.statement = js.Block.empty();
     } else {
       js.Expression initializer =
           js.ObjectInitializer(properties, isOneLiner: false);
-      _resource.value = js.js(
+      _resource.statement = js.js.statement(
           r'var # = #', [js.VariableDeclaration(holderLocalName), initializer]);
     }
   }
@@ -432,7 +426,14 @@
   void visitDeferredExpression(js.DeferredExpression node) {
     if (node is StringReference) {
       _finalizer.registerStringReference(node);
-    } else if (node is StringReferenceResource) {
+    } else {
+      visitNode(node);
+    }
+  }
+
+  @override
+  void visitDeferredStatement(js.DeferredStatement node) {
+    if (node is StringReferenceResource) {
       _finalizer.registerStringReferenceResource(node);
     } else {
       visitNode(node);
diff --git a/pkg/compiler/lib/src/js_backend/type_reference.dart b/pkg/compiler/lib/src/js_backend/type_reference.dart
index f33591d..4ec5745 100644
--- a/pkg/compiler/lib/src/js_backend/type_reference.dart
+++ b/pkg/compiler/lib/src/js_backend/type_reference.dart
@@ -162,58 +162,55 @@
   Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
 }
 
-/// A [TypeReferenceResource] is a deferred JavaScript expression determined by
+/// A [TypeReferenceResource] is a deferred JavaScript statement determined by
 /// the finalization of type references. It is the injection point for data or
 /// code to support type references. For example, if the
 /// [TypeReferenceFinalizer] decides that type should be referred to via a
 /// variable, the [TypeReferenceResource] would be set to code that declares and
 /// initializes the variable.
-class TypeReferenceResource extends js.DeferredExpression
+class TypeReferenceResource extends js.DeferredStatement
     implements js.AstContainer {
-  js.Expression _value;
+  js.Statement _statement;
 
   @override
   final js.JavaScriptNodeSourceInformation sourceInformation;
 
   TypeReferenceResource() : sourceInformation = null;
-  TypeReferenceResource._(this._value, this.sourceInformation);
+  TypeReferenceResource._(this._statement, this.sourceInformation);
 
-  set value(js.Expression value) {
-    assert(!isFinalized && value != null);
-    _value = value;
+  set statement(js.Statement statement) {
+    assert(!isFinalized && statement != null);
+    _statement = statement;
   }
 
   @override
-  js.Expression get value {
+  js.Statement get statement {
     assert(isFinalized, 'TypeReferenceResource is unassigned');
-    return _value;
+    return _statement;
   }
 
   @override
-  bool get isFinalized => _value != null;
-
-  @override
-  int get precedenceLevel => value.precedenceLevel;
+  bool get isFinalized => _statement != null;
 
   @override
   TypeReferenceResource withSourceInformation(
       js.JavaScriptNodeSourceInformation newSourceInformation) {
     if (newSourceInformation == sourceInformation) return this;
     if (newSourceInformation == null) return this;
-    return TypeReferenceResource._(_value, newSourceInformation);
+    return TypeReferenceResource._(_statement, newSourceInformation);
   }
 
   @override
-  Iterable<js.Node> get containedNodes => isFinalized ? [_value] : const [];
+  Iterable<js.Node> get containedNodes => isFinalized ? [_statement] : const [];
 
   @override
   void visitChildren<T>(js.NodeVisitor<T> visitor) {
-    _value?.accept<T>(visitor);
+    _statement?.accept<T>(visitor);
   }
 
   @override
   void visitChildren1<R, A>(js.NodeVisitor1<R, A> visitor, A arg) {
-    _value?.accept1<R, A>(visitor, arg);
+    _statement?.accept1<R, A>(visitor, arg);
   }
 }
 
@@ -322,10 +319,7 @@
     }
 
     if (properties.isEmpty) {
-      // We don't have a deferred statement sequence. "0;" is the smallest we
-      // can do with an expression statement.
-      // TODO(sra): Add deferred expression statement sequences.
-      _resource.value = js.js('0');
+      _resource.statement = js.Block.empty();
     } else {
       js.Expression initializer =
           js.ObjectInitializer(properties, isOneLiner: false);
@@ -335,7 +329,7 @@
             [js.VariableDeclaration(helperLocal), helperAccess, initializer]);
         initializer = js.js('#()', js.Parentheses(function));
       }
-      _resource.value = js.js(r'var # = #',
+      _resource.statement = js.js.statement(r'var # = #',
           [js.VariableDeclaration(typesHolderLocalName), initializer]);
     }
   }
@@ -551,7 +545,14 @@
   void visitDeferredExpression(js.DeferredExpression node) {
     if (node is TypeReference) {
       _finalizer._registerTypeReference(node);
-    } else if (node is TypeReferenceResource) {
+    } else {
+      visitNode(node);
+    }
+  }
+
+  @override
+  void visitDeferredStatement(js.DeferredStatement node) {
+    if (node is TypeReferenceResource) {
       _finalizer._registerTypeReferenceResource(node);
     } else {
       visitNode(node);
diff --git a/pkg/compiler/test/codegen/string_reference_test.dart b/pkg/compiler/test/codegen/string_reference_test.dart
index f7e20d2..27d64d6 100644
--- a/pkg/compiler/test/codegen/string_reference_test.dart
+++ b/pkg/compiler/test/codegen/string_reference_test.dart
@@ -11,6 +11,7 @@
     show StringReference, StringReferenceResource, StringReferenceFinalizerImpl;
 
 import 'package:compiler/src/js/js.dart' show prettyPrint;
+import 'package:js_ast/js_ast.dart' as js;
 
 void test(List<String> strings, String expected, {bool minified: false}) {
   var finalizer =
@@ -24,7 +25,8 @@
   finalizer.registerStringReferenceResource(resource);
   finalizer.finalize();
 
-  Expect.equals(expected.trim(), prettyPrint(resource).trim());
+  // Wrap the resource in a block, as they would print in actual code.
+  Expect.equals(expected.trim(), prettyPrint(js.Block([resource])).trim());
 }
 
 extension on List<String> {
@@ -37,20 +39,26 @@
 
 void main() {
   // No strings yields an empty pool.
-  test([], '0');
+  test([], r'''
+{
+}''');
 
   // Single occurrence strings are not pooled.
   test(
     ['Yellow', 'Blue', 'Crimson'],
-    '0',
+    r'''
+{
+}''',
   );
 
   // Repeated strings that are long enough are pooled.
   test(
     ['Yellow', 'Blue', 'Blue', 'Crimson', 'Crimson'],
     r'''
-var string$ = {
-  Crimso: "Crimson"
+{
+  var string$ = {
+    Crimso: "Crimson"
+  };
 }''',
   );
 
@@ -65,11 +73,13 @@
   test(
     greets * 2,
     r'''
-var string$ = {
-  Great_: "Great work!",
-  GreetiA: "Greetings Alice",
-  GreetiBH: "Greetings Bob Henry",
-  GreetiBS: "Greetings Bob Smith"
+{
+  var string$ = {
+    Great_: "Great work!",
+    GreetiA: "Greetings Alice",
+    GreetiBH: "Greetings Bob Henry",
+    GreetiBS: "Greetings Bob Smith"
+  };
 }''',
   );
 
@@ -77,9 +87,11 @@
   test(
     ['xylograph', '!pingpong'] * 2,
     r'''
-var string$ = {
-  _pingp: "!pingpong",
-  xylogr: "xylograph"
+{
+  var string$ = {
+    _pingp: "!pingpong",
+    xylogr: "xylograph"
+  };
 }''',
   );
 
@@ -94,20 +106,24 @@
   test(
     strings1,
     r'''
-var string$ = {
-  a_x21pin: "a !pingpong",
-  a_x25per: "a %percent",
-  a_x78ylo: "a xylograph"
+{
+  var string$ = {
+    a_x21pin: "a !pingpong",
+    a_x25per: "a %percent",
+    a_x78ylo: "a xylograph"
+  };
 }''',
   );
 
   // Minified version keeps the strings in the same order as unminified, and
   // tries to allocate the same minified name.
   const minified1 = r'''
-var string$ = {
-  l: "a !pingpong",
-  o: "a %percent",
-  n: "a xylograph"
+{
+  var string$ = {
+    l: "a !pingpong",
+    o: "a %percent",
+    n: "a xylograph"
+  };
 }''';
 
   final strings2 = [