Batch 'inherit'

- Add an inheritMany method for the common case of many subclasses.
- Copy helper functions to locals to get js-minification of helpers in main part.

Change-Id: Ideff5ebe90e5ad7a3728290772c5b8bd4d6f580b
Reviewed-on: https://dart-review.googlesource.com/c/80525
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index ac8fbb6..e13bf8f 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -113,6 +113,13 @@
   }
 }
 
+// Batched version of [inherit] for multiple classes from one superclass.
+function inheritMany(sup, classes) {
+  for (var i = 0; i < classes.length; i++) {
+    inherit(classes[i], sup);
+  }
+}
+
 // Mixes in the properties of [mixin] into [cls].
 function mixin(cls, mixin) {
   copyProperties(mixin.prototype, cls.prototype);
@@ -292,7 +299,7 @@
   typesOffset = #embeddedTypes.length;
 
   // TODO(floitsch): extend natives.
-  hunk(inherit, mixin, lazy, makeConstList, convertToFastObject, installTearOff,
+  hunk(inherit, inheritMany, mixin, lazy, makeConstList, convertToFastObject, installTearOff,
        setFunctionNamesIfNecessary, updateHolder, updateTypes,
        setOrUpdateInterceptorsByTag, setOrUpdateLeafTags,
        #embeddedGlobalsObject, holders, #staticState);
@@ -317,7 +324,8 @@
   function softDef(o) {
     softDef = function(o) {};  // Replace ourselves.
     #deferredGlobal[#softId](
-        holders, #embeddedGlobalsObject, #staticState, inherit, mixin,
+        holders, #embeddedGlobalsObject, #staticState,
+        inherit, inheritMany, mixin,
         installTearOff);
     if (o != null) {
       // TODO(29574): should we do something different for Firefox?
@@ -416,7 +424,7 @@
 ///
 /// This template is used for Dart 2.
 const String deferredBoilerplateDart2 = '''
-function(inherit, mixin, lazy, makeConstList, convertToFastObject,
+function(inherit, inheritMany, mixin, lazy, makeConstList, convertToFastObject,
          installTearOff, setFunctionNamesIfNecessary, updateHolder, updateTypes,
          setOrUpdateInterceptorsByTag, setOrUpdateLeafTags,
          #embeddedGlobalsObject, holdersList, #staticState) {
@@ -466,8 +474,9 @@
 /// hierarchy, and add methods the prototypes.
 const String softDeferredBoilerplate = '''
 #deferredGlobal[#softId] =
-    function(holdersList, #embeddedGlobalsObject, #staticState, inherit, mixin,
-    installTearOff) {
+  function(holdersList, #embeddedGlobalsObject, #staticState,
+           inherit, inheritMany, mixin,
+           installTearOff) {
 
 // Installs the holders as local variables.
 #installHoldersAsLocals;
@@ -1067,15 +1076,17 @@
   js.Statement emitInheritance(Fragment fragment, {bool softDeferred = false}) {
     List<js.Statement> inheritCalls = [];
     List<js.Statement> mixinCalls = [];
+    // local caches of functions to allow minifaction of function name in call.
+    Map<String, js.Expression> locals = {};
 
-    Set<Class> classesInFragment = new Set<Class>();
+    Set<Class> classesInFragment = Set();
     for (Library library in fragment.libraries) {
       classesInFragment.addAll(library.classes
           .where(((Class cls) => cls.isSoftDeferred == softDeferred)));
     }
 
-    Map<Class, List<Class>> subclasses = <Class, List<Class>>{};
-    Set<Class> seen = new Set<Class>();
+    Map<Class, List<Class>> subclasses = {};
+    Set<Class> seen = Set();
 
     void collect(cls) {
       if (cls == null || seen.contains(cls)) return;
@@ -1096,41 +1107,43 @@
         collect(cls);
 
         if (cls.mixinClass != null) {
-          mixinCalls.add(js.js.statement('mixin(#, #)',
+          locals['_mixin'] ??= js.js('mixin');
+          mixinCalls.add(js.js.statement('_mixin(#, #)',
               [classReference(cls), classReference(cls.mixinClass)]));
         }
       }
     }
 
-    js.Expression temp = null;
     for (Class superclass in subclasses.keys) {
       List<Class> list = subclasses[superclass];
       js.Expression superclassReference = (superclass == null)
           ? new js.LiteralNull()
           : classReference(superclass);
       if (list.length == 1) {
-        inheritCalls.add(js.js.statement('inherit(#, #)',
+        locals['_inherit'] ??= js.js('inherit');
+        inheritCalls.add(js.js.statement('_inherit(#, #)',
             [classReference(list.single), superclassReference]));
       } else {
-        // Hold common superclass in temporary for sequence of calls.
-        if (temp == null) {
-          String tempName = '_';
-          temp = new js.VariableUse(tempName);
-          var declaration = new js.VariableDeclaration(tempName);
-          inheritCalls.add(
-              js.js.statement('var # = #', [declaration, superclassReference]));
-        } else {
-          inheritCalls
-              .add(js.js.statement('# = #', [temp, superclassReference]));
-        }
-        for (Class cls in list) {
-          inheritCalls.add(
-              js.js.statement('inherit(#, #)', [classReference(cls), temp]));
-        }
+        locals['_inheritMany'] ??= js.js('inheritMany');
+        var listElements = list.map(classReference).toList();
+        inheritCalls.add(js.js.statement('_inheritMany(#, #)',
+            [superclassReference, js.ArrayInitializer(listElements)]));
       }
     }
 
-    return wrapPhase('inheritance', inheritCalls.toList()..addAll(mixinCalls));
+    List<js.Statement> statements = [];
+    if (locals.isNotEmpty) {
+      List<js.VariableInitialization> initializations = [];
+      locals.forEach((local, value) {
+        initializations.add(
+            js.VariableInitialization(js.VariableDeclaration(local), value));
+      });
+      statements.add(
+          js.ExpressionStatement(js.VariableDeclarationList(initializations)));
+    }
+    statements.addAll(inheritCalls);
+    statements.addAll(mixinCalls);
+    return wrapPhase('inheritance', statements);
   }
 
   /// Emits the setup of method aliases.