Version 2.16.0-24.0.dev

Merge commit '3d1f6a48b79e2cc5a4ee9d2ea3a7a385a3b33eaa' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 98ff8fb..686137d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -1783,6 +1783,11 @@
   void _removePotentiallyAffectedLibraries(String path) {
     var affected = <FileState>{};
     _fsState.collectAffected(path, affected);
+
+    for (var file in affected) {
+      file.invalidateLibraryCycle();
+    }
+
     _libraryContext?.elementFactory.removeLibraries(
       affected.map((e) => e.uriStr).toSet(),
     );
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 433d6e3..a275ba2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -352,6 +352,11 @@
     _libraryCycle = cycle;
   }
 
+  void invalidateLibraryCycle() {
+    _libraryCycle?.invalidate();
+    _libraryCycle = null;
+  }
+
   /// Return a new parsed unresolved [CompilationUnit].
   CompilationUnitImpl parse([AnalysisErrorListener? errorListener]) {
     errorListener ??= AnalysisErrorListener.NULL_LISTENER;
@@ -765,6 +770,20 @@
   /// Collected files that transitively reference a file with the [path].
   /// These files are potentially affected by the change.
   void collectAffected(String path, Set<FileState> affected) {
+    // TODO(scheglov) This should not be necessary.
+    // We use affected files to remove library elements, and we can only get
+    // these library elements when we link or load them, using library cycles.
+    // And we get library cycles by asking `directReferencedFiles`.
+    while (true) {
+      final knownFiles = this.knownFiles.toList();
+      for (var file in knownFiles.toList()) {
+        file.directReferencedFiles;
+      }
+      if (this.knownFiles.length == knownFiles.length) {
+        break;
+      }
+    }
+
     collectAffected(FileState file) {
       if (affected.add(file)) {
         for (var other in file.referencingFiles) {
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index ae463a8..d8985b7 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -93,7 +93,7 @@
   int get enabledConstructorTearOffLowerings => ConstructorTearOffLowering.all;
 
   @override
-  List<String> get extraRequiredLibraries => _requiredLibraries[name]!;
+  List<String> get extraRequiredLibraries => requiredLibraries[name]!;
 
   @override
   List<String> get extraIndexedLibraries => const [
@@ -222,49 +222,86 @@
 
 // TODO(sigmund): this "extraRequiredLibraries" needs to be removed...
 // compile-platform should just specify which libraries to compile instead.
-const _requiredLibraries = <String, List<String>>{
+const requiredLibraries = <String, List<String>>{
   'dart2js': [
+    'dart:_async_await_error_codes',
     'dart:_dart2js_runtime_metrics',
     'dart:_foreign_helper',
+    'dart:_http',
     'dart:_interceptors',
     'dart:_internal',
+    'dart:_js',
     'dart:_js_annotations',
     'dart:_js_embedded_names',
     'dart:_js_helper',
     'dart:_js_names',
+    'dart:_js_primitives',
     'dart:_late_helper',
+    'dart:_metadata',
     'dart:_native_typed_data',
+    'dart:_recipe_syntax',
+    'dart:_rti',
     'dart:async',
     'dart:collection',
+    'dart:convert',
+    'dart:developer',
     'dart:html',
     'dart:html_common',
     'dart:indexed_db',
     'dart:io',
+    'dart:isolate',
     'dart:js',
     'dart:js_util',
+    'dart:math',
     'dart:svg',
+    'dart:typed_data',
     'dart:web_audio',
     'dart:web_gl',
   ],
   'dart2js_server': [
+    'dart:_async_await_error_codes',
     'dart:_dart2js_runtime_metrics',
     'dart:_foreign_helper',
+    'dart:_http',
     'dart:_interceptors',
     'dart:_internal',
+    'dart:_js',
     'dart:_js_annotations',
     'dart:_js_embedded_names',
     'dart:_js_helper',
     'dart:_js_names',
+    'dart:_js_primitives',
     'dart:_late_helper',
     'dart:_native_typed_data',
+    'dart:_recipe_syntax',
+    'dart:_rti',
     'dart:async',
     'dart:collection',
+    'dart:convert',
+    'dart:developer',
     'dart:io',
+    'dart:isolate',
     'dart:js',
     'dart:js_util',
+    'dart:math',
+    'dart:typed_data',
   ]
 };
 
+/// Extends the Dart2jsTarget to transform outlines to meet the requirements
+/// of summaries in bazel and package-build.
+class Dart2jsSummaryTarget extends Dart2jsTarget with SummaryMixin {
+  @override
+  final List<Uri> sources;
+
+  @override
+  final bool excludeNonSources;
+
+  Dart2jsSummaryTarget(String name, this.sources, this.excludeNonSources,
+      TargetFlags targetFlags)
+      : super(name, targetFlags);
+}
+
 class Dart2jsConstantsBackend extends ConstantsBackend {
   @override
   final bool supportsUnevaluatedConstants;
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 6195548..1593208 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -9,6 +9,7 @@
 import 'dart:async';
 
 import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/kernel/dart2js_target.dart';
 import 'package:front_end/src/compute_platform_binaries_location.dart'
     show computePlatformBinariesLocation;
 import 'package:modular_test/src/io_pipeline.dart';
@@ -32,36 +33,26 @@
   _packageConfig = await loadPackageConfigUri(packageConfigUri);
   await _resolveScripts();
   await Future.wait([
+    // TODO(joshualitt): Factor modular steps into a library so we can test
+    // modular analysis alongside the existing pipeline.
     runSuite(
         sdkRoot.resolve('tests/modular/'),
         'tests/modular',
         _options,
         IOPipeline([
-          SourceToDillStep(),
-          ModularAnalysisStep(),
-          ComputeClosedWorldStep(useModularAnalysis: true),
+          OutlineDillCompilationStep(),
+          FullDillCompilationStep(),
+          ComputeClosedWorldStep(useModularAnalysis: false),
           GlobalAnalysisStep(),
           Dart2jsCodegenStep(codeId0),
           Dart2jsCodegenStep(codeId1),
           Dart2jsEmissionStep(),
           RunD8(),
         ], cacheSharedModules: true)),
-    runSuite(
-        sdkRoot.resolve('tests/modular/'),
-        'tests/modular',
-        _options,
-        IOPipeline([
-          SourceToDillStep(),
-          ComputeClosedWorldStep(useModularAnalysis: false),
-          LegacyGlobalAnalysisStep(),
-          LegacyDart2jsCodegenStep(codeId0),
-          LegacyDart2jsCodegenStep(codeId1),
-          LegacyDart2jsEmissionStep(),
-          RunD8(),
-        ], cacheSharedModules: true))
   ]);
 }
 
+const dillSummaryId = DataId("sdill");
 const dillId = DataId("dill");
 const modularUpdatedDillId = DataId("mdill");
 const modularDataId = DataId("mdata");
@@ -86,27 +77,21 @@
   return '{${fields.join(',')}}';
 }
 
-// Step that compiles sources in a module to a .dill file.
-class SourceToDillStep implements IOModularStep {
-  @override
-  List<DataId> get resultData => const [dillId];
+abstract class CFEStep implements IOModularStep {
+  final String stepName;
+
+  CFEStep(this.stepName);
 
   @override
   bool get needsSources => true;
 
   @override
-  List<DataId> get dependencyDataNeeded => const [dillId];
-
-  @override
-  List<DataId> get moduleDataNeeded => const [];
-
-  @override
   bool get onlyOnMain => false;
 
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    if (_options.verbose) print("\nstep: source-to-dill on $module");
+    if (_options.verbose) print("\nstep: $stepName on $module");
 
     // We use non file-URI schemes for representeing source locations in a
     // root-agnostic way. This allows us to refer to file across modules and
@@ -181,44 +166,45 @@
         .writeAsString('$packagesContents');
 
     List<String> sources;
-    List<String> extraArgs;
+    List<String> extraArgs = ['--packages-file', '$rootScheme:/.packages'];
     if (module.isSdk) {
       // When no flags are passed, we can skip compilation and reuse the
       // platform.dill created by build.py.
       if (flags.isEmpty) {
         var platform = computePlatformBinariesLocation()
             .resolve("dart2js_platform_unsound.dill");
-        var destination = root.resolveUri(toUri(module, dillId));
+        var destination = root.resolveUri(toUri(module, outputData));
         if (_options.verbose) {
           print('command:\ncp $platform $destination');
         }
         await File.fromUri(platform).copy(destination.toFilePath());
         return;
       }
-      sources = ['dart:core'];
-      extraArgs = ['--libraries-file', '$rootScheme:///sdk/lib/libraries.json'];
+      sources = requiredLibraries['dart2js'] + ['dart:core'];
+      extraArgs += [
+        '--libraries-file',
+        '$rootScheme:///sdk/lib/libraries.json'
+      ];
       assert(transitiveDependencies.isEmpty);
     } else {
       sources = module.sources.map(sourceToImportUri).toList();
-      extraArgs = ['--packages-file', '$rootScheme:/.packages'];
     }
 
     // TODO(joshualitt): Ensure the kernel worker has some way to specify
     // --no-sound-null-safety
     List<String> args = [
       _kernelWorkerScript,
-      '--no-summary-only',
-      '--target',
-      'dart2js',
+      ...stepArguments,
+      '--exclude-non-sources',
       '--multi-root',
       '$root',
       '--multi-root-scheme',
       rootScheme,
       ...extraArgs,
       '--output',
-      '${toUri(module, dillId)}',
+      '${toUri(module, outputData)}',
       ...(transitiveDependencies
-          .expand((m) => ['--input-linked', '${toUri(m, dillId)}'])),
+          .expand((m) => ['--input-summary', '${toUri(m, inputData)}'])),
       ...(sources.expand((String uri) => ['--source', uri])),
       ...(flags.expand((String flag) => ['--enable-experiment', flag])),
     ];
@@ -228,12 +214,73 @@
     _checkExitCode(result, this, module);
   }
 
+  List<String> get stepArguments;
+
+  DataId get inputData;
+
+  DataId get outputData;
+
   @override
   void notifyCached(Module module) {
-    if (_options.verbose) print("\ncached step: source-to-dill on $module");
+    if (_options.verbose) print("\ncached step: $stepName on $module");
   }
 }
 
+// Step that compiles sources in a module to a summary .dill file.
+class OutlineDillCompilationStep extends CFEStep {
+  @override
+  List<DataId> get resultData => const [dillSummaryId];
+
+  @override
+  bool get needsSources => true;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [];
+
+  @override
+  List<String> get stepArguments =>
+      ['--target', 'dart2js_summary', '--summary-only'];
+
+  @override
+  DataId get inputData => dillSummaryId;
+
+  @override
+  DataId get outputData => dillSummaryId;
+
+  OutlineDillCompilationStep() : super('outline-dill-compilation');
+}
+
+// Step that compiles sources in a module to a .dill file.
+class FullDillCompilationStep extends CFEStep {
+  @override
+  List<DataId> get resultData => const [dillId];
+
+  @override
+  bool get needsSources => true;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [];
+
+  // TODO(joshualitt): we need a --no-summary argument to cfe.
+  @override
+  List<String> get stepArguments =>
+      ['--target', 'dart2js', '--no-summary-only'];
+
+  @override
+  DataId get inputData => dillSummaryId;
+
+  @override
+  DataId get outputData => dillId;
+
+  FullDillCompilationStep() : super('full-dill-compilation');
+}
+
 class ModularAnalysisStep implements IOModularStep {
   @override
   List<DataId> get resultData => const [modularDataId, modularUpdatedDillId];
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 5746a68..99bfae9 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -329,6 +329,7 @@
 destination
 destinations
 destroy
+destructive
 deterministic
 dev
 device
@@ -620,6 +621,7 @@
 int64
 int8
 integrate
+intends
 intentionally
 interested
 interim
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index a75573e..9b60711 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -62,6 +62,7 @@
         'flutter',
         'flutter_runner',
         'dart2js',
+        'dart2js_summary',
         'ddc',
       ],
       help: 'Build kernel for the vm, flutter, flutter_runner, dart2js or ddc')
@@ -174,6 +175,14 @@
             'error: --summary-only not supported for the dart2js target');
       }
       break;
+    case 'dart2js_summary':
+      target = new Dart2jsSummaryTarget(
+          'dart2js', sources, excludeNonSources, targetFlags);
+      if (!summaryOnly) {
+        out.writeln(
+            'error: --no-summary-only not supported for the dart2js summary target');
+      }
+      break;
     case 'ddc':
       // TODO(jakemac):If `generateKernel` changes to return a summary
       // component, process the component instead.
@@ -358,47 +367,13 @@
   }
 }
 
-/// Extends the DevCompilerTarget to transform outlines to meet the requirements
-/// of summaries in bazel and package-build.
-///
-/// Build systems like package-build may provide the same input file twice to
-/// the summary worker, but only intends to have it in one output summary.  The
-/// convention is that if it is listed as a source, it is intended to be part of
-/// the output, if the source file was loaded as a dependency, then it was
-/// already included in a different summary.  The transformation below ensures
-/// that the output summary doesn't include those implicit inputs.
-///
-/// Note: this transformation is destructive and is only intended to be used
-/// when generating summaries.
-class DevCompilerSummaryTarget extends DevCompilerTarget {
+class DevCompilerSummaryTarget extends DevCompilerTarget with SummaryMixin {
   final List<Uri> sources;
   final bool excludeNonSources;
 
   DevCompilerSummaryTarget(
       this.sources, this.excludeNonSources, TargetFlags targetFlags)
       : super(targetFlags);
-
-  @override
-  void performOutlineTransformations(Component component) {
-    super.performOutlineTransformations(component);
-    if (!excludeNonSources) return;
-
-    List<Library> libraries = new List.from(component.libraries);
-    component.libraries.clear();
-    Set<Uri> include = sources.toSet();
-    for (var lib in libraries) {
-      if (include.contains(lib.importUri)) {
-        component.libraries.add(lib);
-      } else {
-        // Excluding the library also means that their canonical names will not
-        // be computed as part of serialization, so we need to do that
-        // preemtively here to avoid errors when serializing references to
-        // elements of these libraries.
-        component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
-        lib.computeCanonicalNames();
-      }
-    }
-  }
 }
 
 Uri toUri(String uriString) {
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 1cb1b2c..14f30a3 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -918,3 +918,44 @@
 
   TestTargetWrapper(Target target, this.flags) : super(target);
 }
+
+/// Extends a Target to transform outlines to meet the requirements
+/// of summaries in bazel and package-build.
+///
+/// Build systems like package-build may provide the same input file twice to
+/// the summary worker, but only intends to have it in one output summary.  The
+/// convention is that if it is listed as a source, it is intended to be part of
+/// the output, if the source file was loaded as a dependency, then it was
+/// already included in a different summary.  The transformation below ensures
+/// that the output summary doesn't include those implicit inputs.
+///
+/// Note: this transformation is destructive and is only intended to be used
+/// when generating summaries.
+mixin SummaryMixin on Target {
+  List<Uri> get sources;
+  bool get excludeNonSources;
+
+  @override
+  void performOutlineTransformations(Component component) {
+    super.performOutlineTransformations(component);
+    if (!excludeNonSources) return;
+
+    List<Library> libraries = new List.from(component.libraries);
+    component.libraries.clear();
+    Set<Uri> include = sources.toSet();
+    for (Library library in libraries) {
+      if (include.contains(library.importUri)) {
+        component.libraries.add(library);
+      } else {
+        // Excluding the library also means that their canonical names will not
+        // be computed as part of serialization, so we need to do that
+        // preemptively here to avoid errors when serializing references to
+        // elements of these libraries.
+        component.root
+            .getChildFromUri(library.importUri)
+            .bindTo(library.reference);
+        library.computeCanonicalNames();
+      }
+    }
+  }
+}
diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc
index ccd8144..d560dab 100644
--- a/runtime/bin/run_vm_tests.cc
+++ b/runtime/bin/run_vm_tests.cc
@@ -384,7 +384,8 @@
       dart::bin::DartUtils::ReadFile, dart::bin::DartUtils::WriteFile,
       dart::bin::DartUtils::CloseFile, /*entropy_source=*/nullptr,
       /*get_service_assets=*/nullptr, start_kernel_isolate,
-      /*code_observer=*/nullptr);
+      /*code_observer=*/nullptr, /*post_task=*/nullptr,
+      /*post_task_data*/ nullptr);
   if (error != nullptr) {
     Syslog::PrintErr("Failed to initialize VM: %s\n", error);
     free(error);
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index b62c1a7..5d189df 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -831,7 +831,7 @@
  * The current version of the Dart_InitializeFlags. Should be incremented every
  * time Dart_InitializeFlags changes in a binary incompatible way.
  */
-#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000004)
+#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000005)
 
 /** Forward declaration */
 struct Dart_CodeObserver;
@@ -857,6 +857,50 @@
   Dart_OnNewCodeCallback on_new_code;
 } Dart_CodeObserver;
 
+typedef struct _Dart_Task* Dart_Task;
+typedef enum {
+  Dart_TaskPriority_Default,
+} Dart_TaskPriority;
+typedef struct {
+  /**
+   * Placeholder.
+   */
+  Dart_TaskPriority priority;
+  /**
+   * Time after which the task should run according to the clock of
+   * Dart_TimelineGetMicros.
+   */
+  int64_t time_point;
+} Dart_TaskData;
+/**
+ * Callback provided by the embedder that is used by the VM to eventually run
+ * various tasks. If no callback is provided, these tasks will run on a
+ * VM-internal thread pool. This callback allows the embedder to make its own
+ * choices around the scheduling of these tasks: when they run, how many threads
+ * are servicing these tasks, the priorities of said threads, etc.
+ * The callback can be invoked as early as during the Dart_Initialize call.
+ *
+ * \param post_task_data
+ *     The data provided to Dart_InitializeParams.post_task_data.
+ * \param task
+ *     A task that should eventually be passed to Dart_RunTask.
+ * \param task_data
+ *     Hints about when the task should run.
+ */
+typedef void (*Dart_PostTaskCallback)(void* post_task_data,
+                                      Dart_Task task,
+                                      Dart_TaskData task_data);
+
+/**
+ * Runs a task given to the Dart_PostTaskCallback. Must not be called
+ * synchronously in response to any callback from the VM. In particular, must
+ * not be called synchronously by the implemention of a Dart native function
+ * or Dart_Post_TaskCallback.
+ *
+ * Requires there to be no current isolate or isolate group.
+ */
+DART_EXPORT void Dart_RunTask(Dart_Task task);
+
 /**
  * Describes how to initialize the VM. Used with Dart_Initialize.
  *
@@ -884,6 +928,8 @@
  *    See Dart_GetVMServiceAssetsArchive.
  * \param code_observer An external code observer callback function.
  *    The observer can be invoked as early as during the Dart_Initialize() call.
+ * \param post_task A task scheduling callback function.
+ *    See Dart_PostTaskCallback.
  */
 typedef struct {
   int32_t version;
@@ -903,6 +949,8 @@
   Dart_GetVMServiceAssetsArchive get_service_assets;
   bool start_kernel_isolate;
   Dart_CodeObserver* code_observer;
+  Dart_PostTaskCallback post_task;
+  void* post_task_data;
 } Dart_InitializeParams;
 
 /**
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 2dbd8d3..7425dd6 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -69,6 +69,8 @@
 Dart_FileCloseCallback Dart::file_close_callback_ = NULL;
 Dart_EntropySource Dart::entropy_source_callback_ = NULL;
 Dart_GCEventCallback Dart::gc_event_callback_ = nullptr;
+Dart_PostTaskCallback Dart::post_task_callback_ = nullptr;
+void* Dart::post_task_data_ = nullptr;
 
 // Structure for managing read-only global handles allocation used for
 // creating global read-only handles that are pre created and initialized
@@ -259,7 +261,9 @@
                      Dart_EntropySource entropy_source,
                      Dart_GetVMServiceAssetsArchive get_service_assets,
                      bool start_kernel_isolate,
-                     Dart_CodeObserver* observer) {
+                     Dart_CodeObserver* observer,
+                     Dart_PostTaskCallback post_task,
+                     void* post_task_data) {
   CheckOffsets();
 
   if (!Flags::Initialized()) {
@@ -294,6 +298,8 @@
   set_thread_exit_callback(thread_exit);
   SetFileCallbacks(file_open, file_read, file_write, file_close);
   set_entropy_source_callback(entropy_source);
+  set_post_task_callback(post_task);
+  set_post_task_data(post_task_data);
   OS::Init();
   NOT_IN_PRODUCT(CodeObservers::Init());
   if (observer != nullptr) {
@@ -531,18 +537,21 @@
                  Dart_EntropySource entropy_source,
                  Dart_GetVMServiceAssetsArchive get_service_assets,
                  bool start_kernel_isolate,
-                 Dart_CodeObserver* observer) {
+                 Dart_CodeObserver* observer,
+                 Dart_PostTaskCallback post_task,
+                 void* post_task_data) {
   if (!init_state_.SetInitializing()) {
     return Utils::StrDup(
         "Bad VM initialization state, "
         "already initialized or "
         "multiple threads initializing the VM.");
   }
-  char* retval = DartInit(vm_isolate_snapshot, instructions_snapshot,
-                          create_group, initialize_isolate, shutdown, cleanup,
-                          cleanup_group, thread_exit, file_open, file_read,
-                          file_write, file_close, entropy_source,
-                          get_service_assets, start_kernel_isolate, observer);
+  char* retval =
+      DartInit(vm_isolate_snapshot, instructions_snapshot, create_group,
+               initialize_isolate, shutdown, cleanup, cleanup_group,
+               thread_exit, file_open, file_read, file_write, file_close,
+               entropy_source, get_service_assets, start_kernel_isolate,
+               observer, post_task, post_task_data);
   if (retval != NULL) {
     init_state_.ResetInitializing();
     return retval;
@@ -823,6 +832,8 @@
   Service::SetEmbedderStreamCallbacks(NULL, NULL);
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   VirtualMemory::Cleanup();
+  post_task_callback_ = nullptr;
+  post_task_data_ = nullptr;
   return NULL;
 }
 
diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h
index 9d0cdf1..a8c4a56 100644
--- a/runtime/vm/dart.h
+++ b/runtime/vm/dart.h
@@ -44,7 +44,9 @@
                     Dart_EntropySource entropy_source,
                     Dart_GetVMServiceAssetsArchive get_service_assets,
                     bool start_kernel_isolate,
-                    Dart_CodeObserver* observer);
+                    Dart_CodeObserver* observer,
+                    Dart_PostTaskCallback post_task,
+                    void* post_task_data);
 
   // Returns null if cleanup succeeds, otherwise returns an error message
   // (caller owns error message and has to free it).
@@ -123,6 +125,14 @@
   static void set_thread_exit_callback(Dart_ThreadExitCallback cback) {
     thread_exit_callback_ = cback;
   }
+  static Dart_PostTaskCallback post_task_callback() {
+    return post_task_callback_;
+  }
+  static void set_post_task_callback(Dart_PostTaskCallback cback) {
+    post_task_callback_ = cback;
+  }
+  static void* post_task_data() { return post_task_data_; }
+  static void set_post_task_data(void* data) { post_task_data_ = data; }
   static void SetFileCallbacks(Dart_FileOpenCallback file_open,
                                Dart_FileReadCallback file_read,
                                Dart_FileWriteCallback file_write,
@@ -174,7 +184,9 @@
                         Dart_EntropySource entropy_source,
                         Dart_GetVMServiceAssetsArchive get_service_assets,
                         bool start_kernel_isolate,
-                        Dart_CodeObserver* observer);
+                        Dart_CodeObserver* observer,
+                        Dart_PostTaskCallback post_task,
+                        void* post_task_data);
 
   static constexpr const char* kVmIsolateName = "vm-isolate";
 
@@ -194,6 +206,8 @@
   static Dart_FileCloseCallback file_close_callback_;
   static Dart_EntropySource entropy_source_callback_;
   static Dart_GCEventCallback gc_event_callback_;
+  static Dart_PostTaskCallback post_task_callback_;
+  static void* post_task_data_;
 };
 
 }  // namespace dart
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 082476f..fe7dcc4 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1211,14 +1211,14 @@
         "Invalid Dart_InitializeParams version.");
   }
 
-  return Dart::Init(params->vm_snapshot_data, params->vm_snapshot_instructions,
-                    params->create_group, params->initialize_isolate,
-                    params->shutdown_isolate, params->cleanup_isolate,
-                    params->cleanup_group, params->thread_exit,
-                    params->file_open, params->file_read, params->file_write,
-                    params->file_close, params->entropy_source,
-                    params->get_service_assets, params->start_kernel_isolate,
-                    params->code_observer);
+  return Dart::Init(
+      params->vm_snapshot_data, params->vm_snapshot_instructions,
+      params->create_group, params->initialize_isolate,
+      params->shutdown_isolate, params->cleanup_isolate, params->cleanup_group,
+      params->thread_exit, params->file_open, params->file_read,
+      params->file_write, params->file_close, params->entropy_source,
+      params->get_service_assets, params->start_kernel_isolate,
+      params->code_observer, params->post_task, params->post_task_data);
 }
 
 DART_EXPORT char* Dart_Cleanup() {
@@ -2063,6 +2063,16 @@
   return true;
 }
 
+DART_EXPORT void Dart_RunTask(Dart_Task task) {
+  Thread* T = Thread::Current();
+  Isolate* I = T == nullptr ? nullptr : T->isolate();
+  CHECK_NO_ISOLATE(I);
+  API_TIMELINE_BEGIN_END(T);
+  ThreadPool::Task* task_impl = reinterpret_cast<ThreadPool::Task*>(task);
+  task_impl->Run();
+  delete task_impl;
+}
+
 DART_EXPORT Dart_Handle Dart_HandleMessage() {
   Thread* T = Thread::Current();
   Isolate* I = T->isolate();
diff --git a/runtime/vm/thread_pool.cc b/runtime/vm/thread_pool.cc
index 93df2ff..f450555 100644
--- a/runtime/vm/thread_pool.cc
+++ b/runtime/vm/thread_pool.cc
@@ -82,6 +82,22 @@
 }
 
 bool ThreadPool::RunImpl(std::unique_ptr<Task> task) {
+  Dart_PostTaskCallback post_task = Dart::post_task_callback();
+  if (post_task != nullptr) {
+    {
+      MonitorLocker ml(&pool_monitor_);
+      if (shutting_down_) {
+        return false;
+      }
+    }
+    Dart_TaskData data;
+    data.priority = Dart_TaskPriority_Default;
+    data.time_point = 0;
+    post_task(Dart::post_task_data(),
+              reinterpret_cast<Dart_Task>(task.release()), data);
+    return true;
+  }
+
   Worker* new_worker = nullptr;
   {
     MonitorLocker ml(&pool_monitor_);
diff --git a/tools/VERSION b/tools/VERSION
index cd8bf2f..f0a8718 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 16
 PATCH 0
-PRERELEASE 23
+PRERELEASE 24
 PRERELEASE_PATCH 0
\ No newline at end of file