[ddc] Compile SDK as a bundle of libraries

Cleanup temporary code paths in the stable version of compiler.dart.

Change-Id: I4b60d90345bf8653060772789f55e16fb5db60d7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385185
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>
diff --git a/pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js b/pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js
index 26dae95..564199f 100644
--- a/pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js
+++ b/pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js
@@ -754,7 +754,7 @@
       }
       if (!!configuration.jsInteropNonNullAsserts) {
         runtimeLibrary.jsInteropNonNullAsserts(
-           configuration.jsInteropNonNullAsserts);
+          configuration.jsInteropNonNullAsserts);
       }
       if (!!configuration.dynamicModuleLoader) {
         let loader = configuration.dynamicModuleLoader;
@@ -1393,6 +1393,7 @@
     // The name of the entrypoint module. Set when the application starts for
     // the first time and used during a hot restart.
     savedEntryPointLibraryName = null;
+    savedDartSdkRuntimeOptions = null;
 
     createEmptyLibrary() {
       return Object.create(null);
@@ -1425,13 +1426,11 @@
       if (currentLibrary == null) {
         currentLibrary = this.createEmptyLibrary();
         // Run the initialization logic.
-        // TODO(nshahan): Refactor so the init function does not return a value,
-        // it should modify the library object passed in.
         let initializer = this.libraryInitializers[libraryName];
         if (initializer == null) {
           throw 'Library not defined: ' + libraryName + '. Failed to initialize.';
         }
-        currentLibrary = initializer(currentLibrary);
+        initializer(currentLibrary);
         // We make the library available in the map before linking to break out
         // of cycles in library dependencies.
         // Invariant: during linking a library dependency can be read in a state
@@ -1478,13 +1477,34 @@
       });
     }
 
+    /**
+     * Forces the SDK libraries with side effects on the JavaScript side to be
+     * initialized and linked.
+     *
+     * These side effects could be required for correct Dart semantics
+     * (ex: dart:_interceptors) or observable from a carefully crafted user
+     * program (ex: dart:html). In either case, the dependencies on the side
+     * effects are not expressed through a Dart import so the libraries need
+     * to be loaded manually before the user program starts running.
+     */
+    triggerSDKLibrariesSideEffects() {
+      this.initializeAndLinkLibrary('dart:_runtime');
+      this.initializeAndLinkLibrary('dart:_interceptors');
+      this.initializeAndLinkLibrary('dart:_native_typed_data');
+      this.initializeAndLinkLibrary('dart:html');
+      this.initializeAndLinkLibrary('dart:svg');
+      this.initializeAndLinkLibrary('dart:web_audio');
+      this.initializeAndLinkLibrary('dart:web_gl');
+    }
+
     // See docs on `DartDevEmbedder.runMain`.
     runMain(entryPointLibraryName, dartSdkRuntimeOptions) {
+      this.triggerSDKLibrariesSideEffects();
       this.setDartSDKRuntimeOptions(dartSdkRuntimeOptions);
-
       console.log('Starting application from main method in: ' + entryPointLibraryName + '.');
       let entryPointLibrary = this.initializeAndLinkLibrary(entryPointLibraryName);
       this.savedEntryPointLibraryName = entryPointLibraryName;
+      this.savedDartSdkRuntimeOptions = dartSdkRuntimeOptions;
       entryPointLibrary.main();
     }
 
@@ -1582,15 +1602,22 @@
       if (!this.savedEntryPointLibraryName) {
         throw "Error: Hot restart requested before application started.";
       }
-      console.log('Hot restarting...');
-      // TODO(nshahan): Stop calling hotRestart in the SDK when the libraries
-      // have real initialization functions.
+      // TODO(nshahan): Stop calling hotRestart in the SDK when scheduled
+      // futures no longer keep lazy initialized values from the previous
+      // generation alive.
       let dart = this.importLibrary('dart:_runtime');
       dart.hotRestart();
       // Clear all libraries.
       this.libraries = Object.create(null);
+      this.triggerSDKLibrariesSideEffects();
+      this.setDartSDKRuntimeOptions(this.savedDartSdkRuntimeOptions);
       let entryPointLibrary = this.initializeAndLinkLibrary(this.savedEntryPointLibraryName);
+      // TODO(nshahan): Start sharing a single source of truth for the restart
+      // generation between the dart:_runtime and this module system.
       this.hotRestartGeneration += 1;
+      console.log('Hot restarting application from main method in: ' +
+        this.savedEntryPointLibraryName + ' (generation: ' +
+        this.hotRestartGeneration + ').');
       entryPointLibrary.main();
     }
   }
@@ -1657,7 +1684,7 @@
 
     /**
      * DDC's entrypoint for triggering a hot reload.
-     * 
+     *
      * Previous generations may continue to run until all specified files
      * have been loaded and initialized.
      *
diff --git a/pkg/dev_compiler/lib/src/compiler/module_builder.dart b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
index aaf8101..ae4d825 100644
--- a/pkg/dev_compiler/lib/src/compiler/module_builder.dart
+++ b/pkg/dev_compiler/lib/src/compiler/module_builder.dart
@@ -494,80 +494,44 @@
     return items;
   }
 
-  /// Build statements for [exports].
-  // TODO(nshahan): Delete and build the SDK libraries/module like any other.
-  static List<Statement> temporaryBuildSdkExports(
-      List<ExportDeclaration> exports) {
-    var items = <Statement>[];
-    if (exports.isNotEmpty) {
-      for (var export in exports) {
-        // Dart SDK module must export the libraries via a definition until it
-        // can be separated into individual libraries.
-        var name = export.exportedNames!.single;
-        var alias = name.asName ?? name.name!;
-        items.add(js.statement(
-            'dartDevEmbedder.defineLibrary(#, function(_) { return #; })',
-            [(export.exported as ExportClause).from, alias]));
-      }
-    }
-    return items;
-  }
-
   Program build(Program module) {
-    if (module is LibraryBundle) {
-      var body = <ModuleItem>[];
-      // Collect imports/exports/statements.
-      for (var library in module.libraries) {
-        // Handle each library separately.
-        imports.clear();
-        statements.clear();
+    if (module is! LibraryBundle) {
+      // TODO(nshahan): Delete and update the argument type when this is the
+      // only supported module format.
+      throw ArgumentError.value(
+          module,
+          '`DdcLibraryBundleBuilder` requires `LibraryBundle`s as input to '
+          '`.build()`.');
+    }
+    var body = <ModuleItem>[];
+    // Collect imports/exports/statements.
+    for (var library in module.libraries) {
+      // Handle each library separately.
+      imports.clear();
+      statements.clear();
 
-        visitProgram(library);
-        var moduleImports = _collectModuleImports(imports);
-        var importStatements = <Statement>[];
-        for (var p in moduleImports) {
-          var moduleVar = p.key;
-          var import = p.value;
-          importStatements.addAll(buildImports(moduleVar, import));
-        }
-        // Prepend import statements.
-        statements.insertAll(0, importStatements);
-        // Package the library into an initialization function.
-        var initFunction = NamedFunction(
-            loadFunctionIdentifier(library.name!),
-            js.fun("function(#) { 'use strict'; #; return #; }",
-                [library.librarySelfVar!, statements, library.librarySelfVar!]),
-            true);
-        var resultModule = js.statement('dartDevEmbedder.defineLibrary(#, #)',
-            [js.string(library.name!), initFunction]);
-        body.add(resultModule);
-      }
-      // Append all library definitions into a single file.
-      return Program([...module.header, ...body]);
-    } else {
-      // TODO(nshahan): Migrate the SDK to the LibraryBundle format.
-      // Collect imports/exports/statements.
-      visitProgram(module);
-      var importNames = <Expression>[];
+      visitProgram(library);
       var moduleImports = _collectModuleImports(imports);
       var importStatements = <Statement>[];
       for (var p in moduleImports) {
         var moduleVar = p.key;
         var import = p.value;
-        importNames.add(import.from);
         importStatements.addAll(buildImports(moduleVar, import));
       }
       // Prepend import statements.
       statements.insertAll(0, importStatements);
-      // Append export statements.
-      statements.addAll(temporaryBuildSdkExports(exports));
-      // Wrap the SDK statements into a self invoking function.
-      var moduleName = module.name!;
-      var resultModule = NamedFunction(loadFunctionIdentifier(moduleName),
-          js.fun("function() { 'use strict'; #; }", [statements]), true);
-      var moduleDef = js.statement('#()', [resultModule]);
-      return Program(<ModuleItem>[...module.header, moduleDef]);
+      // Package the library into an initialization function.
+      var initFunction = NamedFunction(
+          loadFunctionIdentifier(library.name!),
+          js.fun("function(#) { 'use strict'; #; return #; }",
+              [library.librarySelfVar!, statements, library.librarySelfVar!]),
+          true);
+      var resultModule = js.statement('dartDevEmbedder.defineLibrary(#, #)',
+          [js.string(library.name!), initFunction]);
+      body.add(resultModule);
     }
+    // Append all library definitions into a single file.
+    return Program([...module.header, ...body]);
   }
 }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index 6bce825..6cd1a2c 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -591,9 +591,11 @@
   var hierarchy = ClassHierarchy(component, coreTypes);
   var options = SharedCompilerOptions.fromSdkRequiredArguments(argResults);
 
-  var compiler = ProgramCompiler(
-      component, hierarchy, options, const {}, const {},
-      coreTypes: coreTypes);
+  var compiler = options.emitLibraryBundle
+      ? LibraryBundleCompiler(component, hierarchy, options, const {}, const {},
+          coreTypes: coreTypes)
+      : ProgramCompiler(component, hierarchy, options, const {}, const {},
+          coreTypes: coreTypes);
   var jsModule = compiler.emitModule(component);
   var outFiles = <Future>[];
 
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index ea955fe..a23f4e0 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -542,6 +542,7 @@
   /// only in incremental mode, for expression compilation only.
   @override
   js_ast.Program emitModule(Component component) {
+    assert(!_options.emitLibraryBundle);
     if (_moduleEmitted) {
       throw StateError('Can only call emitModule once.');
     }
@@ -922,33 +923,10 @@
       _emitLibraryProcedures(library);
       _emitTopLevelFields(library.fields);
     }
-    if (_options.emitLibraryBundle) {
-      // TODO(nshahan): Remove when the Dart SDK can be compiled with the
-      // `LibraryBundleCompiler`.
-      _moduleItems.add(_emitEmptyLinkMethod(
-          _jsLibraryName(library), _emitLibraryName(library)));
-    }
     _staticTypeContext.leaveLibrary(_currentLibrary!);
     _currentLibrary = null;
   }
 
-  /// Returns an empty placeholder link method for the libraries in the SDK.
-  ///
-  /// This is a temporary solution to allow the Dart SDK to act like it was
-  /// compiled as a bundle of individual libraries.
-  // TODO(nshahan): Remove when the Dart SDK can be compiled with the
-  // `LibraryBundleCompiler`.
-  js_ast.Statement _emitEmptyLinkMethod(
-      String libraryName, js_ast.Identifier libraryId) {
-    assert(_options.emitLibraryBundle && _isBuildingSdk);
-    var functionName = _emitTemporaryId('link__$libraryName');
-    return js.statement('# = #', [
-      js_ast.PropertyAccess.field(libraryId, 'link'),
-      js_ast.NamedFunction(
-          functionName, js_ast.Fun(const [], js_ast.Block(const [])))
-    ]);
-  }
-
   void _emitExports(Library library) {
     assert(_currentLibrary == null);
     _currentLibrary = library;
@@ -7974,71 +7952,38 @@
       items.add(js.statement('#.library = #', [_runtimeModule, libraryProto]));
       exports.add(js_ast.NameSpecifier(_runtimeModule));
     }
-    if (_options.emitLibraryBundle) {
-      assert(_isBuildingSdk);
-      for (var library in libraries) {
-        js_ast.Identifier libraryId;
-        if (_isSdkInternalRuntime(library)) {
-          libraryId = _runtimeModule;
-        } else if (_isDartLibrary(library, '_rti')) {
-          libraryId = _rtiLibraryId;
-        } else {
-          libraryId = js_ast.TemporaryId(_jsLibraryName(library));
-        }
-        _libraries[library] = libraryId;
-        var alias = _jsLibraryAlias(library);
-        var aliasId = alias == null ? null : js_ast.TemporaryId(alias);
-        items.add(js_ast.ExportDeclaration(js_ast.ExportClause(
-            [js_ast.NameSpecifier(libraryId, asName: aliasId)],
-            from: js.string('${library.importUri}'))));
-        // The initialization object for the runtime library is created above so
-        // it is skipped here.
-        if (_isSdkInternalRuntime(library)) continue;
-        items.add(js.statement(
-            'const # = Object.create(#.library)', [libraryId, _runtimeModule]));
+    for (var library in libraries) {
+      if (_isBuildingSdk && _isSdkInternalRuntime(library)) {
+        _libraries[library] = _runtimeModule;
+        continue;
       }
-      // dart:_runtime has a magic library that holds extension method symbols.
-      // TODO(nshahan): Could this be created with a kernel transform or just
-      // become a member in dart:_runtime?
-      items.add(js.statement('const # = Object.create(#.library)',
-          [_extensionSymbolsModule, _runtimeModule]));
-      items.add(js_ast.ExportDeclaration(js_ast.ExportClause(
-          [js_ast.NameSpecifier(_extensionSymbolsModule)],
-          from: js.string('dartx'))));
-    } else {
-      for (var library in libraries) {
-        if (_isBuildingSdk && _isSdkInternalRuntime(library)) {
-          _libraries[library] = _runtimeModule;
-          continue;
-        }
-        var libraryId = _isBuildingSdk && _isDartLibrary(library, '_rti')
-            ? _rtiLibraryId
-            : js_ast.TemporaryId(_jsLibraryName(library));
+      var libraryId = _isBuildingSdk && _isDartLibrary(library, '_rti')
+          ? _rtiLibraryId
+          : js_ast.TemporaryId(_jsLibraryName(library));
 
-        _libraries[library] = libraryId;
-        var alias = _jsLibraryAlias(library);
-        var aliasId = alias == null ? null : js_ast.TemporaryId(alias);
+      _libraries[library] = libraryId;
+      var alias = _jsLibraryAlias(library);
+      var aliasId = alias == null ? null : js_ast.TemporaryId(alias);
 
-        // TODO(vsm): Change back to `const`.
-        // See https://github.com/dart-lang/sdk/issues/40380.
-        items.add(js.statement(
-            'var # = Object.create(#.library)', [libraryId, _runtimeModule]));
-        exports.add(js_ast.NameSpecifier(libraryId, asName: aliasId));
-      }
-
-      // dart:_runtime has a magic module that holds extension method symbols.
-      // TODO(jmesserly): find a cleaner design for this.
-      if (_isBuildingSdk) {
-        var id = _extensionSymbolsModule;
-        // TODO(vsm): Change back to `const`.
-        // See https://github.com/dart-lang/sdk/issues/40380.
-        items.add(js.statement(
-            'var # = Object.create(#.library)', [id, _runtimeModule]));
-        exports.add(js_ast.NameSpecifier(id));
-      }
-      items.add(js_ast.ExportDeclaration(js_ast.ExportClause(exports)));
+      // TODO(vsm): Change back to `const`.
+      // See https://github.com/dart-lang/sdk/issues/40380.
+      items.add(js.statement(
+          'var # = Object.create(#.library)', [libraryId, _runtimeModule]));
+      exports.add(js_ast.NameSpecifier(libraryId, asName: aliasId));
     }
 
+    // dart:_runtime has a magic module that holds extension method symbols.
+    // TODO(jmesserly): find a cleaner design for this.
+    if (_isBuildingSdk) {
+      var id = _extensionSymbolsModule;
+      // TODO(vsm): Change back to `const`.
+      // See https://github.com/dart-lang/sdk/issues/40380.
+      items.add(js
+          .statement('var # = Object.create(#.library)', [id, _runtimeModule]));
+      exports.add(js_ast.NameSpecifier(id));
+    }
+    items.add(js_ast.ExportDeclaration(js_ast.ExportClause(exports)));
+
     if (_isBuildingSdk) {
       // Initialize the private name function.
       // To bootstrap the SDK, this needs to be emitted before other code.
@@ -8299,15 +8244,6 @@
   /// field of the result.
   js_ast.Program _finishModule(List<js_ast.ModuleItem> items, String moduleName,
       {List<js_ast.Comment> header = const []}) {
-    if (_options.emitLibraryBundle) {
-      assert(_isBuildingSdk);
-      // Manually add a link method for the runtime "dartx" library. It is
-      // synthetically created by DDC and doesn't have an associated kernel
-      // library node.
-      // TODO(nshahan): Remove when the Dart SDK can be compiled with the
-      // `LibraryBundleCompiler`.
-      _moduleItems.add(_emitEmptyLinkMethod('dartx', _extensionSymbolsModule));
-    }
     // TODO(jmesserly): there's probably further consolidation we can do
     // between DDC's two backends, by moving more code into this method, as the
     // code between `startModule` and `finishModule` is very similar in both.
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler_new.dart b/pkg/dev_compiler/lib/src/kernel/compiler_new.dart
index 9e70a5f..3e52d8f 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler_new.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler_new.dart
@@ -121,6 +121,7 @@
 
   @override
   js_ast.Program emitModule(Component component) {
+    assert(_options.emitLibraryBundle);
     _ticker?.logMs('Emitting library bundle');
     var compiledLibraries = <js_ast.Program>[];
     for (var library in component.libraries) {
@@ -889,14 +890,6 @@
             [_emitLibraryName(_rtiLibrary)]).toStatement());
       });
     }
-    // Visit directives (for exports)
-    _emitExports(library);
-    _ticker?.logMs('Emitted exports');
-
-    // Declare imports and extension symbols
-    _emitImportsAndExtensionSymbols(items,
-        forceExtensionSymbols: allowedNativeTest(library.importUri));
-    _ticker?.logMs('Emitted imports and extension symbols');
 
     // Insert a check that runs when loading this module to verify that the null
     // safety mode it was compiled in matches the mode used when compiling the
@@ -926,6 +919,15 @@
     _moduleItems.add(_emitLibraryLinkMethod(_currentLibrary!));
     _ticker?.logMs('Emitted library link method');
 
+    // Visit directives (for exports)
+    _emitExports(library);
+    _ticker?.logMs('Emitted exports');
+
+    // Declare imports and extension symbols
+    _emitImportsAndExtensionSymbols(items,
+        forceExtensionSymbols: allowedNativeTest(library.importUri));
+    _ticker?.logMs('Emitted imports and extension symbols');
+
     // Emit the hoisted type table cache variables
     items.addAll(_typeTable.dischargeBoundTypes());
     _ticker?.logMs('Emitted type table');
@@ -8023,12 +8025,8 @@
     for (var import in _imports.keys) {
       modules.putIfAbsent(_libraryToModule(import), () => []).add(import);
     }
-
-    String? coreModuleName;
-    if (!_libraries.containsKey(_coreLibrary)) {
-      coreModuleName = _libraryToModule(_coreLibrary);
-    }
-
+    // TODO(nshahan): Update this code and the representation of
+    // `ImportDeclaration`s when other module formats are no longer supported.
     modules.forEach((module, libraries) {
       if (!_incrementalMode || _incrementalModules.containsKey(module)) {
         var usedLibraries = _incrementalModules[module];
@@ -8060,24 +8058,11 @@
             }
           }
         }
-        if (module == coreModuleName) {
-          if (!_incrementalMode ||
-              usedLibraries!.contains(_runtimeLibraryId.name)) {
-            items.add(js_ast.ImportDeclaration(
-                from: js.string('dart:_runtime'),
-                namedImports: [js_ast.NameSpecifier(_runtimeLibraryId)]));
-          }
-          if (!_incrementalMode ||
-              usedLibraries!.contains(_extensionSymbolsLibraryId.name)) {
-            items.add(js_ast.ImportDeclaration(
-                from: js.string(_extensionSymbolHolderName),
-                namedImports: [
-                  js_ast.NameSpecifier(_extensionSymbolsLibraryId)
-                ]));
-          }
-        }
       }
     });
+    items.add(js_ast.ImportDeclaration(
+        from: js.string(_extensionSymbolHolderName),
+        namedImports: [js_ast.NameSpecifier(_extensionSymbolsLibraryId)]));
   }
 
   /// Emits extension methods into [items].