Version 2.12.0-30.0.dev

Merge commit '82320be8d96530cdbdcbea2abd06f113afacd9ff' into 'dev'
diff --git a/DEPS b/DEPS
index 51d9353..bdb73d4 100644
--- a/DEPS
+++ b/DEPS
@@ -116,19 +116,19 @@
   "idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7",
   "intl_tag": "0.17.0-nullsafety",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
-  "json_rpc_2_rev": "8f189db8f0c299187a0e8fa959dba7e9b0254be5",
+  "json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
   "linter_tag": "0.1.124",
   "logging_rev": "9d2a7fdd05b09bc06474881152b5baaf38fd1329",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "6f89681d59541ddb1cf3a58efbdaa2304ffc3f51",
   "matcher_rev": "9cae8faa7868bf3a88a7ba45eb0bd128e66ac515",
-  "mime_tag": "0.9.7",
+  "mime_rev": "07635f7774447503248fbc6afb3911e9000a477e",
   "mockito_rev": "d39ac507483b9891165e422ec98d9fb480037c8b",
   "mustache_rev": "664737ecad027e6b96d0d1e627257efa0e46fcb1",
   "oauth2_tag": "1.6.0",
   "package_config_rev": "9c586d04bd26fef01215fd10e7ab96a3050cfa64",
   "path_rev": "62ecd5a78ffe5734d14ed0df76d20309084cd04a",
-  "pedantic_rev": "24b38df72430d7e21cb4257828580becb9a39c72",
+  "pedantic_rev": "a884ea2db943b8756cc94385990bd750aec06928",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
   "pool_rev": "eedbd5fde84f9a1a8da643b475305a81841da599",
   "protobuf_rev": "3746c8fd3f2b0147623a8e3db89c3ff4330de760",
@@ -137,7 +137,7 @@
   "resource_rev": "6b79867d0becf5395e5819a75720963b8298e9a7",
   "root_certificates_rev": "7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8",
   "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
-  "shelf_static_rev": "v0.2.8",
+  "shelf_static_rev": "a6168f162df88b0eef7e328629bf020122d5039e",
   "shelf_packages_handler_tag": "2.0.0",
   "shelf_proxy_tag": "0.1.0+7",
   "shelf_rev": "289309adc6c39aab0a63db676d550c517fc1cc2d",
@@ -368,7 +368,7 @@
   Var("dart_root") + "/third_party/pkg/matcher":
       Var("dart_git") + "matcher.git" + "@" + Var("matcher_rev"),
   Var("dart_root") + "/third_party/pkg/mime":
-      Var("dart_git") + "mime.git" + "@" + Var("mime_tag"),
+      Var("dart_git") + "mime.git" + "@" + Var("mime_rev"),
   Var("dart_root") + "/third_party/pkg/mockito":
       Var("dart_git") + "mockito.git" + "@" + Var("mockito_rev"),
   Var("dart_root") + "/third_party/pkg/mustache":
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
index dcad472..d32c54a 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_nnbd_test.dart
@@ -6,7 +6,7 @@
 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart'
     as typeSchemaElimination;
 import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
+import 'package:kernel/testing/type_parser_environment.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,13 +18,10 @@
 
 @reflectiveTest
 class TypeSchemaEliminationTest {
-  static const DartType unknownType = const UnknownType();
-
-  CoreTypes coreTypes = new _MockCoreTypes();
-
-  DartType get dynamicType => const DynamicType();
-
-  DartType get nullType => new NullType();
+  final Env env = new Env("");
+  final Map<String, DartType Function()> additionalTypes = {
+    "UNKNOWN": () => new UnknownType()
+  };
 
   DartType greatestClosure(DartType schema) {
     return typeSchemaElimination.greatestClosure(
@@ -36,116 +33,62 @@
         schema, const DynamicType(), const NeverType(Nullability.nonNullable));
   }
 
+  void testGreatest(String type, String expectedClosure) {
+    expect(
+        greatestClosure(env.parseType(type, additionalTypes: additionalTypes)),
+        env.parseType(expectedClosure, additionalTypes: additionalTypes));
+  }
+
+  void testLeast(String type, String expectedClosure) {
+    expect(leastClosure(env.parseType(type, additionalTypes: additionalTypes)),
+        env.parseType(expectedClosure, additionalTypes: additionalTypes));
+  }
+
   void test_greatestClosure_contravariant() {
-    expect(
-        greatestClosure(new FunctionType(
-                [unknownType], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '(Never) →* dynamic');
-    expect(
-        greatestClosure(new FunctionType([], dynamicType, Nullability.legacy,
-                namedParameters: [new NamedType('foo', unknownType)]))
-            .leakingDebugToString(),
-        '({foo: Never}) →* dynamic');
+    testGreatest("(UNKNOWN) ->* dynamic", "(Never) ->* dynamic");
+    testGreatest("({UNKNOWN foo}) ->* dynamic", "({Never foo}) ->* dynamic");
   }
 
   void test_greatestClosure_contravariant_contravariant() {
-    expect(
-        greatestClosure(new FunctionType([
-          new FunctionType([unknownType], dynamicType, Nullability.legacy)
-        ], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '((dynamic) →* dynamic) →* dynamic');
+    testGreatest("((UNKNOWN) ->* dynamic) ->* dynamic",
+        "((dynamic) ->* dynamic) ->* dynamic");
   }
 
   void test_greatestClosure_covariant() {
-    expect(
-        greatestClosure(new FunctionType([], unknownType, Nullability.legacy))
-            .leakingDebugToString(),
-        '() →* dynamic');
-    expect(
-        greatestClosure(new InterfaceType(
-                coreTypes.listClass, Nullability.legacy, [unknownType]))
-            .leakingDebugToString(),
-        'dart.core::List<dynamic>*');
+    testGreatest("() ->* UNKNOWN", "() ->* dynamic");
+    testGreatest("List<UNKNOWN>*", "List<dynamic>*");
   }
 
   void test_greatestClosure_function_multipleUnknown() {
-    expect(
-        greatestClosure(new FunctionType(
-            [unknownType, unknownType], unknownType, Nullability.legacy,
-            namedParameters: [
-              new NamedType('a', unknownType),
-              new NamedType('b', unknownType)
-            ])).leakingDebugToString(),
-        '(Never, Never, {a: Never, b: Never}) →* dynamic');
+    testGreatest("(UNKNOWN, UNKNOWN, {UNKNOWN a, UNKNOWN b}) ->* UNKNOWN",
+        "(Never, Never, {Never a, Never b}) ->* dynamic");
   }
 
   void test_greatestClosure_simple() {
-    expect(greatestClosure(unknownType).leakingDebugToString(), 'dynamic');
+    testGreatest("UNKNOWN", "dynamic");
   }
 
   void test_leastClosure_contravariant() {
-    expect(
-        leastClosure(new FunctionType(
-                [unknownType], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '(dynamic) →* dynamic');
-    expect(
-        leastClosure(new FunctionType([], dynamicType, Nullability.legacy,
-                namedParameters: [new NamedType('foo', unknownType)]))
-            .leakingDebugToString(),
-        '({foo: dynamic}) →* dynamic');
+    testLeast("(UNKNOWN) ->* dynamic", "(dynamic) ->* dynamic");
+    testLeast("({UNKNOWN foo}) ->* dynamic", "({dynamic foo}) ->* dynamic");
   }
 
   void test_leastClosure_contravariant_contravariant() {
-    expect(
-        leastClosure(new FunctionType([
-          new FunctionType([unknownType], dynamicType, Nullability.legacy)
-        ], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '((Never) →* dynamic) →* dynamic');
+    testLeast("((UNKNOWN) ->* dynamic) ->* dynamic",
+        "((Never) ->* dynamic) ->* dynamic");
   }
 
   void test_leastClosure_covariant() {
-    expect(
-        leastClosure(new FunctionType([], unknownType, Nullability.legacy))
-            .leakingDebugToString(),
-        '() →* Never');
-    expect(
-        leastClosure(new InterfaceType(
-                coreTypes.listClass, Nullability.legacy, [unknownType]))
-            .leakingDebugToString(),
-        'dart.core::List<Never>*');
+    testLeast("() ->* UNKNOWN", "() ->* Never");
+    testLeast("List<UNKNOWN>*", "List<Never>*");
   }
 
   void test_leastClosure_function_multipleUnknown() {
-    expect(
-        leastClosure(new FunctionType(
-            [unknownType, unknownType], unknownType, Nullability.legacy,
-            namedParameters: [
-              new NamedType('a', unknownType),
-              new NamedType('b', unknownType)
-            ])).leakingDebugToString(),
-        '(dynamic, dynamic, {a: dynamic, b: dynamic}) →* Never');
+    testLeast("(UNKNOWN, UNKNOWN, {UNKNOWN a, UNKNOWN b}) ->* UNKNOWN",
+        "(dynamic, dynamic, {dynamic a, dynamic b}) ->* Never");
   }
 
   void test_leastClosure_simple() {
-    expect(leastClosure(unknownType).leakingDebugToString(), 'Never');
+    testLeast("UNKNOWN", "Never");
   }
 }
-
-class _MockCoreTypes implements CoreTypes {
-  @override
-  final Class listClass = new Class(name: 'List');
-
-  @override
-  final Class objectClass = new Class(name: 'Object');
-
-  _MockCoreTypes() {
-    new Library(Uri.parse('dart:core'),
-        name: 'dart.core', classes: [listClass, objectClass]);
-  }
-
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_test.dart
index a4c8ee3..dd4a329 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_elimination_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_elimination_test.dart
@@ -6,7 +6,7 @@
 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart'
     as typeSchemaElimination;
 import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
+import 'package:kernel/testing/type_parser_environment.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,134 +18,77 @@
 
 @reflectiveTest
 class TypeSchemaEliminationTest {
-  static const DartType unknownType = const UnknownType();
-
-  CoreTypes coreTypes = new _MockCoreTypes();
-
-  DartType get dynamicType => const DynamicType();
-
-  DartType get objectType => coreTypes.objectLegacyRawType;
+  final Env env = new Env("");
+  final Map<String, DartType Function()> additionalTypes = {
+    "UNKNOWN": () => new UnknownType()
+  };
 
   DartType greatestClosure(DartType schema) {
     return typeSchemaElimination.greatestClosure(
-        schema, dynamicType, const NullType());
+        schema, new DynamicType(), new NullType());
   }
 
   DartType leastClosure(DartType schema) {
     return typeSchemaElimination.leastClosure(
-        schema, dynamicType, const NullType());
+        schema, new DynamicType(), new NullType());
+  }
+
+  void testGreatest(String type, String expectedClosure) {
+    expect(
+        greatestClosure(env.parseType(type, additionalTypes: additionalTypes)),
+        env.parseType(expectedClosure, additionalTypes: additionalTypes));
+  }
+
+  void testLeast(String type, String expectedClosure) {
+    expect(leastClosure(env.parseType(type, additionalTypes: additionalTypes)),
+        env.parseType(expectedClosure, additionalTypes: additionalTypes));
   }
 
   void test_greatestClosure_contravariant() {
-    expect(
-        greatestClosure(new FunctionType(
-                [unknownType], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '(Null) →* dynamic');
-    expect(
-        greatestClosure(new FunctionType([], dynamicType, Nullability.legacy,
-                namedParameters: [new NamedType('foo', unknownType)]))
-            .leakingDebugToString(),
-        '({foo: Null}) →* dynamic');
+    testGreatest("(UNKNOWN) ->* dynamic", "(Null) ->* dynamic");
+    testGreatest("({UNKNOWN foo}) ->* dynamic", "({Null foo}) ->* dynamic");
   }
 
   void test_greatestClosure_contravariant_contravariant() {
-    expect(
-        greatestClosure(new FunctionType([
-          new FunctionType([unknownType], dynamicType, Nullability.legacy)
-        ], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '((dynamic) →* dynamic) →* dynamic');
+    testGreatest("((UNKNOWN) ->* dynamic) ->* dynamic",
+        "((dynamic) ->* dynamic) ->* dynamic");
   }
 
   void test_greatestClosure_covariant() {
-    expect(
-        greatestClosure(new FunctionType([], unknownType, Nullability.legacy))
-            .leakingDebugToString(),
-        '() →* dynamic');
-    expect(
-        greatestClosure(new InterfaceType(
-                coreTypes.listClass, Nullability.legacy, [unknownType]))
-            .leakingDebugToString(),
-        'dart.core::List<dynamic>*');
+    testGreatest("() ->* UNKNOWN", "() ->* dynamic");
+    testGreatest("List<UNKNOWN>*", "List<dynamic>*");
   }
 
   void test_greatestClosure_function_multipleUnknown() {
-    expect(
-        greatestClosure(new FunctionType(
-            [unknownType, unknownType], unknownType, Nullability.legacy,
-            namedParameters: [
-              new NamedType('a', unknownType),
-              new NamedType('b', unknownType)
-            ])).leakingDebugToString(),
-        '(Null, Null, {a: Null, b: Null}) →* dynamic');
+    testGreatest("(UNKNOWN, UNKNOWN, {UNKNOWN a, UNKNOWN b}) ->* UNKNOWN",
+        "(Null, Null, {Null a, Null b}) ->* dynamic");
   }
 
   void test_greatestClosure_simple() {
-    expect(greatestClosure(unknownType).leakingDebugToString(), 'dynamic');
+    testGreatest("UNKNOWN", "dynamic");
   }
 
   void test_leastClosure_contravariant() {
-    expect(
-        leastClosure(new FunctionType(
-                [unknownType], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '(dynamic) →* dynamic');
-    expect(
-        leastClosure(new FunctionType([], dynamicType, Nullability.legacy,
-                namedParameters: [new NamedType('foo', unknownType)]))
-            .leakingDebugToString(),
-        '({foo: dynamic}) →* dynamic');
+    testLeast("(UNKNOWN) ->* dynamic", "(dynamic) ->* dynamic");
+    testLeast("({UNKNOWN foo}) ->* dynamic", "({dynamic foo}) ->* dynamic");
   }
 
   void test_leastClosure_contravariant_contravariant() {
-    expect(
-        leastClosure(new FunctionType([
-          new FunctionType([unknownType], dynamicType, Nullability.legacy)
-        ], dynamicType, Nullability.legacy))
-            .leakingDebugToString(),
-        '((Null) →* dynamic) →* dynamic');
+    testLeast("((UNKNOWN) ->* UNKNOWN) ->* dynamic",
+        "((Null) ->* dynamic) ->* dynamic");
   }
 
   void test_leastClosure_covariant() {
-    expect(
-        leastClosure(new FunctionType([], unknownType, Nullability.legacy))
-            .leakingDebugToString(),
-        '() →* Null');
-    expect(
-        leastClosure(new InterfaceType(
-                coreTypes.listClass, Nullability.legacy, [unknownType]))
-            .leakingDebugToString(),
-        'dart.core::List<Null>*');
+    testLeast("() ->* UNKNOWN", "() ->* Null");
+    testLeast("List<UNKNOWN>*", "List<Null>*");
   }
 
   void test_leastClosure_function_multipleUnknown() {
-    expect(
-        leastClosure(new FunctionType(
-            [unknownType, unknownType], unknownType, Nullability.legacy,
-            namedParameters: [
-              new NamedType('a', unknownType),
-              new NamedType('b', unknownType)
-            ])).leakingDebugToString(),
-        '(dynamic, dynamic, {a: dynamic, b: dynamic}) →* Null');
+    testLeast("(UNKNOWN, UNKNOWN, {UNKNOWN a, UNKNOWN b}) ->* UNKNOWN",
+        "(dynamic, dynamic, {dynamic a, dynamic b}) ->* Null");
   }
 
   void test_leastClosure_simple() {
-    expect(leastClosure(unknownType).leakingDebugToString(), 'Null');
+    testLeast("UNKNOWN", "Null");
   }
 }
-
-class _MockCoreTypes implements CoreTypes {
-  @override
-  final Class listClass = new Class(name: 'List');
-
-  @override
-  final Class objectClass = new Class(name: 'Object');
-
-  _MockCoreTypes() {
-    new Library(Uri.parse('dart:core'),
-        name: 'dart.core', classes: [listClass, objectClass]);
-  }
-
-  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
-}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 5255e56..7806bde 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1784,6 +1784,7 @@
 maintains
 major
 make
+makers
 makes
 making
 malformed
diff --git a/pkg/kernel/lib/testing/type_parser_environment.dart b/pkg/kernel/lib/testing/type_parser_environment.dart
index 8989064..757ee7a 100644
--- a/pkg/kernel/lib/testing/type_parser_environment.dart
+++ b/pkg/kernel/lib/testing/type_parser_environment.dart
@@ -114,8 +114,10 @@
     coreTypes = new CoreTypes(component);
   }
 
-  DartType parseType(String text) {
-    return _libraryEnvironment.parseType(text);
+  DartType parseType(String text,
+      {Map<String, DartType Function()> additionalTypes}) {
+    return _libraryEnvironment.parseType(text,
+        additionalTypes: additionalTypes);
   }
 
   void extendWithTypeParameters(String typeParameters) {
@@ -154,13 +156,17 @@
 
   TypeParserEnvironment(this.uri, this.fileUri, [this._parent]);
 
-  Node _kernelFromParsedType(ParsedType type) {
-    Node node = type.accept(const _KernelFromParsedType(), this);
+  Node _kernelFromParsedType(ParsedType type,
+      {Map<String, DartType Function()> additionalTypes}) {
+    Node node = type.accept(
+        new _KernelFromParsedType(additionalTypes: additionalTypes), this);
     return node;
   }
 
-  DartType parseType(String text) {
-    return _kernelFromParsedType(type_parser.parse(text).single);
+  DartType parseType(String text,
+      {Map<String, DartType Function()> additionalTypes}) {
+    return _kernelFromParsedType(type_parser.parse(text).single,
+        additionalTypes: additionalTypes);
   }
 
   bool isObject(String name) => name == "Object" && "$uri" == "dart:core";
@@ -209,7 +215,9 @@
 }
 
 class _KernelFromParsedType implements Visitor<Node, TypeParserEnvironment> {
-  const _KernelFromParsedType();
+  final Map<String, DartType Function()> additionalTypes; // Can be null.
+
+  const _KernelFromParsedType({this.additionalTypes});
 
   DartType visitInterfaceType(
       ParsedInterfaceType node, TypeParserEnvironment environment) {
@@ -237,6 +245,8 @@
       // Don't return a const object to ensure we test implementations that use
       // identical.
       return new NullType();
+    } else if (additionalTypes != null && additionalTypes.containsKey(name)) {
+      return additionalTypes[name].call();
     }
     TreeNode declaration = environment.lookupDeclaration(name);
     List<ParsedType> arguments = node.arguments;
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 8f0d07d..3428443 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -99,10 +99,6 @@
   static const previewPortOption = 'preview-port';
   static const sdkPathOption = 'sdk-path';
   static const skipImportCheckFlag = 'skip-import-check';
-
-  /// TODO(paulberry): remove this flag once internal sources have been updated.
-  @Deprecated('The migration tool no longer performs "pub outdated" checks')
-  static const skipPubOutdatedFlag = 'skip-pub-outdated';
   static const summaryOption = 'summary';
   static const verboseFlag = 'verbose';
   static const webPreviewFlag = 'web-preview';
@@ -128,36 +124,16 @@
   final bool webPreview;
 
   CommandLineOptions(
-      {@required
-          this.applyChanges,
-      @required
-          this.directory,
-      @required
-          this.ignoreErrors,
-      @required
-          this.ignoreExceptions,
-      @required
-          this.previewHostname,
-      @required
-          this.previewPort,
-      @required
-          this.sdkPath,
-      // TODO(paulberry): make this parameter required once internal sources
-      // have been updated.
-      bool skipImportCheck,
-      // TODO(paulberry): remove this flag once internal sources have been
-      // updated.
-      @Deprecated('The migration tool no longer performs "pub outdated" checks')
-          bool skipPubOutdated = false,
-      @required
-          this.summary,
-      @required
-          this.webPreview})
-      // `skipImportCheck` has replaced `skipPubOutdated`, so if the caller
-      // specifies the latter but not the former, carry it over.
-      // TODO(paulberry): remove this logic once internal sources have been
-      // updated.
-      : skipImportCheck = skipImportCheck ?? skipPubOutdated;
+      {@required this.applyChanges,
+      @required this.directory,
+      @required this.ignoreErrors,
+      @required this.ignoreExceptions,
+      @required this.previewHostname,
+      @required this.previewPort,
+      @required this.sdkPath,
+      @required this.skipImportCheck,
+      @required this.summary,
+      @required this.webPreview});
 }
 
 // TODO(devoncarew): Refactor so this class extends DartdevCommand.
diff --git a/runtime/tests/vm/dart/scavenger_abort_2_test.dart b/runtime/tests/vm/dart/scavenger_abort_2_test.dart
new file mode 100644
index 0000000..e3183da
--- /dev/null
+++ b/runtime/tests/vm/dart/scavenger_abort_2_test.dart
@@ -0,0 +1,288 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:io";
+import "package:expect/expect.dart";
+
+// The sizes of these classes are co-prime multiples of the allocation unit to
+// increase the likelihood that scavenging fails from fragmentation.
+
+// header + 13 fields = 7 allocation units
+class A {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13;
+  }
+}
+
+// header + 17 fields = 9 allocation units
+class B {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+  dynamic field14;
+  dynamic field15;
+  dynamic field16;
+  dynamic field17;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13 +
+        field14 +
+        field15 +
+        field16 +
+        field17;
+  }
+}
+
+// header + 19 fields = 10 allocation units
+class C {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+  dynamic field14;
+  dynamic field15;
+  dynamic field16;
+  dynamic field17;
+  dynamic field18;
+  dynamic field19;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13 +
+        field14 +
+        field15 +
+        field16 +
+        field17 +
+        field18 +
+        field19;
+  }
+}
+
+class Old {
+  dynamic next;
+  dynamic new1;
+  dynamic new2;
+  dynamic new3;
+  dynamic new4;
+  dynamic new5;
+  dynamic new6;
+  dynamic new7;
+  dynamic new8;
+  dynamic new9;
+  dynamic new10;
+  dynamic new11;
+  dynamic new12;
+  dynamic new13;
+  dynamic new14;
+  dynamic new15;
+  dynamic new16;
+  dynamic new17;
+  dynamic new18;
+  dynamic new19;
+  dynamic new20;
+  dynamic new21;
+  dynamic new22;
+  dynamic new23;
+  dynamic new24;
+  dynamic new25;
+  dynamic new26;
+  dynamic new27;
+  dynamic new28;
+  dynamic new29;
+  dynamic new30;
+  dynamic new31;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return new1 +
+        new2 +
+        new3 +
+        new4 +
+        new5 +
+        new6 +
+        new7 +
+        new8 +
+        new9 +
+        new10 +
+        new11 +
+        new12 +
+        new13 +
+        new14 +
+        new15 +
+        new16 +
+        new17 +
+        new18 +
+        new19 +
+        new20 +
+        new21 +
+        new22 +
+        new23 +
+        new24 +
+        new25 +
+        new26 +
+        new27 +
+        new28 +
+        new29 +
+        new30 +
+        new31;
+  }
+}
+
+fill(old) {
+  // Note the allocation order is different from the field order. The objects
+  // will be scavenged in field order, causing the objects to be rearranged to
+  // produce new-space fragmentation.
+  old.new1 = new C();
+  old.new4 = new C();
+  old.new7 = new C();
+  old.new10 = new C();
+  old.new13 = new C();
+  old.new16 = new C();
+
+  old.new2 = new B();
+  old.new5 = new B();
+  old.new8 = new B();
+  old.new11 = new B();
+  old.new14 = new B();
+  old.new17 = new B();
+
+  old.new3 = new A();
+  old.new6 = new A();
+  old.new9 = new A();
+  old.new12 = new A();
+  old.new15 = new A();
+  old.new18 = new A();
+}
+
+makeOld() {
+  // 2/4 MB worth of Old.
+  print("PHASE1");
+  var head;
+  for (var i = 0; i < 16384; i++) {
+    var old = new Old();
+    old.next = head;
+    head = old;
+  }
+
+  // 32/64 MB worth of new objects, all directly reachable from the
+  // remembered set.
+  print("PHASE2");
+  for (var old = head; old != null; old = old.next) {
+    fill(old);
+  }
+
+  print("PHASE3");
+  return head;
+}
+
+main(List<String> argsIn) async {
+  if (argsIn.contains("--testee")) {
+    // Trigger OOM.
+    // Must read the fields to prevent the writes from being optimized away. If
+    // the writes are optimized away, most of the tree is collectible right away
+    // and we timeout instead of triggering OOM.
+    print(makeOld());
+    return;
+  }
+
+  var exec = Platform.executable;
+  var args = Platform.executableArguments +
+      [
+        "--new_gen_semi_max_size=4" /*MB*/,
+        "--old_gen_heap_size=15" /*MB*/,
+        "--verbose_gc",
+        "--verify_store_buffer",
+        Platform.script.toFilePath(),
+        "--testee"
+      ];
+  print("+ $exec ${args.join(' ')}");
+
+  var result = await Process.run(exec, args);
+  print("Command stdout:");
+  print(result.stdout);
+  print("Command stderr:");
+  print(result.stderr);
+
+  Expect.equals(255, result.exitCode,
+      "Should see runtime exception error code, not SEGV");
+
+  Expect.isTrue(
+      result.stderr.contains("Unhandled exception:\nOut of Memory") ||
+          result.stderr.contains("Unhandled exception:\r\nOut of Memory"),
+      "Should see the Dart OutOfMemoryError");
+
+  Expect.isFalse(result.stderr.contains("error: Out of memory"),
+      "Should not see the C++ OUT_OF_MEMORY()");
+}
diff --git a/runtime/tests/vm/dart_2/scavenger_abort_2_test.dart b/runtime/tests/vm/dart_2/scavenger_abort_2_test.dart
new file mode 100644
index 0000000..e3183da
--- /dev/null
+++ b/runtime/tests/vm/dart_2/scavenger_abort_2_test.dart
@@ -0,0 +1,288 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:io";
+import "package:expect/expect.dart";
+
+// The sizes of these classes are co-prime multiples of the allocation unit to
+// increase the likelihood that scavenging fails from fragmentation.
+
+// header + 13 fields = 7 allocation units
+class A {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13;
+  }
+}
+
+// header + 17 fields = 9 allocation units
+class B {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+  dynamic field14;
+  dynamic field15;
+  dynamic field16;
+  dynamic field17;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13 +
+        field14 +
+        field15 +
+        field16 +
+        field17;
+  }
+}
+
+// header + 19 fields = 10 allocation units
+class C {
+  dynamic field1;
+  dynamic field2;
+  dynamic field3;
+  dynamic field4;
+  dynamic field5;
+  dynamic field6;
+  dynamic field7;
+  dynamic field8;
+  dynamic field9;
+  dynamic field10;
+  dynamic field11;
+  dynamic field12;
+  dynamic field13;
+  dynamic field14;
+  dynamic field15;
+  dynamic field16;
+  dynamic field17;
+  dynamic field18;
+  dynamic field19;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return field1 +
+        field2 +
+        field3 +
+        field4 +
+        field5 +
+        field6 +
+        field7 +
+        field8 +
+        field9 +
+        field10 +
+        field11 +
+        field12 +
+        field13 +
+        field14 +
+        field15 +
+        field16 +
+        field17 +
+        field18 +
+        field19;
+  }
+}
+
+class Old {
+  dynamic next;
+  dynamic new1;
+  dynamic new2;
+  dynamic new3;
+  dynamic new4;
+  dynamic new5;
+  dynamic new6;
+  dynamic new7;
+  dynamic new8;
+  dynamic new9;
+  dynamic new10;
+  dynamic new11;
+  dynamic new12;
+  dynamic new13;
+  dynamic new14;
+  dynamic new15;
+  dynamic new16;
+  dynamic new17;
+  dynamic new18;
+  dynamic new19;
+  dynamic new20;
+  dynamic new21;
+  dynamic new22;
+  dynamic new23;
+  dynamic new24;
+  dynamic new25;
+  dynamic new26;
+  dynamic new27;
+  dynamic new28;
+  dynamic new29;
+  dynamic new30;
+  dynamic new31;
+
+  // Prevent fields from being optimized away as write-only.
+  String toString() {
+    return new1 +
+        new2 +
+        new3 +
+        new4 +
+        new5 +
+        new6 +
+        new7 +
+        new8 +
+        new9 +
+        new10 +
+        new11 +
+        new12 +
+        new13 +
+        new14 +
+        new15 +
+        new16 +
+        new17 +
+        new18 +
+        new19 +
+        new20 +
+        new21 +
+        new22 +
+        new23 +
+        new24 +
+        new25 +
+        new26 +
+        new27 +
+        new28 +
+        new29 +
+        new30 +
+        new31;
+  }
+}
+
+fill(old) {
+  // Note the allocation order is different from the field order. The objects
+  // will be scavenged in field order, causing the objects to be rearranged to
+  // produce new-space fragmentation.
+  old.new1 = new C();
+  old.new4 = new C();
+  old.new7 = new C();
+  old.new10 = new C();
+  old.new13 = new C();
+  old.new16 = new C();
+
+  old.new2 = new B();
+  old.new5 = new B();
+  old.new8 = new B();
+  old.new11 = new B();
+  old.new14 = new B();
+  old.new17 = new B();
+
+  old.new3 = new A();
+  old.new6 = new A();
+  old.new9 = new A();
+  old.new12 = new A();
+  old.new15 = new A();
+  old.new18 = new A();
+}
+
+makeOld() {
+  // 2/4 MB worth of Old.
+  print("PHASE1");
+  var head;
+  for (var i = 0; i < 16384; i++) {
+    var old = new Old();
+    old.next = head;
+    head = old;
+  }
+
+  // 32/64 MB worth of new objects, all directly reachable from the
+  // remembered set.
+  print("PHASE2");
+  for (var old = head; old != null; old = old.next) {
+    fill(old);
+  }
+
+  print("PHASE3");
+  return head;
+}
+
+main(List<String> argsIn) async {
+  if (argsIn.contains("--testee")) {
+    // Trigger OOM.
+    // Must read the fields to prevent the writes from being optimized away. If
+    // the writes are optimized away, most of the tree is collectible right away
+    // and we timeout instead of triggering OOM.
+    print(makeOld());
+    return;
+  }
+
+  var exec = Platform.executable;
+  var args = Platform.executableArguments +
+      [
+        "--new_gen_semi_max_size=4" /*MB*/,
+        "--old_gen_heap_size=15" /*MB*/,
+        "--verbose_gc",
+        "--verify_store_buffer",
+        Platform.script.toFilePath(),
+        "--testee"
+      ];
+  print("+ $exec ${args.join(' ')}");
+
+  var result = await Process.run(exec, args);
+  print("Command stdout:");
+  print(result.stdout);
+  print("Command stderr:");
+  print(result.stderr);
+
+  Expect.equals(255, result.exitCode,
+      "Should see runtime exception error code, not SEGV");
+
+  Expect.isTrue(
+      result.stderr.contains("Unhandled exception:\nOut of Memory") ||
+          result.stderr.contains("Unhandled exception:\r\nOut of Memory"),
+      "Should see the Dart OutOfMemoryError");
+
+  Expect.isFalse(result.stderr.contains("error: Out of memory"),
+      "Should not see the C++ OUT_OF_MEMORY()");
+}
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc
index c095e9e..6620468 100644
--- a/runtime/vm/compiler/ffi/marshaller.cc
+++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -4,9 +4,13 @@
 
 #include "vm/compiler/ffi/marshaller.h"
 
+#include "platform/assert.h"
+#include "platform/globals.h"
 #include "vm/compiler/ffi/frame_rebase.h"
+#include "vm/compiler/ffi/native_calling_convention.h"
 #include "vm/compiler/ffi/native_location.h"
 #include "vm/compiler/ffi/native_type.h"
+#include "vm/log.h"
 #include "vm/raw_object.h"
 #include "vm/stack_frame.h"
 #include "vm/symbols.h"
@@ -17,6 +21,56 @@
 
 namespace ffi {
 
+// Argument #0 is the function pointer.
+const intptr_t kNativeParamsStartAt = 1;
+
+// Representations of the arguments and return value of a C signature function.
+static const NativeFunctionType& NativeFunctionSignature(
+    Zone* zone,
+    const Function& c_signature) {
+  ASSERT(c_signature.NumOptionalParameters() == 0);
+  ASSERT(c_signature.NumOptionalPositionalParameters() == 0);
+
+  const intptr_t num_arguments =
+      c_signature.num_fixed_parameters() - kNativeParamsStartAt;
+  auto& argument_representations =
+      *new ZoneGrowableArray<const NativeType*>(zone, num_arguments);
+  for (intptr_t i = 0; i < num_arguments; i++) {
+    AbstractType& arg_type = AbstractType::Handle(
+        zone, c_signature.ParameterTypeAt(i + kNativeParamsStartAt));
+    const auto& rep = NativeType::FromAbstractType(zone, arg_type);
+    argument_representations.Add(&rep);
+  }
+
+  const auto& result_type =
+      AbstractType::Handle(zone, c_signature.result_type());
+  const auto& result_representation =
+      NativeType::FromAbstractType(zone, result_type);
+
+  const auto& result = *new (zone) NativeFunctionType(argument_representations,
+                                                      result_representation);
+  return result;
+}
+
+BaseMarshaller::BaseMarshaller(Zone* zone, const Function& dart_signature)
+    : zone_(zone),
+      dart_signature_(dart_signature),
+      c_signature_(Function::ZoneHandle(zone, dart_signature.FfiCSignature())),
+      native_calling_convention_(NativeCallingConvention::FromSignature(
+          zone,
+          NativeFunctionSignature(zone_, c_signature_))) {
+  ASSERT(dart_signature_.IsZoneHandle());
+}
+
+AbstractTypePtr BaseMarshaller::CType(intptr_t arg_index) const {
+  if (arg_index == kResultIndex) {
+    return c_signature_.result_type();
+  }
+
+  // Skip #0 argument, the function pointer.
+  return c_signature_.ParameterTypeAt(arg_index + kNativeParamsStartAt);
+}
+
 bool BaseMarshaller::ContainsHandles() const {
   return dart_signature_.FfiCSignatureContainsHandles();
 }
@@ -80,14 +134,14 @@
   static NativeLocations& TranslateArgumentLocations(
       Zone* zone,
       const NativeLocations& arg_locs) {
-    auto& pushed_locs = *(new NativeLocations(arg_locs.length()));
+    auto& pushed_locs = *(new (zone) NativeLocations(arg_locs.length()));
 
     CallbackArgumentTranslator translator;
     for (intptr_t i = 0, n = arg_locs.length(); i < n; i++) {
       translator.AllocateArgument(*arg_locs[i]);
     }
     for (intptr_t i = 0, n = arg_locs.length(); i < n; ++i) {
-      pushed_locs.Add(&translator.TranslateArgument(*arg_locs[i], zone));
+      pushed_locs.Add(&translator.TranslateArgument(zone, *arg_locs[i]));
     }
 
     return pushed_locs;
@@ -97,16 +151,17 @@
   void AllocateArgument(const NativeLocation& arg) {
     if (arg.IsStack()) return;
 
-    ASSERT(arg.IsRegisters() || arg.IsFpuRegisters());
     if (arg.IsRegisters()) {
       argument_slots_required_ += arg.AsRegisters().num_regs();
-    } else {
+    } else if (arg.IsFpuRegisters()) {
       argument_slots_required_ += 8 / target::kWordSize;
+    } else {
+      UNREACHABLE();
     }
   }
 
-  const NativeLocation& TranslateArgument(const NativeLocation& arg,
-                                          Zone* zone) {
+  const NativeLocation& TranslateArgument(Zone* zone,
+                                          const NativeLocation& arg) {
     if (arg.IsStack()) {
       // Add extra slots after the saved arguments for the return address and
       // frame pointer of the dummy arguments frame, which will be between the
@@ -152,10 +207,9 @@
 CallbackMarshaller::CallbackMarshaller(Zone* zone,
                                        const Function& dart_signature)
     : BaseMarshaller(zone, dart_signature),
-      callback_locs_(
-          CallbackArgumentTranslator::TranslateArgumentLocations(zone_,
-                                                                 arg_locs_)) {}
-
+      callback_locs_(CallbackArgumentTranslator::TranslateArgumentLocations(
+          zone_,
+          native_calling_convention_.argument_locations())) {}
 }  // namespace ffi
 
 }  // namespace compiler
diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h
index d175ddc..be567b6 100644
--- a/runtime/vm/compiler/ffi/marshaller.h
+++ b/runtime/vm/compiler/ffi/marshaller.h
@@ -11,6 +11,7 @@
 
 #include <platform/globals.h>
 
+#include "platform/assert.h"
 #include "vm/compiler/backend/locations.h"
 #include "vm/compiler/ffi/callback.h"
 #include "vm/compiler/ffi/native_calling_convention.h"
@@ -24,16 +25,32 @@
 
 namespace ffi {
 
+// Values below 0 index result (result might be multiple if composite).
+const intptr_t kResultIndex = -1;
+
 // Provides the mapping from the native calling convention to the Dart calling
 // convention.
 //
 // This class is set up in a query-able way so that it's underlying logic can
 // be extended to support more native ABI features and calling conventions.
-//
-// TODO(36730): Add a way to query arguments that are broken into multiple
-// parts.
-class BaseMarshaller : public NativeCallingConvention {
+class BaseMarshaller : public ZoneAllocated {
  public:
+  intptr_t num_args() const {
+    return native_calling_convention_.argument_locations().length();
+  }
+
+  intptr_t StackTopInBytes() const {
+    return native_calling_convention_.StackTopInBytes();
+  }
+
+  // The location of the argument at `arg_index`.
+  const NativeLocation& Location(intptr_t arg_index) const {
+    if (arg_index == kResultIndex) {
+      return native_calling_convention_.return_location();
+    }
+    return *native_calling_convention_.argument_locations().At(arg_index);
+  }
+
   // Unboxed representation on how the value is passed or received from regular
   // Dart code.
   Representation RepInDart(intptr_t arg_index) const {
@@ -62,6 +79,11 @@
     return Location(arg_index).payload_type();
   }
 
+  // The C Type (expressed in a Dart Type) of the argument at `arg_index`.
+  //
+  // Excluding the #0 argument which is the function pointer.
+  AbstractTypePtr CType(intptr_t arg_index) const;
+
   // Requires boxing or unboxing.
   bool IsPointer(intptr_t arg_index) const {
     return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
@@ -83,18 +105,16 @@
   StringPtr function_name() const { return dart_signature_.name(); }
 
  protected:
-  BaseMarshaller(Zone* zone, const Function& dart_signature)
-      : NativeCallingConvention(
-            zone,
-            Function::ZoneHandle(zone, dart_signature.FfiCSignature())),
-        dart_signature_(dart_signature) {
-    ASSERT(dart_signature_.IsZoneHandle());
-  }
+  BaseMarshaller(Zone* zone, const Function& dart_signature);
 
- private:
+  ~BaseMarshaller() {}
+
+  Zone* zone_;
   // Contains the function pointer as argument #0.
   // The Dart signature is used for the function and argument names.
   const Function& dart_signature_;
+  const Function& c_signature_;
+  const NativeCallingConvention& native_calling_convention_;
 };
 
 class CallMarshaller : public BaseMarshaller {
@@ -103,6 +123,9 @@
       : BaseMarshaller(zone, dart_signature) {}
 
   dart::Location LocInFfiCall(intptr_t arg_index) const;
+
+ protected:
+  ~CallMarshaller() {}
 };
 
 class CallbackMarshaller : public BaseMarshaller {
@@ -125,6 +148,8 @@
   }
 
  protected:
+  ~CallbackMarshaller() {}
+
   const NativeLocations& callback_locs_;
 };
 
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
index 84081aa..2ac1445 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
@@ -16,9 +16,6 @@
 
 namespace ffi {
 
-// Argument #0 is the function pointer.
-const intptr_t kNativeParamsStartAt = 1;
-
 const intptr_t kNoFpuRegister = -1;
 
 // In Soft FP, floats and doubles get passed in integer registers.
@@ -44,29 +41,6 @@
   return rep;
 }
 
-// Representations of the arguments to a C signature function.
-static ZoneGrowableArray<const NativeType*>& ArgumentRepresentations(
-    Zone* zone,
-    const Function& signature) {
-  const intptr_t num_arguments =
-      signature.num_fixed_parameters() - kNativeParamsStartAt;
-  auto& result = *new ZoneGrowableArray<const NativeType*>(zone, num_arguments);
-  for (intptr_t i = 0; i < num_arguments; i++) {
-    AbstractType& arg_type = AbstractType::Handle(
-        zone, signature.ParameterTypeAt(i + kNativeParamsStartAt));
-    const auto& rep = NativeType::FromAbstractType(zone, arg_type);
-    result.Add(&rep);
-  }
-  return result;
-}
-
-// Representation of the result of a C signature function.
-static NativeType& ResultRepresentation(Zone* zone, const Function& signature) {
-  AbstractType& result_type =
-      AbstractType::Handle(zone, signature.result_type());
-  return NativeType::FromAbstractType(zone, result_type);
-}
-
 // Represents the state of a stack frame going into a call, between allocations
 // of argument locations.
 class ArgumentAllocator : public ValueObject {
@@ -90,9 +64,9 @@
         if (CallingConventions::kArgumentIntRegXorFpuReg) {
           ASSERT(cpu_regs_used == CallingConventions::kNumArgRegs);
         }
+        // Transfer on stack.
       }
-    } else {
-      ASSERT(payload_type_converted.IsInt());
+    } else if (payload_type_converted.IsInt()) {
       // Some calling conventions require the callee to make the lowest 32 bits
       // in registers non-garbage.
       const auto& container_type =
@@ -115,8 +89,12 @@
         if (cpu_regs_used + 1 <= CallingConventions::kNumArgRegs) {
           return *new (zone_) NativeRegistersLocation(
               payload_type, container_type, AllocateCpuRegister());
+        } else {
+          // Transfer on stack.
         }
       }
+    } else {
+      UNREACHABLE();
     }
 
     return AllocateStack(payload_type);
@@ -227,7 +205,7 @@
     Zone* zone,
     const ZoneGrowableArray<const NativeType*>& arg_reps) {
   intptr_t num_arguments = arg_reps.length();
-  auto& result = *new NativeLocations(zone, num_arguments);
+  auto& result = *new (zone) NativeLocations(zone, num_arguments);
 
   // Loop through all arguments and assign a register or a stack location.
   ArgumentAllocator frame_state(zone);
@@ -239,8 +217,8 @@
 }
 
 // Location for the result of a C signature function.
-static NativeLocation& ResultLocation(Zone* zone,
-                                      const NativeType& payload_type) {
+static const NativeLocation& ResultLocation(Zone* zone,
+                                            const NativeType& payload_type) {
   const auto& payload_type_converted = ConvertIfSoftFp(zone, payload_type);
   const auto& container_type =
       CallingConventions::kReturnRegisterExtension == kExtendedTo4
@@ -263,46 +241,45 @@
                                              CallingConventions::kReturnReg);
 }
 
-NativeCallingConvention::NativeCallingConvention(Zone* zone,
-                                                 const Function& c_signature)
-    : zone_(ASSERT_NOTNULL(zone)),
-      c_signature_(c_signature),
-      arg_locs_(
-          ArgumentLocations(zone_,
-                            ArgumentRepresentations(zone_, c_signature_))),
-      result_loc_(
-          ResultLocation(zone_, ResultRepresentation(zone_, c_signature_))) {}
-
-intptr_t NativeCallingConvention::num_args() const {
-  ASSERT(c_signature_.NumOptionalParameters() == 0);
-  ASSERT(c_signature_.NumOptionalPositionalParameters() == 0);
-
-  // Subtract the #0 argument, the function pointer.
-  return c_signature_.num_fixed_parameters() - kNativeParamsStartAt;
-}
-
-AbstractTypePtr NativeCallingConvention::CType(intptr_t arg_index) const {
-  if (arg_index == kResultIndex) {
-    return c_signature_.result_type();
-  }
-
-  // Skip #0 argument, the function pointer.
-  return c_signature_.ParameterTypeAt(arg_index + kNativeParamsStartAt);
+const NativeCallingConvention& NativeCallingConvention::FromSignature(
+    Zone* zone,
+    const NativeFunctionType& signature) {
+  const auto& argument_locations =
+      ArgumentLocations(zone, signature.argument_types());
+  const auto& return_location = ResultLocation(zone, signature.return_type());
+  return *new (zone)
+      NativeCallingConvention(argument_locations, return_location);
 }
 
 intptr_t NativeCallingConvention::StackTopInBytes() const {
-  const intptr_t num_arguments = arg_locs_.length();
+  const intptr_t num_arguments = argument_locations_.length();
   intptr_t max_height_in_bytes = 0;
   for (intptr_t i = 0; i < num_arguments; i++) {
-    if (Location(i).IsStack()) {
-      const intptr_t height = Location(i).AsStack().offset_in_bytes() +
-                              Location(i).container_type().SizeInBytes();
-      max_height_in_bytes = Utils::Maximum(height, max_height_in_bytes);
-    }
+    max_height_in_bytes = Utils::Maximum(
+        max_height_in_bytes, argument_locations_[i]->StackTopInBytes());
   }
   return Utils::RoundUp(max_height_in_bytes, compiler::target::kWordSize);
 }
 
+const char* NativeCallingConvention::ToCString() const {
+  char buffer[1024];
+  BufferFormatter bf(buffer, 1024);
+  PrintTo(&bf);
+  return Thread::Current()->zone()->MakeCopyOfString(buffer);
+}
+
+void NativeCallingConvention::PrintTo(BaseTextBuffer* f) const {
+  f->AddString("(");
+  for (intptr_t i = 0; i < argument_locations_.length(); i++) {
+    if (i > 0) {
+      f->AddString(", ");
+    }
+    argument_locations_[i]->PrintTo(f);
+  }
+  f->AddString(") => ");
+  return_location_.PrintTo(f);
+}
+
 }  // namespace ffi
 
 }  // namespace compiler
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.h b/runtime/vm/compiler/ffi/native_calling_convention.h
index 5eae152..4948910 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.h
+++ b/runtime/vm/compiler/ffi/native_calling_convention.h
@@ -23,43 +23,35 @@
 
 using NativeLocations = ZoneGrowableArray<const NativeLocation*>;
 
-
-// Values below 0 index result (result might be multiple if composite).
-const intptr_t kResultIndex = -1;
-
 // Calculates native calling convention, is not aware of Dart calling
 // convention constraints.
 //
-// This class is meant to be extended or embedded in a class that is aware
-// of Dart calling convention constraints.
+// This class is meant to beembedded in a class that is aware of Dart calling
+// convention constraints.
 class NativeCallingConvention : public ZoneAllocated {
  public:
-  NativeCallingConvention(Zone* zone, const Function& c_signature);
+  static const NativeCallingConvention& FromSignature(
+      Zone* zone,
+      const NativeFunctionType& signature);
 
-  // Excluding the #0 argument which is the function pointer.
-  intptr_t num_args() const;
-
-  // The C Type (expressed in a Dart Type) of the argument at `arg_index`.
-  //
-  // Excluding the #0 argument which is the function pointer.
-  AbstractTypePtr CType(intptr_t arg_index) const;
-
-  // The location of the argument at `arg_index`.
-  const NativeLocation& Location(intptr_t arg_index) const {
-    if (arg_index == kResultIndex) {
-      return result_loc_;
-    }
-    return *arg_locs_.At(arg_index);
+  const NativeLocations& argument_locations() const {
+    return argument_locations_;
   }
+  const NativeLocation& return_location() const { return return_location_; }
 
   intptr_t StackTopInBytes() const;
 
- protected:
-  Zone* const zone_;
-  // Contains the function pointer as argument #0.
-  const Function& c_signature_;
-  const NativeLocations& arg_locs_;
-  const NativeLocation& result_loc_;
+  void PrintTo(BaseTextBuffer* f) const;
+  const char* ToCString() const;
+
+ private:
+  NativeCallingConvention(const NativeLocations& argument_locations,
+                          const NativeLocation& return_location)
+      : argument_locations_(argument_locations),
+        return_location_(return_location) {}
+
+  const NativeLocations& argument_locations_;
+  const NativeLocation& return_location_;
 };
 
 }  // namespace ffi
diff --git a/runtime/vm/compiler/ffi/native_location.h b/runtime/vm/compiler/ffi/native_location.h
index 78caa24..6c7e980 100644
--- a/runtime/vm/compiler/ffi/native_location.h
+++ b/runtime/vm/compiler/ffi/native_location.h
@@ -9,6 +9,7 @@
 #error "AOT runtime should not use compiler sources (including header files)"
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
+#include "platform/assert.h"
 #include "vm/compiler/backend/locations.h"
 #include "vm/compiler/ffi/native_type.h"
 #include "vm/growable_array.h"
@@ -34,14 +35,15 @@
 // * The container type, equal to or larger than the payload. If the
 //   container is larger than the payload, the upper bits are defined by sign
 //   or zero extension.
+//   Container type is also used to denote an integer container when floating
+//   point values are passed in integer registers.
 //
 // NativeLocations can express things that dart::Locations cannot express:
 // * Multiple consecutive registers.
 // * Multiple sizes of FPU registers (e.g. S, D, and Q on Arm32).
 // * Arbitrary byte-size stack locations, at byte-size offsets.
 //   (The Location class uses word-size offsets.)
-// * Pointers including a backing location on the stack.
-// * No location.
+// * Pointers to a memory location.
 // * Split between multiple registers and stack.
 //
 // NativeLocations cannot express the following dart::Locations:
@@ -106,6 +108,9 @@
     UNREACHABLE();
   }
 
+  // Return the top of the stack in bytes.
+  virtual intptr_t StackTopInBytes() const { return 0; }
+
   // Equality of location, ignores the payload and container native types.
   virtual bool Equals(const NativeLocation& other) const { UNREACHABLE(); }
 
@@ -303,6 +308,10 @@
 
   virtual NativeStackLocation& Split(Zone* zone, intptr_t index) const;
 
+  virtual intptr_t StackTopInBytes() const {
+    return offset_in_bytes() + container_type().SizeInBytes();
+  }
+
   virtual void PrintTo(BaseTextBuffer* f) const;
 
   virtual bool Equals(const NativeLocation& other) const;
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 5bb9f4c..c5cd230 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -6,8 +6,12 @@
 
 #include "platform/assert.h"
 #include "platform/globals.h"
+#include "vm/class_id.h"
 #include "vm/compiler/runtime_api.h"
+#include "vm/growable_array.h"
+#include "vm/log.h"
 #include "vm/object.h"
+#include "vm/symbols.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 #include "vm/compiler/backend/locations.h"
@@ -324,6 +328,25 @@
   f->Printf("%s", PrimitiveTypeToCString(representation_));
 }
 
+const char* NativeFunctionType::ToCString() const {
+  char buffer[1024];
+  BufferFormatter bf(buffer, 1024);
+  PrintTo(&bf);
+  return Thread::Current()->zone()->MakeCopyOfString(buffer);
+}
+
+void NativeFunctionType::PrintTo(BaseTextBuffer* f) const {
+  f->AddString("(");
+  for (intptr_t i = 0; i < argument_types_.length(); i++) {
+    if (i > 0) {
+      f->AddString(", ");
+    }
+    argument_types_[i]->PrintTo(f);
+  }
+  f->AddString(") => ");
+  return_type_.PrintTo(f);
+}
+
 const NativeType& NativeType::WidenTo4Bytes(Zone* zone) const {
   if (IsInt() && SizeInBytes() <= 2) {
     if (IsSigned()) {
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 66d5f31..693b7c0 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -10,6 +10,7 @@
 #include "platform/assert.h"
 #include "vm/allocation.h"
 #include "vm/compiler/runtime_api.h"
+#include "vm/growable_array.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 #include "vm/compiler/backend/locations.h"
@@ -66,7 +67,7 @@
   virtual bool IsFloat() const { return false; }
   virtual bool IsVoid() const { return false; }
 
-  virtual bool IsSigned() const = 0;
+  virtual bool IsSigned() const { return false; }
 
   // The size in bytes of this representation.
   //
@@ -84,7 +85,7 @@
   virtual bool IsExpressibleAsRepresentation() const { return false; }
 
   // Unboxed Representation if it exists.
-  virtual Representation AsRepresentation() const = 0;
+  virtual Representation AsRepresentation() const { UNREACHABLE(); }
 
   // Unboxed Representation, over approximates if needed.
   Representation AsRepresentationOverApprox(Zone* zone_) const {
@@ -167,6 +168,25 @@
   const PrimitiveType representation_;
 };
 
+using NativeTypes = ZoneGrowableArray<const NativeType*>;
+
+class NativeFunctionType : public ZoneAllocated {
+ public:
+  NativeFunctionType(const NativeTypes& argument_types,
+                     const NativeType& return_type)
+      : argument_types_(argument_types), return_type_(return_type) {}
+
+  const NativeTypes& argument_types() const { return argument_types_; }
+  const NativeType& return_type() const { return return_type_; }
+
+  void PrintTo(BaseTextBuffer* f) const;
+  const char* ToCString() const;
+
+ private:
+  const NativeTypes& argument_types_;
+  const NativeType& return_type_;
+};
+
 }  // namespace ffi
 
 }  // namespace compiler
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 5a146e0..0c3d7cd 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1555,8 +1555,6 @@
   }
 }
 
-void Debugger::OnIsolateRunnable() {}
-
 bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
   ActivationFrame* top_frame = TopDartFrame();
   if (!IsAtAsyncJump(top_frame)) {
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index e5fd4ac..18691a4 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -470,8 +470,6 @@
   void NotifyIsolateCreated();
   void Shutdown();
 
-  void OnIsolateRunnable();
-
   void NotifyCompilation(const Function& func);
   void NotifyDoneLoading();
 
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index 8b9ac83..77cc4e5 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -86,25 +86,35 @@
   virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* p = first; p <= last; p++) {
       ObjectPtr old_target = *p;
+      ObjectPtr new_target;
       if (IsForwardingObject(old_target)) {
-        ObjectPtr new_target = GetForwardedObject(old_target);
-        if (visiting_object_ == nullptr) {
-          *p = new_target;
-        } else if (visiting_object_->ptr()->IsCardRemembered()) {
-          visiting_object_->ptr()->StoreArrayPointer(p, new_target, thread_);
-        } else {
-          visiting_object_->ptr()->StorePointer(p, new_target, thread_);
-        }
+        new_target = GetForwardedObject(old_target);
+      } else {
+        // Though we do not need to update the slot's value when it is not
+        // forwarded, we do need to recheck the generational barrier. In
+        // particular, the remembered bit may be incorrectly false if this
+        // become was the result of aborting a scavenge while visiting the
+        // remembered set.
+        new_target = old_target;
+      }
+      if (visiting_object_ == nullptr) {
+        *p = new_target;
+      } else if (visiting_object_->ptr()->IsCardRemembered()) {
+        visiting_object_->ptr()->StoreArrayPointer(p, new_target, thread_);
+      } else {
+        visiting_object_->ptr()->StorePointer(p, new_target, thread_);
       }
     }
   }
 
   void VisitingObject(ObjectPtr obj) {
     visiting_object_ = obj;
+    // The incoming remembered bit may be unreliable. Clear it so we can
+    // consistently reapply the barrier to all slots.
     if ((obj != nullptr) && obj->IsOldObject() && obj->ptr()->IsRemembered()) {
       ASSERT(!obj->IsForwardingCorpse());
       ASSERT(!obj->IsFreeListElement());
-      thread_->StoreBufferAddObjectGC(obj);
+      obj->ptr()->ClearRememberedBit();
     }
   }
 
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index e45ebd5..0de6a9f 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -830,7 +830,7 @@
       if (raw_obj->IsHeapObject() && raw_obj->IsNewObject()) {
         if (!is_remembered_) {
           FATAL3(
-              "Old object %#" Px "references new object %#" Px
+              "Old object %#" Px " references new object %#" Px
               ", but it is not"
               " in any store buffer. Consider using rr to watch the slot %p and"
               " reverse-continue to find the store with a missing barrier.\n",
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 222e5a3..ca08cac 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2061,7 +2061,6 @@
   set_is_runnable(true);
 #ifndef PRODUCT
   if (!Isolate::IsSystemIsolate(this)) {
-    debugger()->OnIsolateRunnable();
     if (FLAG_pause_isolates_on_unhandled_exceptions) {
       debugger()->SetExceptionPauseInfo(kPauseOnUnhandledExceptions);
     }
diff --git a/tools/VERSION b/tools/VERSION
index 5928929..a506401 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 29
+PRERELEASE 30
 PRERELEASE_PATCH 0
\ No newline at end of file