Version 2.13.0-41.0.dev

Merge commit '4302f058dd6a4a54b602e9a5a4db0bc5cedd578e' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 02af79d..87231e2 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -116,9 +116,12 @@
       server.clientCapabilities?.workspace,
       edits
           .map((e) => FileEditInformation(
-              server.getVersionedDocumentIdentifier(e.file),
-              server.getLineInfo(e.file),
-              e.edits))
+                server.getVersionedDocumentIdentifier(e.file),
+                server.getLineInfo(e.file),
+                e.edits,
+                // fileStamp == 1 is used by the server to indicate the file needs creating.
+                newFile: e.fileStamp == -1,
+              ))
           .toList());
 }
 
@@ -1253,14 +1256,36 @@
   final clientSupportsTextDocumentEdits =
       capabilities?.workspaceEdit?.documentChanges == true;
   if (clientSupportsTextDocumentEdits) {
+    final clientSupportsCreate = capabilities?.workspaceEdit?.resourceOperations
+            ?.contains(ResourceOperationKind.Create) ??
+        false;
+    final changes = <
+        Either4<lsp.TextDocumentEdit, lsp.CreateFile, lsp.RenameFile,
+            lsp.DeleteFile>>[];
+
+    // Convert each SourceEdit to either a TextDocumentEdit or a
+    // CreateFile + a TextDocumentEdit depending on whether it's a new
+    // file.
+    for (final edit in edits) {
+      if (clientSupportsCreate && edit.newFile) {
+        final create = lsp.CreateFile(uri: edit.doc.uri);
+        final createUnion = Either4<lsp.TextDocumentEdit, lsp.CreateFile,
+            lsp.RenameFile, lsp.DeleteFile>.t2(create);
+        changes.add(createUnion);
+      }
+
+      final textDocEdit = toTextDocumentEdit(edit);
+      final textDocEditUnion = Either4<lsp.TextDocumentEdit, lsp.CreateFile,
+          lsp.RenameFile, lsp.DeleteFile>.t1(textDocEdit);
+      changes.add(textDocEditUnion);
+    }
+
     return lsp.WorkspaceEdit(
         documentChanges: Either2<
             List<lsp.TextDocumentEdit>,
             List<
                 Either4<lsp.TextDocumentEdit, lsp.CreateFile, lsp.RenameFile,
-                    lsp.DeleteFile>>>.t1(
-      edits.map(toTextDocumentEdit).toList(),
-    ));
+                    lsp.DeleteFile>>>.t2(changes));
   } else {
     return lsp.WorkspaceEdit(changes: toWorkspaceEditChanges(edits));
   }
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 0307417..e1cc013 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -305,6 +305,8 @@
   final OptionalVersionedTextDocumentIdentifier doc;
   final LineInfo lineInfo;
   final List<server.SourceEdit> edits;
+  final bool newFile;
 
-  FileEditInformation(this.doc, this.lineInfo, this.edits);
+  FileEditInformation(this.doc, this.lineInfo, this.edits,
+      {this.newFile = false});
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index c2753fe..cb7e610 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -161,8 +161,6 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/generated/parser.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart'
-    hide AnalysisError, Element, ElementKind;
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
@@ -182,96 +180,13 @@
     try {
       var processor = FixProcessor(context);
       var fixes = await processor.compute();
-      var fixAllFixes = await _computeFixAllFixes(context, fixes);
-      return List.from(fixes)..addAll(fixAllFixes);
+      // todo (pq): add fixes from FixInFileProcessor
+      // https://github.com/dart-lang/sdk/issues/45026
+      return fixes;
     } on CancelCorrectionException {
       return const <Fix>[];
     }
   }
-
-  Future<List<Fix>> _computeFixAllFixes(
-      DartFixContext context, List<Fix> fixes) async {
-    final analysisError = context.error;
-    final allAnalysisErrors = context.resolveResult.errors.toList();
-
-    // Validate inputs:
-    // - return if no fixes
-    // - return if no other analysis errors
-    if (fixes.isEmpty || allAnalysisErrors.length < 2) {
-      return const <Fix>[];
-    }
-
-    // Remove any analysis errors that don't have the expected error code name
-    allAnalysisErrors
-        .removeWhere((e) => analysisError.errorCode.name != e.errorCode.name);
-    if (allAnalysisErrors.length < 2) {
-      return const <Fix>[];
-    }
-
-    // A map between each FixKind and the List of associated fixes
-    var map = <FixKind, List<Fix>>{};
-
-    // Populate the HashMap by looping through all AnalysisErrors, creating a
-    // new FixProcessor to compute the other fixes that can be applied with this
-    // one.
-    // For each fix, put the fix into the HashMap.
-    for (var i = 0; i < allAnalysisErrors.length; i++) {
-      final FixContext fixContext = DartFixContextImpl(
-        context.instrumentationService,
-        context.workspace,
-        context.resolveResult,
-        allAnalysisErrors[i],
-        (name) => [],
-      );
-      var processorI = FixProcessor(fixContext);
-      var fixesListI = await processorI.compute();
-      for (var f in fixesListI) {
-        if (!map.containsKey(f.kind)) {
-          map[f.kind] = <Fix>[]..add(f);
-        } else {
-          map[f.kind].add(f);
-        }
-      }
-    }
-
-    // For each FixKind in the HashMap, union each list together, then return
-    // the set of unioned fixes.
-    var result = <Fix>[];
-    map.forEach((FixKind kind, List<Fix> fixesList) {
-      if (fixesList.first.kind.canBeAppliedTogether()) {
-        var unionFix = _unionFixList(fixesList);
-        if (unionFix != null) {
-          result.add(unionFix);
-        }
-      }
-    });
-    return result;
-  }
-
-  Fix _unionFixList(List<Fix> fixList) {
-    if (fixList == null || fixList.isEmpty) {
-      return null;
-    } else if (fixList.length == 1) {
-      return fixList[0];
-    }
-    var sourceChange = SourceChange(fixList[0].kind.appliedTogetherMessage);
-    sourceChange.edits = List.from(fixList[0].change.edits);
-    var edits = <SourceEdit>[];
-    edits.addAll(fixList[0].change.edits[0].edits);
-    sourceChange.linkedEditGroups =
-        List.from(fixList[0].change.linkedEditGroups);
-    for (var i = 1; i < fixList.length; i++) {
-      edits.addAll(fixList[i].change.edits[0].edits);
-      sourceChange.linkedEditGroups.addAll(fixList[i].change.linkedEditGroups);
-    }
-    // Sort the list of SourceEdits so that when the edits are applied, they
-    // are applied from the end of the file to the top of the file.
-    edits.sort((s1, s2) => s2.offset - s1.offset);
-
-    sourceChange.edits[0].edits = edits;
-
-    return Fix(fixList[0].kind, sourceChange);
-  }
 }
 
 /// Computer for Dart "fix all in file" fixes.
diff --git a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
index a0c3f5b..eadaf30 100644
--- a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:linter/src/rules.dart';
+import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -96,6 +97,38 @@
     expect(contents[mainFilePath], equals(expectedContent));
   }
 
+  Future<void> test_createFile() async {
+    const content = '''
+    import '[[newfile.dart]]';
+    ''';
+
+    final expectedCreatedFile =
+        path.join(path.dirname(mainFilePath), 'newfile.dart');
+
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize(
+      textDocumentCapabilities: withCodeActionKinds(
+          emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
+      workspaceCapabilities: withResourceOperationKinds(
+          emptyWorkspaceClientCapabilities, [ResourceOperationKind.Create]),
+    );
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        range: rangeFromMarkers(content));
+    final fixAction = findEditAction(codeActions,
+        CodeActionKind('quickfix.create.file'), "Create file 'newfile.dart'");
+
+    expect(fixAction, isNotNull);
+    expect(fixAction.edit.documentChanges, isNotNull);
+
+    // Ensure applying the changes creates the file and with the expected content.
+    final contents = {
+      mainFilePath: withoutMarkers(content),
+    };
+    applyDocumentChanges(contents, fixAction.edit.documentChanges);
+    expect(contents[expectedCreatedFile], isNotEmpty);
+  }
+
   Future<void> test_filtersCorrectly() async {
     const content = '''
     import 'dart:async';
@@ -239,8 +272,6 @@
   @override
   String get testPackageLanguageVersion => latestLanguageVersion;
 
-  /// todo (pq): prefer_is_empty newly produces fix-all-fixes; update this test appropriately
-  @failingTest
   Future<void> test_fixAll_notForAmbigiousProducers() async {
     // The ReplaceWithIsEmpty producer does not provide a FixKind up-front, as
     // it may produce `REPLACE_WITH_IS_EMPTY` or `REPLACE_WITH_IS_NOT_EMPTY`
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index ce6fb8b..f9d072c 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -403,6 +403,19 @@
     });
   }
 
+  ClientCapabilitiesWorkspace withResourceOperationKinds(
+    ClientCapabilitiesWorkspace source,
+    List<ResourceOperationKind> kinds,
+  ) {
+    return extendWorkspaceCapabilities(source, {
+      'workspaceEdit': {
+        'documentChanges':
+            true, // docChanges aren't included in resourceOperations
+        'resourceOperations': kinds.map((k) => k.toJson()).toList(),
+      }
+    });
+  }
+
   TextDocumentClientCapabilities withSignatureHelpContentFormat(
     TextDocumentClientCapabilities source,
     List<MarkupKind> formats,
@@ -572,8 +585,23 @@
     Map<String, String> oldFileContent,
     List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> changes,
   ) {
-    // TODO(dantup): Implement handling of resource changes (not currently used).
-    throw 'Test helper applyResourceChanges not currently supported';
+    for (final change in changes) {
+      change.map(
+        (textDocEdit) => applyTextDocumentEdits(oldFileContent, [textDocEdit]),
+        (create) => applyResourceCreate(oldFileContent, create),
+        (rename) => throw 'applyResourceChanges:Delete not currently supported',
+        (delete) => throw 'applyResourceChanges:Delete not currently supported',
+      );
+    }
+  }
+
+  void applyResourceCreate(
+      Map<String, String> oldFileContent, CreateFile create) {
+    final path = Uri.parse(create.uri).toFilePath();
+    if (oldFileContent.containsKey(path)) {
+      throw 'Recieved create instruction for $path which already existed.';
+    }
+    oldFileContent[path] = '';
   }
 
   String applyTextDocumentEdit(String content, TextDocumentEdit edit) {
@@ -585,7 +613,8 @@
     edits.forEach((edit) {
       final path = Uri.parse(edit.textDocument.uri).toFilePath();
       if (!oldFileContent.containsKey(path)) {
-        throw 'Recieved edits for $path which was not provided as a file to be edited';
+        throw 'Recieved edits for $path which was not provided as a file to be edited. '
+            'Perhaps a CreateFile change was missing from the edits?';
       }
       oldFileContent[path] = applyTextDocumentEdit(oldFileContent[path], edit);
     });
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_explicit_cast_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_explicit_cast_test.dart
index ebf1233..797f097 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_explicit_cast_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_explicit_cast_test.dart
@@ -56,6 +56,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_general_all() async {
     await resolveTestCode('''
 f(A a) {
@@ -98,6 +99,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_list_all() async {
     await resolveTestCode('''
 f(List<A> a) {
@@ -140,6 +142,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_map_all() async {
     await resolveTestCode('''
 f(Map<A, B> a) {
@@ -186,6 +189,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_needsParens_all() async {
     await resolveTestCode('''
 f(A a) {
@@ -232,6 +236,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_set_all() async {
     await resolveTestCode('''
 f(Set<A> a) {
@@ -284,6 +289,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_declaration_general_all() async {
     await resolveTestCode('''
 f(A a) {
@@ -322,6 +328,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_declaration_list_all() async {
     await resolveTestCode('''
 f(List<A> a) {
@@ -360,6 +367,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_declaration_map_all() async {
     await resolveTestCode('''
 f(Map<A, B> a) {
@@ -402,6 +410,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_declaration_needsParens_all() async {
     await resolveTestCode('''
 f(A a) {
@@ -444,6 +453,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_declaration_set_all() async {
     await resolveTestCode('''
 f(Set<A> a) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
index 95302e6..c7b5820 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
@@ -37,6 +37,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_nonBoolCondition_all() async {
     await resolveTestCode('''
 main(String p, String q) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
index 09cfd74..14c84b6 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_cast_test.dart
@@ -39,6 +39,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_assignment_all() async {
     await resolveTestCode('''
 main(Object p, Object q) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart
index 88b7f30..c8c9629 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unnecessary_parentheses_test.dart
@@ -23,6 +23,7 @@
   @override
   String get lintCode => LintNames.unnecessary_parenthesis;
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_all() async {
     await resolveTestCode('''
 void f() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
index 3fb232f..6a9cc3e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_import_test.dart
@@ -27,6 +27,7 @@
     useLineEndingsForPlatform = false;
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_all_diverseImports() async {
     await resolveTestCode('''
 import 'dart:math';
@@ -41,6 +42,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_all_diverseImports2() async {
     await resolveTestCode('''
 import 'dart:async';
@@ -62,6 +64,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_all_singleLine() async {
     await resolveTestCode('''
 import 'dart:math'; import 'dart:math'; import 'dart:math';
@@ -111,6 +114,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_multipleOfSame_all() async {
     await resolveTestCode('''
 import 'dart:math';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_boolean_with_bool_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_boolean_with_bool_test.dart
index 1e38a6f..0fa2fd0 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_boolean_with_bool_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_boolean_with_bool_test.dart
@@ -20,6 +20,7 @@
   @override
   FixKind get kind => DartFixKind.REPLACE_BOOLEAN_WITH_BOOL;
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_all() async {
     await resolveTestCode('''
 main() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
index 89ecb3f..9a6d0ed 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
@@ -33,6 +33,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_isNull_all() async {
     await resolveTestCode('''
 main(p, q) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/use_not_eq_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/use_not_eq_null_test.dart
index a22c3f8..bf7e9db 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/use_not_eq_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/use_not_eq_null_test.dart
@@ -33,6 +33,7 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
   Future<void> test_isNotNull_all() async {
     await resolveTestCode('''
 main(p, q) {
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index 550c6e1..4a7f62a 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -579,9 +579,7 @@
   return CompilerResult(0);
 }
 
-// Compute code size to embed in the generated JavaScript
-// for this module.  Return `null` to indicate when size could not be properly
-// computed for this module.
+/// Compute code size to embed in the generated JavaScript for this module.
 int _computeDartSize(Component component) {
   var dartSize = 0;
   var uriToSource = component.uriToSource;
@@ -589,7 +587,11 @@
     var libUri = lib.fileUri;
     var importUri = lib.importUri;
     var source = uriToSource[libUri];
-    if (source == null) return null;
+    if (source == null) {
+      // Sources that only contain external declarations have nothing to add to
+      // the sum.
+      continue;
+    }
     dartSize += source.source.length;
     for (var part in lib.parts) {
       var partUri = part.partUri;
@@ -599,7 +601,11 @@
       }
       var fileUri = libUri.resolve(partUri);
       var partSource = uriToSource[fileUri];
-      if (partSource == null) return null;
+      if (partSource == null) {
+        // Sources that only contain external declarations have nothing to add
+        // to the sum.
+        continue;
+      }
       dartSize += partSource.source.length;
     }
   }
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index aa9a2c4..d2de37c 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -145,18 +145,30 @@
 
    private:
     void VisitPointers(ObjectPtr* from, ObjectPtr* to) {
-      for (ObjectPtr* raw = from; raw <= to; raw++) {
-        if (!(*raw)->IsHeapObject() || (*raw)->untag()->IsCanonical()) {
-          continue;
-        }
-        if (visited_->GetValueExclusive(*raw) == 1) {
-          continue;
-        }
-        visited_->SetValueExclusive(*raw, 1);
-        working_set_->Add(*raw);
+      for (ObjectPtr* ptr = from; ptr <= to; ptr++) {
+        VisitObject(*ptr);
       }
     }
 
+    void VisitCompressedPointers(uword heap_base,
+                                 CompressedObjectPtr* from,
+                                 CompressedObjectPtr* to) {
+      for (CompressedObjectPtr* ptr = from; ptr <= to; ptr++) {
+        VisitObject(ptr->Decompress(heap_base));
+      }
+    }
+
+    void VisitObject(ObjectPtr obj) {
+      if (!obj->IsHeapObject() || obj->untag()->IsCanonical()) {
+        return;
+      }
+      if (visited_->GetValueExclusive(obj) == 1) {
+        return;
+      }
+      visited_->SetValueExclusive(obj, 1);
+      working_set_->Add(obj);
+    }
+
     WeakTable* visited_;
     MallocGrowableArray<ObjectPtr>* const working_set_;
   };
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 2c1ebd4..22ded31 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -962,7 +962,7 @@
           precompiler_(precompiler),
           subinstance_(Object::Handle()) {}
 
-    virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+    void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
       for (ObjectPtr* current = first; current <= last; current++) {
         subinstance_ = *current;
         if (subinstance_.IsInstance()) {
@@ -972,6 +972,18 @@
       subinstance_ = Object::null();
     }
 
+    void VisitCompressedPointers(uword heap_base,
+                                 CompressedObjectPtr* first,
+                                 CompressedObjectPtr* last) {
+      for (CompressedObjectPtr* current = first; current <= last; current++) {
+        subinstance_ = current->Decompress(heap_base);
+        if (subinstance_.IsInstance()) {
+          precompiler_->AddConstObject(Instance::Cast(subinstance_));
+        }
+      }
+      subinstance_ = Object::null();
+    }
+
    private:
     Precompiler* precompiler_;
     Object& subinstance_;
diff --git a/runtime/vm/gdb_helpers.cc b/runtime/vm/gdb_helpers.cc
index 61baa78..0dfa825 100644
--- a/runtime/vm/gdb_helpers.cc
+++ b/runtime/vm/gdb_helpers.cc
@@ -64,6 +64,15 @@
       OS::PrintErr("%p: %s\n", p, obj.ToCString());
     }
   }
+
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* p = first; p <= last; p++) {
+      Object& obj = Object::Handle(p->Decompress(heap_base));
+      OS::PrintErr("%p: %s\n", p, obj.ToCString());
+    }
+  }
 };
 
 DART_EXPORT
diff --git a/runtime/vm/heap/become.cc b/runtime/vm/heap/become.cc
index ea959e1..e51678a 100644
--- a/runtime/vm/heap/become.cc
+++ b/runtime/vm/heap/become.cc
@@ -83,7 +83,7 @@
         thread_(thread),
         visiting_object_(nullptr) {}
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* p = first; p <= last; p++) {
       ObjectPtr old_target = *p;
       ObjectPtr new_target;
@@ -107,6 +107,32 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* p = first; p <= last; p++) {
+      ObjectPtr old_target = p->Decompress(heap_base);
+      ObjectPtr new_target;
+      if (IsForwardingObject(old_target)) {
+        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_->untag()->IsCardRemembered()) {
+        visiting_object_->untag()->StoreArrayPointer(p, new_target, thread_);
+      } else {
+        visiting_object_->untag()->StorePointer(p, new_target, thread_);
+      }
+    }
+  }
+
   void VisitingObject(ObjectPtr obj) {
     visiting_object_ = obj;
     // The incoming remembered bit may be unreliable. Clear it so we can
diff --git a/runtime/vm/heap/compactor.cc b/runtime/vm/heap/compactor.cc
index 5a432f7..7006f3e 100644
--- a/runtime/vm/heap/compactor.cc
+++ b/runtime/vm/heap/compactor.cc
@@ -631,6 +631,42 @@
   *ptr = new_target;
 }
 
+DART_FORCE_INLINE
+void GCCompactor::ForwardCompressedPointer(uword heap_base,
+                                           CompressedObjectPtr* ptr) {
+  ObjectPtr old_target = ptr->Decompress(heap_base);
+  if (old_target->IsSmiOrNewObject()) {
+    return;  // Not moved.
+  }
+
+  uword old_addr = UntaggedObject::ToAddr(old_target);
+  intptr_t lo = 0;
+  intptr_t hi = image_page_hi_;
+  while (lo <= hi) {
+    intptr_t mid = (hi - lo + 1) / 2 + lo;
+    ASSERT(mid >= lo);
+    ASSERT(mid <= hi);
+    if (old_addr < image_page_ranges_[mid].start) {
+      hi = mid - 1;
+    } else if (old_addr >= image_page_ranges_[mid].end) {
+      lo = mid + 1;
+    } else {
+      return;  // Not moved (unaligned image page).
+    }
+  }
+
+  OldPage* page = OldPage::Of(old_target);
+  ForwardingPage* forwarding_page = page->forwarding_page();
+  if (forwarding_page == NULL) {
+    return;  // Not moved (VM isolate, large page, code page).
+  }
+
+  ObjectPtr new_target =
+      UntaggedObject::FromAddr(forwarding_page->Lookup(old_addr));
+  ASSERT(!new_target->IsSmiOrNewObject());
+  *ptr = new_target;
+}
+
 void GCCompactor::VisitTypedDataViewPointers(TypedDataViewPtr view,
                                              ObjectPtr* first,
                                              ObjectPtr* last) {
@@ -673,6 +709,14 @@
   }
 }
 
+void GCCompactor::VisitCompressedPointers(uword heap_base,
+                                          CompressedObjectPtr* first,
+                                          CompressedObjectPtr* last) {
+  for (CompressedObjectPtr* ptr = first; ptr <= last; ptr++) {
+    ForwardCompressedPointer(heap_base, ptr);
+  }
+}
+
 void GCCompactor::VisitHandle(uword addr) {
   FinalizablePersistentHandle* handle =
       reinterpret_cast<FinalizablePersistentHandle*>(addr);
diff --git a/runtime/vm/heap/compactor.h b/runtime/vm/heap/compactor.h
index 4ba033b..9dfa96d 100644
--- a/runtime/vm/heap/compactor.h
+++ b/runtime/vm/heap/compactor.h
@@ -38,10 +38,14 @@
   void SetupImagePageBoundaries();
   void ForwardStackPointers();
   void ForwardPointer(ObjectPtr* ptr);
+  void ForwardCompressedPointer(uword heap_base, CompressedObjectPtr* ptr);
   void VisitTypedDataViewPointers(TypedDataViewPtr view,
                                   ObjectPtr* first,
                                   ObjectPtr* last);
   void VisitPointers(ObjectPtr* first, ObjectPtr* last);
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last);
   void VisitHandle(uword addr);
 
   Heap* heap_;
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 5a6229b..3a79f14 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -121,6 +121,11 @@
   // is safe.
   NO_SANITIZE_THREAD
   ObjectPtr LoadPointerIgnoreRace(ObjectPtr* ptr) { return *ptr; }
+  NO_SANITIZE_THREAD
+  CompressedObjectPtr LoadCompressedPointerIgnoreRace(
+      CompressedObjectPtr* ptr) {
+    return *ptr;
+  }
 
   void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* current = first; current <= last; current++) {
@@ -128,6 +133,15 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* current = first; current <= last; current++) {
+      MarkObject(
+          LoadCompressedPointerIgnoreRace(current).Decompress(heap_base));
+    }
+  }
+
   void EnqueueWeakProperty(WeakPropertyPtr raw_weak) {
     ASSERT(raw_weak->IsHeapObject());
     ASSERT(raw_weak->IsOldObject());
@@ -479,6 +493,12 @@
       }
     }
   }
+
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    UNREACHABLE();  // ObjectIdRing is not compressed.
+  }
 };
 
 void GCMarker::ProcessObjectIdTable(Thread* thread) {
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 3004841..1add15e 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -145,6 +145,20 @@
     ASSERT((index >= 0) && (index < card_table_size()));
     card_table_[index] = 1;
   }
+#if defined(DART_COMPRESSED_POINTERS)
+  void RememberCard(CompressedObjectPtr const* slot) {
+    ASSERT(Contains(reinterpret_cast<uword>(slot)));
+    if (card_table_ == NULL) {
+      card_table_ = reinterpret_cast<uint8_t*>(
+          calloc(card_table_size(), sizeof(uint8_t)));
+    }
+    intptr_t offset =
+        reinterpret_cast<uword>(slot) - reinterpret_cast<uword>(this);
+    intptr_t index = offset >> kBytesPerCardLog2;
+    ASSERT((index >= 0) && (index < card_table_size()));
+    card_table_[index] = 1;
+  }
+#endif
   void VisitRememberedCards(ObjectPointerVisitor* visitor);
 
  private:
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index baba05a..5a8c0d5 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -160,7 +160,7 @@
     view->untag()->RecomputeDataFieldForInternalTypedData();
   }
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     ASSERT(Utils::IsAligned(first, sizeof(*first)));
     ASSERT(Utils::IsAligned(last, sizeof(*last)));
     for (ObjectPtr* current = first; current <= last; current++) {
@@ -168,6 +168,16 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    ASSERT(Utils::IsAligned(first, sizeof(*first)));
+    ASSERT(Utils::IsAligned(last, sizeof(*last)));
+    for (CompressedObjectPtr* current = first; current <= last; current++) {
+      ScavengeCompressedPointer(heap_base, current);
+    }
+  }
+
   void VisitingOldObject(ObjectPtr obj) {
     ASSERT((obj == nullptr) || obj->IsOldObject());
     visiting_old_object_ = obj;
@@ -253,7 +263,7 @@
   NewPage* tail() const { return tail_; }
 
  private:
-  void UpdateStoreBuffer(ObjectPtr* p, ObjectPtr obj) {
+  void UpdateStoreBuffer(ObjectPtr obj) {
     ASSERT(obj->IsHeapObject());
     // If the newly written object is not a new object, drop it immediately.
     if (!obj->IsNewObject() || visiting_old_object_->untag()->IsRemembered()) {
@@ -272,6 +282,57 @@
       return;
     }
 
+    ObjectPtr new_obj = ScavengeObject(raw_obj);
+
+    // Update the reference.
+    if (!new_obj->IsNewObject()) {
+      // Setting the mark bit above must not be ordered after a publishing store
+      // of this object. Note this could be a publishing store even if the
+      // object was promoted by an early invocation of ScavengePointer. Compare
+      // Object::Allocate.
+      reinterpret_cast<std::atomic<ObjectPtr>*>(p)->store(
+          new_obj, std::memory_order_release);
+    } else {
+      *p = new_obj;
+    }
+
+    // Update the store buffer as needed.
+    if (visiting_old_object_ != nullptr) {
+      UpdateStoreBuffer(new_obj);
+    }
+  }
+
+  DART_FORCE_INLINE
+  void ScavengeCompressedPointer(uword heap_base, CompressedObjectPtr* p) {
+    // ScavengePointer cannot be called recursively.
+    ObjectPtr raw_obj = p->Decompress(heap_base);
+
+    if (raw_obj->IsSmiOrOldObject()) {  // Could be tested without decompression
+      return;
+    }
+
+    ObjectPtr new_obj = ScavengeObject(raw_obj);
+
+    // Update the reference.
+    if (!new_obj->IsNewObject()) {
+      // Setting the mark bit above must not be ordered after a publishing store
+      // of this object. Note this could be a publishing store even if the
+      // object was promoted by an early invocation of ScavengePointer. Compare
+      // Object::Allocate.
+      reinterpret_cast<std::atomic<ObjectPtr>*>(p)->store(
+          new_obj, std::memory_order_release);
+    } else {
+      *p = new_obj;
+    }
+
+    // Update the store buffer as needed.
+    if (visiting_old_object_ != nullptr) {
+      UpdateStoreBuffer(new_obj);
+    }
+  }
+
+  DART_FORCE_INLINE
+  ObjectPtr ScavengeObject(ObjectPtr raw_obj) {
     uword raw_addr = UntaggedObject::ToAddr(raw_obj);
     // The scavenger is only expects objects located in the from space.
     ASSERT(from_->Contains(raw_addr));
@@ -356,21 +417,7 @@
       }
     }
 
-    // Update the reference.
-    if (!new_obj->IsNewObject()) {
-      // Setting the mark bit above must not be ordered after a publishing store
-      // of this object. Note this could be a publishing store even if the
-      // object was promoted by an early invocation of ScavengePointer. Compare
-      // Object::Allocate.
-      reinterpret_cast<std::atomic<ObjectPtr>*>(p)->store(
-          new_obj, std::memory_order_release);
-    } else {
-      *p = new_obj;
-    }
-    // Update the store buffer as needed.
-    if (visiting_old_object_ != nullptr) {
-      UpdateStoreBuffer(p, new_obj);
-    }
+    return new_obj;
   }
 
   DART_FORCE_INLINE
@@ -783,6 +830,12 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* from,
+                               CompressedObjectPtr* to) {
+    UNREACHABLE();  // Store buffer blocks are not compressed.
+  }
+
  private:
   ObjectSet* const in_store_buffer_;
 };
@@ -831,6 +884,12 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* from,
+                               CompressedObjectPtr* to) {
+    UNREACHABLE();  // Store buffer blocks are not compressed.
+  }
+
  private:
   const ObjectSet* const in_store_buffer_;
   const SemiSpace* const to_;
diff --git a/runtime/vm/heap/scavenger_test.cc b/runtime/vm/heap/scavenger_test.cc
index 8d405eb..03c8f94 100644
--- a/runtime/vm/heap/scavenger_test.cc
+++ b/runtime/vm/heap/scavenger_test.cc
@@ -20,7 +20,10 @@
 class FailingObjectPointerVisitor : public ObjectPointerVisitor {
  public:
   FailingObjectPointerVisitor() : ObjectPointerVisitor(NULL) {}
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) { EXPECT(false); }
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
     EXPECT(false);
   }
 };
diff --git a/runtime/vm/heap/verifier.cc b/runtime/vm/heap/verifier.cc
index b0e0d70..abeb1cc 100644
--- a/runtime/vm/heap/verifier.cc
+++ b/runtime/vm/heap/verifier.cc
@@ -60,6 +60,24 @@
   }
 }
 
+void VerifyPointersVisitor::VisitCompressedPointers(uword heap_base,
+                                                    CompressedObjectPtr* first,
+                                                    CompressedObjectPtr* last) {
+  for (CompressedObjectPtr* current = first; current <= last; current++) {
+    ObjectPtr raw_obj = current->Decompress(heap_base);
+    if (raw_obj->IsHeapObject()) {
+      if (!allocated_set_->Contains(raw_obj)) {
+        if (raw_obj->IsInstructions() &&
+            allocated_set_->Contains(OldPage::ToWritable(raw_obj))) {
+          continue;
+        }
+        uword raw_addr = UntaggedObject::ToAddr(raw_obj);
+        FATAL1("Invalid object pointer encountered %#" Px "\n", raw_addr);
+      }
+    }
+  }
+}
+
 void VerifyWeakPointersVisitor::VisitHandle(uword addr) {
   FinalizablePersistentHandle* handle =
       reinterpret_cast<FinalizablePersistentHandle*>(addr);
diff --git a/runtime/vm/heap/verifier.h b/runtime/vm/heap/verifier.h
index 3733125..0b490cd 100644
--- a/runtime/vm/heap/verifier.h
+++ b/runtime/vm/heap/verifier.h
@@ -47,7 +47,10 @@
                                  ObjectSet* allocated_set)
       : ObjectPointerVisitor(isolate_group), allocated_set_(allocated_set) {}
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last);
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last);
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last);
 
   static void VerifyPointers(MarkExpectation mark_expectation = kForbidMarked);
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 15d93cc..8aa06a4 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2642,6 +2642,26 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* from,
+                               CompressedObjectPtr* to) {
+    if (old_obj_->IsArray()) {
+      for (CompressedObjectPtr* slot = from; slot <= to; ++slot) {
+        ObjectPtr value = slot->Decompress(heap_base);
+        if (value->IsHeapObject()) {
+          old_obj_->untag()->CheckArrayPointerStore(slot, value, thread_);
+        }
+      }
+    } else {
+      for (CompressedObjectPtr* slot = from; slot <= to; ++slot) {
+        ObjectPtr value = slot->Decompress(heap_base);
+        if (value->IsHeapObject()) {
+          old_obj_->untag()->CheckHeapPointerStore(value, thread_);
+        }
+      }
+    }
+  }
+
  private:
   Thread* thread_;
   ObjectPtr old_obj_;
@@ -18406,6 +18426,14 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    if (first != last) {
+      has_pointers_ = true;
+    }
+  }
+
  private:
   bool has_pointers_;
 
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index e66e67b..aae4940 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -53,21 +53,32 @@
   // Marks and pushes. Used to initialize this stack with roots.
   // We can use ObjectIdTable normally used by serializers because it
   // won't be in use while handling a service request (ObjectGraph's only use).
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* current = first; current <= last; ++current) {
-      if ((*current)->IsHeapObject() &&
-          !(*current)->untag()->InVMIsolateHeap() &&
-          object_ids_->GetValueExclusive(*current) == 0) {  // not visited yet
-        if (!include_vm_objects_ && !IsUserClass((*current)->GetClassId())) {
-          continue;
-        }
-        object_ids_->SetValueExclusive(*current, 1);
-        Node node;
-        node.ptr = current;
-        node.obj = *current;
-        node.gc_root_type = gc_root_type();
-        data_.Add(node);
+      Visit(current, *current);
+    }
+  }
+
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* current = first; current <= last; ++current) {
+      Visit(current, current->Decompress(heap_base));
+    }
+  }
+
+  void Visit(void* ptr, ObjectPtr obj) {
+    if (obj->IsHeapObject() && !obj->untag()->InVMIsolateHeap() &&
+        object_ids_->GetValueExclusive(obj) == 0) {  // not visited yet
+      if (!include_vm_objects_ && !IsUserClass(obj->GetClassId())) {
+        return;
       }
+      object_ids_->SetValueExclusive(obj, 1);
+      Node node;
+      node.ptr = ptr;
+      node.obj = obj;
+      node.gc_root_type = gc_root_type();
+      data_.Add(node);
     }
   }
 
@@ -112,7 +123,7 @@
 
  private:
   struct Node {
-    ObjectPtr* ptr;  // kSentinel for the sentinel node.
+    void* ptr;  // kSentinel for the sentinel node.
     ObjectPtr obj;
     const char* gc_root_type;
   };
@@ -165,7 +176,6 @@
   Stack::Node parent = stack_->data_[parent_index];
   uword parent_start = UntaggedObject::ToAddr(parent.obj);
   Stack::Node child = stack_->data_[index_];
-  ASSERT(child.obj == *child.ptr);
   uword child_ptr_addr = reinterpret_cast<uword>(child.ptr);
   intptr_t offset = child_ptr_addr - parent_start;
   if (offset > 0 && offset < parent.obj->untag()->HeapSize()) {
@@ -484,7 +494,7 @@
     raw_obj->untag()->VisitPointers(this);
   }
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* current_ptr = first; current_ptr <= last; current_ptr++) {
       ObjectPtr current_obj = *current_ptr;
       if (current_obj == target_) {
@@ -515,6 +525,40 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* current_ptr = first; current_ptr <= last;
+         current_ptr++) {
+      ObjectPtr current_obj = current_ptr->Decompress(heap_base);
+      if (current_obj == target_) {
+        intptr_t obj_index = length_ * 2;
+        intptr_t offset_index = obj_index + 1;
+        if (!references_.IsNull() && offset_index < references_.Length()) {
+          *scratch_ = source_;
+          references_.SetAt(obj_index, *scratch_);
+
+          *scratch_ = Smi::New(0);
+          uword source_start = UntaggedObject::ToAddr(source_);
+          uword current_ptr_addr = reinterpret_cast<uword>(current_ptr);
+          intptr_t offset = current_ptr_addr - source_start;
+          if (offset > 0 && offset < source_->untag()->HeapSize()) {
+            ASSERT(Utils::IsAligned(offset, kWordSize));
+            *scratch_ = Smi::New(offset >> kWordSizeLog2);
+          } else {
+            // Some internal VM objects visit pointers not contained within the
+            // parent. For instance, UntaggedCode::VisitCodePointers visits
+            // pointers in instructions.
+            ASSERT(!source_->IsDartInstance());
+            *scratch_ = Smi::New(-1);
+          }
+          references_.SetAt(offset_index, *scratch_);
+        }
+        ++length_;
+      }
+    }
+  }
+
  private:
   ObjectPtr source_;
   ObjectPtr target_;
@@ -786,6 +830,14 @@
     writer_->CountReferences(count);
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* from,
+                               CompressedObjectPtr* to) {
+    intptr_t count = to - from + 1;
+    ASSERT(count >= 0);
+    writer_->CountReferences(count);
+  }
+
   void VisitHandle(uword addr) {
     FinalizablePersistentHandle* weak_persistent_handle =
         reinterpret_cast<FinalizablePersistentHandle*>(addr);
@@ -981,6 +1033,23 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* from,
+                               CompressedObjectPtr* to) {
+    if (writing_) {
+      for (CompressedObjectPtr* ptr = from; ptr <= to; ptr++) {
+        ObjectPtr target = ptr->Decompress(heap_base);
+        written_++;
+        total_++;
+        writer_->WriteUnsigned(writer_->GetObjectId(target));
+      }
+    } else {
+      intptr_t count = to - from + 1;
+      ASSERT(count >= 0);
+      counted_ += count;
+    }
+  }
+
   void VisitHandle(uword addr) {
     FinalizablePersistentHandle* weak_persistent_handle =
         reinterpret_cast<FinalizablePersistentHandle*>(addr);
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 6a03bff..6bd76cd 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -434,6 +434,22 @@
     return Type::InstanceSize();                                               \
   }
 
+#if !defined(DART_COMPRESSED_POINTERS)
+#define COMPRESSED_VISITOR(Type) REGULAR_VISITOR(Type)
+#else
+#define COMPRESSED_VISITOR(Type)                                               \
+  intptr_t Untagged##Type::Visit##Type##Pointers(                              \
+      Type##Ptr raw_obj, ObjectPointerVisitor* visitor) {                      \
+    /* Make sure that we got here with the tagged pointer as this. */          \
+    ASSERT(raw_obj->IsHeapObject());                                           \
+    ASSERT_UNCOMPRESSED(Type);                                                 \
+    visitor->VisitCompressedPointers(raw_obj->heap_base(),                     \
+                                     raw_obj->untag()->from(),                 \
+                                     raw_obj->untag()->to());                  \
+    return Type::InstanceSize();                                               \
+  }
+#endif
+
 // It calls the from() and to() methods on the raw object to get the first and
 // last cells that need visiting.
 //
@@ -463,10 +479,22 @@
     return Type::InstanceSize(length);                                         \
   }
 
-// For now there are no compressed pointers:
-#define COMPRESSED_VISITOR(Type) REGULAR_VISITOR(Type)
+#if !defined(DART_COMPRESSED_POINTERS)
 #define VARIABLE_COMPRESSED_VISITOR(Type, get_length)                          \
   VARIABLE_VISITOR(Type, get_length)
+#else
+#define VARIABLE_COMPRESSED_VISITOR(Type, get_length)                          \
+  intptr_t Untagged##Type::Visit##Type##Pointers(                              \
+      Type##Ptr raw_obj, ObjectPointerVisitor* visitor) {                      \
+    /* Make sure that we got here with the tagged pointer as this. */          \
+    ASSERT(raw_obj->IsHeapObject());                                           \
+    intptr_t length = get_length;                                              \
+    visitor->VisitCompressedPointers(raw_obj->heap_base(),                     \
+                                     raw_obj->untag()->from(),                 \
+                                     raw_obj->untag()->to(length));            \
+    return Type::InstanceSize(length);                                         \
+  }
+#endif
 
 // For fixed-length objects that don't have any pointers that need visiting.
 #define NULL_VISITOR(Type)                                                     \
@@ -505,7 +533,7 @@
 REGULAR_VISITOR(TypeParameter)
 REGULAR_VISITOR(PatchClass)
 REGULAR_VISITOR(Function)
-COMPRESSED_VISITOR(Closure)
+REGULAR_VISITOR(Closure)
 REGULAR_VISITOR(ClosureData)
 REGULAR_VISITOR(FfiTrampolineData)
 REGULAR_VISITOR(Script)
@@ -523,9 +551,9 @@
 REGULAR_VISITOR(UnwindError)
 REGULAR_VISITOR(ExternalOneByteString)
 REGULAR_VISITOR(ExternalTwoByteString)
-COMPRESSED_VISITOR(GrowableObjectArray)
-COMPRESSED_VISITOR(LinkedHashMap)
-COMPRESSED_VISITOR(ExternalTypedData)
+REGULAR_VISITOR(GrowableObjectArray)
+REGULAR_VISITOR(LinkedHashMap)
+REGULAR_VISITOR(ExternalTypedData)
 TYPED_DATA_VIEW_VISITOR(TypedDataView)
 REGULAR_VISITOR(ReceivePort)
 REGULAR_VISITOR(StackTrace)
@@ -541,11 +569,10 @@
 VARIABLE_VISITOR(LocalVarDescriptors, raw_obj->untag()->num_entries_)
 VARIABLE_VISITOR(ExceptionHandlers, raw_obj->untag()->num_entries_)
 VARIABLE_VISITOR(Context, raw_obj->untag()->num_variables_)
-VARIABLE_COMPRESSED_VISITOR(Array, Smi::Value(raw_obj->untag()->length()))
-VARIABLE_COMPRESSED_VISITOR(
-    TypedData,
-    TypedData::ElementSizeInBytes(raw_obj->GetClassId()) *
-        Smi::Value(raw_obj->untag()->length_))
+VARIABLE_VISITOR(Array, Smi::Value(raw_obj->untag()->length()))
+VARIABLE_VISITOR(TypedData,
+                 TypedData::ElementSizeInBytes(raw_obj->GetClassId()) *
+                     Smi::Value(raw_obj->untag()->length_))
 VARIABLE_VISITOR(ContextScope, raw_obj->untag()->num_variables_)
 NULL_VISITOR(Mint)
 NULL_VISITOR(Double)
@@ -689,6 +716,12 @@
   OldPage::Of(static_cast<ObjectPtr>(this))->RememberCard(slot);
 }
 
+#if defined(DART_COMPRESSED_POINTERS)
+void UntaggedObject::RememberCard(CompressedObjectPtr const* slot) {
+  OldPage::Of(static_cast<ObjectPtr>(this))->RememberCard(slot);
+}
+#endif
+
 DEFINE_LEAF_RUNTIME_ENTRY(void,
                           RememberCard,
                           2,
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index ca51cc9..6b6cf9a 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -36,9 +36,6 @@
 
 namespace dart {
 
-// For now there are no compressed pointers.
-typedef ObjectPtr RawCompressed;
-
 // Forward declarations.
 class Isolate;
 class IsolateGroup;
@@ -560,6 +557,15 @@
     }
   }
 
+  void StorePointer(CompressedObjectPtr* addr,
+                    ObjectPtr value,
+                    Thread* thread) {
+    *addr = value;
+    if (value->IsHeapObject()) {
+      CheckHeapPointerStore(value, thread);
+    }
+  }
+
   template <typename type>
   void StorePointerUnaligned(type const* addr, type value, Thread* thread) {
     StoreUnaligned(const_cast<type*>(addr), value);
@@ -585,6 +591,15 @@
     }
   }
 
+  void StoreArrayPointer(CompressedObjectPtr* addr,
+                         ObjectPtr value,
+                         Thread* thread) {
+    *addr = value;
+    if (value->IsHeapObject()) {
+      CheckArrayPointerStore(addr, value, thread);
+    }
+  }
+
   template <typename type, std::memory_order order = std::memory_order_relaxed>
   type LoadSmi(type const* addr) const {
     return reinterpret_cast<std::atomic<type>*>(const_cast<type*>(addr))
@@ -642,7 +657,7 @@
         // old-and-not-remembered -> new reference.
         ASSERT(!this->IsRemembered());
         if (this->IsCardRemembered()) {
-          RememberCard(reinterpret_cast<ObjectPtr const*>(addr));
+          RememberCard(addr);
         } else {
           this->SetRememberedBit();
           thread->StoreBufferAddObject(static_cast<ObjectPtr>(this));
@@ -667,6 +682,9 @@
 
   friend class StoreBufferUpdateVisitor;  // RememberCard
   void RememberCard(ObjectPtr const* slot);
+#if defined(DART_COMPRESSED_POINTERS)
+  void RememberCard(CompressedObjectPtr const* slot);
+#endif
 
   friend class Array;
   friend class ByteBuffer;
@@ -2415,7 +2433,7 @@
 
   // The following fields are also declared in the Dart source of class
   // _Closure.
-  VISIT_FROM(RawCompressed, instantiator_type_arguments)
+  VISIT_FROM(ObjectPtr, instantiator_type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, instantiator_type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, function_type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, delayed_type_arguments)
@@ -2423,7 +2441,7 @@
   POINTER_FIELD(ContextPtr, context)
   POINTER_FIELD(SmiPtr, hash)
 
-  VISIT_TO(RawCompressed, hash)
+  VISIT_TO(ObjectPtr, hash)
 
   ObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
 
@@ -2586,8 +2604,8 @@
   void RecomputeDataField() { data_ = internal_data(); }
 
  protected:
-  VISIT_FROM(RawCompressed, length)
-  VISIT_TO_LENGTH(RawCompressed, &length_)
+  VISIT_FROM(ObjectPtr, length)
+  VISIT_TO_LENGTH(ObjectPtr, &length_)
 
   // Variable length data follows here.
 
@@ -2704,12 +2722,12 @@
 class UntaggedArray : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(Array);
 
-  VISIT_FROM(RawCompressed, type_arguments)
+  VISIT_FROM(ObjectPtr, type_arguments)
   ARRAY_POINTER_FIELD(TypeArgumentsPtr, type_arguments)
   SMI_FIELD(SmiPtr, length)
   // Variable length data follows here.
   VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
-  VISIT_TO_LENGTH(RawCompressed, &data()[length - 1])
+  VISIT_TO_LENGTH(ObjectPtr, &data()[length - 1])
 
   friend class LinkedHashMapSerializationCluster;
   friend class LinkedHashMapDeserializationCluster;
@@ -2739,11 +2757,11 @@
 class UntaggedGrowableObjectArray : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(GrowableObjectArray);
 
-  VISIT_FROM(RawCompressed, type_arguments)
+  VISIT_FROM(ObjectPtr, type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, type_arguments)
   SMI_FIELD(SmiPtr, length)
   POINTER_FIELD(ArrayPtr, data)
-  VISIT_TO(RawCompressed, data)
+  VISIT_TO(ObjectPtr, data)
   ObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
 
   friend class SnapshotReader;
@@ -2753,14 +2771,14 @@
 class UntaggedLinkedHashMap : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(LinkedHashMap);
 
-  VISIT_FROM(RawCompressed, type_arguments)
+  VISIT_FROM(ObjectPtr, type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, type_arguments)
   POINTER_FIELD(TypedDataPtr, index)
   POINTER_FIELD(SmiPtr, hash_mask)
   POINTER_FIELD(ArrayPtr, data)
   POINTER_FIELD(SmiPtr, used_data)
   POINTER_FIELD(SmiPtr, deleted_keys)
-  VISIT_TO(RawCompressed, deleted_keys)
+  VISIT_TO(ObjectPtr, deleted_keys)
 
   friend class SnapshotReader;
 };
@@ -2828,16 +2846,16 @@
   RAW_HEAP_OBJECT_IMPLEMENTATION(ExternalTypedData);
 
  protected:
-  VISIT_FROM(RawCompressed, length)
-  VISIT_TO(RawCompressed, length)
+  VISIT_FROM(ObjectPtr, length)
+  VISIT_TO(ObjectPtr, length)
 };
 
 class UntaggedPointer : public UntaggedPointerBase {
   RAW_HEAP_OBJECT_IMPLEMENTATION(Pointer);
 
-  VISIT_FROM(RawCompressed, type_arguments)
+  VISIT_FROM(ObjectPtr, type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, type_arguments)
-  VISIT_TO(RawCompressed, type_arguments)
+  VISIT_TO(ObjectPtr, type_arguments)
 
   friend class Pointer;
 };
@@ -2995,9 +3013,9 @@
 class UntaggedFutureOr : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(FutureOr);
 
-  VISIT_FROM(RawCompressed, type_arguments)
+  VISIT_FROM(ObjectPtr, type_arguments)
   POINTER_FIELD(TypeArgumentsPtr, type_arguments)
-  VISIT_TO(RawCompressed, type_arguments)
+  VISIT_TO(ObjectPtr, type_arguments)
 
   friend class SnapshotReader;
 };
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index f90b098..b3ee8a3 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1585,6 +1585,17 @@
   }
 }
 
+void SnapshotWriterVisitor::VisitCompressedPointers(uword heap_base,
+                                                    CompressedObjectPtr* first,
+                                                    CompressedObjectPtr* last) {
+  ASSERT(Utils::IsAligned(first, sizeof(*first)));
+  ASSERT(Utils::IsAligned(last, sizeof(*last)));
+  for (CompressedObjectPtr* current = first; current <= last; current++) {
+    ObjectPtr raw_obj = current->Decompress(heap_base);
+    writer_->WriteObjectImpl(raw_obj, as_references_);
+  }
+}
+
 MessageWriter::MessageWriter(bool can_send_any_object)
     : SnapshotWriter(Thread::Current(),
                      Snapshot::kMessage,
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 8ad3cb1..93678d0 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -740,7 +740,10 @@
         writer_(writer),
         as_references_(as_references) {}
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last);
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last);
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last);
 
  private:
   SnapshotWriter* writer_;
diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h
index 68dc056..c191404 100644
--- a/runtime/vm/tagged_pointer.h
+++ b/runtime/vm/tagged_pointer.h
@@ -188,6 +188,14 @@
       : tagged_pointer_(reinterpret_cast<uword>(heap_object) + kHeapObjectTag) {
   }
 
+  ObjectPtr Decompress(uword heap_base) const { return *this; }
+  uword heap_base() const {
+    ASSERT(IsHeapObject());
+    ASSERT(!IsInstructions());
+    ASSERT(!IsInstructionsSection());
+    return tagged_pointer_ & ~(4ULL * GB - 1);
+  }
+
  protected:
   uword untagged_pointer() const {
     ASSERT(IsHeapObject());
@@ -205,6 +213,34 @@
 }
 #endif
 
+#if !defined(DART_COMPRESSED_POINTERS)
+typedef ObjectPtr CompressedObjectPtr;
+#define DEFINE_COMPRESSED_POINTER(klass, base)                                 \
+  typedef klass##Ptr Compressed##klass##Ptr;
+#else
+class CompressedObjectPtr {
+ public:
+  explicit CompressedObjectPtr(ObjectPtr uncompressed)
+      : compressed_pointer_(
+            static_cast<int32_t>(static_cast<uword>(uncompressed))) {}
+
+  ObjectPtr Decompress(uword heap_base) const {
+    return static_cast<ObjectPtr>(
+        static_cast<uword>(static_cast<word>(compressed_pointer_)) + heap_base);
+  }
+
+  const ObjectPtr& operator=(const ObjectPtr& other) {
+    compressed_pointer_ = static_cast<int32_t>(static_cast<uword>(other));
+    return other;
+  }
+
+ protected:
+  int32_t compressed_pointer_;
+};
+#define DEFINE_COMPRESSED_POINTER(klass, base)                                 \
+  class Compressed##klass##Ptr : public Compressed##base##Ptr {};
+#endif
+
 #define DEFINE_TAGGED_POINTER(klass, base)                                     \
   class Untagged##klass;                                                       \
   class klass##Ptr : public base##Ptr {                                        \
@@ -228,7 +264,8 @@
     constexpr klass##Ptr(std::nullptr_t) : base##Ptr(nullptr) {} /* NOLINT */  \
     explicit klass##Ptr(const UntaggedObject* untagged)                        \
         : base##Ptr(reinterpret_cast<uword>(untagged) + kHeapObjectTag) {}     \
-  };
+  };                                                                           \
+  DEFINE_COMPRESSED_POINTER(klass, base)
 
 DEFINE_TAGGED_POINTER(Class, Object)
 DEFINE_TAGGED_POINTER(PatchClass, Object)
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index caff423..3308788 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -734,6 +734,12 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    UNREACHABLE();  // Stack slots are not compressed.
+  }
+
  private:
   Thread* const thread_;
   Thread* const current_;
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index 80be01e..4303b5f 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -83,7 +83,7 @@
   explicit ObjectCounter(IsolateGroup* isolate_group, const Object* obj)
       : ObjectPointerVisitor(isolate_group), obj_(obj), count_(0) {}
 
-  virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
+  void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
     for (ObjectPtr* current = first; current <= last; ++current) {
       if (*current == obj_->ptr()) {
         ++count_;
@@ -91,6 +91,16 @@
     }
   }
 
+  void VisitCompressedPointers(uword heap_base,
+                               CompressedObjectPtr* first,
+                               CompressedObjectPtr* last) {
+    for (CompressedObjectPtr* current = first; current <= last; ++current) {
+      if (current->Decompress(heap_base) == obj_->ptr()) {
+        ++count_;
+      }
+    }
+  }
+
   intptr_t count() const { return count_; }
 
  private:
diff --git a/runtime/vm/visitor.h b/runtime/vm/visitor.h
index eb9d343..8365418 100644
--- a/runtime/vm/visitor.h
+++ b/runtime/vm/visitor.h
@@ -35,6 +35,9 @@
 
   // Range of pointers to visit 'first' <= pointer <= 'last'.
   virtual void VisitPointers(ObjectPtr* first, ObjectPtr* last) = 0;
+  virtual void VisitCompressedPointers(uword heap_base,
+                                       CompressedObjectPtr* first,
+                                       CompressedObjectPtr* last) = 0;
 
   // len argument is the number of pointers to visit starting from 'p'.
   void VisitPointers(ObjectPtr* p, intptr_t len) {
diff --git a/tools/VERSION b/tools/VERSION
index b2b8996..0933579 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file