Version 2.15.0-233.0.dev

Merge commit '098d517e9fc49c3ee0495673ce7eb78e3dfef600' into 'dev'
diff --git a/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart b/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
index a1b1989..c2d764a 100644
--- a/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
+++ b/pkg/analysis_server/lib/src/provisional/completion/dart/completion_dart.dart
@@ -19,10 +19,14 @@
 /// An object that contributes results for the `completion.getSuggestions`
 /// request results.
 abstract class DartCompletionContributor {
+  final DartCompletionRequest request;
+  final SuggestionBuilder builder;
+
+  DartCompletionContributor(this.request, this.builder);
+
   /// Return a [Future] that completes when the suggestions appropriate for the
   /// given completion [request] have been added to the [builder].
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder);
+  Future<void> computeSuggestions();
 }
 
 /// The information about a requested list of completions within a Dart file.
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
index 05f7592..44f88c6 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/arglist_contributor.dart
@@ -14,20 +14,18 @@
 /// A contributor that produces suggestions for named expression labels that
 /// correspond to named parameters when completing in argument lists.
 class ArgListContributor extends DartCompletionContributor {
-  /// The request that is currently being handled.
-  late DartCompletionRequest request;
-
-  /// The suggestion builder used to build suggestions.
-  late SuggestionBuilder builder;
-
   /// The argument list that is the containing node of the target, or `null` if
   /// the containing node of the target is not an argument list (such as when
   /// it's a named expression).
   ArgumentList? argumentList;
 
+  ArgListContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var parameters = request.target.executableElement?.parameters ??
         request.target.functionType?.parameters;
     if (parameters == null) {
@@ -39,8 +37,6 @@
       argumentList = node;
     }
 
-    this.request = request;
-    this.builder = builder;
     _addSuggestions(parameters);
   }
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/combinator_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/combinator_contributor.dart
index 12fe155..138c369 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/combinator_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/combinator_contributor.dart
@@ -11,9 +11,13 @@
 /// A contributor that produces suggestions based on the members of a library
 /// when the completion is in a show or hide combinator of an import or export.
 class CombinatorContributor extends DartCompletionContributor {
+  CombinatorContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var node = request.target.containingNode;
     if (node is! Combinator) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index cec2be3..f8298ff 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -126,30 +126,32 @@
     // Request Dart specific completions from each contributor
     var builder = SuggestionBuilder(dartRequest, listener: listener);
     var contributors = <DartCompletionContributor>[
-      ArgListContributor(),
-      CombinatorContributor(),
-      ExtensionMemberContributor(),
-      FieldFormalContributor(),
-      KeywordContributor(),
-      LabelContributor(),
-      LibraryMemberContributor(),
-      LibraryPrefixContributor(),
-      LocalLibraryContributor(),
-      LocalReferenceContributor(),
-      NamedConstructorContributor(),
-      if (enableOverrideContributor) OverrideContributor(),
-      RedirectingContributor(),
-      StaticMemberContributor(),
-      TypeMemberContributor(),
-      if (enableUriContributor) UriContributor(),
-      VariableNameContributor()
+      ArgListContributor(dartRequest, builder),
+      CombinatorContributor(dartRequest, builder),
+      ExtensionMemberContributor(dartRequest, builder),
+      FieldFormalContributor(dartRequest, builder),
+      KeywordContributor(dartRequest, builder),
+      LabelContributor(dartRequest, builder),
+      LibraryMemberContributor(dartRequest, builder),
+      LibraryPrefixContributor(dartRequest, builder),
+      LocalLibraryContributor(dartRequest, builder),
+      LocalReferenceContributor(dartRequest, builder),
+      NamedConstructorContributor(dartRequest, builder),
+      if (enableOverrideContributor) OverrideContributor(dartRequest, builder),
+      RedirectingContributor(dartRequest, builder),
+      StaticMemberContributor(dartRequest, builder),
+      TypeMemberContributor(dartRequest, builder),
+      if (enableUriContributor) UriContributor(dartRequest, builder),
+      VariableNameContributor(dartRequest, builder),
     ];
 
     if (includedElementKinds != null) {
       _addIncludedElementKinds(dartRequest);
       _addIncludedSuggestionRelevanceTags(dartRequest);
     } else {
-      contributors.add(ImportedReferenceContributor());
+      contributors.add(
+        ImportedReferenceContributor(dartRequest, builder),
+      );
     }
 
     try {
@@ -157,7 +159,7 @@
         await performance.runAsync(
           'DartCompletionManager - ${contributor.runtimeType}',
           (_) async {
-            await contributor.computeSuggestions(dartRequest, builder);
+            await contributor.computeSuggestions();
           },
         );
         request.checkAborted();
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
index 476125a..58e3b83 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/extension_member_contributor.dart
@@ -16,9 +16,13 @@
 class ExtensionMemberContributor extends DartCompletionContributor {
   late MemberSuggestionBuilder memberBuilder;
 
+  ExtensionMemberContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var containingLibrary = request.libraryElement;
     memberBuilder = MemberSuggestionBuilder(request, builder);
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
index e722f99..d073956 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/field_formal_contributor.dart
@@ -12,9 +12,13 @@
 /// already initialized. More concretely, this class produces suggestions for
 /// expressions of the form `this.^` in a constructor's parameter list.
 class FieldFormalContributor extends DartCompletionContributor {
+  FieldFormalContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var node = request.target.containingNode;
     // TODO(brianwilkerson) We should suggest field formal parameters even if
     //  the user hasn't already typed the `this.` prefix, by including the
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
index e2a6c60..c10194f 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/imported_reference_contributor.dart
@@ -10,9 +10,13 @@
 
 /// A contributor for calculating suggestions for imported top level members.
 class ImportedReferenceContributor extends DartCompletionContributor {
+  ImportedReferenceContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     if (!request.includeIdentifiers) {
       return;
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
index 268064a..a32f625 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/keyword_contributor.dart
@@ -27,9 +27,13 @@
 /// A contributor that produces suggestions based on the set of keywords that
 /// are valid at the completion point.
 class KeywordContributor extends DartCompletionContributor {
+  KeywordContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     // Don't suggest anything right after double or integer literals.
     if (request.target.isDoubleOrIntLiteral()) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
index 9434271..c3d8f57 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/label_contributor.dart
@@ -14,9 +14,13 @@
 /// scope. More concretely, this class produces completions in `break` and
 /// `continue` statements.
 class LabelContributor extends DartCompletionContributor {
+  LabelContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var optype = (request as DartCompletionRequestImpl).opType;
 
     // Collect suggestions from the specific child [AstNode] that contains
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
index 75ae6b50..460324a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_member_contributor.dart
@@ -14,9 +14,13 @@
 /// produces suggestions for expressions of the form `p.^`, where `p` is a
 /// prefix.
 class LibraryMemberContributor extends DartCompletionContributor {
+  LibraryMemberContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     // Determine if the target looks like a library prefix.
     var targetId = request.dotTarget;
     if (targetId is SimpleIdentifier && !request.target.isCascade) {
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
index 0e0a8a4..5fad45d 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/library_prefix_contributor.dart
@@ -8,9 +8,13 @@
 /// A contributor that produces suggestions based on the prefixes defined on
 /// import directives.
 class LibraryPrefixContributor extends DartCompletionContributor {
+  LibraryPrefixContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     if (!request.includeIdentifiers) {
       return;
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
index f14d554..e0b113d 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_library_contributor.dart
@@ -151,9 +151,13 @@
 /// the library in which the completion is requested but outside the file in
 /// which the completion is requested.
 class LocalLibraryContributor extends DartCompletionContributor {
+  LocalLibraryContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     if (!request.includeIdentifiers) {
       return;
     }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
index b97e431..39c1dda 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/local_reference_contributor.dart
@@ -31,9 +31,13 @@
   /// been shadowed by local declarations.
   _VisibilityTracker visibilityTracker = _VisibilityTracker();
 
+  LocalReferenceContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var opType = request.opType;
     AstNode? node = request.target.containingNode;
 
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/named_constructor_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/named_constructor_contributor.dart
index 0632487..c120d04 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/named_constructor_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/named_constructor_contributor.dart
@@ -12,9 +12,13 @@
 /// for expressions of the form `C.^` or `C<E>.^`, where `C` is the name of a
 /// class.
 class NamedConstructorContributor extends DartCompletionContributor {
+  NamedConstructorContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var node = request.target.containingNode;
     if (node is ConstructorName) {
       var libraryElement = request.libraryElement;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
index 1cccc64..0e62f8e 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
@@ -10,10 +10,14 @@
 
 /// A completion contributor used to suggest replacing partial identifiers
 /// inside a class declaration with templates for inherited members.
-class OverrideContributor implements DartCompletionContributor {
+class OverrideContributor extends DartCompletionContributor {
+  OverrideContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var targetId = _getTargetId(request.target);
     if (targetId == null) {
       return;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/redirecting_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/redirecting_contributor.dart
index 3db1549..4fd743a 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/redirecting_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/redirecting_contributor.dart
@@ -11,9 +11,13 @@
 /// expressions of the form `this.^` or `super.^` in a constructor's initializer
 /// list or after an `=` in a factory constructor.
 class RedirectingContributor extends DartCompletionContributor {
+  RedirectingContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var entity = request.target.entity;
     if (entity is SimpleIdentifier) {
       var parent = entity.parent;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
index 15b6c33..1faa088 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/static_member_contributor.dart
@@ -12,9 +12,13 @@
 /// suggestions for expressions of the form `C.^`, where `C` is the name of a
 /// class, enum, or extension.
 class StaticMemberContributor extends DartCompletionContributor {
+  StaticMemberContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var library = request.libraryElement;
     bool isVisible(Element element) => element.isAccessibleIn(library);
     var targetId = request.dotTarget;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
index e67d203..f15812e 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/type_member_contributor.dart
@@ -17,9 +17,13 @@
 /// expressions of the form `o.^`, where `o` is an expression denoting an
 /// instance of a type.
 class TypeMemberContributor extends DartCompletionContributor {
+  TypeMemberContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     // Recompute the target because resolution might have changed it.
     var expression = request.dotTarget;
     if (expression == null ||
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
index 227c538..cbb8337 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/uri_contributor.dart
@@ -12,9 +12,13 @@
 /// A contributor that produces suggestions based on the content of the file
 /// system to complete within URIs in import, export and part directives.
 class UriContributor extends DartCompletionContributor {
+  UriContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var visitor = _UriSuggestionBuilder(request, builder);
     request.target.containingNode.accept(visitor);
   }
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
index ba74ab0..97b2249 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/variable_name_contributor.dart
@@ -11,9 +11,13 @@
 /// A contributor that produces suggestions for variable names based on the
 /// static type of the variable.
 class VariableNameContributor extends DartCompletionContributor {
+  VariableNameContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
   @override
-  Future<void> computeSuggestions(
-      DartCompletionRequest request, SuggestionBuilder builder) async {
+  Future<void> computeSuggestions() async {
     var opType = request.opType;
 
     // Collect suggestions from the specific child [AstNode] that contains
diff --git a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
index 789ff5f..8dfaa60 100644
--- a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/arglist_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
@@ -109,8 +110,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return ArgListContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return ArgListContributor(request, builder);
   }
 }
 
diff --git a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
index ea96470..e41e5a2 100644
--- a/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/combinator_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/combinator_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,8 +19,11 @@
 @reflectiveTest
 class CombinatorContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return CombinatorContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return CombinatorContributor(request, builder);
   }
 
   Future<void> test_Block_inherited_local() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
index 51ab571..8545220 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_contributor_util.dart
@@ -43,24 +43,20 @@
 /// suggestions.
 abstract class DartCompletionContributorTest
     extends _BaseDartCompletionContributorTest {
-  late DartCompletionContributor contributor;
-
   @nonVirtual
   @override
   Future<List<CompletionSuggestion>> computeContributedSuggestions(
       DartCompletionRequest request) async {
     var builder = SuggestionBuilder(request);
-    await contributor.computeSuggestions(request, builder);
+    var contributor = createContributor(request, builder);
+    await contributor.computeSuggestions();
     return builder.suggestions.toList();
   }
 
-  DartCompletionContributor createContributor();
-
-  @override
-  void setUp() {
-    super.setUp();
-    contributor = createContributor();
-  }
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  );
 }
 
 abstract class _BaseDartCompletionContributorTest extends AbstractContextTest {
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
index 8ebde10..38fb1ef 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_manager_test.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/imported_reference_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
@@ -26,8 +27,11 @@
 @reflectiveTest
 class CompletionManagerTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return ImportedReferenceContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return ImportedReferenceContributor(request, builder);
   }
 
   Future<void> test_resolveDirectives() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
index 5def996..4e72a0e 100644
--- a/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/extension_member_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/extension_member_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'completion_contributor_util.dart';
@@ -17,8 +18,11 @@
 @reflectiveTest
 class ExtensionMemberContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return ExtensionMemberContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return ExtensionMemberContributor(request, builder);
   }
 
   Future<void> test_extended_members_inExtension_field() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/field_formal_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/field_formal_contributor_test.dart
index d28095f..f38df45 100644
--- a/pkg/analysis_server/test/services/completion/dart/field_formal_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/field_formal_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,8 +19,11 @@
 @reflectiveTest
 class FieldFormalContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return FieldFormalContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return FieldFormalContributor(request, builder);
   }
 
   /// https://github.com/dart-lang/sdk/issues/39028
diff --git a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
index 05f16bd..b1cfe12 100644
--- a/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/imported_reference_contributor_test.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/imported_reference_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -22,8 +23,11 @@
   bool get isNullExpectedReturnTypeConsideredDynamic => false;
 
   @override
-  DartCompletionContributor createContributor() {
-    return ImportedReferenceContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return ImportedReferenceContributor(request, builder);
   }
 }
 
diff --git a/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
index 1673cfd..1d96cae 100644
--- a/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/keyword_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/keyword_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
@@ -443,8 +444,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return KeywordContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return KeywordContributor(request, builder);
   }
 
   /// Return `true` if the given [feature] is enabled.
diff --git a/pkg/analysis_server/test/services/completion/dart/label_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/label_contributor_test.dart
index 9204f6d..ad09ba3 100644
--- a/pkg/analysis_server/test/services/completion/dart/label_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/label_contributor_test.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/label_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -33,8 +34,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return LabelContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return LabelContributor(request, builder);
   }
 
   Future<void> test_break_ignores_outer_functions_using_closure() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
index 023cef5..d4a3f76 100644
--- a/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/library_member_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/library_member_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,8 +19,11 @@
 @reflectiveTest
 class LibraryMemberContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return LibraryMemberContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return LibraryMemberContributor(request, builder);
   }
 
   Future<void> test_extension() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/library_prefix_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/library_prefix_contributor_test.dart
index ae303ec..96ecfce 100644
--- a/pkg/analysis_server/test/services/completion/dart/library_prefix_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/library_prefix_contributor_test.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/library_prefix_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -34,8 +35,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return LibraryPrefixContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return LibraryPrefixContributor(request, builder);
   }
 
   Future<void> test_Block() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
index 6417557..d5cfe06 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_library_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/local_library_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,8 +19,11 @@
 @reflectiveTest
 class LocalLibraryContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return LocalLibraryContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return LocalLibraryContributor(request, builder);
   }
 
   Future<void> test_partFile_Constructor() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index 243cdb5..25888ec 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/local_reference_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -22,8 +23,11 @@
   bool get isNullExpectedReturnTypeConsideredDynamic => false;
 
   @override
-  DartCompletionContributor createContributor() {
-    return LocalReferenceContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return LocalReferenceContributor(request, builder);
   }
 
   Future<void> test_ArgDefaults_function() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/named_constructor_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/named_constructor_contributor_test.dart
index 0e35485..2c3ed18 100644
--- a/pkg/analysis_server/test/services/completion/dart/named_constructor_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/named_constructor_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/named_constructor_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -39,8 +40,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return NamedConstructorContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return NamedConstructorContributor(request, builder);
   }
 
   Future<void> test_ConstructorName_importedClass() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
index 7b27e88..3178bc4 100644
--- a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/override_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -17,8 +18,11 @@
 @reflectiveTest
 class OverrideContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return OverrideContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return OverrideContributor(request, builder);
   }
 
   Future<void> test_alreadyOverridden() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/static_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/static_member_contributor_test.dart
index 0cecf86..1c6e558 100644
--- a/pkg/analysis_server/test/services/completion/dart/static_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/static_member_contributor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/static_member_contributor.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -18,8 +19,11 @@
 @reflectiveTest
 class StaticMemberContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return StaticMemberContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return StaticMemberContributor(request, builder);
   }
 
   Future<void> test_class_static_notPrivate() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
index c9d8574..8633ece 100644
--- a/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/type_member_contributor_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/services/completion/dart/type_member_contributor.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
@@ -48,8 +49,11 @@
   }
 
   @override
-  DartCompletionContributor createContributor() {
-    return TypeMemberContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return TypeMemberContributor(request, builder);
   }
 
   Future<void> test_ArgDefaults_method() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/uri_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/uri_contributor_test.dart
index 0620fbf..f2e442c 100644
--- a/pkg/analysis_server/test/services/completion/dart/uri_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/uri_contributor_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/services/completion/dart/uri_contributor.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
@@ -25,8 +26,11 @@
   String get testPackageTestPath => '$testPackageRootPath/test';
 
   @override
-  DartCompletionContributor createContributor() {
-    return UriContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return UriContributor(request, builder);
   }
 
   Future<void> test_after_import() async {
@@ -550,8 +554,11 @@
 @reflectiveTest
 class UriContributorWindowsTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return UriContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return UriContributor(request, builder);
   }
 
   @override
diff --git a/pkg/analysis_server/test/services/completion/dart/variable_name_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/variable_name_contributor_test.dart
index 3b75a53..97bac2b 100644
--- a/pkg/analysis_server/test/services/completion/dart/variable_name_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/variable_name_contributor_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
 import 'package:analysis_server/src/services/completion/dart/variable_name_contributor.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -18,8 +19,11 @@
 @reflectiveTest
 class VariableNameContributorTest extends DartCompletionContributorTest {
   @override
-  DartCompletionContributor createContributor() {
-    return VariableNameContributor();
+  DartCompletionContributor createContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) {
+    return VariableNameContributor(request, builder);
   }
 
   Future<void> test_ExpressionStatement_dont_suggest_type() async {
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 033f4c4..926b8da 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -42,6 +42,8 @@
     'Double',
   ];
 
+  static const _primitiveBoolNativeType = 'Bool';
+
   static const _structClassName = 'Struct';
 
   static const _unionClassName = 'Union';
@@ -432,6 +434,8 @@
         return true;
       case _PrimitiveDartType.int:
         return true;
+      case _PrimitiveDartType.bool:
+        return true;
       case _PrimitiveDartType.void_:
         return false;
       case _PrimitiveDartType.handle:
@@ -490,6 +494,7 @@
           return allowHandle;
         case _PrimitiveDartType.double:
         case _PrimitiveDartType.int:
+        case _PrimitiveDartType.bool:
           return true;
         case _PrimitiveDartType.none:
           // These are the cases below.
@@ -560,6 +565,9 @@
         if (_primitiveDoubleNativeTypes.contains(name)) {
           return _PrimitiveDartType.double;
         }
+        if (name == _primitiveBoolNativeType) {
+          return _PrimitiveDartType.bool;
+        }
         if (name == 'Void') {
           return _PrimitiveDartType.void_;
         }
@@ -580,6 +588,8 @@
         return _PrimitiveDartType.int;
       } else if (_primitiveDoubleNativeTypes.contains(name)) {
         return _PrimitiveDartType.double;
+      } else if (_primitiveBoolNativeType == name) {
+        return _PrimitiveDartType.bool;
       }
     }
     return _PrimitiveDartType.none;
@@ -738,6 +748,8 @@
       return dartType.isDartCoreInt;
     } else if (nativeReturnType == _PrimitiveDartType.double) {
       return dartType.isDartCoreDouble;
+    } else if (nativeReturnType == _PrimitiveDartType.bool) {
+      return dartType.isDartCoreBool;
     } else if (nativeReturnType == _PrimitiveDartType.void_) {
       return dartType.isVoid;
     } else if (nativeReturnType == _PrimitiveDartType.handle) {
@@ -827,6 +839,8 @@
         _validateAnnotations(fieldType, annotations, _PrimitiveDartType.int);
       } else if (declaredType.isDartCoreDouble) {
         _validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
+      } else if (declaredType.isDartCoreBool) {
+        _validateAnnotations(fieldType, annotations, _PrimitiveDartType.bool);
       } else if (declaredType.isPointer) {
         _validateNoAnnotations(annotations);
       } else if (declaredType.isArray) {
@@ -1162,6 +1176,7 @@
 enum _PrimitiveDartType {
   double,
   int,
+  bool,
   void_,
   handle,
   none,
@@ -1337,6 +1352,8 @@
         return false;
       } else if (declaredType.isDartCoreDouble) {
         return false;
+      } else if (declaredType.isDartCoreBool) {
+        return false;
       } else if (declaredType.isPointer) {
         return false;
       } else if (declaredType.isCompoundSubtype) {
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect
index f15273b..1d8965d 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect
@@ -43,6 +43,9 @@
   ffi::AllocatorAlloc,
   ffi::Array,
   ffi::ArrayArray,
+  ffi::Bool,
+  ffi::BoolArray,
+  ffi::BoolPointer,
   ffi::DartRepresentationOf,
   ffi::Dart_CObject,
   ffi::Double,
@@ -113,6 +116,9 @@
   ffi::AllocatorAlloc,
   ffi::Array,
   ffi::ArrayArray,
+  ffi::Bool,
+  ffi::BoolArray,
+  ffi::BoolPointer,
   ffi::DartRepresentationOf,
   ffi::Dart_CObject,
   ffi::Double,
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect
index 0b32258..6bf1e19 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect
@@ -43,6 +43,9 @@
   ffi::AllocatorAlloc,
   ffi::Array,
   ffi::ArrayArray,
+  ffi::Bool,
+  ffi::BoolArray,
+  ffi::BoolPointer,
   ffi::DartRepresentationOf,
   ffi::Dart_CObject,
   ffi::Double,
@@ -113,6 +116,9 @@
   ffi::AllocatorAlloc,
   ffi::Array,
   ffi::ArrayArray,
+  ffi::Bool,
+  ffi::BoolArray,
+  ffi::BoolPointer,
   ffi::DartRepresentationOf,
   ffi::Dart_CObject,
   ffi::Double,
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index c39a22f..dc51195 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -39,6 +39,7 @@
   kOpaque,
   kStruct,
   kHandle,
+  kBool,
 }
 
 const Set<NativeType> nativeIntTypes = <NativeType>{
@@ -75,6 +76,7 @@
   NativeType.kOpaque: 'Opaque',
   NativeType.kStruct: 'Struct',
   NativeType.kHandle: 'Handle',
+  NativeType.kBool: 'Bool',
 };
 
 const int UNKNOWN = 0;
@@ -102,6 +104,7 @@
   NativeType.kOpaque: UNKNOWN,
   NativeType.kStruct: UNKNOWN,
   NativeType.kHandle: WORD_SIZE,
+  NativeType.kBool: 1,
 };
 
 /// The struct layout in various ABIs.
@@ -178,6 +181,7 @@
 
 /// Load, store, and elementAt are rewired to their static type for these types.
 const List<NativeType> optimizedTypes = [
+  NativeType.kBool,
   NativeType.kInt8,
   NativeType.kInt16,
   NativeType.kInt32,
@@ -210,6 +214,7 @@
   final Class objectClass;
   final Class intClass;
   final Class doubleClass;
+  final Class boolClass;
   final Class listClass;
   final Class typeClass;
   final Procedure unsafeCastMethod;
@@ -319,6 +324,7 @@
         objectClass = coreTypes.objectClass,
         intClass = coreTypes.intClass,
         doubleClass = coreTypes.doubleClass,
+        boolClass = coreTypes.boolClass,
         listClass = coreTypes.listClass,
         typeClass = coreTypes.typeClass,
         unsafeCastMethod =
@@ -514,6 +520,7 @@
   /// [IntPtr]                             -> [int]
   /// [Double]                             -> [double]
   /// [Float]                              -> [double]
+  /// [Bool]                               -> [bool]
   /// [Void]                               -> [void]
   /// [Pointer]<T>                         -> [Pointer]<T>
   /// T extends [Pointer]                  -> T
@@ -555,6 +562,9 @@
     if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) {
       return InterfaceType(doubleClass, Nullability.legacy);
     }
+    if (nativeType_ == NativeType.kBool) {
+      return InterfaceType(boolClass, Nullability.legacy);
+    }
     if (nativeType_ == NativeType.kVoid) {
       return VoidType();
     }
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 647c2ae..dc17bb0 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -35,6 +35,10 @@
   CHECK(((EXPECTED * 0.99) <= (ACTUAL) && (EXPECTED * 1.01) >= (ACTUAL)) ||    \
         ((EXPECTED * 0.99) >= (ACTUAL) && (EXPECTED * 1.01) <= (ACTUAL)))
 
+struct Struct1ByteBool {
+  bool a0;
+};
+
 struct Struct1ByteInt {
   int8_t a0;
 };
@@ -113,6 +117,19 @@
   int8_t a1;
 };
 
+struct Struct10BytesHomogeneousBool {
+  bool a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+};
+
 struct Struct12BytesHomogeneousFloat {
   float a0;
   float a1;
@@ -419,6 +436,10 @@
   uint8_t a0[8];
 };
 
+struct Struct10BytesInlineArrayBool {
+  bool a0[10];
+};
+
 struct StructInlineArrayIrregular {
   Struct3BytesInt2ByteAligned a0[2];
   uint8_t a1;
@@ -4746,6 +4767,134 @@
 }
 
 // Used for testing structs and unions by value.
+// Passing bools and a struct with bools.
+// Exhausts the registers to test bools and the bool struct alignment on the
+// stack.
+DART_EXPORT int32_t PassUint8Boolx9Struct10BytesHomogeneousBoolBool(
+    uint8_t a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesHomogeneousBool a10,
+    bool a11) {
+  std::cout << "PassUint8Boolx9Struct10BytesHomogeneousBoolBool"
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", " << a2 << ", "
+            << a3 << ", " << a4 << ", " << a5 << ", " << a6 << ", " << a7
+            << ", " << a8 << ", " << a9 << ", (" << a10.a0 << ", " << a10.a1
+            << ", " << a10.a2 << ", " << a10.a3 << ", " << a10.a4 << ", "
+            << a10.a5 << ", " << a10.a6 << ", " << a10.a7 << ", " << a10.a8
+            << ", " << a10.a9 << "), " << a11 << ")"
+            << "\n";
+
+  int32_t result = 0;
+
+  result += a0;
+  result += a1 ? 1 : 0;
+  result += a2 ? 1 : 0;
+  result += a3 ? 1 : 0;
+  result += a4 ? 1 : 0;
+  result += a5 ? 1 : 0;
+  result += a6 ? 1 : 0;
+  result += a7 ? 1 : 0;
+  result += a8 ? 1 : 0;
+  result += a9 ? 1 : 0;
+  result += a10.a0 ? 1 : 0;
+  result += a10.a1 ? 1 : 0;
+  result += a10.a2 ? 1 : 0;
+  result += a10.a3 ? 1 : 0;
+  result += a10.a4 ? 1 : 0;
+  result += a10.a5 ? 1 : 0;
+  result += a10.a6 ? 1 : 0;
+  result += a10.a7 ? 1 : 0;
+  result += a10.a8 ? 1 : 0;
+  result += a10.a9 ? 1 : 0;
+  result += a11 ? 1 : 0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Passing bools and a struct with bools.
+// Exhausts the registers to test bools and the bool struct alignment on the
+// stack.
+DART_EXPORT int32_t PassUint8Boolx9Struct10BytesInlineArrayBoolBool(
+    uint8_t a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesInlineArrayBool a10,
+    bool a11) {
+  std::cout << "PassUint8Boolx9Struct10BytesInlineArrayBoolBool"
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", " << a2 << ", "
+            << a3 << ", " << a4 << ", " << a5 << ", " << a6 << ", " << a7
+            << ", " << a8 << ", " << a9 << ", ([" << a10.a0[0] << ", "
+            << a10.a0[1] << ", " << a10.a0[2] << ", " << a10.a0[3] << ", "
+            << a10.a0[4] << ", " << a10.a0[5] << ", " << a10.a0[6] << ", "
+            << a10.a0[7] << ", " << a10.a0[8] << ", " << a10.a0[9] << "]), "
+            << a11 << ")"
+            << "\n";
+
+  int32_t result = 0;
+
+  result += a0;
+  result += a1 ? 1 : 0;
+  result += a2 ? 1 : 0;
+  result += a3 ? 1 : 0;
+  result += a4 ? 1 : 0;
+  result += a5 ? 1 : 0;
+  result += a6 ? 1 : 0;
+  result += a7 ? 1 : 0;
+  result += a8 ? 1 : 0;
+  result += a9 ? 1 : 0;
+  result += a10.a0[0] ? 1 : 0;
+  result += a10.a0[1] ? 1 : 0;
+  result += a10.a0[2] ? 1 : 0;
+  result += a10.a0[3] ? 1 : 0;
+  result += a10.a0[4] ? 1 : 0;
+  result += a10.a0[5] ? 1 : 0;
+  result += a10.a0[6] ? 1 : 0;
+  result += a10.a0[7] ? 1 : 0;
+  result += a10.a0[8] ? 1 : 0;
+  result += a10.a0[9] ? 1 : 0;
+  result += a11 ? 1 : 0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result;
+}
+
+// Used for testing structs and unions by value.
+// Returning a bool.
+DART_EXPORT bool PassUint8Struct1ByteBool(uint8_t a0, Struct1ByteBool a1) {
+  std::cout << "PassUint8Struct1ByteBool"
+            << "(" << static_cast<int>(a0) << ", (" << a1.a0 << "))"
+            << "\n";
+
+  uint64_t result = 0;
+
+  result += a0;
+  result += a1.a0 ? 1 : 0;
+
+  std::cout << "result = " << result << "\n";
+
+  return result % 2 != 0;
+}
+
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
   std::cout << "ReturnStruct1ByteInt"
@@ -12261,6 +12410,215 @@
 }
 
 // Used for testing structs and unions by value.
+// Passing bools and a struct with bools.
+// Exhausts the registers to test bools and the bool struct alignment on the
+// stack.
+DART_EXPORT intptr_t TestPassUint8Boolx9Struct10BytesHomogeneousBoolBool(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int32_t (*f)(uint8_t a0,
+                 bool a1,
+                 bool a2,
+                 bool a3,
+                 bool a4,
+                 bool a5,
+                 bool a6,
+                 bool a7,
+                 bool a8,
+                 bool a9,
+                 Struct10BytesHomogeneousBool a10,
+                 bool a11)) {
+  uint8_t a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  Struct10BytesHomogeneousBool a10 = {};
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0 = true;
+  a10.a1 = false;
+  a10.a2 = true;
+  a10.a3 = false;
+  a10.a4 = true;
+  a10.a5 = false;
+  a10.a6 = true;
+  a10.a7 = false;
+  a10.a8 = true;
+  a10.a9 = false;
+  a11 = true;
+
+  std::cout << "Calling TestPassUint8Boolx9Struct10BytesHomogeneousBoolBool("
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", " << a2 << ", "
+            << a3 << ", " << a4 << ", " << a5 << ", " << a6 << ", " << a7
+            << ", " << a8 << ", " << a9 << ", (" << a10.a0 << ", " << a10.a1
+            << ", " << a10.a2 << ", " << a10.a3 << ", " << a10.a4 << ", "
+            << a10.a5 << ", " << a10.a6 << ", " << a10.a7 << ", " << a10.a8
+            << ", " << a10.a9 << "), " << a11 << ")"
+            << ")\n";
+
+  int32_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(11, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Passing bools and a struct with bools.
+// Exhausts the registers to test bools and the bool struct alignment on the
+// stack.
+DART_EXPORT intptr_t TestPassUint8Boolx9Struct10BytesInlineArrayBoolBool(
+    // NOLINTNEXTLINE(whitespace/parens)
+    int32_t (*f)(uint8_t a0,
+                 bool a1,
+                 bool a2,
+                 bool a3,
+                 bool a4,
+                 bool a5,
+                 bool a6,
+                 bool a7,
+                 bool a8,
+                 bool a9,
+                 Struct10BytesInlineArrayBool a10,
+                 bool a11)) {
+  uint8_t a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  Struct10BytesInlineArrayBool a10 = {};
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0[0] = true;
+  a10.a0[1] = false;
+  a10.a0[2] = true;
+  a10.a0[3] = false;
+  a10.a0[4] = true;
+  a10.a0[5] = false;
+  a10.a0[6] = true;
+  a10.a0[7] = false;
+  a10.a0[8] = true;
+  a10.a0[9] = false;
+  a11 = true;
+
+  std::cout << "Calling TestPassUint8Boolx9Struct10BytesInlineArrayBoolBool("
+            << "(" << static_cast<int>(a0) << ", " << a1 << ", " << a2 << ", "
+            << a3 << ", " << a4 << ", " << a5 << ", " << a6 << ", " << a7
+            << ", " << a8 << ", " << a9 << ", ([" << a10.a0[0] << ", "
+            << a10.a0[1] << ", " << a10.a0[2] << ", " << a10.a0[3] << ", "
+            << a10.a0[4] << ", " << a10.a0[5] << ", " << a10.a0[6] << ", "
+            << a10.a0[7] << ", " << a10.a0[8] << ", " << a10.a0[9] << "]), "
+            << a11 << ")"
+            << ")\n";
+
+  int32_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_EQ(11, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_EQ(0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  CHECK_EQ(0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
+// Returning a bool.
+DART_EXPORT intptr_t TestPassUint8Struct1ByteBool(
+    // NOLINTNEXTLINE(whitespace/parens)
+    bool (*f)(uint8_t a0, Struct1ByteBool a1)) {
+  uint8_t a0;
+  Struct1ByteBool a1 = {};
+
+  a0 = 1;
+  a1.a0 = false;
+
+  std::cout << "Calling TestPassUint8Struct1ByteBool("
+            << "(" << static_cast<int>(a0) << ", (" << a1.a0 << "))"
+            << ")\n";
+
+  bool result = f(a0, a1);
+
+  std::cout << "result = " << result << "\n";
+
+  CHECK_APPROX(1, result);
+
+  // Pass argument that will make the Dart callback throw.
+  a0 = 42;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result);
+
+  // Pass argument that will make the Dart callback return null.
+  a0 = 84;
+
+  result = f(a0, a1);
+
+  CHECK_APPROX(0.0, result);
+
+  return 0;
+}
+
+// Used for testing structs and unions by value.
 // Smallest struct with data.
 DART_EXPORT intptr_t TestReturnStruct1ByteInt(
     // NOLINTNEXTLINE(whitespace/parens)
diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc
index ee431c8..95028ee 100644
--- a/runtime/bin/security_context.cc
+++ b/runtime/bin/security_context.cc
@@ -755,12 +755,12 @@
 }
 
 static Dart_Handle ASN1TimeToMilliseconds(ASN1_TIME* aTime) {
-  ASN1_UTCTIME* epoch_start = M_ASN1_UTCTIME_new();
+  ASN1_UTCTIME* epoch_start = ASN1_UTCTIME_new();
   ASN1_UTCTIME_set_string(epoch_start, "700101000000Z");
   int days;
   int seconds;
   int result = ASN1_TIME_diff(&days, &seconds, epoch_start, aTime);
-  M_ASN1_UTCTIME_free(epoch_start);
+  ASN1_UTCTIME_free(epoch_start);
   if (result != 1) {
     // TODO(whesse): Propagate an error to Dart.
     Syslog::PrintErr("ASN1Time error %d\n", result);
diff --git a/runtime/tools/ffi/sdk_lib_ffi_generator.dart b/runtime/tools/ffi/sdk_lib_ffi_generator.dart
index 3b68036..d44c2ec 100644
--- a/runtime/tools/ffi/sdk_lib_ffi_generator.dart
+++ b/runtime/tools/ffi/sdk_lib_ffi_generator.dart
@@ -27,6 +27,7 @@
   Config("IntPtr", "int", kDoNotEmit, kIntPtrElementSize),
   Config("Float", "double", "Float32List", 4),
   Config("Double", "double", "Float64List", 8),
+  Config("Bool", "bool", kDoNotEmit, 1),
 ];
 
 //
@@ -92,8 +93,12 @@
     }
   } else if (nativeType == "Float") {
     property = "float";
-  } else {
+  } else if (nativeType == "Double") {
     property = "double";
+  } else if (nativeType == "Bool") {
+    property = "bool";
+  } else {
+    throw "Unexpected type: $nativeType";
   }
 
   const platformIntPtr = """
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 377964b..fd3faae 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -206,6 +206,7 @@
 #if defined(DEBUG)
     for (intptr_t i = 0; i < class_array.Length(); i++) {
       cls ^= class_array.At(i);
+      // Recognized a new class, but forgot to add @pragma('vm:entrypoint')?
       ASSERT(cls.is_declaration_loaded());
     }
 #endif
diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h
index 8d17bea..bc52316 100644
--- a/runtime/vm/class_id.h
+++ b/runtime/vm/class_id.h
@@ -156,7 +156,8 @@
 #define CLASS_LIST_FFI_TYPE_MARKER(V)                                          \
   CLASS_LIST_FFI_NUMERIC(V)                                                    \
   V(Void)                                                                      \
-  V(Handle)
+  V(Handle)                                                                    \
+  V(Bool)
 
 #define CLASS_LIST_FFI(V)                                                      \
   V(NativeFunction)                                                            \
@@ -404,9 +405,9 @@
     case kPointerCid:
     case kDynamicLibraryCid:
 #define CASE_FFI_CID(name) case kFfi##name##Cid:
-    CLASS_LIST_FFI(CASE_FFI_CID)
+      CLASS_LIST_FFI(CASE_FFI_CID)
 #undef CASE_FFI_CID
-    return true;
+      return true;
     default:
       return false;
   }
diff --git a/runtime/vm/compiler/ffi/callback.cc b/runtime/vm/compiler/ffi/callback.cc
index d1d9b67..ef58014 100644
--- a/runtime/vm/compiler/ffi/callback.cc
+++ b/runtime/vm/compiler/ffi/callback.cc
@@ -52,7 +52,8 @@
   //
   // Exceptional return values currently cannot be pointers because we don't
   // have constant pointers.
-  ASSERT(exceptional_return.IsNull() || exceptional_return.IsNumber());
+  ASSERT(exceptional_return.IsNull() || exceptional_return.IsNumber() ||
+         exceptional_return.IsBool());
   if (!exceptional_return.IsSmi() && exceptional_return.IsNew()) {
     function.SetFfiCallbackExceptionalReturn(Instance::Handle(
         zone, exceptional_return.CopyShallowToOldSpace(thread)));
diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h
index 9d9f3cc..fc29f67 100644
--- a/runtime/vm/compiler/ffi/marshaller.h
+++ b/runtime/vm/compiler/ffi/marshaller.h
@@ -109,6 +109,10 @@
     return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
            kFfiHandleCid;
   }
+  bool IsBool(intptr_t arg_index) const {
+    return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
+           kFfiBoolCid;
+  }
 
   bool IsCompound(intptr_t arg_index) const {
     const auto& type = AbstractType::Handle(zone_, CType(arg_index));
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 125fa3e..6dfaeb7 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -357,6 +357,7 @@
       return kInt16;
     case kFfiInt32Cid:
       return kInt32;
+    case kFfiBoolCid:
     case kFfiUint8Cid:
       return kUint8;
     case kFfiUint16Cid:
diff --git a/runtime/vm/compiler/ffi/native_type_vm_test.cc b/runtime/vm/compiler/ffi/native_type_vm_test.cc
index 52f0835..196ae4c 100644
--- a/runtime/vm/compiler/ffi/native_type_vm_test.cc
+++ b/runtime/vm/compiler/ffi/native_type_vm_test.cc
@@ -25,6 +25,28 @@
   EXPECT(native_type.IsPrimitive());
 }
 
+// In all calling conventions `bool` behaves as `uint8_t` when it comes to:
+// - size
+// - alignment in structs
+// - alignment on stack
+// - upper bytes in cpu registers.
+ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Bool_FromAbstractType) {
+  Zone* Z = thread->zone();
+
+  const auto& ffi_library = Library::Handle(Library::FfiLibrary());
+  const auto& bool_class = Class::Handle(GetClass(ffi_library, "Bool"));
+  const auto& bool_type = Type::Handle(bool_class.DeclarationType());
+  const auto& bool_native_type = NativeType::FromAbstractType(Z, bool_type);
+
+  const auto& uint8_native_type = *new (Z) NativePrimitiveType(kUint8);
+
+  EXPECT(bool_native_type.Equals(uint8_native_type));
+  EXPECT_EQ(1, bool_native_type.SizeInBytes());
+  EXPECT_STREQ("uint8", bool_native_type.ToCString());
+  EXPECT(bool_native_type.IsInt());
+  EXPECT(bool_native_type.IsPrimitive());
+}
+
 // Test that we construct `NativeType` correctly from `Type`.
 ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Struct_FromAbstractType) {
   Zone* Z = thread->zone();
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index ab29403..091658f 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -3959,6 +3959,48 @@
   return Fragment(unbox);
 }
 
+// TODO(http://dartbug.com/47487): Support unboxed output value.
+Fragment FlowGraphBuilder::BoolToInt() {
+  // TODO(http://dartbug.com/36855) Build IfThenElseInstr, instead of letting
+  // the optimizer turn this into that.
+
+  LocalVariable* expression_temp = parsed_function_->expression_temp_var();
+
+  Fragment instructions;
+  TargetEntryInstr* is_true;
+  TargetEntryInstr* is_false;
+
+  instructions += BranchIfTrue(&is_true, &is_false);
+  JoinEntryInstr* join = BuildJoinEntry();
+
+  {
+    Fragment store_1(is_true);
+    store_1 += IntConstant(1);
+    store_1 += StoreLocal(TokenPosition::kNoSource, expression_temp);
+    store_1 += Drop();
+    store_1 += Goto(join);
+  }
+
+  {
+    Fragment store_0(is_false);
+    store_0 += IntConstant(0);
+    store_0 += StoreLocal(TokenPosition::kNoSource, expression_temp);
+    store_0 += Drop();
+    store_0 += Goto(join);
+  }
+
+  instructions = Fragment(instructions.entry, join);
+  instructions += LoadLocal(expression_temp);
+  return instructions;
+}
+
+Fragment FlowGraphBuilder::IntToBool() {
+  Fragment body;
+  body += IntConstant(0);
+  body += StrictCompare(Token::kNE_STRICT);
+  return body;
+}
+
 Fragment FlowGraphBuilder::NativeReturn(
     const compiler::ffi::CallbackMarshaller& marshaller) {
   auto* instr = new (Z)
@@ -4314,6 +4356,10 @@
     }
 
     body += Box(marshaller.RepInDart(arg_index));
+
+    if (marshaller.IsBool(arg_index)) {
+      body += IntToBool();
+    }
   }
   return body;
 }
@@ -4332,6 +4378,10 @@
   } else if (marshaller.IsHandle(arg_index)) {
     body += WrapHandle(api_local_scope);
   } else {
+    if (marshaller.IsBool(arg_index)) {
+      body += BoolToInt();
+    }
+
     body += UnboxTruncate(marshaller.RepInDart(arg_index));
   }
 
@@ -4388,6 +4438,7 @@
     }
     function_body += LoadLocal(
         parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
+    // TODO(http://dartbug.com/47486): Support entry without checking for null.
     // Check for 'null'.
     function_body += CheckNullOptimized(
         String::ZoneHandle(
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 20c5b77c..7c9154d 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -271,6 +271,12 @@
   // target representation.
   Fragment UnboxTruncate(Representation to);
 
+  // Converts a true to 1 and false to 0.
+  Fragment BoolToInt();
+
+  // Converts 0 to false and the rest to true.
+  Fragment IntToBool();
+
   // Creates an ffi.Pointer holding a given address (TOS).
   Fragment FfiPointerFromAddress(const Type& result_type);
 
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 63ce112..ee81b99 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -109,6 +109,7 @@
   V(ExternalOneByteString, "_ExternalOneByteString")                           \
   V(ExternalTwoByteString, "_ExternalTwoByteString")                           \
   V(FallThroughError, "FallThroughError")                                      \
+  V(FfiBool, "Bool")                                                           \
   V(FfiCallback, "_FfiCallback")                                               \
   V(FfiDouble, "Double")                                                       \
   V(FfiDynamicLibrary, "DynamicLibrary")                                       \
diff --git a/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart b/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart
index cc7831b..426a170 100644
--- a/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_native_type_patch.dart
@@ -69,6 +69,10 @@
 
 @patch
 @pragma("vm:entry-point")
+abstract class Bool extends NativeType {}
+
+@patch
+@pragma("vm:entry-point")
 abstract class Void extends NativeType {}
 
 @patch
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index b3faeab..3f7276b 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -313,6 +313,15 @@
 external void _storePointer<S extends NativeType>(
     Object typedDataBase, int offsetInBytes, Pointer<S> value);
 
+bool _loadBool(Object typedDataBase, int offsetInBytes) =>
+    _loadUint8(typedDataBase, offsetInBytes) != 0;
+
+void _storeBool(Object typedDataBase, int offsetInBytes, bool value) =>
+    _storeUint8(typedDataBase, offsetInBytes, value ? 1 : 0);
+
+Pointer<Bool> _elementAtBool(Pointer<Bool> pointer, int index) =>
+    Pointer.fromAddress(pointer.address + 1 * index);
+
 Pointer<Int8> _elementAtInt8(Pointer<Int8> pointer, int index) =>
     Pointer.fromAddress(pointer.address + 1 * index);
 
@@ -548,6 +557,20 @@
   Float64List asTypedList(int elements) => _asExternalTypedData(this, elements);
 }
 
+extension BoolPointer on Pointer<Bool> {
+  @patch
+  bool get value => _loadBool(this, 0);
+
+  @patch
+  set value(bool value) => _storeBool(this, 0, value);
+
+  @patch
+  bool operator [](int index) => _loadBool(this, index);
+
+  @patch
+  operator []=(int index, bool value) => _storeBool(this, index, value);
+}
+
 extension Int8Array on Array<Int8> {
   @patch
   int operator [](int index) {
@@ -702,6 +725,20 @@
   }
 }
 
+extension BoolArray on Array<Bool> {
+  @patch
+  bool operator [](int index) {
+    _checkIndex(index);
+    return _loadBool(_typedDataBase, index);
+  }
+
+  @patch
+  operator []=(int index, bool value) {
+    _checkIndex(index);
+    return _storeBool(_typedDataBase, index, value);
+  }
+}
+
 //
 // End of generated code.
 //
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 0bbf753..fa98c88 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -561,6 +561,20 @@
   external Float64List asTypedList(int length);
 }
 
+/// Extension on [Pointer] specialized for the type argument [Bool].
+extension BoolPointer on Pointer<Bool> {
+  /// The bool at [address].
+  external bool get value;
+
+  external void set value(bool value);
+
+  /// The bool at `address + index`.
+  external bool operator [](int index);
+
+  /// The bool at `address + index`.
+  external void operator []=(int index, bool value);
+}
+
 /// Bounds checking indexing methods on [Array]s of [Int8].
 extension Int8Array on Array<Int8> {
   external int operator [](int index);
@@ -638,6 +652,13 @@
   external void operator []=(int index, double value);
 }
 
+/// Bounds checking indexing methods on [Array]s of [Bool].
+extension BoolArray on Array<Bool> {
+  external bool operator [](int index);
+
+  external void operator []=(int index, bool value);
+}
+
 //
 // End of generated code.
 //
diff --git a/sdk/lib/ffi/native_type.dart b/sdk/lib/ffi/native_type.dart
index 31e4b72..217a68a 100644
--- a/sdk/lib/ffi/native_type.dart
+++ b/sdk/lib/ffi/native_type.dart
@@ -122,6 +122,14 @@
   const Double();
 }
 
+/// Represents a native bool in C.
+///
+/// [Bool] is not constructible in the Dart code and serves purely as marker
+/// in type signatures.
+class Bool extends NativeType {
+  const Bool();
+}
+
 /// Represents a void type in C.
 ///
 /// [Void] is not constructible in the Dart code and serves purely as marker in
diff --git a/tests/ffi/bool_test.dart b/tests/ffi/bool_test.dart
new file mode 100644
index 0000000..19f5594
--- /dev/null
+++ b/tests/ffi/bool_test.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2021, 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.
+//
+// Dart test program for testing dart:ffi bools.
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'function_structs_by_value_generated_compounds.dart';
+
+void main() {
+  testSizeOf();
+  testStoreLoad();
+  testStoreLoadIndexed();
+  testStruct();
+  testStruct2();
+  testInlineArray();
+}
+
+void testSizeOf() {
+  Expect.equals(1, sizeOf<Bool>());
+}
+
+void testStoreLoad() {
+  final p = calloc<Bool>();
+  p.value = true;
+  Expect.equals(true, p.value);
+  p.value = false;
+  Expect.equals(false, p.value);
+  calloc.free(p);
+}
+
+void testStoreLoadIndexed() {
+  final p = calloc<Bool>(2);
+  p[0] = true;
+  p[1] = false;
+  Expect.equals(true, p[0]);
+  Expect.equals(false, p[1]);
+  calloc.free(p);
+}
+
+void testStruct() {
+  final p = calloc<Struct1ByteBool>();
+  p.ref.a0 = true;
+  Expect.equals(true, p.ref.a0);
+  p.ref.a0 = false;
+  Expect.equals(false, p.ref.a0);
+  calloc.free(p);
+}
+
+void testStruct2() {
+  final p = calloc<Struct10BytesHomogeneousBool>();
+  p.ref.a0 = true;
+  p.ref.a1 = false;
+  p.ref.a2 = true;
+  p.ref.a3 = false;
+  p.ref.a4 = true;
+  p.ref.a5 = false;
+  p.ref.a6 = true;
+  p.ref.a7 = false;
+  p.ref.a8 = true;
+  p.ref.a9 = false;
+  Expect.equals(true, p.ref.a0);
+  Expect.equals(false, p.ref.a1);
+  Expect.equals(true, p.ref.a2);
+  Expect.equals(false, p.ref.a3);
+  Expect.equals(true, p.ref.a4);
+  Expect.equals(false, p.ref.a5);
+  Expect.equals(true, p.ref.a6);
+  Expect.equals(false, p.ref.a7);
+  Expect.equals(true, p.ref.a8);
+  Expect.equals(false, p.ref.a9);
+  calloc.free(p);
+}
+
+void testInlineArray() {
+  final p = calloc<Struct10BytesInlineArrayBool>();
+  final array = p.ref.a0;
+  for (int i = 0; i < 10; i++) {
+    array[i] = i % 2 == 0;
+  }
+  for (int i = 0; i < 10; i++) {
+    Expect.equals(i % 2 == 0, array[i]);
+  }
+  calloc.free(p);
+}
diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
index c0521d7..232b7a5 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -351,6 +351,21 @@
           passUnion16BytesNestedFloatx10, 0.0),
       passUnion16BytesNestedFloatx10AfterCallback),
   CallbackTest.withCheck(
+      "PassUint8Boolx9Struct10BytesHomogeneousBoolBool",
+      Pointer.fromFunction<PassUint8Boolx9Struct10BytesHomogeneousBoolBoolType>(
+          passUint8Boolx9Struct10BytesHomogeneousBoolBool, 0),
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolAfterCallback),
+  CallbackTest.withCheck(
+      "PassUint8Boolx9Struct10BytesInlineArrayBoolBool",
+      Pointer.fromFunction<PassUint8Boolx9Struct10BytesInlineArrayBoolBoolType>(
+          passUint8Boolx9Struct10BytesInlineArrayBoolBool, 0),
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolAfterCallback),
+  CallbackTest.withCheck(
+      "PassUint8Struct1ByteBool",
+      Pointer.fromFunction<PassUint8Struct1ByteBoolType>(
+          passUint8Struct1ByteBool, false),
+      passUint8Struct1ByteBoolAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -7583,6 +7598,299 @@
   Expect.approxEquals(10.0, result);
 }
 
+typedef PassUint8Boolx9Struct10BytesHomogeneousBoolBoolType = Int32 Function(
+    Uint8,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Struct10BytesHomogeneousBool,
+    Bool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0 = 0;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 = false;
+Struct10BytesHomogeneousBool
+    passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10 =
+    Pointer<Struct10BytesHomogeneousBool>.fromAddress(0).ref;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 = false;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBoolResult = 0;
+
+int passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a0 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 ? 1 : 0;
+
+  passUint8Boolx9Struct10BytesHomogeneousBoolBoolResult = result;
+
+  return result;
+}
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBool(
+    int a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesHomogeneousBool a10,
+    bool a11) {
+  print(
+      "passUint8Boolx9Struct10BytesHomogeneousBoolBool(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUint8Boolx9Struct10BytesHomogeneousBoolBool throwing on purpose!");
+  }
+
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0 = a0;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 = a1;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 = a2;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 = a3;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 = a4;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 = a5;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 = a6;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 = a7;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 = a8;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 = a9;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10 = a10;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 = a11;
+
+  final result =
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Boolx9Struct10BytesHomogeneousBoolBoolAfterCallback() {
+  final result =
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(11, result);
+}
+
+typedef PassUint8Boolx9Struct10BytesInlineArrayBoolBoolType = Int32 Function(
+    Uint8,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Struct10BytesInlineArrayBool,
+    Bool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0 = 0;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 = false;
+Struct10BytesInlineArrayBool
+    passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10 =
+    Pointer<Struct10BytesInlineArrayBool>.fromAddress(0).ref;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 = false;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBoolResult = 0;
+
+int passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[0] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[1] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[2] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[3] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[4] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[5] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[6] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[7] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[8] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[9] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 ? 1 : 0;
+
+  passUint8Boolx9Struct10BytesInlineArrayBoolBoolResult = result;
+
+  return result;
+}
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBool(
+    int a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesInlineArrayBool a10,
+    bool a11) {
+  print(
+      "passUint8Boolx9Struct10BytesInlineArrayBoolBool(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUint8Boolx9Struct10BytesInlineArrayBoolBool throwing on purpose!");
+  }
+
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0 = a0;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 = a1;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 = a2;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 = a3;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 = a4;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 = a5;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 = a6;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 = a7;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 = a8;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 = a9;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10 = a10;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 = a11;
+
+  final result =
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Boolx9Struct10BytesInlineArrayBoolBoolAfterCallback() {
+  final result =
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(11, result);
+}
+
+typedef PassUint8Struct1ByteBoolType = Bool Function(Uint8, Struct1ByteBool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct1ByteBool_a0 = 0;
+Struct1ByteBool passUint8Struct1ByteBool_a1 =
+    Pointer<Struct1ByteBool>.fromAddress(0).ref;
+
+// Result variable also global, so we can delete it after the callback.
+bool passUint8Struct1ByteBoolResult = false;
+
+bool passUint8Struct1ByteBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Struct1ByteBool_a0;
+  result += passUint8Struct1ByteBool_a1.a0 ? 1 : 0;
+
+  passUint8Struct1ByteBoolResult = result % 2 != 0;
+
+  return result % 2 != 0;
+}
+
+/// Returning a bool.
+bool passUint8Struct1ByteBool(int a0, Struct1ByteBool a1) {
+  print("passUint8Struct1ByteBool(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUint8Struct1ByteBool throwing on purpose!");
+  }
+
+  passUint8Struct1ByteBool_a0 = a0;
+  passUint8Struct1ByteBool_a1 = a1;
+
+  final result = passUint8Struct1ByteBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Struct1ByteBoolAfterCallback() {
+  final result = passUint8Struct1ByteBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi/function_structs_by_value_generated_compounds.dart b/tests/ffi/function_structs_by_value_generated_compounds.dart
index 2c411cf..2f41b75 100644
--- a/tests/ffi/function_structs_by_value_generated_compounds.dart
+++ b/tests/ffi/function_structs_by_value_generated_compounds.dart
@@ -7,6 +7,13 @@
 
 import 'dart:ffi';
 
+class Struct1ByteBool extends Struct {
+  @Bool()
+  external bool a0;
+
+  String toString() => "(${a0})";
+}
+
 class Struct1ByteInt extends Struct {
   @Int8()
   external int a0;
@@ -177,6 +184,41 @@
   String toString() => "(${a0}, ${a1})";
 }
 
+class Struct10BytesHomogeneousBool extends Struct {
+  @Bool()
+  external bool a0;
+
+  @Bool()
+  external bool a1;
+
+  @Bool()
+  external bool a2;
+
+  @Bool()
+  external bool a3;
+
+  @Bool()
+  external bool a4;
+
+  @Bool()
+  external bool a5;
+
+  @Bool()
+  external bool a6;
+
+  @Bool()
+  external bool a7;
+
+  @Bool()
+  external bool a8;
+
+  @Bool()
+  external bool a9;
+
+  String toString() =>
+      "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})";
+}
+
 class Struct12BytesHomogeneousFloat extends Struct {
   @Float()
   external double a0;
@@ -938,6 +980,13 @@
   String toString() => "(${[for (var i0 = 0; i0 < 8; i0 += 1) a0[i0]]})";
 }
 
+class Struct10BytesInlineArrayBool extends Struct {
+  @Array(10)
+  external Array<Bool> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 10; i0 += 1) a0[i0]]})";
+}
+
 class StructInlineArrayIrregular extends Struct {
   @Array(2)
   external Array<Struct3BytesInt2ByteAligned> a0;
diff --git a/tests/ffi/function_structs_by_value_generated_leaf_test.dart b/tests/ffi/function_structs_by_value_generated_leaf_test.dart
index 9efc77b..2067463 100644
--- a/tests/ffi/function_structs_by_value_generated_leaf_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_leaf_test.dart
@@ -86,6 +86,9 @@
     testPassUnion9BytesNestedIntx10Leaf();
     testPassUnion16BytesNestedInlineArrayFloatx10Leaf();
     testPassUnion16BytesNestedFloatx10Leaf();
+    testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf();
+    testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf();
+    testPassUint8Struct1ByteBoolLeaf();
     testReturnStruct1ByteIntLeaf();
     testReturnStruct3BytesHomogeneousUint8Leaf();
     testReturnStruct3BytesInt2ByteAlignedLeaf();
@@ -5295,6 +5298,169 @@
   calloc.free(a9Pointer);
 }
 
+final passUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf =
+    ffiTestFunctions
+        .lookupFunction<
+                Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+                    Bool, Bool, Struct10BytesHomogeneousBool, Bool),
+                int Function(
+                    int,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    Struct10BytesHomogeneousBool,
+                    bool)>("PassUint8Boolx9Struct10BytesHomogeneousBoolBool",
+            isLeaf: true);
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesHomogeneousBool>();
+  final Struct10BytesHomogeneousBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0 = true;
+  a10.a1 = false;
+  a10.a2 = true;
+  a10.a3 = false;
+  a10.a4 = true;
+  a10.a5 = false;
+  a10.a6 = true;
+  a10.a7 = false;
+  a10.a8 = true;
+  a10.a9 = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf =
+    ffiTestFunctions
+        .lookupFunction<
+                Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+                    Bool, Bool, Struct10BytesInlineArrayBool, Bool),
+                int Function(
+                    int,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    Struct10BytesInlineArrayBool,
+                    bool)>("PassUint8Boolx9Struct10BytesInlineArrayBoolBool",
+            isLeaf: true);
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesInlineArrayBool>();
+  final Struct10BytesInlineArrayBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0[0] = true;
+  a10.a0[1] = false;
+  a10.a0[2] = true;
+  a10.a0[3] = false;
+  a10.a0[4] = true;
+  a10.a0[5] = false;
+  a10.a0[6] = true;
+  a10.a0[7] = false;
+  a10.a0[8] = true;
+  a10.a0[9] = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Struct1ByteBoolLeaf = ffiTestFunctions.lookupFunction<
+    Bool Function(Uint8, Struct1ByteBool),
+    bool Function(
+        int, Struct1ByteBool)>("PassUint8Struct1ByteBool", isLeaf: true);
+
+/// Returning a bool.
+void testPassUint8Struct1ByteBoolLeaf() {
+  int a0;
+  final a1Pointer = calloc<Struct1ByteBool>();
+  final Struct1ByteBool a1 = a1Pointer.ref;
+
+  a0 = 1;
+  a1.a0 = false;
+
+  final result = passUint8Struct1ByteBoolLeaf(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+
+  calloc.free(a1Pointer);
+}
+
 final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true);
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index 14557da..463118d 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -86,6 +86,9 @@
     testPassUnion9BytesNestedIntx10();
     testPassUnion16BytesNestedInlineArrayFloatx10();
     testPassUnion16BytesNestedFloatx10();
+    testPassUint8Boolx9Struct10BytesHomogeneousBoolBool();
+    testPassUint8Boolx9Struct10BytesInlineArrayBoolBool();
+    testPassUint8Struct1ByteBool();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -5224,6 +5227,164 @@
   calloc.free(a9Pointer);
 }
 
+final passUint8Boolx9Struct10BytesHomogeneousBoolBool =
+    ffiTestFunctions.lookupFunction<
+        Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+            Bool, Struct10BytesHomogeneousBool, Bool),
+        int Function(
+            int,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            Struct10BytesHomogeneousBool,
+            bool)>("PassUint8Boolx9Struct10BytesHomogeneousBoolBool");
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesHomogeneousBoolBool() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesHomogeneousBool>();
+  final Struct10BytesHomogeneousBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0 = true;
+  a10.a1 = false;
+  a10.a2 = true;
+  a10.a3 = false;
+  a10.a4 = true;
+  a10.a5 = false;
+  a10.a6 = true;
+  a10.a7 = false;
+  a10.a8 = true;
+  a10.a9 = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesHomogeneousBoolBool(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Boolx9Struct10BytesInlineArrayBoolBool =
+    ffiTestFunctions.lookupFunction<
+        Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+            Bool, Struct10BytesInlineArrayBool, Bool),
+        int Function(
+            int,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            Struct10BytesInlineArrayBool,
+            bool)>("PassUint8Boolx9Struct10BytesInlineArrayBoolBool");
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesInlineArrayBoolBool() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesInlineArrayBool>();
+  final Struct10BytesInlineArrayBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0[0] = true;
+  a10.a0[1] = false;
+  a10.a0[2] = true;
+  a10.a0[3] = false;
+  a10.a0[4] = true;
+  a10.a0[5] = false;
+  a10.a0[6] = true;
+  a10.a0[7] = false;
+  a10.a0[8] = true;
+  a10.a0[9] = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesInlineArrayBoolBool(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Struct1ByteBool = ffiTestFunctions.lookupFunction<
+    Bool Function(Uint8, Struct1ByteBool),
+    bool Function(int, Struct1ByteBool)>("PassUint8Struct1ByteBool");
+
+/// Returning a bool.
+void testPassUint8Struct1ByteBool() {
+  int a0;
+  final a1Pointer = calloc<Struct1ByteBool>();
+  final Struct1ByteBool a1 = a1Pointer.ref;
+
+  a0 = 1;
+  a1.a0 = false;
+
+  final result = passUint8Struct1ByteBool(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+
+  calloc.free(a1Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index 7236aed..d0825f6 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -6,6 +6,7 @@
 
 import 'utils.dart';
 
+const bool_ = FundamentalType(PrimitiveType.bool_);
 const int8 = FundamentalType(PrimitiveType.int8);
 const int16 = FundamentalType(PrimitiveType.int16);
 const int32 = FundamentalType(PrimitiveType.int32);
@@ -19,6 +20,7 @@
 const double_ = FundamentalType(PrimitiveType.double_);
 
 enum PrimitiveType {
+  bool_,
   int8,
   int16,
   int32,
@@ -33,6 +35,7 @@
 }
 
 const primitiveNames = [
+  "bool",
   "int8",
   "int16",
   "int32",
@@ -47,7 +50,7 @@
 ];
 
 const intptrSize = -1;
-const primitiveSizesInBytes = [1, 2, 4, 8, 1, 2, 4, 8, intptrSize, 4, 8];
+const primitiveSizesInBytes = [1, 1, 2, 4, 8, 1, 2, 4, 8, intptrSize, 4, 8];
 
 abstract class CType {
   String get cType;
@@ -67,6 +70,9 @@
   /// All members have a integer type.
   bool get isOnlyInteger;
 
+  /// All members have a bool type.
+  bool get isOnlyBool;
+
   String toString() => dartCType;
 
   const CType();
@@ -77,12 +83,15 @@
 
   const FundamentalType(this.primitive);
 
+  bool get isBool => primitive == PrimitiveType.bool_;
   bool get isFloatingPoint =>
       primitive == PrimitiveType.float || primitive == PrimitiveType.double_;
-  bool get isInteger => !isFloatingPoint;
+  bool get isInteger => !isFloatingPoint && !isBool;
   bool get isOnlyFloatingPoint => isFloatingPoint;
   bool get isOnlyInteger => isInteger;
+  bool get isOnlyBool => isBool;
   bool get isUnsigned =>
+      primitive == PrimitiveType.bool_ ||
       primitive == PrimitiveType.uint8 ||
       primitive == PrimitiveType.uint16 ||
       primitive == PrimitiveType.uint32 ||
@@ -93,7 +102,13 @@
 
   String get cType => "${name}${isInteger ? "_t" : ""}";
   String get dartCType => name.upperCaseFirst();
-  String get dartType => isInteger ? "int" : "double";
+  String get dartType {
+    if (isInteger) return 'int';
+    if (isOnlyFloatingPoint) return 'double';
+    if (isBool) return 'bool';
+    throw 'Unknown type $primitive';
+  }
+
   String get dartStructFieldAnnotation => "@${dartCType}()";
   bool get hasSize => primitive != PrimitiveType.intptr;
   int get size {
@@ -118,6 +133,7 @@
 
   bool get isOnlyFloatingPoint => false;
   bool get isOnlyInteger => true;
+  bool get isOnlyBool => false;
 }
 
 /// Used to give [StructType] fields and [FunctionType] arguments names.
@@ -186,11 +202,11 @@
   String get dartSuperClass;
 
   bool get isOnlyFloatingPoint =>
-      !memberTypes.map((e) => e.isOnlyFloatingPoint).contains(false);
-  bool get isOnlyInteger =>
-      !memberTypes.map((e) => e.isOnlyInteger).contains(false);
+      memberTypes.every((e) => e.isOnlyFloatingPoint);
+  bool get isOnlyInteger => memberTypes.every((e) => e.isOnlyInteger);
+  bool get isOnlyBool => memberTypes.every((e) => e.isOnlyBool);
 
-  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint;
+  bool get isMixed => !isOnlyInteger && !isOnlyFloatingPoint && !isOnlyBool;
 
   bool get hasNestedStructs =>
       members.map((e) => e.type is StructType).contains(true);
@@ -219,8 +235,7 @@
   String get cKeyword => "struct";
   String get dartSuperClass => "Struct";
 
-  bool get hasSize =>
-      !memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
+  bool get hasSize => memberTypes.every((e) => e.hasSize) && !hasPadding;
   int get size => memberTypes.fold(0, (int acc, e) => acc + e.size);
 
   bool get hasPacking => packing != null;
@@ -273,6 +288,8 @@
       result += "Float";
     } else if (isOnlyInteger) {
       result += "Int";
+    } else if (isOnlyBool) {
+      result += "Bool";
     } else {
       result += "Mixed";
     }
@@ -287,7 +304,7 @@
   String get cKeyword => "union";
   String get dartSuperClass => "Union";
 
-  bool get hasSize => !memberTypes.map((e) => e.hasSize).contains(false);
+  bool get hasSize => memberTypes.every((e) => e.hasSize);
   int get size => memberTypes.fold(0, (int acc, e) => math.max(acc, e.size));
 
   String get name {
@@ -364,6 +381,7 @@
 
   bool get isOnlyFloatingPoint => elementType.isOnlyFloatingPoint;
   bool get isOnlyInteger => elementType.isOnlyInteger;
+  bool get isOnlyBool => elementType.isOnlyBool;
 }
 
 class FunctionType extends CType {
@@ -396,6 +414,7 @@
 
   bool get isOnlyFloatingPoint => throw "Not implemented";
   bool get isOnlyInteger => throw "Not implemented";
+  bool get isOnlyBool => throw "Not implemented";
 
   /// Group consecutive [arguments] by same type.
   ///
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index 78c187f..a5d62af 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -374,6 +374,54 @@
 Union with homogenous floats."""),
   FunctionType(List.filled(10, union16bytesFloat2), double_, """
 Union with homogenous floats."""),
+  FunctionType(
+      [
+        uint8,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        struct10bytesBool,
+        bool_,
+      ],
+      int32,
+      """
+Passing bools and a struct with bools.
+Exhausts the registers to test bools and the bool struct alignment on the
+stack."""),
+  FunctionType(
+      [
+        uint8,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        bool_,
+        structInlineArrayBool,
+        bool_,
+      ],
+      int32,
+      """
+Passing bools and a struct with bools.
+Exhausts the registers to test bools and the bool struct alignment on the
+stack."""),
+  FunctionType(
+      [
+        uint8,
+        struct1byteBool,
+      ],
+      bool_,
+      """
+Returning a bool."""),
 ];
 
 final functionsStructReturn = [
@@ -558,6 +606,7 @@
 ];
 
 final compounds = [
+  struct1byteBool,
   struct1byteInt,
   struct3bytesInt,
   struct3bytesInt2,
@@ -571,6 +620,7 @@
   struct8BytesMixed,
   struct9bytesInt,
   struct9bytesInt2,
+  struct10bytesBool,
   struct12bytesFloat,
   struct16bytesFloat,
   struct16bytesMixed,
@@ -598,6 +648,7 @@
   structNestedBigger,
   structNestedEvenBigger,
   structInlineArray,
+  structInlineArrayBool,
   structInlineArrayIrregular,
   structInlineArray100Bytes,
   structInlineArrayBig,
@@ -623,6 +674,7 @@
   union16bytesFloat2,
 ];
 
+final struct1byteBool = StructType([bool_]);
 final struct1byteInt = StructType([int8]);
 final struct3bytesInt = StructType(List.filled(3, uint8));
 final struct3bytesInt2 = StructType.disambiguate([int16, int8], "2ByteAligned");
@@ -638,6 +690,7 @@
 final struct9bytesInt = StructType(List.filled(9, uint8));
 final struct9bytesInt2 =
     StructType.disambiguate([int64, int8], "4Or8ByteAligned");
+final struct10bytesBool = StructType(List.filled(10, bool_));
 final struct12bytesFloat = StructType([float, float, float]);
 
 /// The largest homogenous float that goes into FPU registers on softfp and
@@ -716,6 +769,8 @@
 
 final structInlineArray = StructType([FixedLengthArrayType(uint8, 8)]);
 
+final structInlineArrayBool = StructType([FixedLengthArrayType(bool_, 10)]);
+
 final structInlineArrayIrregular = StructType.override(
     [FixedLengthArrayType(struct3bytesInt2, 2), uint8], "InlineArrayIrregular");
 
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 03f16ff..51de2db 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -102,7 +102,9 @@
   String addToResultStatements(String variableName) {
     switch (this.runtimeType) {
       case FundamentalType:
-        return "result += $variableName;\n";
+        final this_ = this as FundamentalType;
+        final boolToInt = this_.isBool ? ' ? 1 : 0' : '';
+        return "result += $variableName$boolToInt;\n";
 
       case StructType:
         final this_ = this as StructType;
@@ -214,15 +216,21 @@
   String nextValue(FundamentalType type) {
     int argumentValue = i;
     i++;
+    if (type.isBool) {
+      argumentValue = argumentValue % 2;
+    }
     if (type.isSigned && i % 2 == 0) {
       argumentValue = -argumentValue;
     }
     sum += argumentValue;
     if (type.isFloatingPoint) {
       return argumentValue.toDouble().toString();
-    } else {
+    } else if (type.isInteger) {
       return argumentValue.toString();
+    } else if (type.isBool) {
+      return argumentValue == 1 ? 'true' : 'false';
     }
+    throw 'Unknown type $type';
   }
 
   String sumValue(FundamentalType type) {
@@ -261,7 +269,13 @@
         if (this_.isInteger) {
           return "${dartType} ${variableName} = 0;\n";
         }
-        return "${dartType} ${variableName} = 0.0;\n";
+        if (this_.isFloatingPoint) {
+          return "${dartType} ${variableName} = 0.0;\n";
+        }
+        if (this_.isBool) {
+          return "${dartType} ${variableName} = false;\n";
+        }
+        throw 'Unknown type $this_';
 
       case StructType:
       case UnionType:
@@ -345,8 +359,13 @@
         if (this_.isInteger) {
           return "Expect.equals(${expected}, ${actual});";
         }
-        assert(this_.isFloatingPoint);
-        return "Expect.approxEquals(${expected}, ${actual});";
+        if (this_.isFloatingPoint) {
+          return "Expect.approxEquals(${expected}, ${actual});";
+        }
+        if (this_.isBool) {
+          return "Expect.equals(${expected} % 2 != 0, ${actual});";
+        }
+        throw 'Unexpected type $this_';
 
       case StructType:
         final this_ = this as StructType;
@@ -594,15 +613,24 @@
     bool structsAsPointers = false;
     String assignReturnGlobal = "";
     String buildReturnValue = "";
+    String returnValueType = returnValue.dartType;
+    String result = 'result';
+    if (returnValueType == 'bool') {
+      // We can't sum a bool;
+      returnValueType = 'int';
+      result = 'result % 2 != 0';
+    }
+
     switch (testType) {
       case TestType.structArguments:
         // Sum all input values.
+
         buildReturnValue = """
-        ${returnValue.dartType} result = 0;
+        $returnValueType result = 0;
 
         ${arguments.addToResultStatements('${dartName}_')}
         """;
-        assignReturnGlobal = "${dartName}Result = result;";
+        assignReturnGlobal = "${dartName}Result = $result;";
         break;
       case TestType.structReturn:
         // Allocate a struct.
@@ -675,7 +703,7 @@
 
       $assignReturnGlobal
 
-      return result;
+      return $result;
     }
 
     ${reason.makeDartDocComment()}
@@ -719,10 +747,15 @@
   String get dartCallbackTestConstructor {
     String exceptionalReturn = "";
     if (returnValue is FundamentalType) {
-      if ((returnValue as FundamentalType).isFloatingPoint) {
+      final returnValue_ = returnValue as FundamentalType;
+      if (returnValue_.isFloatingPoint) {
         exceptionalReturn = ", 0.0";
-      } else {
+      } else if (returnValue_.isInteger) {
         exceptionalReturn = ", 0";
+      } else if (returnValue_.isBool) {
+        exceptionalReturn = ", false";
+      } else {
+        throw 'Unexpected type $returnValue_';
       }
     }
     return """
@@ -734,24 +767,31 @@
 
   String get cCallCode {
     String body = "";
+    String returnValueType = returnValue.cType;
+    String returnStatement = 'return result;';
+    if (returnValueType == 'bool') {
+      // We can't sum in a bool.
+      returnValueType = 'uint64_t';
+      returnStatement = 'return result % 2 != 0;';
+    }
     switch (testType) {
       case TestType.structArguments:
         body = """
-        ${returnValue.cType} result = 0;
+        $returnValueType result = 0;
 
         ${arguments.addToResultStatements()}
         """;
         break;
       case TestType.structReturn:
         body = """
-        ${returnValue.cType} result = {};
+        $returnValueType result = {};
 
         ${arguments.copyValueStatements("", "result.")}
         """;
         break;
       case TestType.structReturnArgument:
         body = """
-        ${returnValue.cType} result = ${structReturnArgument.name};
+        $returnValueType result = ${structReturnArgument.name};
         """;
         break;
     }
@@ -769,7 +809,7 @@
 
       ${returnValue.coutStatement("result")}
 
-      return result;
+      $returnStatement
     }
 
     """;
diff --git a/tests/ffi_2/bool_test.dart b/tests/ffi_2/bool_test.dart
new file mode 100644
index 0000000..36af8a8
--- /dev/null
+++ b/tests/ffi_2/bool_test.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2021, 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.
+//
+// Dart test program for testing dart:ffi bools.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'function_structs_by_value_generated_compounds.dart';
+
+void main() {
+  testSizeOf();
+  testStoreLoad();
+  testStoreLoadIndexed();
+  testStruct();
+  testStruct2();
+  testInlineArray();
+}
+
+void testSizeOf() {
+  Expect.equals(1, sizeOf<Bool>());
+}
+
+void testStoreLoad() {
+  final p = calloc<Bool>();
+  p.value = true;
+  Expect.equals(true, p.value);
+  p.value = false;
+  Expect.equals(false, p.value);
+  calloc.free(p);
+}
+
+void testStoreLoadIndexed() {
+  final p = calloc<Bool>(2);
+  p[0] = true;
+  p[1] = false;
+  Expect.equals(true, p[0]);
+  Expect.equals(false, p[1]);
+  calloc.free(p);
+}
+
+void testStruct() {
+  final p = calloc<Struct1ByteBool>();
+  p.ref.a0 = true;
+  Expect.equals(true, p.ref.a0);
+  p.ref.a0 = false;
+  Expect.equals(false, p.ref.a0);
+  calloc.free(p);
+}
+
+void testStruct2() {
+  final p = calloc<Struct10BytesHomogeneousBool>();
+  p.ref.a0 = true;
+  p.ref.a1 = false;
+  p.ref.a2 = true;
+  p.ref.a3 = false;
+  p.ref.a4 = true;
+  p.ref.a5 = false;
+  p.ref.a6 = true;
+  p.ref.a7 = false;
+  p.ref.a8 = true;
+  p.ref.a9 = false;
+  Expect.equals(true, p.ref.a0);
+  Expect.equals(false, p.ref.a1);
+  Expect.equals(true, p.ref.a2);
+  Expect.equals(false, p.ref.a3);
+  Expect.equals(true, p.ref.a4);
+  Expect.equals(false, p.ref.a5);
+  Expect.equals(true, p.ref.a6);
+  Expect.equals(false, p.ref.a7);
+  Expect.equals(true, p.ref.a8);
+  Expect.equals(false, p.ref.a9);
+  calloc.free(p);
+}
+
+void testInlineArray() {
+  final p = calloc<Struct10BytesInlineArrayBool>();
+  final array = p.ref.a0;
+  for (int i = 0; i < 10; i++) {
+    array[i] = i % 2 == 0;
+  }
+  for (int i = 0; i < 10; i++) {
+    Expect.equals(i % 2 == 0, array[i]);
+  }
+  calloc.free(p);
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
index ba0f7ff..d593bd1 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
@@ -353,6 +353,21 @@
           passUnion16BytesNestedFloatx10, 0.0),
       passUnion16BytesNestedFloatx10AfterCallback),
   CallbackTest.withCheck(
+      "PassUint8Boolx9Struct10BytesHomogeneousBoolBool",
+      Pointer.fromFunction<PassUint8Boolx9Struct10BytesHomogeneousBoolBoolType>(
+          passUint8Boolx9Struct10BytesHomogeneousBoolBool, 0),
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolAfterCallback),
+  CallbackTest.withCheck(
+      "PassUint8Boolx9Struct10BytesInlineArrayBoolBool",
+      Pointer.fromFunction<PassUint8Boolx9Struct10BytesInlineArrayBoolBoolType>(
+          passUint8Boolx9Struct10BytesInlineArrayBoolBool, 0),
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolAfterCallback),
+  CallbackTest.withCheck(
+      "PassUint8Struct1ByteBool",
+      Pointer.fromFunction<PassUint8Struct1ByteBoolType>(
+          passUint8Struct1ByteBool, false),
+      passUint8Struct1ByteBoolAfterCallback),
+  CallbackTest.withCheck(
       "ReturnStruct1ByteInt",
       Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
       returnStruct1ByteIntAfterCallback),
@@ -7833,6 +7848,311 @@
   Expect.approxEquals(10.0, result);
 }
 
+typedef PassUint8Boolx9Struct10BytesHomogeneousBoolBoolType = Int32 Function(
+    Uint8,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Struct10BytesHomogeneousBool,
+    Bool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0 = 0;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 = false;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 = false;
+Struct10BytesHomogeneousBool
+    passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10 =
+    Pointer<Struct10BytesHomogeneousBool>.fromAddress(0).ref;
+bool passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 = false;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBoolResult = 0;
+
+int passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a0 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10.a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 ? 1 : 0;
+
+  passUint8Boolx9Struct10BytesHomogeneousBoolBoolResult = result;
+
+  return result;
+}
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+int passUint8Boolx9Struct10BytesHomogeneousBoolBool(
+    int a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesHomogeneousBool a10,
+    bool a11) {
+  print(
+      "passUint8Boolx9Struct10BytesHomogeneousBoolBool(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUint8Boolx9Struct10BytesHomogeneousBoolBool throwing on purpose!");
+  }
+
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a0 = a0;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a1 = a1;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a2 = a2;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a3 = a3;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a4 = a4;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a5 = a5;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a6 = a6;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a7 = a7;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a8 = a8;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a9 = a9;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a10 = a10;
+  passUint8Boolx9Struct10BytesHomogeneousBoolBool_a11 = a11;
+
+  final result =
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Boolx9Struct10BytesHomogeneousBoolBoolAfterCallback() {
+  final result =
+      passUint8Boolx9Struct10BytesHomogeneousBoolBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(11, result);
+}
+
+typedef PassUint8Boolx9Struct10BytesInlineArrayBoolBoolType = Int32 Function(
+    Uint8,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Bool,
+    Struct10BytesInlineArrayBool,
+    Bool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0 = 0;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 = false;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 = false;
+Struct10BytesInlineArrayBool
+    passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10 =
+    Pointer<Struct10BytesInlineArrayBool>.fromAddress(0).ref;
+bool passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 = false;
+
+// Result variable also global, so we can delete it after the callback.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBoolResult = 0;
+
+int passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[0] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[1] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[2] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[3] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[4] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[5] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[6] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[7] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[8] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10.a0[9] ? 1 : 0;
+  result += passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 ? 1 : 0;
+
+  passUint8Boolx9Struct10BytesInlineArrayBoolBoolResult = result;
+
+  return result;
+}
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+int passUint8Boolx9Struct10BytesInlineArrayBoolBool(
+    int a0,
+    bool a1,
+    bool a2,
+    bool a3,
+    bool a4,
+    bool a5,
+    bool a6,
+    bool a7,
+    bool a8,
+    bool a9,
+    Struct10BytesInlineArrayBool a10,
+    bool a11) {
+  print(
+      "passUint8Boolx9Struct10BytesInlineArrayBoolBool(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception(
+        "PassUint8Boolx9Struct10BytesInlineArrayBoolBool throwing on purpose!");
+  }
+
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a0 = a0;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a1 = a1;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a2 = a2;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a3 = a3;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a4 = a4;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a5 = a5;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a6 = a6;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a7 = a7;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a8 = a8;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a9 = a9;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a10 = a10;
+  passUint8Boolx9Struct10BytesInlineArrayBoolBool_a11 = a11;
+
+  final result =
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Boolx9Struct10BytesInlineArrayBoolBoolAfterCallback() {
+  final result =
+      passUint8Boolx9Struct10BytesInlineArrayBoolBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(11, result);
+}
+
+typedef PassUint8Struct1ByteBoolType = Bool Function(Uint8, Struct1ByteBool);
+
+// Global variables to be able to test inputs after callback returned.
+int passUint8Struct1ByteBool_a0 = 0;
+Struct1ByteBool passUint8Struct1ByteBool_a1 =
+    Pointer<Struct1ByteBool>.fromAddress(0).ref;
+
+// Result variable also global, so we can delete it after the callback.
+bool passUint8Struct1ByteBoolResult = false;
+
+bool passUint8Struct1ByteBoolCalculateResult() {
+  int result = 0;
+
+  result += passUint8Struct1ByteBool_a0;
+  result += passUint8Struct1ByteBool_a1.a0 ? 1 : 0;
+
+  passUint8Struct1ByteBoolResult = result % 2 != 0;
+
+  return result % 2 != 0;
+}
+
+/// Returning a bool.
+bool passUint8Struct1ByteBool(int a0, Struct1ByteBool a1) {
+  print("passUint8Struct1ByteBool(${a0}, ${a1})");
+
+  // In legacy mode, possibly return null.
+  if (a0 == 84) {
+    print("returning null!");
+    return null;
+  }
+
+  // In both nnbd and legacy mode, possibly throw.
+  if (a0 == 42 || a0 == 84) {
+    print("throwing!");
+    throw Exception("PassUint8Struct1ByteBool throwing on purpose!");
+  }
+
+  passUint8Struct1ByteBool_a0 = a0;
+  passUint8Struct1ByteBool_a1 = a1;
+
+  final result = passUint8Struct1ByteBoolCalculateResult();
+
+  print("result = $result");
+
+  return result;
+}
+
+void passUint8Struct1ByteBoolAfterCallback() {
+  final result = passUint8Struct1ByteBoolCalculateResult();
+
+  print("after callback result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+}
+
 typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
 
 // Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi_2/function_structs_by_value_generated_compounds.dart b/tests/ffi_2/function_structs_by_value_generated_compounds.dart
index ede77a3..8b7931f 100644
--- a/tests/ffi_2/function_structs_by_value_generated_compounds.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_compounds.dart
@@ -9,6 +9,13 @@
 
 import 'dart:ffi';
 
+class Struct1ByteBool extends Struct {
+  @Bool()
+  bool a0;
+
+  String toString() => "(${a0})";
+}
+
 class Struct1ByteInt extends Struct {
   @Int8()
   int a0;
@@ -179,6 +186,41 @@
   String toString() => "(${a0}, ${a1})";
 }
 
+class Struct10BytesHomogeneousBool extends Struct {
+  @Bool()
+  bool a0;
+
+  @Bool()
+  bool a1;
+
+  @Bool()
+  bool a2;
+
+  @Bool()
+  bool a3;
+
+  @Bool()
+  bool a4;
+
+  @Bool()
+  bool a5;
+
+  @Bool()
+  bool a6;
+
+  @Bool()
+  bool a7;
+
+  @Bool()
+  bool a8;
+
+  @Bool()
+  bool a9;
+
+  String toString() =>
+      "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})";
+}
+
 class Struct12BytesHomogeneousFloat extends Struct {
   @Float()
   double a0;
@@ -940,6 +982,13 @@
   String toString() => "(${[for (var i0 = 0; i0 < 8; i0 += 1) a0[i0]]})";
 }
 
+class Struct10BytesInlineArrayBool extends Struct {
+  @Array(10)
+  Array<Bool> a0;
+
+  String toString() => "(${[for (var i0 = 0; i0 < 10; i0 += 1) a0[i0]]})";
+}
+
 class StructInlineArrayIrregular extends Struct {
   @Array(2)
   Array<Struct3BytesInt2ByteAligned> a0;
diff --git a/tests/ffi_2/function_structs_by_value_generated_leaf_test.dart b/tests/ffi_2/function_structs_by_value_generated_leaf_test.dart
index 3199ccc..3a75d81 100644
--- a/tests/ffi_2/function_structs_by_value_generated_leaf_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_leaf_test.dart
@@ -88,6 +88,9 @@
     testPassUnion9BytesNestedIntx10Leaf();
     testPassUnion16BytesNestedInlineArrayFloatx10Leaf();
     testPassUnion16BytesNestedFloatx10Leaf();
+    testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf();
+    testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf();
+    testPassUint8Struct1ByteBoolLeaf();
     testReturnStruct1ByteIntLeaf();
     testReturnStruct3BytesHomogeneousUint8Leaf();
     testReturnStruct3BytesInt2ByteAlignedLeaf();
@@ -5297,6 +5300,169 @@
   calloc.free(a9Pointer);
 }
 
+final passUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf =
+    ffiTestFunctions
+        .lookupFunction<
+                Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+                    Bool, Bool, Struct10BytesHomogeneousBool, Bool),
+                int Function(
+                    int,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    Struct10BytesHomogeneousBool,
+                    bool)>("PassUint8Boolx9Struct10BytesHomogeneousBoolBool",
+            isLeaf: true);
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesHomogeneousBool>();
+  final Struct10BytesHomogeneousBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0 = true;
+  a10.a1 = false;
+  a10.a2 = true;
+  a10.a3 = false;
+  a10.a4 = true;
+  a10.a5 = false;
+  a10.a6 = true;
+  a10.a7 = false;
+  a10.a8 = true;
+  a10.a9 = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf =
+    ffiTestFunctions
+        .lookupFunction<
+                Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+                    Bool, Bool, Struct10BytesInlineArrayBool, Bool),
+                int Function(
+                    int,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    bool,
+                    Struct10BytesInlineArrayBool,
+                    bool)>("PassUint8Boolx9Struct10BytesInlineArrayBoolBool",
+            isLeaf: true);
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesInlineArrayBool>();
+  final Struct10BytesInlineArrayBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0[0] = true;
+  a10.a0[1] = false;
+  a10.a0[2] = true;
+  a10.a0[3] = false;
+  a10.a0[4] = true;
+  a10.a0[5] = false;
+  a10.a0[6] = true;
+  a10.a0[7] = false;
+  a10.a0[8] = true;
+  a10.a0[9] = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Struct1ByteBoolLeaf = ffiTestFunctions.lookupFunction<
+    Bool Function(Uint8, Struct1ByteBool),
+    bool Function(
+        int, Struct1ByteBool)>("PassUint8Struct1ByteBool", isLeaf: true);
+
+/// Returning a bool.
+void testPassUint8Struct1ByteBoolLeaf() {
+  int a0;
+  final a1Pointer = calloc<Struct1ByteBool>();
+  final Struct1ByteBool a1 = a1Pointer.ref;
+
+  a0 = 1;
+  a1.a0 = false;
+
+  final result = passUint8Struct1ByteBoolLeaf(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+
+  calloc.free(a1Pointer);
+}
+
 final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true);
diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart
index 3c3f791..f817df0 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -88,6 +88,9 @@
     testPassUnion9BytesNestedIntx10();
     testPassUnion16BytesNestedInlineArrayFloatx10();
     testPassUnion16BytesNestedFloatx10();
+    testPassUint8Boolx9Struct10BytesHomogeneousBoolBool();
+    testPassUint8Boolx9Struct10BytesInlineArrayBoolBool();
+    testPassUint8Struct1ByteBool();
     testReturnStruct1ByteInt();
     testReturnStruct3BytesHomogeneousUint8();
     testReturnStruct3BytesInt2ByteAligned();
@@ -5226,6 +5229,164 @@
   calloc.free(a9Pointer);
 }
 
+final passUint8Boolx9Struct10BytesHomogeneousBoolBool =
+    ffiTestFunctions.lookupFunction<
+        Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+            Bool, Struct10BytesHomogeneousBool, Bool),
+        int Function(
+            int,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            Struct10BytesHomogeneousBool,
+            bool)>("PassUint8Boolx9Struct10BytesHomogeneousBoolBool");
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesHomogeneousBoolBool() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesHomogeneousBool>();
+  final Struct10BytesHomogeneousBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0 = true;
+  a10.a1 = false;
+  a10.a2 = true;
+  a10.a3 = false;
+  a10.a4 = true;
+  a10.a5 = false;
+  a10.a6 = true;
+  a10.a7 = false;
+  a10.a8 = true;
+  a10.a9 = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesHomogeneousBoolBool(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Boolx9Struct10BytesInlineArrayBoolBool =
+    ffiTestFunctions.lookupFunction<
+        Int32 Function(Uint8, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool,
+            Bool, Struct10BytesInlineArrayBool, Bool),
+        int Function(
+            int,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            bool,
+            Struct10BytesInlineArrayBool,
+            bool)>("PassUint8Boolx9Struct10BytesInlineArrayBoolBool");
+
+/// Passing bools and a struct with bools.
+/// Exhausts the registers to test bools and the bool struct alignment on the
+/// stack.
+void testPassUint8Boolx9Struct10BytesInlineArrayBoolBool() {
+  int a0;
+  bool a1;
+  bool a2;
+  bool a3;
+  bool a4;
+  bool a5;
+  bool a6;
+  bool a7;
+  bool a8;
+  bool a9;
+  final a10Pointer = calloc<Struct10BytesInlineArrayBool>();
+  final Struct10BytesInlineArrayBool a10 = a10Pointer.ref;
+  bool a11;
+
+  a0 = 1;
+  a1 = false;
+  a2 = true;
+  a3 = false;
+  a4 = true;
+  a5 = false;
+  a6 = true;
+  a7 = false;
+  a8 = true;
+  a9 = false;
+  a10.a0[0] = true;
+  a10.a0[1] = false;
+  a10.a0[2] = true;
+  a10.a0[3] = false;
+  a10.a0[4] = true;
+  a10.a0[5] = false;
+  a10.a0[6] = true;
+  a10.a0[7] = false;
+  a10.a0[8] = true;
+  a10.a0[9] = false;
+  a11 = true;
+
+  final result = passUint8Boolx9Struct10BytesInlineArrayBoolBool(
+      a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+  print("result = $result");
+
+  Expect.equals(11, result);
+
+  calloc.free(a10Pointer);
+}
+
+final passUint8Struct1ByteBool = ffiTestFunctions.lookupFunction<
+    Bool Function(Uint8, Struct1ByteBool),
+    bool Function(int, Struct1ByteBool)>("PassUint8Struct1ByteBool");
+
+/// Returning a bool.
+void testPassUint8Struct1ByteBool() {
+  int a0;
+  final a1Pointer = calloc<Struct1ByteBool>();
+  final Struct1ByteBool a1 = a1Pointer.ref;
+
+  a0 = 1;
+  a1.a0 = false;
+
+  final result = passUint8Struct1ByteBool(a0, a1);
+
+  print("result = $result");
+
+  Expect.equals(1 % 2 != 0, result);
+
+  calloc.free(a1Pointer);
+}
+
 final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
     Struct1ByteInt Function(Int8),
     Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
diff --git a/tools/VERSION b/tools/VERSION
index c4e82d3..f8b3ab0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 232
+PRERELEASE 233
 PRERELEASE_PATCH 0
\ No newline at end of file