Version 2.12.0-75.0.dev

Merge commit '40535c3159bedc411d0c45176e1208dc47042ec0' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/issue44276.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/issue44276.dart
new file mode 100644
index 0000000..a1f87af
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/nullability/data/issue44276.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class Base {
+  int? value1;
+  int? value2;
+}
+
+int? fun() {
+  Base? a;
+  // if `a` is `null`, then `a?.value1` evaluates to `null`, so the RHS of the
+  // `??` will be evaluated.  Therefore, we can't promote `a` to non-nullable in
+  // `a?.value2`.
+  final b = a?.value1 ?? a?.value2;
+  return b;
+}
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 33d22cf..4b1b121 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
@@ -110,9 +110,8 @@
   @override
   void visitPropertyAccessorElement(PropertyAccessorElement element) {
     if (opType.includeReturnValueSuggestions) {
-      if (element.enclosingElement is ClassElement) {
-        // TODO(brianwilkerson) Verify that we cannot reach this point and
-        //  remove the dead code.
+      var parent = element.enclosingElement;
+      if (parent is ClassElement || parent is ExtensionElement) {
         builder.suggestAccessor(element, inheritanceDistance: -1.0);
       } else {
         builder.suggestTopLevelPropertyAccessor(element, prefix: prefix);
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 9bdc9a2..4c87bce 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
@@ -9,7 +9,6 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
 import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
 import 'package:analyzer_plugin/src/utilities/visitors/local_declaration_visitor.dart'
@@ -204,8 +203,6 @@
         targetIsFunctionalArgument = request.target.isFunctionalArgument(),
         super(request.offset);
 
-  TypeProvider get typeProvider => request.libraryElement.typeProvider;
-
   CompletionSuggestionKind get _defaultKind => targetIsFunctionalArgument
       ? CompletionSuggestionKind.IDENTIFIER
       : opType.suggestKind;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
index a9b34fc..127d348 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
@@ -809,7 +809,10 @@
   void suggestTopLevelPropertyAccessor(PropertyAccessorElement accessor,
       {CompletionSuggestionKind kind = CompletionSuggestionKind.INVOCATION,
       String prefix}) {
-    assert(accessor.enclosingElement is CompilationUnitElement);
+    assert(
+        accessor.enclosingElement is CompilationUnitElement,
+        'Enclosing element of ${accessor.runtimeType} is '
+        '${accessor.enclosingElement.runtimeType}.');
     if (accessor.isSynthetic) {
       // Avoid visiting a field twice. All fields induce a getter, but only
       // non-final fields induce a setter, so we don't add a suggestion for a
diff --git a/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart b/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
index a0470c9..2e467ab 100644
--- a/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
+++ b/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
@@ -89,10 +89,9 @@
 /// A flag that causes additional output to be produced.
 const String VERBOSE = 'verbose';
 
-/// A [Counter] to track the performance of the new relevance to the old
-/// relevance.
-Counter oldVsNewComparison =
-    Counter('use old vs new relevance rank comparison');
+/// A [Counter] to track the performance of each of the completion strategies
+/// that are being compared.
+Counter rankComparison = Counter('relevance rank comparison');
 
 /// Create a parser that can be used to parse the command-line arguments.
 ArgParser createArgParser() {
@@ -185,6 +184,12 @@
   /// The name associated with this set of metrics.
   final String name;
 
+  /// The function to be executed when this metrics collector is enabled.
+  final void Function() enableFunction;
+
+  /// The function to be executed when this metrics collector is disabled.
+  final void Function() disableFunction;
+
   Counter completionCounter = Counter('successful/ unsuccessful completions');
 
   Counter completionMissedTokenCounter =
@@ -289,7 +294,23 @@
   /// longest top compute for top-level declarations.
   List<CompletionResult> topLevelSlowestResults = [];
 
-  CompletionMetrics(this.name);
+  CompletionMetrics(this.name, this.enableFunction, this.disableFunction);
+
+  /// Perform any operations required in order to revert computing the kind of
+  /// completions represented by this metrics collector.
+  void disable() {
+    if (disableFunction != null) {
+      disableFunction();
+    }
+  }
+
+  /// Perform any initialization required in order to compute the kind of
+  /// completions represented by this metrics collector.
+  void enable() {
+    if (enableFunction != null) {
+      enableFunction();
+    }
+  }
 
   /// Record this completion result, this method handles the worst ranked items
   /// as well as the longest sets of results to compute.
@@ -436,7 +457,8 @@
   /// The int to be returned from the [compute] call.
   int resultCode;
 
-  CompletionMetrics metricsNewMode;
+  /// A list of the metrics to be computed.
+  final List<CompletionMetrics> targetMetrics = [];
 
   final OverlayResourceProvider _provider =
       OverlayResourceProvider(PhysicalResourceProvider.INSTANCE);
@@ -453,7 +475,9 @@
 
   Future<int> compute() async {
     resultCode = 0;
-    metricsNewMode = CompletionMetrics('useNewRelevance = true');
+    // To compare two or more changes to completions, add a `CompletionMetrics`
+    // object with enable and disable functions to the list of `targetMetrics`.
+    targetMetrics.add(CompletionMetrics('shipping', null, null));
     final collection = AnalysisContextCollection(
       includedPaths: [rootPath],
       resourceProvider: PhysicalResourceProvider.INSTANCE,
@@ -461,17 +485,19 @@
     for (var context in collection.contexts) {
       await _computeInContext(context.contextRoot);
     }
-    printMetrics(metricsNewMode);
+    for (var metrics in targetMetrics) {
+      printMetrics(metrics);
 
-    print('');
-    print('====================');
-    oldVsNewComparison.printCounterValues();
-    print('====================');
+      print('');
+      print('====================');
+      rankComparison.printCounterValues();
+      print('====================');
 
-    if (verbose) {
-      printWorstResults(metricsNewMode);
-      printSlowestResults(metricsNewMode);
-      printMissingInformation(metricsNewMode);
+      if (verbose) {
+        printWorstResults(metrics);
+        printSlowestResults(metrics);
+        printMissingInformation(metrics);
+      }
     }
     return resultCode;
   }
@@ -522,7 +548,7 @@
           }
         }
 
-        print('missing completion (`useNewRelevance = true`):');
+        print('missing completion (${metrics.name}):');
         print('$expectedCompletion');
         if (closeMatchSuggestion != null) {
           print('    close matching completion that was in the list:');
@@ -886,12 +912,23 @@
                   printMissedCompletions);
             }
 
-            // Compute the completions.
-            var listener = MetricsSuggestionListener();
-            await handleExpectedCompletion(
-                listener: listener,
-                metrics: metricsNewMode,
-                printMissedCompletions: verbose);
+            var bestRank = -1;
+            var bestName = '';
+            for (var metrics in targetMetrics) {
+              // Compute the completions.
+              metrics.enable();
+              var listener = MetricsSuggestionListener();
+              var rank = await handleExpectedCompletion(
+                  listener: listener,
+                  metrics: metrics,
+                  printMissedCompletions: verbose);
+              if (bestRank < 0 || rank < bestRank) {
+                bestRank = rank;
+                bestName = metrics.name;
+              }
+              metrics.disable();
+            }
+            rankComparison.count(bestName);
 
             // If an overlay option is being used, remove the overlay applied
             // earlier
@@ -899,9 +936,10 @@
               _provider.removeOverlay(filePath);
             }
           }
-        } catch (e) {
+        } catch (exception, stackTrace) {
           print('Exception caught analyzing: $filePath');
-          print(e.toString());
+          print(exception.toString());
+          print(stackTrace);
           resultCode = 1;
         }
       }
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 2b8458e..6680abf 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -1149,8 +1149,12 @@
             lhsResult.inferredType, equalsName, node.fileOffset)
         .member;
 
+    // This ends any shorting in `node.left`.
+    Expression left = lhsResult.expression;
+
     inferrer.flowAnalysis
         .ifNullExpression_rightBegin(node.left, lhsResult.inferredType);
+
     // - Let J = T0 if K is `?` else K.
     // - Infer e1 in context J to get T1
     ExpressionInferenceResult rhsResult;
@@ -1173,15 +1177,13 @@
         .getStandardUpperBound(nonNullableLhsType, rhsResult.inferredType,
             inferrer.library.library);
     Expression replacement;
-    if (lhsResult.expression is ThisExpression) {
-      replacement = lhsResult.expression;
+    if (left is ThisExpression) {
+      replacement = left;
     } else {
       VariableDeclaration variable =
-          createVariable(lhsResult.expression, lhsResult.inferredType);
+          createVariable(left, lhsResult.inferredType);
       MethodInvocation equalsNull = createEqualsNull(
-          lhsResult.expression.fileOffset,
-          createVariableGet(variable),
-          equalsMember);
+          left.fileOffset, createVariableGet(variable), equalsMember);
       VariableGet variableGet = createVariableGet(variable);
       if (inferrer.library.isNonNullableByDefault &&
           !identical(nonNullableLhsType, originalLhsType)) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 54f31d9..4855bd5 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -480,6 +480,7 @@
     'ffi_2',
     'language_2/',
     'lib_2/',
+    'samples_2/',
     'service_2/',
     'standalone_2/',
     'vm/dart_2/', // in runtime/tests
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart b/pkg/front_end/testcases/nnbd/issue44276.dart
new file mode 100644
index 0000000..8f49a9a
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+void main() {
+  fun();
+}
+
+class Base {
+  int? value1;
+  int? value2;
+}
+
+int? fun() {
+  Base? a;
+  final b = a?.value1 ?? a?.value2;
+  return b;
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.outline.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.outline.expect
new file mode 100644
index 0000000..665128f
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.outline.expect
@@ -0,0 +1,14 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int? value1;
+  field core::int? value2;
+  synthetic constructor •() → self::Base
+    ;
+}
+static method main() → void
+  ;
+static method fun() → core::int?
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.strong.expect
new file mode 100644
index 0000000..9f8c187
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.strong.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int? value1 = null;
+  field core::int? value2 = null;
+  synthetic constructor •() → self::Base
+    : super core::Object::•()
+    ;
+}
+static method main() → void {
+  self::fun();
+}
+static method fun() → core::int? {
+  self::Base? a;
+  final core::int? b = let final core::int? #t1 = let final self::Base? #t2 = a in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Base}.{self::Base::value1} in #t1.{core::num::==}(null) ?{core::int?} let final self::Base? #t3 = a in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Base}.{self::Base::value2} : #t1{core::int};
+  return b;
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.strong.transformed.expect
new file mode 100644
index 0000000..9f8c187
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.strong.transformed.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int? value1 = null;
+  field core::int? value2 = null;
+  synthetic constructor •() → self::Base
+    : super core::Object::•()
+    ;
+}
+static method main() → void {
+  self::fun();
+}
+static method fun() → core::int? {
+  self::Base? a;
+  final core::int? b = let final core::int? #t1 = let final self::Base? #t2 = a in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Base}.{self::Base::value1} in #t1.{core::num::==}(null) ?{core::int?} let final self::Base? #t3 = a in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Base}.{self::Base::value2} : #t1{core::int};
+  return b;
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline.expect
new file mode 100644
index 0000000..be5c153
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+void main() {}
+
+class Base {
+  int? value1;
+  int? value2;
+}
+
+int? fun() {}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..75f8632
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+class Base {
+  int? value1;
+  int? value2;
+}
+
+int? fun() {}
+void main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.weak.expect
new file mode 100644
index 0000000..9f8c187
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.weak.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int? value1 = null;
+  field core::int? value2 = null;
+  synthetic constructor •() → self::Base
+    : super core::Object::•()
+    ;
+}
+static method main() → void {
+  self::fun();
+}
+static method fun() → core::int? {
+  self::Base? a;
+  final core::int? b = let final core::int? #t1 = let final self::Base? #t2 = a in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Base}.{self::Base::value1} in #t1.{core::num::==}(null) ?{core::int?} let final self::Base? #t3 = a in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Base}.{self::Base::value2} : #t1{core::int};
+  return b;
+}
diff --git a/pkg/front_end/testcases/nnbd/issue44276.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue44276.dart.weak.transformed.expect
new file mode 100644
index 0000000..9f8c187
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44276.dart.weak.transformed.expect
@@ -0,0 +1,19 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Base extends core::Object {
+  field core::int? value1 = null;
+  field core::int? value2 = null;
+  synthetic constructor •() → self::Base
+    : super core::Object::•()
+    ;
+}
+static method main() → void {
+  self::fun();
+}
+static method fun() → core::int? {
+  self::Base? a;
+  final core::int? b = let final core::int? #t1 = let final self::Base? #t2 = a in #t2.{core::Object::==}(null) ?{core::int?} null : #t2{self::Base}.{self::Base::value1} in #t1.{core::num::==}(null) ?{core::int?} let final self::Base? #t3 = a in #t3.{core::Object::==}(null) ?{core::int?} null : #t3{self::Base}.{self::Base::value2} : #t1{core::int};
+  return b;
+}
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index b73f36c..6d682de 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -15,7 +15,7 @@
 import 'utils.dart';
 
 const _defaultTestSelectors = [
-  'samples',
+  'samples_2',
   'standalone_2',
   'corelib_2',
   'language_2',
diff --git a/pkg/test_runner/lib/src/test_configurations.dart b/pkg/test_runner/lib/src/test_configurations.dart
index 3179fec..4487429 100644
--- a/pkg/test_runner/lib/src/test_configurations.dart
+++ b/pkg/test_runner/lib/src/test_configurations.dart
@@ -36,6 +36,7 @@
   Path('runtime/observatory_2/tests/service_2'),
   Path('runtime/observatory_2/tests/observatory_ui_2'),
   Path('samples'),
+  Path('samples_2'),
   Path('samples-dev'),
   Path('tests/corelib'),
   Path('tests/corelib_2'),
diff --git a/pkg/testing/lib/src/analyze.dart b/pkg/testing/lib/src/analyze.dart
index 0faf99f..099d020 100644
--- a/pkg/testing/lib/src/analyze.dart
+++ b/pkg/testing/lib/src/analyze.dart
@@ -235,11 +235,7 @@
   }
   Stopwatch sw = new Stopwatch()..start();
   Process process = await startDart(
-      analyzer,
-      const <String>["--batch"],
-      dartArguments
-        ..remove("-c")
-        ..add("-DuseFastaScanner=true"));
+      analyzer, const <String>["--batch"], dartArguments..remove("-c"));
   process.stdin.writeln(arguments.join(" "));
   process.stdin.close();
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b3773a3..801ca53 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -17365,7 +17365,23 @@
              ->isolate_group()
              ->subtype_test_cache_mutex()
              ->IsOwnedByCurrentThread());
+  GetCurrentCheck(ix, instance_class_id_or_function, destination_type,
+                  instance_type_arguments, instantiator_type_arguments,
+                  function_type_arguments,
+                  instance_parent_function_type_arguments,
+                  instance_delayed_type_arguments, test_result);
+}
 
+void SubtypeTestCache::GetCurrentCheck(
+    intptr_t ix,
+    Object* instance_class_id_or_function,
+    AbstractType* destination_type,
+    TypeArguments* instance_type_arguments,
+    TypeArguments* instantiator_type_arguments,
+    TypeArguments* function_type_arguments,
+    TypeArguments* instance_parent_function_type_arguments,
+    TypeArguments* instance_delayed_type_arguments,
+    Bool* test_result) const {
   Array& data = Array::Handle(cache());
   SubtypeTestCacheTable entries(data);
   auto entry = entries[ix];
@@ -17381,12 +17397,166 @@
   *test_result ^= entry.Get<kTestResult>();
 }
 
+bool SubtypeTestCache::HasCheck(
+    const Object& instance_class_id_or_function,
+    const AbstractType& destination_type,
+    const TypeArguments& instance_type_arguments,
+    const TypeArguments& instantiator_type_arguments,
+    const TypeArguments& function_type_arguments,
+    const TypeArguments& instance_parent_function_type_arguments,
+    const TypeArguments& instance_delayed_type_arguments,
+    intptr_t* index,
+    Bool* result) const {
+  ASSERT(Thread::Current()
+             ->isolate_group()
+             ->subtype_test_cache_mutex()
+             ->IsOwnedByCurrentThread());
+  const intptr_t last_index = NumberOfChecks();
+  const auto& data = Array::Handle(cache());
+
+  SubtypeTestCacheTable entries(data);
+  for (intptr_t i = 0; i < last_index; i++) {
+    const auto entry = entries[i];
+    if (entry.Get<kInstanceClassIdOrFunction>() ==
+            instance_class_id_or_function.raw() &&
+        entry.Get<kDestinationType>() == destination_type.raw() &&
+        entry.Get<kInstanceTypeArguments>() == instance_type_arguments.raw() &&
+        entry.Get<kInstantiatorTypeArguments>() ==
+            instantiator_type_arguments.raw() &&
+        entry.Get<kFunctionTypeArguments>() == function_type_arguments.raw() &&
+        entry.Get<kInstanceParentFunctionTypeArguments>() ==
+            instance_parent_function_type_arguments.raw() &&
+        entry.Get<kInstanceDelayedFunctionTypeArguments>() ==
+            instance_delayed_type_arguments.raw()) {
+      if (index != nullptr) {
+        *index = i;
+      }
+      if (result != nullptr) {
+        *result ^= entry.Get<kTestResult>();
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+void SubtypeTestCache::WriteEntryToBuffer(Zone* zone,
+                                          BaseTextBuffer* buffer,
+                                          intptr_t index,
+                                          const char* line_prefix) const {
+  ASSERT(Thread::Current()
+             ->isolate_group()
+             ->subtype_test_cache_mutex()
+             ->IsOwnedByCurrentThread());
+  WriteCurrentEntryToBuffer(zone, buffer, index, line_prefix);
+}
+
+void SubtypeTestCache::WriteCurrentEntryToBuffer(
+    Zone* zone,
+    BaseTextBuffer* buffer,
+    intptr_t index,
+    const char* line_prefix) const {
+  const char* separator =
+      line_prefix == nullptr ? ", " : OS::SCreate(zone, "\n%s", line_prefix);
+  auto& instance_class_id_or_function = Object::Handle(zone);
+  auto& destination_type = AbstractType::Handle(zone);
+  auto& instance_type_arguments = TypeArguments::Handle(zone);
+  auto& instantiator_type_arguments = TypeArguments::Handle(zone);
+  auto& function_type_arguments = TypeArguments::Handle(zone);
+  auto& instance_parent_function_type_arguments = TypeArguments::Handle(zone);
+  auto& instance_delayed_type_arguments = TypeArguments::Handle(zone);
+  auto& result = Bool::Handle(zone);
+  GetCurrentCheck(index, &instance_class_id_or_function, &destination_type,
+                  &instance_type_arguments, &instantiator_type_arguments,
+                  &function_type_arguments,
+                  &instance_parent_function_type_arguments,
+                  &instance_delayed_type_arguments, &result);
+  ASSERT(!result.IsNull());
+  buffer->Printf(
+      "[ %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px
+      ", %#" Px " ]",
+      static_cast<uword>(instance_class_id_or_function.raw()),
+      static_cast<uword>(destination_type.raw()),
+      static_cast<uword>(instance_type_arguments.raw()),
+      static_cast<uword>(instantiator_type_arguments.raw()),
+      static_cast<uword>(function_type_arguments.raw()),
+      static_cast<uword>(instance_parent_function_type_arguments.raw()),
+      static_cast<uword>(instance_delayed_type_arguments.raw()),
+      static_cast<uword>(result.raw()));
+  if (instance_class_id_or_function.IsSmi()) {
+    buffer->Printf("%sclass id: %" Pd "", separator,
+                   Smi::Cast(instance_class_id_or_function).Value());
+  } else {
+    ASSERT(instance_class_id_or_function.IsFunction());
+    buffer->Printf("%sfunction: %s", separator,
+                   Function::Cast(instance_class_id_or_function)
+                       .ToFullyQualifiedCString());
+  }
+  if (!destination_type.IsNull()) {
+    buffer->Printf("%sdestination type: %s", separator,
+                   destination_type.ToCString());
+    if (!destination_type.IsInstantiated()) {
+      AbstractType& test_type = AbstractType::Handle(
+          zone, destination_type.InstantiateFrom(instantiator_type_arguments,
+                                                 function_type_arguments,
+                                                 kAllFree, Heap::kNew));
+      const auto& type_class = Class::Handle(zone, test_type.type_class());
+      buffer->Printf("%sinstantiated type: %s", separator,
+                     test_type.ToCString());
+      buffer->Printf("%sinstantiated type class id: %" Pd "", separator,
+                     type_class.id());
+    }
+  }
+  if (!instance_type_arguments.IsNull()) {
+    if (instance_class_id_or_function.IsSmi()) {
+      buffer->Printf("%sinstance type arguments: %s", separator,
+                     instance_type_arguments.ToCString());
+    } else {
+      ASSERT(instance_class_id_or_function.IsFunction());
+      buffer->Printf("%sclosure instantiator function type arguments: %s",
+                     separator, instance_type_arguments.ToCString());
+    }
+  }
+  if (!instantiator_type_arguments.IsNull()) {
+    buffer->Printf("%sinstantiator type arguments: %s", separator,
+                   instantiator_type_arguments.ToCString());
+  }
+  if (!function_type_arguments.IsNull()) {
+    buffer->Printf("%sfunction type arguments: %s", separator,
+                   function_type_arguments.ToCString());
+  }
+  if (!instance_parent_function_type_arguments.IsNull()) {
+    ASSERT(instance_class_id_or_function.IsFunction());
+    buffer->Printf("%sclosure parent function type arguments: %s", separator,
+                   instance_parent_function_type_arguments.ToCString());
+  }
+  if (!instance_delayed_type_arguments.IsNull()) {
+    ASSERT(instance_class_id_or_function.IsFunction());
+    buffer->Printf("%sclosure delayed function type arguments: %s", separator,
+                   instance_delayed_type_arguments.ToCString());
+  }
+  buffer->Printf("%sresult: %s", separator, result.ToCString());
+}
+
 void SubtypeTestCache::Reset() const {
   set_cache(Array::Handle(cached_array_));
 }
 
 const char* SubtypeTestCache::ToCString() const {
-  return "SubtypeTestCache";
+  auto const zone = Thread::Current()->zone();
+  ZoneTextBuffer buffer(zone);
+  const intptr_t num_checks = NumberOfChecks();
+  buffer.AddString("SubtypeTestCache(");
+  for (intptr_t i = 0; i < num_checks; i++) {
+    if (i != 0) {
+      buffer.AddString(",");
+    }
+    buffer.AddString("{ entry: ");
+    WriteCurrentEntryToBuffer(zone, &buffer, i);
+    buffer.AddString(" }");
+  }
+  buffer.AddString(")");
+  return buffer.buffer();
 }
 
 LoadingUnitPtr LoadingUnit::New() {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index ddfb7be..28ab1c2 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -6846,7 +6846,6 @@
   };
 
   virtual intptr_t NumberOfChecks() const;
-  // For a non-dynamic SubtypeTestCache, destination_type is unused.
   void AddCheck(const Object& instance_class_id_or_function,
                 const AbstractType& destination_type,
                 const TypeArguments& instance_type_arguments,
@@ -6855,7 +6854,6 @@
                 const TypeArguments& instance_parent_function_type_arguments,
                 const TypeArguments& instance_delayed_type_arguments,
                 const Bool& test_result) const;
-  // For a non-dynamic SubtypeTestCache, destination_type is always set to null.
   void GetCheck(intptr_t ix,
                 Object* instance_class_id_or_function,
                 AbstractType* destination_type,
@@ -6865,6 +6863,50 @@
                 TypeArguments* instance_parent_function_type_arguments,
                 TypeArguments* instance_delayed_type_arguments,
                 Bool* test_result) const;
+
+  // Like GetCheck(), but does not require the subtype test cache mutex and so
+  // may see an outdated view of the cache.
+  void GetCurrentCheck(intptr_t ix,
+                       Object* instance_class_id_or_function,
+                       AbstractType* destination_type,
+                       TypeArguments* instance_type_arguments,
+                       TypeArguments* instantiator_type_arguments,
+                       TypeArguments* function_type_arguments,
+                       TypeArguments* instance_parent_function_type_arguments,
+                       TypeArguments* instance_delayed_type_arguments,
+                       Bool* test_result) const;
+
+  // Returns whether all the elements of an existing cache entry, excluding
+  // the result, match the non-pointer arguments. The pointer arguments are
+  // out parameters as follows:
+  //
+  // If [index] is not nullptr, then it is set to the matching entry's index.
+  // If [result] is not nullptr, then it is set to the matching entry's result.
+  bool HasCheck(const Object& instance_class_id_or_function,
+                const AbstractType& destination_type,
+                const TypeArguments& instance_type_arguments,
+                const TypeArguments& instantiator_type_arguments,
+                const TypeArguments& function_type_arguments,
+                const TypeArguments& instance_parent_function_type_arguments,
+                const TypeArguments& instance_delayed_type_arguments,
+                intptr_t* index,
+                Bool* result) const;
+
+  // Writes the cache entry at index [index] to the given text buffer.
+  //
+  // The output is comma separated on a single line if [line_prefix] is nullptr,
+  // otherwise line breaks followed by [line_prefix] is used as a separator.
+  void WriteEntryToBuffer(Zone* zone,
+                          BaseTextBuffer* buffer,
+                          intptr_t index,
+                          const char* line_prefix = nullptr) const;
+
+  // Like WriteEntryToBuffer(), but does not require the subtype test cache
+  // mutex and so may see an outdated view of the cache.
+  void WriteCurrentEntryToBuffer(Zone* zone,
+                                 BaseTextBuffer* buffer,
+                                 intptr_t index,
+                                 const char* line_prefix = nullptr) const;
   void Reset() const;
 
   static SubtypeTestCachePtr New();
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index e5065d0..f241c0c 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -618,6 +618,32 @@
       instance_type_arguments = instance.GetTypeArguments();
     }
   }
+  if (FLAG_trace_type_checks) {
+    const auto& instance_class_name =
+        String::Handle(zone, instance_class.Name());
+    TextBuffer buffer(256);
+    buffer.Printf("  Updating test cache %#" Px " with result %s for:\n",
+                  static_cast<uword>(new_cache.raw()), result.ToCString());
+    if (instance.IsString()) {
+      buffer.Printf("    instance: '%s'\n", instance.ToCString());
+    } else {
+      buffer.Printf("    instance: %s\n", instance.ToCString());
+    }
+    buffer.Printf("    class: %s (%" Pd ")\n", instance_class_name.ToCString(),
+                  instance_class.id());
+    buffer.Printf(
+        "    raw entry: [ %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px
+        ", %#" Px ", %#" Px ", %#" Px " ]\n",
+        static_cast<uword>(instance_class_id_or_function.raw()),
+        static_cast<uword>(destination_type.raw()),
+        static_cast<uword>(instance_type_arguments.raw()),
+        static_cast<uword>(instantiator_type_arguments.raw()),
+        static_cast<uword>(function_type_arguments.raw()),
+        static_cast<uword>(instance_parent_function_type_arguments.raw()),
+        static_cast<uword>(instance_delayed_type_arguments.raw()),
+        static_cast<uword>(result.raw()));
+    OS::PrintErr("%s", buffer.buffer());
+  }
   {
     SafepointMutexLocker ml(
         thread->isolate_group()->subtype_test_cache_mutex());
@@ -641,42 +667,31 @@
            instance_parent_function_type_arguments.IsCanonical());
     ASSERT(instance_delayed_type_arguments.IsNull() ||
            instance_delayed_type_arguments.IsCanonical());
-    auto& last_instance_class_id_or_function = Object::Handle(zone);
-    auto& last_destination_type = AbstractType::Handle(zone);
-    auto& last_instance_type_arguments = TypeArguments::Handle(zone);
-    auto& last_instantiator_type_arguments = TypeArguments::Handle(zone);
-    auto& last_function_type_arguments = TypeArguments::Handle(zone);
-    auto& last_instance_parent_function_type_arguments =
-        TypeArguments::Handle(zone);
-    auto& last_instance_delayed_type_arguments = TypeArguments::Handle(zone);
-    Bool& last_result = Bool::Handle(zone);
-    for (intptr_t i = 0; i < len; ++i) {
-      new_cache.GetCheck(i, &last_instance_class_id_or_function,
-                         &last_destination_type, &last_instance_type_arguments,
-                         &last_instantiator_type_arguments,
-                         &last_function_type_arguments,
-                         &last_instance_parent_function_type_arguments,
-                         &last_instance_delayed_type_arguments, &last_result);
-      if ((last_instance_class_id_or_function.raw() ==
-           instance_class_id_or_function.raw()) &&
-          (last_instance_type_arguments.raw() ==
-           instance_type_arguments.raw()) &&
-          (last_instantiator_type_arguments.raw() ==
-           instantiator_type_arguments.raw()) &&
-          (last_function_type_arguments.raw() ==
-           function_type_arguments.raw()) &&
-          (last_instance_parent_function_type_arguments.raw() ==
-           instance_parent_function_type_arguments.raw()) &&
-          (last_instance_delayed_type_arguments.raw() ==
-           instance_delayed_type_arguments.raw()) &&
-          (last_destination_type.raw() == destination_type.raw())) {
-        if (!FLAG_enable_isolate_groups) {
-          FATAL("Duplicate subtype test cache entry");
-        }
-        // Some other isolate might have updated the cache between entry was
-        // found missing and now.
-        return;
+    intptr_t colliding_index = -1;
+    auto& old_result = Bool::Handle(zone);
+    if (new_cache.HasCheck(
+            instance_class_id_or_function, destination_type,
+            instance_type_arguments, instantiator_type_arguments,
+            function_type_arguments, instance_parent_function_type_arguments,
+            instance_delayed_type_arguments, &colliding_index, &old_result)) {
+      if (FLAG_trace_type_checks) {
+        TextBuffer buffer(256);
+        buffer.Printf("  Collision for test cache %#" Px " at index %" Pd ":\n",
+                      static_cast<uword>(new_cache.raw()), colliding_index);
+        buffer.Printf("    entry: ");
+        new_cache.WriteEntryToBuffer(zone, &buffer, colliding_index, "      ");
+        OS::PrintErr("%s\n", buffer.buffer());
       }
+      if (!FLAG_enable_isolate_groups) {
+        FATAL("Duplicate subtype test cache entry");
+      }
+      if (old_result.raw() != result.raw()) {
+        FATAL("Existing subtype test cache entry has result %s, not %s",
+              old_result.ToCString(), result.ToCString());
+      }
+      // Some other isolate might have updated the cache between entry was
+      // found missing and now.
+      return;
     }
     new_cache.AddCheck(instance_class_id_or_function, destination_type,
                        instance_type_arguments, instantiator_type_arguments,
@@ -684,56 +699,13 @@
                        instance_parent_function_type_arguments,
                        instance_delayed_type_arguments, result);
     if (FLAG_trace_type_checks) {
-      AbstractType& test_type =
-          AbstractType::Handle(zone, destination_type.raw());
-      if (!test_type.IsInstantiated()) {
-        test_type = test_type.InstantiateFrom(instantiator_type_arguments,
-                                              function_type_arguments, kAllFree,
-                                              Heap::kNew);
-      }
-      const auto& type_class = Class::Handle(zone, test_type.type_class());
-      const auto& instance_class_name =
-          String::Handle(zone, instance_class.Name());
       TextBuffer buffer(256);
-      buffer.Printf("  Updated test cache %#" Px " ix: %" Pd " with ",
+      buffer.Printf("  Added new entry to test cache %#" Px " at index %" Pd
+                    ":\n",
                     static_cast<uword>(new_cache.raw()), len);
-      buffer.Printf(
-          "[ %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px ", %#" Px "",
-          static_cast<uword>(instance_class_id_or_function.raw()),
-          static_cast<uword>(instance_type_arguments.raw()),
-          static_cast<uword>(instantiator_type_arguments.raw()),
-          static_cast<uword>(function_type_arguments.raw()),
-          static_cast<uword>(instance_parent_function_type_arguments.raw()),
-          static_cast<uword>(instance_delayed_type_arguments.raw()));
-      buffer.Printf(", %#" Px "", static_cast<uword>(destination_type.raw()));
-      buffer.Printf(" %#" Px " ]\n", static_cast<uword>(result.raw()));
-      buffer.Printf("    tested type: %s (%" Pd ")\n", test_type.ToCString(),
-                    type_class.id());
-      buffer.Printf("    destination type: %s\n", destination_type.ToCString());
-      buffer.Printf("    instance: %s\n", instance.ToCString());
-      if (instance_class.IsClosureClass()) {
-        buffer.Printf("    function: %s\n",
-                      Function::Cast(instance_class_id_or_function)
-                          .ToFullyQualifiedCString());
-        buffer.Printf("    closure instantiator function type arguments: %s\n",
-                      instance_type_arguments.ToCString());
-        buffer.Printf("    closure parent function type arguments: %s\n",
-                      instance_parent_function_type_arguments.ToCString());
-        buffer.Printf("    closure delayed function type arguments: %s\n",
-                      instance_parent_function_type_arguments.ToCString());
-      } else {
-        buffer.Printf("    class: %s (%" Pd ")\n",
-                      instance_class_name.ToCString(),
-                      Smi::Cast(instance_class_id_or_function).Value());
-        buffer.Printf("    instance type arguments: %s\n",
-                      instance_type_arguments.ToCString());
-      }
-      buffer.Printf("    instantiator type arguments: %s\n",
-                    instantiator_type_arguments.ToCString());
-      buffer.Printf("    function type arguments: %s\n",
-                    function_type_arguments.ToCString());
-      buffer.Printf("    result: %s\n", result.ToCString());
-      OS::PrintErr("%s", buffer.buffer());
+      buffer.Printf("    new entry: ");
+      new_cache.WriteEntryToBuffer(zone, &buffer, len, "      ");
+      OS::PrintErr("%s\n", buffer.buffer());
     }
   }
 }
@@ -882,6 +854,7 @@
       UNREACHABLE();
     }
 
+    ASSERT(!dst_name.IsNull());
     // Throw a dynamic type error.
     const TokenPosition location = GetCallerLocation();
     const AbstractType& src_type =
diff --git a/samples/ffi/async/async_test.dart b/samples/ffi/async/async_test.dart
index 297a76e..f438582 100644
--- a/samples/ffi/async/async_test.dart
+++ b/samples/ffi/async/async_test.dart
@@ -6,8 +6,6 @@
 //
 // SharedObjects=ffi_test_dynamic_library ffi_test_functions
 
-// @dart = 2.9
-
 import 'sample_async_callback.dart' as sample0;
 import 'sample_native_port_call.dart' as sample1;
 
diff --git a/samples/ffi/async/sample_async_callback.dart b/samples/ffi/async/sample_async_callback.dart
index 5e6d79a..59ae31b 100644
--- a/samples/ffi/async/sample_async_callback.dart
+++ b/samples/ffi/async/sample_async_callback.dart
@@ -8,8 +8,6 @@
 //
 // TODO(dartbug.com/37022): Update this when we get real async callbacks.
 
-// @dart = 2.9
-
 import 'dart:ffi';
 import 'dart:isolate';
 
diff --git a/samples/ffi/async/sample_native_port_call.dart b/samples/ffi/async/sample_native_port_call.dart
index a2c3ee4..302a50f 100644
--- a/samples/ffi/async/sample_native_port_call.dart
+++ b/samples/ffi/async/sample_native_port_call.dart
@@ -16,8 +16,6 @@
 // The advantage is that finalizers can be used when passing ownership of data
 // (buffers) from C to Dart.
 
-// @dart = 2.9
-
 import 'dart:ffi';
 import 'dart:isolate';
 import 'dart:typed_data';
@@ -79,8 +77,8 @@
 }
 
 class CppRequest {
-  final SendPort replyPort;
-  final int pendingCall;
+  final SendPort? replyPort;
+  final int? pendingCall;
   final String method;
   final Uint8List data;
 
@@ -114,9 +112,9 @@
     final int argument = cppRequest.data[0];
     final int result = myCallback1(argument);
     final cppResponse =
-        CppResponse(cppRequest.pendingCall, Uint8List.fromList([result]));
+        CppResponse(cppRequest.pendingCall!, Uint8List.fromList([result]));
     print('Dart:   Responding: $cppResponse');
-    cppRequest.replyPort.send(cppResponse.toCppMessage());
+    cppRequest.replyPort!.send(cppResponse.toCppMessage());
   } else if (cppRequest.method == 'myCallback2') {
     final int argument = cppRequest.data[0];
     myCallback2(argument);
diff --git a/samples/ffi/coordinate.dart b/samples/ffi/coordinate.dart
index 4ff34f5..8a54ba9 100644
--- a/samples/ffi/coordinate.dart
+++ b/samples/ffi/coordinate.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import "package:ffi/ffi.dart";
@@ -11,12 +9,12 @@
 /// Sample struct for dart:ffi library.
 class Coordinate extends Struct {
   @Double()
-  double x;
+  external double x;
 
   @Double()
-  double y;
+  external double y;
 
-  Pointer<Coordinate> next;
+  external Pointer<Coordinate> next;
 
   factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
     return allocate<Coordinate>().ref
diff --git a/samples/ffi/dylib_utils.dart b/samples/ffi/dylib_utils.dart
index 39653fa..220c0fc 100644
--- a/samples/ffi/dylib_utils.dart
+++ b/samples/ffi/dylib_utils.dart
@@ -2,13 +2,10 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 import 'dart:io' show Platform;
 
-String _platformPath(String name, {String path}) {
-  if (path == null) path = "";
+String _platformPath(String name, String path) {
   if (Platform.isLinux || Platform.isAndroid)
     return path + "lib" + name + ".so";
   if (Platform.isMacOS) return path + "lib" + name + ".dylib";
@@ -16,7 +13,7 @@
   throw Exception("Platform not implemented");
 }
 
-DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
-  String fullPath = _platformPath(name, path: path);
+DynamicLibrary dlopenPlatformSpecific(String name, {String path = ""}) {
+  String fullPath = _platformPath(name, path);
   return DynamicLibrary.open(fullPath);
 }
diff --git a/samples/ffi/resource_management/pool.dart b/samples/ffi/resource_management/pool.dart
index 776c331..7064f9f 100644
--- a/samples/ffi/resource_management/pool.dart
+++ b/samples/ffi/resource_management/pool.dart
@@ -4,8 +4,6 @@
 //
 // Explicit pool used for managing resources.
 
-// @dart = 2.9
-
 import "dart:async";
 import 'dart:convert';
 import 'dart:ffi';
diff --git a/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart b/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart
index 8d3401b..cab2cb0 100644
--- a/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart
+++ b/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart
@@ -4,8 +4,6 @@
 //
 // Sample illustrating resources are not cleaned up when isolate is shutdown.
 
-// @dart = 2.9
-
 import 'dart:io';
 import "dart:isolate";
 import 'dart:ffi';
@@ -24,7 +22,7 @@
       receiveFromHelper.sendPort,
     );
     print("Main: Helper started.");
-    Pointer<SomeResource> resource;
+    Pointer<SomeResource> resource = nullptr;
     receiveFromHelper.listen((message) {
       if (message is int) {
         resource = Pointer<SomeResource>.fromAddress(message);
diff --git a/samples/ffi/resource_management/pool_sample.dart b/samples/ffi/resource_management/pool_sample.dart
index 87c5e39..00ee1ae 100644
--- a/samples/ffi/resource_management/pool_sample.dart
+++ b/samples/ffi/resource_management/pool_sample.dart
@@ -4,8 +4,6 @@
 //
 // Sample illustrating resource management with an explicit pool.
 
-// @dart = 2.9
-
 import 'dart:ffi';
 
 import 'package:expect/expect.dart';
diff --git a/samples/ffi/resource_management/pool_zoned_sample.dart b/samples/ffi/resource_management/pool_zoned_sample.dart
index 0b4e8ff..05f1794 100644
--- a/samples/ffi/resource_management/pool_zoned_sample.dart
+++ b/samples/ffi/resource_management/pool_zoned_sample.dart
@@ -4,8 +4,6 @@
 //
 // Sample illustrating resource management with an implicit pool in the zone.
 
-// @dart = 2.9
-
 import 'dart:ffi';
 
 import 'package:expect/expect.dart';
diff --git a/samples/ffi/resource_management/resource_management_test.dart b/samples/ffi/resource_management/resource_management_test.dart
index 4b2bbb0..1cc14f7 100644
--- a/samples/ffi/resource_management/resource_management_test.dart
+++ b/samples/ffi/resource_management/resource_management_test.dart
@@ -6,8 +6,6 @@
 //
 // SharedObjects=ffi_test_dynamic_library ffi_test_functions
 
-// @dart = 2.9
-
 import 'pool_isolate_shutdown_sample.dart' as pool_isolate;
 import 'pool_sample.dart' as pool;
 import 'pool_zoned_sample.dart' as pool_zoned;
diff --git a/samples/ffi/resource_management/unmanaged_sample.dart b/samples/ffi/resource_management/unmanaged_sample.dart
index b8c9971..9c3e0f0 100644
--- a/samples/ffi/resource_management/unmanaged_sample.dart
+++ b/samples/ffi/resource_management/unmanaged_sample.dart
@@ -4,8 +4,6 @@
 //
 // Sample illustrating manual resource management, not advised.
 
-// @dart = 2.9
-
 import 'dart:ffi';
 
 import 'package:expect/expect.dart';
diff --git a/samples/ffi/sample_ffi_bitfield.dart b/samples/ffi/sample_ffi_bitfield.dart
index 91bb65d..cc10a2d 100644
--- a/samples/ffi/sample_ffi_bitfield.dart
+++ b/samples/ffi/sample_ffi_bitfield.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'package:ffi/ffi.dart';
@@ -20,7 +18,7 @@
 /// } ScreenCellAttrs;
 class ScreenCellAttrs extends Struct {
   @Int16()
-  int bits;
+  external int bits;
 
   int get bold => getBits(kBoldFieldOffset, kBoldFieldLength);
   void set bold(int value) =>
diff --git a/samples/ffi/sample_ffi_data.dart b/samples/ffi/sample_ffi_data.dart
index 09dba76..8e713d0 100644
--- a/samples/ffi/sample_ffi_data.dart
+++ b/samples/ffi/sample_ffi_data.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 import 'package:ffi/ffi.dart';
 
diff --git a/samples/ffi/sample_ffi_dynamic_library.dart b/samples/ffi/sample_ffi_dynamic_library.dart
index 94863d0..28772ff 100644
--- a/samples/ffi/sample_ffi_dynamic_library.dart
+++ b/samples/ffi/sample_ffi_dynamic_library.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'dylib_utils.dart';
diff --git a/samples/ffi/sample_ffi_functions.dart b/samples/ffi/sample_ffi_functions.dart
index 4cd30b0..bff74eb 100644
--- a/samples/ffi/sample_ffi_functions.dart
+++ b/samples/ffi/sample_ffi_functions.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'package:ffi/ffi.dart';
@@ -202,46 +200,6 @@
   }
 
   {
-    // Passing in null for an int argument throws a null pointer exception.
-    BinaryOp sumPlus42 =
-        ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
-
-    int x = null;
-    try {
-      sumPlus42(43, x);
-    } on Error {
-      print('Expected exception on passing null for int');
-    }
-  }
-
-  {
-    // Passing in null for a double argument throws a null pointer exception.
-    DoubleUnaryOp times1_337Double = ffiTestFunctions
-        .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
-
-    double x = null;
-    try {
-      times1_337Double(x);
-    } on Error {
-      print('Expected exception on passing null for double');
-    }
-  }
-
-  {
-    // Passing in null for an int argument throws a null pointer exception.
-    VigesimalOp sumManyNumbers = ffiTestFunctions
-        .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
-
-    int x = null;
-    try {
-      sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13,
-          14.0, 15, 16.0, 17, 18.0, x, 20.0);
-    } on Error {
-      print('Expected exception on passing null for int');
-    }
-  }
-
-  {
     // Passing in nullptr for a pointer argument results in a nullptr in c.
     Int64PointerUnOp nullableInt64ElemAt1 =
         ffiTestFunctions.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>(
diff --git a/samples/ffi/sample_ffi_functions_callbacks.dart b/samples/ffi/sample_ffi_functions_callbacks.dart
index 79f9a41..affdcef 100644
--- a/samples/ffi/sample_ffi_functions_callbacks.dart
+++ b/samples/ffi/sample_ffi_functions_callbacks.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'coordinate.dart';
diff --git a/samples/ffi/sample_ffi_functions_callbacks_closures.dart b/samples/ffi/sample_ffi_functions_callbacks_closures.dart
index a6ce9a6..1d08340 100644
--- a/samples/ffi/sample_ffi_functions_callbacks_closures.dart
+++ b/samples/ffi/sample_ffi_functions_callbacks_closures.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'package:expect/expect.dart';
diff --git a/samples/ffi/sample_ffi_functions_structs.dart b/samples/ffi/sample_ffi_functions_structs.dart
index 7129658..9aefb57 100644
--- a/samples/ffi/sample_ffi_functions_structs.dart
+++ b/samples/ffi/sample_ffi_functions_structs.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'coordinate.dart';
diff --git a/samples/ffi/sample_ffi_structs.dart b/samples/ffi/sample_ffi_structs.dart
index d5af59e..bd6d7c7 100644
--- a/samples/ffi/sample_ffi_structs.dart
+++ b/samples/ffi/sample_ffi_structs.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 
 import 'package:ffi/ffi.dart';
diff --git a/samples/ffi/samples_test.dart b/samples/ffi/samples_test.dart
index a3674a4..954aa9c 100644
--- a/samples/ffi/samples_test.dart
+++ b/samples/ffi/samples_test.dart
@@ -6,8 +6,6 @@
 //
 // SharedObjects=ffi_test_dynamic_library ffi_test_functions
 
-// @dart = 2.9
-
 import 'sample_ffi_bitfield.dart' as bitfield;
 import 'sample_ffi_data.dart' as data;
 import 'sample_ffi_dynamic_library.dart' as dynamic_library;
diff --git a/samples/ffi/sqlite/example/main.dart b/samples/ffi/sqlite/example/main.dart
index 6af01d4..480ef42 100644
--- a/samples/ffi/sqlite/example/main.dart
+++ b/samples/ffi/sqlite/example/main.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "../lib/sqlite.dart";
 
 void main() {
diff --git a/samples/ffi/sqlite/lib/sqlite.dart b/samples/ffi/sqlite/lib/sqlite.dart
index 825e434..d6bac5b 100644
--- a/samples/ffi/sqlite/lib/sqlite.dart
+++ b/samples/ffi/sqlite/lib/sqlite.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 /// A synchronous SQLite wrapper.
 ///
 /// Written using dart:ffi.
diff --git a/samples/ffi/sqlite/lib/src/bindings/bindings.dart b/samples/ffi/sqlite/lib/src/bindings/bindings.dart
index a1cdba7..a3d73bd 100644
--- a/samples/ffi/sqlite/lib/src/bindings/bindings.dart
+++ b/samples/ffi/sqlite/lib/src/bindings/bindings.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "dart:ffi";
 import "package:ffi/ffi.dart";
 
@@ -13,7 +11,7 @@
 import "types.dart";
 
 class _SQLiteBindings {
-  DynamicLibrary sqlite;
+  late DynamicLibrary sqlite;
 
   /// Opening A New Database Connection
   ///
@@ -29,10 +27,13 @@
   /// [sqlite3_errmsg] or sqlite3_errmsg16() routines can be used to obtain
   /// an English language description of the error following a failure of any
   /// of the sqlite3_open() routines.
-  int Function(Pointer<Utf8> filename, Pointer<Pointer<Database>> databaseOut,
-      int flags, Pointer<Utf8> vfs) sqlite3_open_v2;
+  late int Function(
+      Pointer<Utf8> filename,
+      Pointer<Pointer<Database>> databaseOut,
+      int flags,
+      Pointer<Utf8> vfs) sqlite3_open_v2;
 
-  int Function(Pointer<Database> database) sqlite3_close_v2;
+  late int Function(Pointer<Database> database) sqlite3_close_v2;
 
   /// Compiling An SQL Statement
   ///
@@ -81,7 +82,7 @@
   /// that is returned (the sqlite3_stmt object) contains a copy of the
   /// original SQL text. This causes the [sqlite3_step] interface to
   /// behave differently in three ways:
-  int Function(
+  late int Function(
       Pointer<Database> database,
       Pointer<Utf8> query,
       int nbytes,
@@ -164,7 +165,7 @@
   /// of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,
   /// then the more specific [error codes] are returned directly
   /// by sqlite3_step().  The use of the "v2" interface is recommended.
-  int Function(Pointer<Statement> statement) sqlite3_step;
+  late int Function(Pointer<Statement> statement) sqlite3_step;
 
   /// CAPI3REF: Reset A Prepared Statement Object
   ///
@@ -187,7 +188,7 @@
   /// [sqlite3_reset] returns an appropriate [Errors].
   ///
   /// ^The [sqlite3_reset] interface does not change the values
-  int Function(Pointer<Statement> statement) sqlite3_reset;
+  late int Function(Pointer<Statement> statement) sqlite3_reset;
 
   /// Destroy A Prepared Statement Object
   ///
@@ -212,14 +213,14 @@
   /// a prepared statement after it has been finalized.  Any use of a prepared
   /// statement after it has been finalized can result in undefined and
   /// undesirable behavior such as segfaults and heap corruption.
-  int Function(Pointer<Statement> statement) sqlite3_finalize;
+  late int Function(Pointer<Statement> statement) sqlite3_finalize;
 
   /// Number Of Columns In A Result Set
   ///
   /// ^Return the number of columns in the result set returned by the
   /// prepared statement. ^This routine returns 0 if pStmt is an SQL
   /// statement that does not return data (for example an [UPDATE]).
-  int Function(Pointer<Statement> statement) sqlite3_column_count;
+  late int Function(Pointer<Statement> statement) sqlite3_column_count;
 
   /// Column Names In A Result Set
   ///
@@ -244,7 +245,7 @@
   /// ^The name of a result column is the value of the "AS" clause for
   /// that column, if there is an AS clause.  If there is no AS clause
   /// then the name of the column is unspecified and may change from
-  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+  late Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_name;
 
   /// CAPI3REF: Declared Datatype Of A Query Result
@@ -274,28 +275,28 @@
   /// strongly typed, but the typing is dynamic not static.  ^Type
   /// is associated with individual values, not with the containers
   /// used to hold those values.
-  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+  late Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_decltype;
 
-  int Function(Pointer<Statement> statement, int columnIndex)
+  late int Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_type;
 
-  Pointer<Value> Function(Pointer<Statement> statement, int columnIndex)
+  late Pointer<Value> Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_value;
 
-  double Function(Pointer<Statement> statement, int columnIndex)
+  late double Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_double;
 
-  int Function(Pointer<Statement> statement, int columnIndex)
+  late int Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_int;
 
-  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+  late Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
       sqlite3_column_text;
 
   /// The sqlite3_errstr() interface returns the English-language text that
   /// describes the result code, as UTF-8. Memory to hold the error message
   /// string is managed internally and must not be freed by the application.
-  Pointer<Utf8> Function(int code) sqlite3_errstr;
+  late Pointer<Utf8> Function(int code) sqlite3_errstr;
 
   /// Error Codes And Messages
   ///
@@ -328,7 +329,7 @@
   /// If an interface fails with SQLITE_MISUSE, that means the interface
   /// was invoked incorrectly by the application.  In that case, the
   /// error code and message may or may not be set.
-  Pointer<Utf8> Function(Pointer<Database> database) sqlite3_errmsg;
+  late Pointer<Utf8> Function(Pointer<Database> database) sqlite3_errmsg;
 
   _SQLiteBindings() {
     sqlite = dlopenPlatformSpecific("sqlite3");
@@ -392,5 +393,5 @@
   }
 }
 
-_SQLiteBindings _cachedBindings;
+_SQLiteBindings? _cachedBindings;
 _SQLiteBindings get bindings => _cachedBindings ??= _SQLiteBindings();
diff --git a/samples/ffi/sqlite/lib/src/bindings/constants.dart b/samples/ffi/sqlite/lib/src/bindings/constants.dart
index 120d567..71aa82e 100644
--- a/samples/ffi/sqlite/lib/src/bindings/constants.dart
+++ b/samples/ffi/sqlite/lib/src/bindings/constants.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 /// Result Codes
 ///
 /// Many SQLite functions return an integer result code from the set shown
diff --git a/samples/ffi/sqlite/lib/src/bindings/signatures.dart b/samples/ffi/sqlite/lib/src/bindings/signatures.dart
index 8e5d6e6..2f38dad 100644
--- a/samples/ffi/sqlite/lib/src/bindings/signatures.dart
+++ b/samples/ffi/sqlite/lib/src/bindings/signatures.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "dart:ffi";
 
 import "package:ffi/ffi.dart";
diff --git a/samples/ffi/sqlite/lib/src/bindings/types.dart b/samples/ffi/sqlite/lib/src/bindings/types.dart
index f6a1736..494cdef 100644
--- a/samples/ffi/sqlite/lib/src/bindings/types.dart
+++ b/samples/ffi/sqlite/lib/src/bindings/types.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "dart:ffi";
 
 /// Database Connection Handle
diff --git a/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart b/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart
index 83ca2ba..a86a58b 100644
--- a/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart
+++ b/samples/ffi/sqlite/lib/src/collections/closable_iterator.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 /// This iterator should be [close]d after use.
 ///
 /// [ClosableIterator]s often use resources which should be freed after use.
diff --git a/samples/ffi/sqlite/lib/src/database.dart b/samples/ffi/sqlite/lib/src/database.dart
index 38121a9..7725609 100644
--- a/samples/ffi/sqlite/lib/src/database.dart
+++ b/samples/ffi/sqlite/lib/src/database.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "dart:collection";
 import "dart:ffi";
 
@@ -23,7 +21,7 @@
 ///
 /// This database interacts with SQLite synchonously.
 class Database {
-  Pointer<types.Database> _database;
+  late Pointer<types.Database> _database;
   bool _open = false;
 
   /// Open a database located at the file [path].
@@ -108,11 +106,8 @@
     return Result._(this, statement, columnIndices);
   }
 
-  SQLiteException _loadError([int errorCode]) {
+  SQLiteException _loadError(int errorCode) {
     String errorMessage = bindings.sqlite3_errmsg(_database).ref.toString();
-    if (errorCode == null) {
-      return SQLiteException(errorMessage);
-    }
     String errorCodeExplanation =
         bindings.sqlite3_errstr(errorCode).ref.toString();
     return SQLiteException(
@@ -126,18 +121,13 @@
 /// Please note that this iterator should be [close]d manually if not all [Row]s
 /// are consumed.
 class Result extends IterableBase<Row> implements ClosableIterable<Row> {
-  final Database _database;
   final ClosableIterator<Row> _iterator;
-  final Pointer<Statement> _statement;
-  final Map<String, int> _columnIndices;
-
-  Row _currentRow = null;
 
   Result._(
-    this._database,
-    this._statement,
-    this._columnIndices,
-  ) : _iterator = _ResultIterator(_statement, _columnIndices) {}
+    Database database,
+    Pointer<Statement> statement,
+    Map<String, int> columnIndices,
+  ) : _iterator = _ResultIterator(statement, columnIndices) {}
 
   void close() => _iterator.close();
 
@@ -148,7 +138,7 @@
   final Pointer<Statement> _statement;
   final Map<String, int> _columnIndices;
 
-  Row _currentRow = null;
+  Row? _currentRow;
   bool _closed = false;
 
   _ResultIterator(this._statement, this._columnIndices) {}
@@ -172,7 +162,7 @@
     if (_closed) {
       throw SQLiteException("The result has already been closed.");
     }
-    return _currentRow;
+    return _currentRow!;
   }
 
   void close() {
@@ -197,7 +187,7 @@
   /// for the column by the query compiler.
   dynamic readColumn(String columnName,
       {Convert convert = Convert.DynamicType}) {
-    return readColumnByIndex(_columnIndices[columnName], convert: convert);
+    return readColumnByIndex(_columnIndices[columnName]!, convert: convert);
   }
 
   /// Reads column [columnName].
@@ -227,7 +217,6 @@
         return readColumnByIndexAsText(columnIndex);
       case Type.Null:
         return null;
-        break;
       default:
     }
   }
@@ -235,7 +224,7 @@
   /// Reads column [columnName] and converts to [Type.Integer] if not an
   /// integer.
   int readColumnAsInt(String columnName) {
-    return readColumnByIndexAsInt(_columnIndices[columnName]);
+    return readColumnByIndexAsInt(_columnIndices[columnName]!);
   }
 
   /// Reads column [columnIndex] and converts to [Type.Integer] if not an
@@ -247,7 +236,7 @@
 
   /// Reads column [columnName] and converts to [Type.Text] if not text.
   String readColumnAsText(String columnName) {
-    return readColumnByIndexAsText(_columnIndices[columnName]);
+    return readColumnByIndexAsText(_columnIndices[columnName]!);
   }
 
   /// Reads column [columnIndex] and converts to [Type.Text] if not text.
@@ -298,7 +287,6 @@
     case "null":
       return Type.Null;
   }
-  if (textRepresentation == null) return Type.Null;
   throw Exception("Unknown type [$textRepresentation]");
 }
 
diff --git a/samples/ffi/sqlite/lib/src/ffi/arena.dart b/samples/ffi/sqlite/lib/src/ffi/arena.dart
index a26e807..d01b64f 100644
--- a/samples/ffi/sqlite/lib/src/ffi/arena.dart
+++ b/samples/ffi/sqlite/lib/src/ffi/arena.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 import "dart:async";
 import "dart:ffi";
 
diff --git a/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart b/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart
index 39653fa..220c0fc 100644
--- a/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart
+++ b/samples/ffi/sqlite/lib/src/ffi/dylib_utils.dart
@@ -2,13 +2,10 @@
 // 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 = 2.9
-
 import 'dart:ffi';
 import 'dart:io' show Platform;
 
-String _platformPath(String name, {String path}) {
-  if (path == null) path = "";
+String _platformPath(String name, String path) {
   if (Platform.isLinux || Platform.isAndroid)
     return path + "lib" + name + ".so";
   if (Platform.isMacOS) return path + "lib" + name + ".dylib";
@@ -16,7 +13,7 @@
   throw Exception("Platform not implemented");
 }
 
-DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
-  String fullPath = _platformPath(name, path: path);
+DynamicLibrary dlopenPlatformSpecific(String name, {String path = ""}) {
+  String fullPath = _platformPath(name, path);
   return DynamicLibrary.open(fullPath);
 }
diff --git a/samples/ffi/sqlite/pubspec.yaml b/samples/ffi/sqlite/pubspec.yaml
index dcc5d40..71d9d59 100644
--- a/samples/ffi/sqlite/pubspec.yaml
+++ b/samples/ffi/sqlite/pubspec.yaml
@@ -4,8 +4,8 @@
   Sqlite3 wrapper. Demo for dart:ffi.
 author: Daco Harkes <dacoharkes@google.com>, Samir Jindel <sjindel@google.com>
 environment:
-  sdk: '>=2.1.0 <3.0.0'
+  sdk: '>=2.12.0-0 <3.0.0'
 dependencies:
-  ffi: ^0.1.3
+  ffi: ^0.2.0-nullsafety.1
 dev_dependencies:
-  test: ^1.5.3
+  test: ^1.16.0-nullsafety.12
diff --git a/samples/ffi/sqlite/test/sqlite_test.dart b/samples/ffi/sqlite/test/sqlite_test.dart
index f7d3744..130c95f 100644
--- a/samples/ffi/sqlite/test/sqlite_test.dart
+++ b/samples/ffi/sqlite/test/sqlite_test.dart
@@ -2,8 +2,6 @@
 // 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 = 2.9
-
 // VMOptions=--optimization-counter-threshold=5
 
 import "dart:ffi";
@@ -49,8 +47,7 @@
       expect(true, 1 <= id && id <= 3);
       String name = r.readColumnByIndex(1);
       expect(true, name is String);
-      String alternativeName = r.readColumn("alternative_name");
-      expect(true, alternativeName is String || alternativeName == null);
+      final alternativeName = r.readColumn("alternative_name") as String?;
       dynamic multiTypedValue = r.readColumn("multi_typed_column");
       expect(
           true,
@@ -76,8 +73,7 @@
       expect(true, 1 <= id && id <= 3);
       String name = r.readColumnByIndex(1);
       expect(true, name is String);
-      String alternativeName = r.readColumn("alternative_name");
-      expect(true, alternativeName is String || alternativeName == null);
+      final alternativeName = r.readColumn("alternative_name") as String?;
       dynamic multiTypedValue = r.readColumn("multi_typed_column");
       expect(
           true,
diff --git a/samples_2/.gitignore b/samples_2/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/samples_2/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/samples_2/README b/samples_2/README
new file mode 100644
index 0000000..0e9d731
--- /dev/null
+++ b/samples_2/README
@@ -0,0 +1,4 @@
+This directory contains sample Dart programs.
+
+The samples in this directory have not been migrated to non-nullable by
+default. Please see directory `samples` for migrated samples.
diff --git a/samples_2/ffi/async/async_test.dart b/samples_2/ffi/async/async_test.dart
new file mode 100644
index 0000000..297a76e
--- /dev/null
+++ b/samples_2/ffi/async/async_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This file exercises the sample files so that they are tested.
+//
+// SharedObjects=ffi_test_dynamic_library ffi_test_functions
+
+// @dart = 2.9
+
+import 'sample_async_callback.dart' as sample0;
+import 'sample_native_port_call.dart' as sample1;
+
+main() {
+  sample0.main();
+  sample1.main();
+}
diff --git a/samples_2/ffi/async/sample_async_callback.dart b/samples_2/ffi/async/sample_async_callback.dart
new file mode 100644
index 0000000..5e6d79a
--- /dev/null
+++ b/samples_2/ffi/async/sample_async_callback.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Sample showing how to do async callbacks by telling the Dart isolate to
+// yields its execution thread to C so it can perform the callbacks on the
+// main Dart thread.
+//
+// TODO(dartbug.com/37022): Update this when we get real async callbacks.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+import 'dart:isolate';
+
+import 'package:expect/expect.dart';
+
+import '../dylib_utils.dart';
+
+int globalResult = 0;
+int numCallbacks1 = 0;
+int numCallbacks2 = 0;
+
+main() async {
+  print("Dart = Dart mutator thread executing Dart.");
+  print("C Da = Dart mutator thread executing C.");
+  print("C T1 = Some C thread executing C.");
+  print("C T2 = Some C thread executing C.");
+  print("C    = C T1 or C T2.");
+  print("Dart: Setup.");
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
+  final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
+      int Function(Pointer<Void>)>("InitDartApiDL");
+  Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
+
+  final interactiveCppRequests = ReceivePort()..listen(requestExecuteCallback);
+  final int nativePort = interactiveCppRequests.sendPort.nativePort;
+  registerCallback1(nativePort, callback1FP);
+  registerCallback2(nativePort, callback2FP);
+  print("Dart: Tell C to start worker threads.");
+  startWorkSimulator();
+
+  // We need to yield control in order to be able to receive messages.
+  while (numCallbacks2 < 3) {
+    print("Dart: Yielding (able to receive messages on port).");
+    await asyncSleep(500);
+  }
+  print("Dart: Received expected number of callbacks.");
+
+  Expect.equals(2, numCallbacks1);
+  Expect.equals(3, numCallbacks2);
+  Expect.equals(14, globalResult);
+
+  print("Dart: Tell C to stop worker threads.");
+  stopWorkSimulator();
+  interactiveCppRequests.close();
+  print("Dart: Done.");
+}
+
+int callback1(int a) {
+  print("Dart:     callback1($a).");
+  numCallbacks1++;
+  return a + 3;
+}
+
+void callback2(int a) {
+  print("Dart:     callback2($a).");
+  globalResult += a;
+  numCallbacks2++;
+}
+
+void requestExecuteCallback(dynamic message) {
+  final int work_address = message;
+  final work = Pointer<Work>.fromAddress(work_address);
+  print("Dart:   Calling into C to execute callback ($work).");
+  executeCallback(work);
+  print("Dart:   Done with callback.");
+}
+
+final callback1FP = Pointer.fromFunction<IntPtr Function(IntPtr)>(callback1, 0);
+
+final callback2FP = Pointer.fromFunction<Void Function(IntPtr)>(callback2);
+
+final dl = dlopenPlatformSpecific("ffi_test_functions");
+
+final registerCallback1 = dl.lookupFunction<
+        Void Function(Int64 sendPort,
+            Pointer<NativeFunction<IntPtr Function(IntPtr)>> functionPointer),
+        void Function(int sendPort,
+            Pointer<NativeFunction<IntPtr Function(IntPtr)>> functionPointer)>(
+    'RegisterMyCallbackBlocking');
+
+final registerCallback2 = dl.lookupFunction<
+        Void Function(Int64 sendPort,
+            Pointer<NativeFunction<Void Function(IntPtr)>> functionPointer),
+        void Function(int sendPort,
+            Pointer<NativeFunction<Void Function(IntPtr)>> functionPointer)>(
+    'RegisterMyCallbackNonBlocking');
+
+final startWorkSimulator =
+    dl.lookupFunction<Void Function(), void Function()>('StartWorkSimulator');
+
+final stopWorkSimulator =
+    dl.lookupFunction<Void Function(), void Function()>('StopWorkSimulator');
+
+final executeCallback = dl.lookupFunction<Void Function(Pointer<Work>),
+    void Function(Pointer<Work>)>('ExecuteCallback');
+
+class Work extends Struct {}
+
+Future asyncSleep(int ms) {
+  return new Future.delayed(Duration(milliseconds: ms));
+}
diff --git a/samples_2/ffi/async/sample_native_port_call.dart b/samples_2/ffi/async/sample_native_port_call.dart
new file mode 100644
index 0000000..a2c3ee4
--- /dev/null
+++ b/samples_2/ffi/async/sample_native_port_call.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// Sample showing how to do calls from C into Dart through native ports.
+//
+// This sample does not use FFI callbacks to do the callbacks at all. Instead,
+// it sends a message to Dart through native ports, decodes the message in Dart
+// does a method call in Dart and sends the result back to C through a native
+// port.
+//
+// The disadvantage of this approach compared to `sample_async_callback.dart`
+// is that it requires more boilerplate, because it does not use the automatic
+// marshalling of data of the FFI.
+//
+// The advantage is that finalizers can be used when passing ownership of data
+// (buffers) from C to Dart.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+import 'dart:isolate';
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+
+import '../dylib_utils.dart';
+
+var globalResult = 0;
+var numCallbacks1 = 0;
+var numCallbacks2 = 0;
+
+main() async {
+  print("Dart = Dart mutator thread executing Dart.");
+  print("C Da = Dart mutator thread executing C.");
+  print("C T1 = Some C thread executing C.");
+  print("C T2 = Some C thread executing C.");
+  print("C    = C T1 or C T2.");
+  print("Dart: Setup.");
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
+  final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>),
+      int Function(Pointer<Void>)>("InitDartApiDL");
+  Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
+
+  final interactiveCppRequests = ReceivePort()..listen(handleCppRequests);
+  final int nativePort = interactiveCppRequests.sendPort.nativePort;
+  registerSendPort(nativePort);
+  print("Dart: Tell C to start worker threads.");
+  startWorkSimulator2();
+
+  // We need to yield control in order to be able to receive messages.
+  while (numCallbacks2 < 3) {
+    print("Dart: Yielding (able to receive messages on port).");
+    await asyncSleep(500);
+  }
+  print("Dart: Received expected number of callbacks.");
+
+  Expect.equals(2, numCallbacks1);
+  Expect.equals(3, numCallbacks2);
+  Expect.equals(14, globalResult);
+
+  print("Dart: Tell C to stop worker threads.");
+  stopWorkSimulator2();
+  interactiveCppRequests.close();
+  print("Dart: Done.");
+}
+
+int myCallback1(int a) {
+  print("Dart:     myCallback1($a).");
+  numCallbacks1++;
+  return a + 3;
+}
+
+void myCallback2(int a) {
+  print("Dart:     myCallback2($a).");
+  globalResult += a;
+  numCallbacks2++;
+}
+
+class CppRequest {
+  final SendPort replyPort;
+  final int pendingCall;
+  final String method;
+  final Uint8List data;
+
+  factory CppRequest.fromCppMessage(List message) {
+    return CppRequest._(message[0], message[1], message[2], message[3]);
+  }
+
+  CppRequest._(this.replyPort, this.pendingCall, this.method, this.data);
+
+  String toString() => 'CppRequest(method: $method, ${data.length} bytes)';
+}
+
+class CppResponse {
+  final int pendingCall;
+  final Uint8List data;
+
+  CppResponse(this.pendingCall, this.data);
+
+  List toCppMessage() => List.from([pendingCall, data], growable: false);
+
+  String toString() => 'CppResponse(message: ${data.length})';
+}
+
+void handleCppRequests(dynamic message) {
+  final cppRequest = CppRequest.fromCppMessage(message);
+  print('Dart:   Got message: $cppRequest');
+
+  if (cppRequest.method == 'myCallback1') {
+    // Use the data in any way you like. Here we just take the first byte as
+    // the argument to the function.
+    final int argument = cppRequest.data[0];
+    final int result = myCallback1(argument);
+    final cppResponse =
+        CppResponse(cppRequest.pendingCall, Uint8List.fromList([result]));
+    print('Dart:   Responding: $cppResponse');
+    cppRequest.replyPort.send(cppResponse.toCppMessage());
+  } else if (cppRequest.method == 'myCallback2') {
+    final int argument = cppRequest.data[0];
+    myCallback2(argument);
+  }
+}
+
+final dl = dlopenPlatformSpecific("ffi_test_functions");
+
+final registerSendPort = dl.lookupFunction<Void Function(Int64 sendPort),
+    void Function(int sendPort)>('RegisterSendPort');
+
+final startWorkSimulator2 =
+    dl.lookupFunction<Void Function(), void Function()>('StartWorkSimulator2');
+
+final stopWorkSimulator2 =
+    dl.lookupFunction<Void Function(), void Function()>('StopWorkSimulator2');
+
+Future asyncSleep(int ms) {
+  return new Future.delayed(Duration(milliseconds: ms), () => true);
+}
diff --git a/samples_2/ffi/coordinate.dart b/samples_2/ffi/coordinate.dart
new file mode 100644
index 0000000..4ff34f5
--- /dev/null
+++ b/samples_2/ffi/coordinate.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import "package:ffi/ffi.dart";
+
+/// Sample struct for dart:ffi library.
+class Coordinate extends Struct {
+  @Double()
+  double x;
+
+  @Double()
+  double y;
+
+  Pointer<Coordinate> next;
+
+  factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
+    return allocate<Coordinate>().ref
+      ..x = x
+      ..y = y
+      ..next = next;
+  }
+}
diff --git a/samples_2/ffi/dylib_utils.dart b/samples_2/ffi/dylib_utils.dart
new file mode 100644
index 0000000..39653fa
--- /dev/null
+++ b/samples_2/ffi/dylib_utils.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+import 'dart:io' show Platform;
+
+String _platformPath(String name, {String path}) {
+  if (path == null) path = "";
+  if (Platform.isLinux || Platform.isAndroid)
+    return path + "lib" + name + ".so";
+  if (Platform.isMacOS) return path + "lib" + name + ".dylib";
+  if (Platform.isWindows) return path + name + ".dll";
+  throw Exception("Platform not implemented");
+}
+
+DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
+  String fullPath = _platformPath(name, path: path);
+  return DynamicLibrary.open(fullPath);
+}
diff --git a/samples_2/ffi/resource_management/pool.dart b/samples_2/ffi/resource_management/pool.dart
new file mode 100644
index 0000000..776c331
--- /dev/null
+++ b/samples_2/ffi/resource_management/pool.dart
@@ -0,0 +1,196 @@
+// Copyright (c) 2019, 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.
+//
+// Explicit pool used for managing resources.
+
+// @dart = 2.9
+
+import "dart:async";
+import 'dart:convert';
+import 'dart:ffi';
+import 'dart:typed_data';
+
+import 'package:ffi/ffi.dart' as packageFfi;
+import 'package:ffi/ffi.dart' show Utf8;
+
+/// Manages native resources.
+///
+/// Primary implementations are [Pool] and [Unmanaged].
+abstract class ResourceManager {
+  /// Allocates memory on the native heap.
+  ///
+  /// The native memory is under management by this [ResourceManager].
+  ///
+  /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
+  /// against the default public heap. Allocation of either element size or count
+  /// of 0 is undefined.
+  ///
+  /// Throws an ArgumentError on failure to allocate.
+  Pointer<T> allocate<T extends NativeType>({int count: 1});
+}
+
+/// Manages native resources.
+class Pool implements ResourceManager {
+  /// Native memory under management by this [Pool].
+  final List<Pointer<NativeType>> _managedMemoryPointers = [];
+
+  /// Callbacks for releasing native resources under management by this [Pool].
+  final List<Function()> _managedResourceReleaseCallbacks = [];
+
+  /// Allocates memory on the native heap.
+  ///
+  /// The native memory is under management by this [Pool].
+  ///
+  /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
+  /// against the default public heap. Allocation of either element size or count
+  /// of 0 is undefined.
+  ///
+  /// Throws an ArgumentError on failure to allocate.
+  Pointer<T> allocate<T extends NativeType>({int count: 1}) {
+    final p = Unmanaged().allocate<T>(count: count);
+    _managedMemoryPointers.add(p);
+    return p;
+  }
+
+  /// Registers [resource] in this pool.
+  ///
+  /// Executes [releaseCallback] on [releaseAll].
+  T using<T>(T resource, Function(T) releaseCallback) {
+    _managedResourceReleaseCallbacks.add(() => releaseCallback(resource));
+    return resource;
+  }
+
+  /// Registers [releaseResourceCallback] to be executed on [releaseAll].
+  void onReleaseAll(Function() releaseResourceCallback) {
+    _managedResourceReleaseCallbacks.add(releaseResourceCallback);
+  }
+
+  /// Releases all resources that this [Pool] manages.
+  void releaseAll() {
+    for (final c in _managedResourceReleaseCallbacks) {
+      c();
+    }
+    _managedResourceReleaseCallbacks.clear();
+    for (final p in _managedMemoryPointers) {
+      Unmanaged().free(p);
+    }
+    _managedMemoryPointers.clear();
+  }
+}
+
+/// Creates a [Pool] to manage native resources.
+///
+/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
+R using<R>(R Function(Pool) f) {
+  final p = Pool();
+  try {
+    return f(p);
+  } finally {
+    p.releaseAll();
+  }
+}
+
+/// Creates a zoned [Pool] to manage native resources.
+///
+/// Pool is availabe through [currentPool].
+///
+/// Please note that all throws are caught and packaged in [RethrownError].
+///
+/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
+R usePool<R>(R Function() f) {
+  final p = Pool();
+  try {
+    return runZoned(() => f(),
+        zoneValues: {#_pool: p},
+        onError: (error, st) => throw RethrownError(error, st));
+  } finally {
+    p.releaseAll();
+  }
+}
+
+/// The [Pool] in the current zone.
+Pool get currentPool => Zone.current[#_pool];
+
+class RethrownError {
+  dynamic original;
+  StackTrace originalStackTrace;
+  RethrownError(this.original, this.originalStackTrace);
+  toString() => """RethrownError(${original})
+${originalStackTrace}""";
+}
+
+/// Does not manage it's resources.
+class Unmanaged implements ResourceManager {
+  /// Allocates memory on the native heap.
+  ///
+  /// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
+  /// against the default public heap. Allocation of either element size or count
+  /// of 0 is undefined.
+  ///
+  /// Throws an ArgumentError on failure to allocate.
+  Pointer<T> allocate<T extends NativeType>({int count = 1}) =>
+      packageFfi.allocate(count: count);
+
+  /// Releases memory on the native heap.
+  ///
+  /// For POSIX-based systems, this uses free. On Windows, it uses HeapFree
+  /// against the default public heap. It may only be used against pointers
+  /// allocated in a manner equivalent to [allocate].
+  ///
+  /// Throws an ArgumentError on failure to free.
+  ///
+  void free(Pointer pointer) => packageFfi.free(pointer);
+}
+
+/// Does not manage it's resources.
+final Unmanaged unmanaged = Unmanaged();
+
+extension Utf8InPool on String {
+  /// Convert a [String] to a Utf8-encoded null-terminated C string.
+  ///
+  /// If 'string' contains NULL bytes, the converted string will be truncated
+  /// prematurely. Unpaired surrogate code points in [string] will be preserved
+  /// in the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
+  ///
+  /// Returns a malloc-allocated pointer to the result.
+  ///
+  /// The memory is managed by the [Pool] passed in as [pool].
+  Pointer<Utf8> toUtf8(ResourceManager pool) {
+    final units = utf8.encode(this);
+    final Pointer<Uint8> result = pool.allocate<Uint8>(count: units.length + 1);
+    final Uint8List nativeString = result.asTypedList(units.length + 1);
+    nativeString.setAll(0, units);
+    nativeString[units.length] = 0;
+    return result.cast();
+  }
+}
+
+extension Utf8Helpers on Pointer<Utf8> {
+  /// Returns the length of a null-terminated string -- the number of (one-byte)
+  /// characters before the first null byte.
+  int strlen() {
+    final Pointer<Uint8> array = this.cast<Uint8>();
+    final Uint8List nativeString = array.asTypedList(_maxSize);
+    return nativeString.indexWhere((char) => char == 0);
+  }
+
+  /// Creates a [String] containing the characters UTF-8 encoded in [this].
+  ///
+  /// [this] must be a zero-terminated byte sequence of valid UTF-8
+  /// encodings of Unicode code points. It may also contain UTF-8 encodings of
+  /// unpaired surrogate code points, which is not otherwise valid UTF-8, but
+  /// which may be created when encoding a Dart string containing an unpaired
+  /// surrogate. See [Utf8Decoder] for details on decoding.
+  ///
+  /// Returns a Dart string containing the decoded code points.
+  String contents() {
+    final int length = strlen();
+    return utf8.decode(Uint8List.view(
+        this.cast<Uint8>().asTypedList(length).buffer, 0, length));
+  }
+}
+
+const int _kMaxSmi64 = (1 << 62) - 1;
+const int _kMaxSmi32 = (1 << 30) - 1;
+final int _maxSize = sizeOf<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
diff --git a/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart b/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart
new file mode 100644
index 0000000..8d3401b
--- /dev/null
+++ b/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2019, 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.
+//
+// Sample illustrating resources are not cleaned up when isolate is shutdown.
+
+// @dart = 2.9
+
+import 'dart:io';
+import "dart:isolate";
+import 'dart:ffi';
+
+import 'package:expect/expect.dart';
+
+import 'pool.dart';
+import '../dylib_utils.dart';
+
+void main() {
+  final receiveFromHelper = ReceivePort();
+
+  Isolate.spawn(helperIsolateMain, receiveFromHelper.sendPort)
+      .then((helperIsolate) {
+    helperIsolate.addOnExitListener(
+      receiveFromHelper.sendPort,
+    );
+    print("Main: Helper started.");
+    Pointer<SomeResource> resource;
+    receiveFromHelper.listen((message) {
+      if (message is int) {
+        resource = Pointer<SomeResource>.fromAddress(message);
+        print("Main: Received resource from helper: $resource.");
+        print("Main: Shutting down helper.");
+        helperIsolate.kill(priority: Isolate.immediate);
+      } else {
+        // Isolate kill message.
+        Expect.isNull(message);
+        print("Main: Helper is shut down.");
+        print(
+            "Main: Trying to use resource after isolate that was supposed to free it was shut down.");
+        useResource(resource);
+        print("Main: Releasing resource manually.");
+        releaseResource(resource);
+        print("Main: Shutting down receive port, end of main.");
+        receiveFromHelper.close();
+      }
+    });
+  });
+}
+
+/// If set to `false`, this sample can segfault due to use after free and
+/// double free.
+const keepHelperIsolateAlive = true;
+
+void helperIsolateMain(SendPort sendToMain) {
+  using((Pool pool) {
+    final resource = pool.using(allocateResource(), releaseResource);
+    pool.onReleaseAll(() {
+      // Will only run print if [keepHelperIsolateAlive] is false.
+      print("Helper: Releasing all resources.");
+    });
+    print("Helper: Resource allocated.");
+    useResource(resource);
+    print("Helper: Sending resource to main: $resource.");
+    sendToMain.send(resource.address);
+    print("Helper: Going to sleep.");
+    if (keepHelperIsolateAlive) {
+      while (true) {
+        sleep(Duration(seconds: 1));
+        print("Helper: sleeping.");
+      }
+    }
+  });
+}
+
+final ffiTestDynamicLibrary =
+    dlopenPlatformSpecific("ffi_test_dynamic_library");
+
+final allocateResource = ffiTestDynamicLibrary.lookupFunction<
+    Pointer<SomeResource> Function(),
+    Pointer<SomeResource> Function()>("AllocateResource");
+
+final useResource = ffiTestDynamicLibrary.lookupFunction<
+    Void Function(Pointer<SomeResource>),
+    void Function(Pointer<SomeResource>)>("UseResource");
+
+final releaseResource = ffiTestDynamicLibrary.lookupFunction<
+    Void Function(Pointer<SomeResource>),
+    void Function(Pointer<SomeResource>)>("ReleaseResource");
+
+/// Represents some opaque resource being managed by a library.
+class SomeResource extends Struct {}
diff --git a/samples_2/ffi/resource_management/pool_sample.dart b/samples_2/ffi/resource_management/pool_sample.dart
new file mode 100644
index 0000000..87c5e39
--- /dev/null
+++ b/samples_2/ffi/resource_management/pool_sample.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2019, 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.
+//
+// Sample illustrating resource management with an explicit pool.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+
+import 'package:expect/expect.dart';
+
+import 'pool.dart';
+import '../dylib_utils.dart';
+
+main() {
+  final ffiTestDynamicLibrary =
+      dlopenPlatformSpecific("ffi_test_dynamic_library");
+
+  final MemMove = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
+      void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
+
+  // To ensure resources are freed, wrap them in a [using] call.
+  using((Pool pool) {
+    final p = pool.allocate<Int64>(count: 2);
+    p[0] = 24;
+    MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
+    print(p[1]);
+    Expect.equals(24, p[1]);
+  });
+
+  // Resources are freed also when abnormal control flow occurs.
+  try {
+    using((Pool pool) {
+      final p = pool.allocate<Int64>(count: 2);
+      p[0] = 25;
+      MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
+      print(p[1]);
+      Expect.equals(25, p[1]);
+      throw Exception("Some random exception");
+    });
+    // `free(p)` has been called.
+  } on Exception catch (e) {
+    print("Caught exception: $e");
+  }
+
+  // In a pool multiple resources can be allocated, which will all be freed
+  // at the end of the scope.
+  using((Pool pool) {
+    final p = pool.allocate<Int64>(count: 2);
+    final p2 = pool.allocate<Int64>(count: 2);
+    p[0] = 1;
+    p[1] = 2;
+    MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
+    Expect.equals(1, p2[0]);
+    Expect.equals(2, p2[1]);
+  });
+
+  // If the resource allocation happens in a different scope, then one either
+  // needs to pass the pool to that scope.
+  f1(Pool pool) {
+    return pool.allocate<Int64>(count: 2);
+  }
+
+  using((Pool pool) {
+    final p = f1(pool);
+    final p2 = f1(pool);
+    p[0] = 1;
+    p[1] = 2;
+    MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
+    Expect.equals(1, p2[0]);
+    Expect.equals(2, p2[1]);
+  });
+
+  // Using Strings.
+  using((Pool pool) {
+    final p = "Hello world!".toUtf8(pool);
+    print(p.contents());
+  });
+
+  final allocateResource = ffiTestDynamicLibrary.lookupFunction<
+      Pointer<SomeResource> Function(),
+      Pointer<SomeResource> Function()>("AllocateResource");
+
+  final useResource = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<SomeResource>),
+      void Function(Pointer<SomeResource>)>("UseResource");
+
+  final releaseResource = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<SomeResource>),
+      void Function(Pointer<SomeResource>)>("ReleaseResource");
+
+  // Using an FFI call to release a resource.
+  using((Pool pool) {
+    final r = pool.using(allocateResource(), releaseResource);
+    useResource(r);
+  });
+
+  // Using an FFI call to release a resource with abnormal control flow.
+  try {
+    using((Pool pool) {
+      final r = pool.using(allocateResource(), releaseResource);
+      useResource(r);
+
+      throw Exception("Some random exception");
+    });
+    // Resource has been freed.
+  } on Exception catch (e) {
+    print("Caught exception: $e");
+  }
+}
+
+/// Represents some opaque resource being managed by a library.
+class SomeResource extends Struct {}
diff --git a/samples_2/ffi/resource_management/pool_zoned_sample.dart b/samples_2/ffi/resource_management/pool_zoned_sample.dart
new file mode 100644
index 0000000..0b4e8ff
--- /dev/null
+++ b/samples_2/ffi/resource_management/pool_zoned_sample.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2019, 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.
+//
+// Sample illustrating resource management with an implicit pool in the zone.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+
+import 'package:expect/expect.dart';
+
+import 'pool.dart';
+import '../dylib_utils.dart';
+
+main() {
+  final ffiTestDynamicLibrary =
+      dlopenPlatformSpecific("ffi_test_dynamic_library");
+
+  final MemMove = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
+      void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
+
+  // To ensure resources are freed, wrap them in a [using] call.
+  usePool(() {
+    final p = currentPool.allocate<Int64>(count: 2);
+    p[0] = 24;
+    MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
+    print(p[1]);
+    Expect.equals(24, p[1]);
+  });
+
+  // Resources are freed also when abnormal control flow occurs.
+  try {
+    usePool(() {
+      final p = currentPool.allocate<Int64>(count: 2);
+      p[0] = 25;
+      MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
+      print(p[1]);
+      Expect.equals(25, p[1]);
+      throw Exception("Some random exception");
+    });
+  } on RethrownError catch (e) {
+    // Note that exceptions are wrapped when using zones.
+    print("Caught exception: ${e.original}");
+  }
+
+  // In a pool multiple resources can be allocated, which will all be freed
+  // at the end of the scope.
+  usePool(() {
+    final p = currentPool.allocate<Int64>(count: 2);
+    final p2 = currentPool.allocate<Int64>(count: 2);
+    p[0] = 1;
+    p[1] = 2;
+    MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
+    Expect.equals(1, p2[0]);
+    Expect.equals(2, p2[1]);
+  });
+
+  // If the resource allocation happens in a different scope, it is in the
+  // same zone, so it's lifetime is automatically managed by the pool.
+  f1() {
+    return currentPool.allocate<Int64>(count: 2);
+  }
+
+  usePool(() {
+    final p = f1();
+    final p2 = f1();
+    p[0] = 1;
+    p[1] = 2;
+    MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
+    Expect.equals(1, p2[0]);
+    Expect.equals(2, p2[1]);
+  });
+
+  // Using Strings.
+  usePool(() {
+    final p = "Hello world!".toUtf8(currentPool);
+    print(p.contents());
+  });
+
+  final allocateResource = ffiTestDynamicLibrary.lookupFunction<
+      Pointer<SomeResource> Function(),
+      Pointer<SomeResource> Function()>("AllocateResource");
+
+  final useResource = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<SomeResource>),
+      void Function(Pointer<SomeResource>)>("UseResource");
+
+  final releaseResource = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<SomeResource>),
+      void Function(Pointer<SomeResource>)>("ReleaseResource");
+
+  // Using an FFI call to release a resource.
+  usePool(() {
+    final r = currentPool.using(allocateResource(), releaseResource);
+    useResource(r);
+  });
+
+  // Using an FFI call to release a resource with abnormal control flow.
+  try {
+    usePool(() {
+      final r = currentPool.using(allocateResource(), releaseResource);
+      useResource(r);
+
+      throw Exception("Some random exception");
+    });
+    // Resource has been freed.
+  } on RethrownError catch (e) {
+    // Note that exceptions are wrapped when using zones.
+    print("Caught exception: ${e.original}");
+  }
+}
+
+/// Represents some opaque resource being managed by a library.
+class SomeResource extends Struct {}
diff --git a/samples_2/ffi/resource_management/resource_management_test.dart b/samples_2/ffi/resource_management/resource_management_test.dart
new file mode 100644
index 0000000..4b2bbb0
--- /dev/null
+++ b/samples_2/ffi/resource_management/resource_management_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2019, 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.
+//
+// This file exercises the sample files so that they are tested.
+//
+// SharedObjects=ffi_test_dynamic_library ffi_test_functions
+
+// @dart = 2.9
+
+import 'pool_isolate_shutdown_sample.dart' as pool_isolate;
+import 'pool_sample.dart' as pool;
+import 'pool_zoned_sample.dart' as pool_zoned;
+import 'unmanaged_sample.dart' as unmanaged;
+
+main() {
+  pool_isolate.main();
+  pool.main();
+  pool_zoned.main();
+  unmanaged.main();
+}
diff --git a/samples_2/ffi/resource_management/unmanaged_sample.dart b/samples_2/ffi/resource_management/unmanaged_sample.dart
new file mode 100644
index 0000000..b8c9971
--- /dev/null
+++ b/samples_2/ffi/resource_management/unmanaged_sample.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2019, 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.
+//
+// Sample illustrating manual resource management, not advised.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+
+import 'package:expect/expect.dart';
+
+import 'pool.dart';
+import '../dylib_utils.dart';
+
+main() {
+  final ffiTestDynamicLibrary =
+      dlopenPlatformSpecific("ffi_test_dynamic_library");
+
+  final MemMove = ffiTestDynamicLibrary.lookupFunction<
+      Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
+      void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
+
+  // To ensure resources are freed, call free manually.
+  //
+  // For automatic management use a Pool.
+  final p = unmanaged.allocate<Int64>(count: 2);
+  p[0] = 24;
+  MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
+  print(p[1]);
+  Expect.equals(24, p[1]);
+  unmanaged.free(p);
+
+  // Using Strings.
+  final p2 = "Hello world!".toUtf8(unmanaged);
+  print(p2.contents());
+  unmanaged.free(p2);
+}
diff --git a/samples_2/ffi/sample_ffi_bitfield.dart b/samples_2/ffi/sample_ffi_bitfield.dart
new file mode 100644
index 0000000..91bb65d
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_bitfield.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'package:ffi/ffi.dart';
+import 'package:expect/expect.dart';
+
+/// typedef struct {
+///     unsigned int bold      : 1;
+///     unsigned int underline : 2;
+///     unsigned int italic    : 1;
+///     unsigned int blink     : 1;
+///     unsigned int reverse   : 1;
+///     unsigned int strike    : 1;
+///     unsigned int font      : 4;
+/// } ScreenCellAttrs;
+class ScreenCellAttrs extends Struct {
+  @Int16()
+  int bits;
+
+  int get bold => getBits(kBoldFieldOffset, kBoldFieldLength);
+  void set bold(int value) =>
+      setBits(kBoldFieldOffset, kBoldFieldLength, value);
+
+  int get underline => getBits(kUnderlineFieldOffset, kUnderlineFieldLength);
+  void set underline(int value) =>
+      setBits(kUnderlineFieldOffset, kUnderlineFieldLength, value);
+
+  int get italic => getBits(kItalicFieldOffset, kItalicFieldLength);
+  void set italic(int value) =>
+      setBits(kItalicFieldOffset, kItalicFieldLength, value);
+
+  int get blink => getBits(kBlinkFieldOffset, kBlinkFieldLength);
+  void set blink(int value) =>
+      setBits(kBlinkFieldOffset, kBlinkFieldLength, value);
+
+  int get reverse => getBits(kReverseFieldOffset, kReverseFieldLength);
+  void set reverse(int value) =>
+      setBits(kReverseFieldOffset, kReverseFieldLength, value);
+
+  int get strike => getBits(kStrikeFieldOffset, kStrikeFieldLength);
+  void set strike(int value) =>
+      setBits(kStrikeFieldOffset, kStrikeFieldLength, value);
+
+  int get font => getBits(kFontFieldOffset, kFontFieldLength);
+  void set font(int value) =>
+      setBits(kFontFieldOffset, kFontFieldLength, value);
+
+  int getBits(int offset, int length) => bits.getBits(offset, length);
+
+  void setBits(int offset, int length, int value) {
+    bits = bits.setBits(offset, length, value);
+  }
+}
+
+const int kBoldFieldOffset = 0;
+const int kBoldFieldLength = 1;
+const int kUnderlineFieldOffset = kBoldFieldOffset + kBoldFieldLength;
+const int kUnderlineFieldLength = 2;
+const int kItalicFieldOffset = kUnderlineFieldOffset + kUnderlineFieldLength;
+const int kItalicFieldLength = 1;
+const int kBlinkFieldOffset = kItalicFieldOffset + kItalicFieldLength;
+const int kBlinkFieldLength = 1;
+const int kReverseFieldOffset = kBlinkFieldOffset + kBlinkFieldLength;
+const int kReverseFieldLength = 1;
+const int kStrikeFieldOffset = kReverseFieldOffset + kReverseFieldLength;
+const int kStrikeFieldLength = 1;
+const int kFontFieldOffset = kStrikeFieldOffset + kStrikeFieldLength;
+const int kFontFieldLength = 4;
+
+/// Extension to use a 64-bit integer as bit field.
+extension IntBitField on int {
+  static int _bitMask(int offset, int lenght) => ((1 << lenght) - 1) << offset;
+
+  /// Read `length` bits at `offset`.
+  ///
+  /// Truncates everything.
+  int getBits(int offset, int length) {
+    final mask = _bitMask(offset, length);
+    return (this & mask) >> offset;
+  }
+
+  /// Returns a new integer value in which `length` bits are overwritten at
+  /// `offset`.
+  ///
+  /// Truncates everything.
+  int setBits(int offset, int length, int value) {
+    final mask = _bitMask(offset, length);
+    return (this & ~mask) | ((value << offset) & mask);
+  }
+}
+
+main() {
+  final p = allocate<ScreenCellAttrs>(count: 3);
+
+  // Zeroes out all fields.
+  p.ref.bits = 0;
+
+  // Set individual fields.
+  p.ref.blink = 0;
+  p.ref.bold = 1;
+  p.ref.font = 15;
+  p.ref.italic = 1;
+  p.ref.reverse = 0;
+  p.ref.strike = 0;
+  p.ref.underline = 2;
+
+  // Read individual fields.
+  print(p.ref.blink);
+  print(p.ref.bold);
+  print(p.ref.font);
+  print(p.ref.italic);
+  print(p.ref.reverse);
+  print(p.ref.strike);
+  print(p.ref.underline);
+
+  // A check for automated testing.
+  Expect.equals(1933, p.ref.bits);
+
+  free(p);
+}
diff --git a/samples_2/ffi/sample_ffi_data.dart b/samples_2/ffi/sample_ffi_data.dart
new file mode 100644
index 0000000..09dba76
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_data.dart
@@ -0,0 +1,241 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+import 'package:ffi/ffi.dart';
+
+main() {
+  print('start main');
+
+  {
+    // Basic operation: allocate, get, set, and free.
+    Pointer<Int64> p = allocate();
+    p.value = 42;
+    int pValue = p.value;
+    print('${p.runtimeType} value: ${pValue}');
+    free(p);
+  }
+
+  {
+    // Undefined behavior before set.
+    Pointer<Int64> p = allocate();
+    int pValue = p.value;
+    print('If not set, returns garbage: ${pValue}');
+    free(p);
+  }
+
+  {
+    // Pointers can be created from an address.
+    Pointer<Int64> pHelper = allocate();
+    pHelper.value = 1337;
+
+    int address = pHelper.address;
+    print('Address: ${address}');
+
+    Pointer<Int64> p = Pointer.fromAddress(address);
+    print('${p.runtimeType} value: ${p.value}');
+
+    free(pHelper);
+  }
+
+  {
+    // Address is zeroed out after free.
+    Pointer<Int64> p = allocate();
+    free(p);
+    print('After free, address is zero: ${p.address}');
+  }
+
+  {
+    // Allocating too much throws an exception.
+    try {
+      int maxMint = 9223372036854775807; // 2^63 - 1
+      allocate<Int64>(count: maxMint);
+    } on Error {
+      print('Expected exception on allocating too much');
+    }
+    try {
+      int maxInt1_8 = 1152921504606846975; // 2^60 -1
+      allocate<Int64>(count: maxInt1_8);
+    } on Error {
+      print('Expected exception on allocating too much');
+    }
+  }
+
+  {
+    // Pointers can be cast into another type,
+    // resulting in the corresponding bits read.
+    Pointer<Int64> p1 = allocate();
+    p1.value = 9223372036854775807; // 2^63 - 1
+
+    Pointer<Int32> p2 = p1.cast();
+    print('${p2.runtimeType} value: ${p2.value}'); // -1
+
+    Pointer<Int32> p3 = p2.elementAt(1);
+    print('${p3.runtimeType} value: ${p3.value}'); // 2^31 - 1
+
+    free(p1);
+  }
+
+  {
+    // Data can be tightly packed in memory.
+    Pointer<Int8> p = allocate(count: 8);
+    for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
+      p.elementAt(i).value = i * 3;
+    }
+    for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
+      print('p.elementAt($i) value: ${p.elementAt(i).value}');
+    }
+    free(p);
+  }
+
+  {
+    // Values that don't fit are truncated.
+    Pointer<Int32> p11 = allocate();
+
+    p11.value = 9223372036854775807;
+
+    print(p11);
+
+    free(p11);
+  }
+
+  {
+    // Doubles.
+    Pointer<Double> p = allocate();
+    p.value = 3.14159265359;
+    print('${p.runtimeType} value: ${p.value}');
+    p.value = 3.14;
+    print('${p.runtimeType} value: ${p.value}');
+    free(p);
+  }
+
+  {
+    // Floats.
+    Pointer<Float> p = allocate();
+    p.value = 3.14159265359;
+    print('${p.runtimeType} value: ${p.value}');
+    p.value = 3.14;
+    print('${p.runtimeType} value: ${p.value}');
+    free(p);
+  }
+
+  {
+    // IntPtr varies in size based on whether the platform is 32 or 64 bit.
+    // Addresses of pointers fit in this size.
+    Pointer<IntPtr> p = allocate();
+    int p14addr = p.address;
+    p.value = p14addr;
+    int pValue = p.value;
+    print('${p.runtimeType} value: ${pValue}');
+    free(p);
+  }
+
+  {
+    // Void pointers are unsized.
+    // The size of the element it is pointing to is undefined,
+    // they cannot be allocated, read, or written.
+
+    Pointer<IntPtr> p1 = allocate();
+    Pointer<Void> p2 = p1.cast();
+    print('${p2.runtimeType} address: ${p2.address}');
+
+    free(p1);
+  }
+
+  {
+    // Pointer to a pointer to something.
+    Pointer<Int16> pHelper = allocate();
+    pHelper.value = 17;
+
+    Pointer<Pointer<Int16>> p = allocate();
+
+    // Storing into a pointer pointer automatically unboxes.
+    p.value = pHelper;
+
+    // Reading from a pointer pointer automatically boxes.
+    Pointer<Int16> pHelper2 = p.value;
+    print('${pHelper2.runtimeType} value: ${pHelper2.value}');
+
+    int pValue = p.value.value;
+    print('${p.runtimeType} value\'s value: ${pValue}');
+
+    free(p);
+    free(pHelper);
+  }
+
+  {
+    // The pointer to pointer types must match up.
+    Pointer<Int8> pHelper = allocate();
+    pHelper.value = 123;
+
+    Pointer<Pointer<Int16>> p = allocate();
+
+    // Trying to store `pHelper` into `p.val` would result in a type mismatch.
+
+    free(pHelper);
+    free(p);
+  }
+
+  {
+    // `nullptr` points to address 0 in c++.
+    Pointer<Pointer<Int8>> pointerToPointer = allocate();
+    Pointer<Int8> value = nullptr;
+    pointerToPointer.value = value;
+    value = pointerToPointer.value;
+    print("Loading a pointer to the 0 address is null: ${value}");
+    free(pointerToPointer);
+  }
+
+  {
+    // The toplevel function sizeOf returns element size in bytes.
+    print('sizeOf<Double>(): ${sizeOf<Double>()}');
+    print('sizeOf<Int16>(): ${sizeOf<Int16>()}');
+    print('sizeOf<IntPtr>(): ${sizeOf<IntPtr>()}');
+  }
+
+  {
+    // With IntPtr pointers, one could manually setup aribtrary data
+    // structres in C memory.
+    //
+    // However, it is advised to use Pointer<Pointer<...>> for that.
+
+    void createChain(Pointer<IntPtr> head, int length, int value) {
+      if (length == 0) {
+        head.value = value;
+        return;
+      }
+      Pointer<IntPtr> next = allocate<IntPtr>();
+      head.value = next.address;
+      createChain(next, length - 1, value);
+    }
+
+    int getChainValue(Pointer<IntPtr> head, int length) {
+      if (length == 0) {
+        return head.value;
+      }
+      Pointer<IntPtr> next = Pointer.fromAddress(head.value);
+      return getChainValue(next, length - 1);
+    }
+
+    void freeChain(Pointer<IntPtr> head, int length) {
+      Pointer<IntPtr> next = Pointer.fromAddress(head.value);
+      free(head);
+      if (length == 0) {
+        return;
+      }
+      freeChain(next, length - 1);
+    }
+
+    int length = 10;
+    Pointer<IntPtr> head = allocate();
+    createChain(head, length, 512);
+    int tailValue = getChainValue(head, length);
+    print('tailValue: ${tailValue}');
+    freeChain(head, length);
+  }
+
+  print("end main");
+}
diff --git a/samples_2/ffi/sample_ffi_dynamic_library.dart b/samples_2/ffi/sample_ffi_dynamic_library.dart
new file mode 100644
index 0000000..94863d0
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_dynamic_library.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'dylib_utils.dart';
+
+typedef NativeDoubleUnOp = Double Function(Double);
+
+typedef DoubleUnOp = double Function(double);
+
+main() {
+  DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
+  print(l);
+  print(l.runtimeType);
+
+  var timesFour = l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour");
+  print(timesFour);
+  print(timesFour.runtimeType);
+
+  print(timesFour(3.0));
+}
diff --git a/samples_2/ffi/sample_ffi_functions.dart b/samples_2/ffi/sample_ffi_functions.dart
new file mode 100644
index 0000000..4cd30b0
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_functions.dart
@@ -0,0 +1,262 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'package:ffi/ffi.dart';
+
+import 'dylib_utils.dart';
+
+typedef NativeUnaryOp = Int32 Function(Int32);
+typedef NativeBinaryOp = Int32 Function(Int32, Int32);
+typedef UnaryOp = int Function(int);
+typedef BinaryOp = int Function(int, int);
+typedef GenericBinaryOp<T> = int Function(int, T);
+typedef NativeQuadOpSigned = Int64 Function(Int64, Int32, Int16, Int8);
+typedef NativeQuadOpUnsigned = Uint64 Function(Uint64, Uint32, Uint16, Uint8);
+typedef NativeFunc4 = IntPtr Function(IntPtr);
+typedef NativeDoubleUnaryOp = Double Function(Double);
+typedef NativeFloatUnaryOp = Float Function(Float);
+typedef NativeDecenaryOp = IntPtr Function(IntPtr, IntPtr, IntPtr, IntPtr,
+    IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr);
+typedef NativeDecenaryOp2 = Int16 Function(
+    Int8, Int16, Int8, Int16, Int8, Int16, Int8, Int16, Int8, Int16);
+typedef NativeDoubleDecenaryOp = Double Function(Double, Double, Double, Double,
+    Double, Double, Double, Double, Double, Double);
+typedef NativeVigesimalOp = Double Function(
+    IntPtr,
+    Float,
+    IntPtr,
+    Double,
+    IntPtr,
+    Float,
+    IntPtr,
+    Double,
+    IntPtr,
+    Float,
+    IntPtr,
+    Double,
+    IntPtr,
+    Float,
+    IntPtr,
+    Double,
+    IntPtr,
+    Float,
+    IntPtr,
+    Double);
+typedef Int64PointerUnOp = Pointer<Int64> Function(Pointer<Int64>);
+typedef QuadOp = int Function(int, int, int, int);
+typedef DoubleUnaryOp = double Function(double);
+typedef DecenaryOp = int Function(
+    int, int, int, int, int, int, int, int, int, int);
+typedef DoubleDecenaryOp = double Function(double, double, double, double,
+    double, double, double, double, double, double);
+typedef VigesimalOp = double Function(
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double,
+    int,
+    double);
+
+main() {
+  print('start main');
+
+  DynamicLibrary ffiTestFunctions =
+      dlopenPlatformSpecific("ffi_test_functions");
+
+  {
+    // A int32 bin op.
+    BinaryOp sumPlus42 =
+        ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
+
+    var result = sumPlus42(3, 17);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Various size arguments.
+    QuadOp intComputation = ffiTestFunctions
+        .lookupFunction<NativeQuadOpSigned, QuadOp>("IntComputation");
+    var result = intComputation(125, 250, 500, 1000);
+    print(result);
+    print(result.runtimeType);
+
+    var mint = 0x7FFFFFFFFFFFFFFF; // 2 ^ 63 - 1
+    result = intComputation(1, 1, 0, mint);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Unsigned int parameters.
+    QuadOp uintComputation = ffiTestFunctions
+        .lookupFunction<NativeQuadOpUnsigned, QuadOp>("UintComputation");
+    var result = uintComputation(0xFF, 0xFFFF, 0xFFFFFFFF, -1);
+    result = uintComputation(1, 1, 0, -1);
+    print(result);
+    print(result.runtimeType);
+    print(-0xFF + 0xFFFF - 0xFFFFFFFF);
+  }
+
+  {
+    // Architecture size argument.
+    Pointer<NativeFunction<NativeFunc4>> p = ffiTestFunctions.lookup("Times3");
+    UnaryOp f6 = p.asFunction();
+    var result = f6(1337);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with double.
+    DoubleUnaryOp times1_337Double = ffiTestFunctions
+        .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
+    var result = times1_337Double(2.0);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with float.
+    DoubleUnaryOp times1_337Float = ffiTestFunctions
+        .lookupFunction<NativeFloatUnaryOp, DoubleUnaryOp>("Times1_337Float");
+    var result = times1_337Float(1000.0);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with many arguments: arguments get passed in registers and stack.
+    DecenaryOp sumManyInts = ffiTestFunctions
+        .lookupFunction<NativeDecenaryOp, DecenaryOp>("SumManyInts");
+    var result = sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with many arguments: arguments get passed in registers and stack.
+    DecenaryOp sumManyInts = ffiTestFunctions
+        .lookupFunction<NativeDecenaryOp2, DecenaryOp>("SumManySmallInts");
+    var result = sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with many double arguments.
+    DoubleDecenaryOp sumManyDoubles = ffiTestFunctions.lookupFunction<
+        NativeDoubleDecenaryOp, DoubleDecenaryOp>("SumManyDoubles");
+    var result =
+        sumManyDoubles(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // Function with many arguments, ints and doubles mixed.
+    VigesimalOp sumManyNumbers = ffiTestFunctions
+        .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
+    var result = sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11,
+        12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0);
+    print(result);
+    print(result.runtimeType);
+  }
+
+  {
+    // pass an array / pointer as argument
+    Int64PointerUnOp assign1337Index1 = ffiTestFunctions
+        .lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
+    Pointer<Int64> p2 = allocate(count: 2);
+    p2.value = 42;
+    p2[1] = 1000;
+    print(p2.elementAt(1).address.toRadixString(16));
+    print(p2[1]);
+    Pointer<Int64> result = assign1337Index1(p2);
+    print(p2[1]);
+    print(assign1337Index1);
+    print(assign1337Index1.runtimeType);
+    print(result);
+    print(result.runtimeType);
+    print(result.address.toRadixString(16));
+    print(result.value);
+  }
+
+  {
+    // Passing in null for an int argument throws a null pointer exception.
+    BinaryOp sumPlus42 =
+        ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
+
+    int x = null;
+    try {
+      sumPlus42(43, x);
+    } on Error {
+      print('Expected exception on passing null for int');
+    }
+  }
+
+  {
+    // Passing in null for a double argument throws a null pointer exception.
+    DoubleUnaryOp times1_337Double = ffiTestFunctions
+        .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
+
+    double x = null;
+    try {
+      times1_337Double(x);
+    } on Error {
+      print('Expected exception on passing null for double');
+    }
+  }
+
+  {
+    // Passing in null for an int argument throws a null pointer exception.
+    VigesimalOp sumManyNumbers = ffiTestFunctions
+        .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
+
+    int x = null;
+    try {
+      sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13,
+          14.0, 15, 16.0, 17, 18.0, x, 20.0);
+    } on Error {
+      print('Expected exception on passing null for int');
+    }
+  }
+
+  {
+    // Passing in nullptr for a pointer argument results in a nullptr in c.
+    Int64PointerUnOp nullableInt64ElemAt1 =
+        ffiTestFunctions.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>(
+            "NullableInt64ElemAt1");
+
+    Pointer<Int64> result = nullableInt64ElemAt1(nullptr);
+    print(result);
+    print(result.runtimeType);
+
+    Pointer<Int64> p2 = allocate(count: 2);
+    result = nullableInt64ElemAt1(p2);
+    print(result);
+    print(result.runtimeType);
+    free(p2);
+  }
+
+  print("end main");
+}
diff --git a/samples_2/ffi/sample_ffi_functions_callbacks.dart b/samples_2/ffi/sample_ffi_functions_callbacks.dart
new file mode 100644
index 0000000..79f9a41
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_functions_callbacks.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'coordinate.dart';
+import 'dylib_utils.dart';
+
+typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
+
+typedef CoordinateTrice = Pointer<Coordinate> Function(
+    Pointer<NativeFunction<NativeCoordinateOp>>, Pointer<Coordinate>);
+
+typedef BinaryOp = int Function(int, int);
+typedef NativeIntptrBinOp = IntPtr Function(IntPtr, IntPtr);
+typedef NativeIntptrBinOpLookup = Pointer<NativeFunction<NativeIntptrBinOp>>
+    Function();
+
+typedef NativeApplyTo42And74Type = IntPtr Function(
+    Pointer<NativeFunction<NativeIntptrBinOp>>);
+
+typedef ApplyTo42And74Type = int Function(
+    Pointer<NativeFunction<NativeIntptrBinOp>>);
+
+int myPlus(int a, int b) {
+  print("myPlus");
+  print(a);
+  print(b);
+  return a + b;
+}
+
+main() {
+  print('start main');
+
+  DynamicLibrary ffiTestFunctions =
+      dlopenPlatformSpecific("ffi_test_functions");
+
+  {
+    // Pass a c pointer to a c function as an argument to a c function.
+    Pointer<NativeFunction<NativeCoordinateOp>> transposeCoordinatePointer =
+        ffiTestFunctions.lookup("TransposeCoordinate");
+    Pointer<NativeFunction<CoordinateTrice>> p2 =
+        ffiTestFunctions.lookup("CoordinateUnOpTrice");
+    CoordinateTrice coordinateUnOpTrice = p2.asFunction();
+    Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
+    c1.next = c1.addressOf;
+    Coordinate result =
+        coordinateUnOpTrice(transposeCoordinatePointer, c1.addressOf).ref;
+    print(result.runtimeType);
+    print(result.x);
+    print(result.y);
+  }
+
+  {
+    // Return a c pointer to a c function from a c function.
+    Pointer<NativeFunction<NativeIntptrBinOpLookup>> p14 =
+        ffiTestFunctions.lookup("IntptrAdditionClosure");
+    NativeIntptrBinOpLookup intptrAdditionClosure = p14.asFunction();
+
+    Pointer<NativeFunction<NativeIntptrBinOp>> intptrAdditionPointer =
+        intptrAdditionClosure();
+    BinaryOp intptrAddition = intptrAdditionPointer.asFunction();
+    print(intptrAddition(10, 27));
+  }
+
+  {
+    Pointer<NativeFunction<NativeIntptrBinOp>> pointer =
+        Pointer.fromFunction(myPlus, 0);
+    print(pointer);
+
+    Pointer<NativeFunction<NativeApplyTo42And74Type>> p17 =
+        ffiTestFunctions.lookup("ApplyTo42And74");
+    ApplyTo42And74Type applyTo42And74 = p17.asFunction();
+
+    int result = applyTo42And74(pointer);
+    print(result);
+  }
+
+  print("end main");
+}
diff --git a/samples_2/ffi/sample_ffi_functions_callbacks_closures.dart b/samples_2/ffi/sample_ffi_functions_callbacks_closures.dart
new file mode 100644
index 0000000..a6ce9a6
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_functions_callbacks_closures.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'dart:ffi';
+
+import 'package:expect/expect.dart';
+
+import 'dylib_utils.dart';
+
+void main() {
+  print('start main');
+
+  doDynamicLinking();
+
+  int counter = 0;
+  void closure() {
+    counter++;
+  }
+
+  // C holds on to this closure through a `Dart_PersistenHandle`.
+  registerClosureCallback(closure);
+
+  // Some time later this closure can be invoked.
+  invokeClosureCallback();
+  Expect.equals(1, counter);
+
+  // When C is done it needs to stop holding on to the closure such that the
+  // Dart GC can collect the closure.
+  releaseClosureCallback();
+
+  print('end main');
+}
+
+final testLibrary = dlopenPlatformSpecific("ffi_test_functions");
+
+final registerClosureCallback =
+    testLibrary.lookupFunction<Void Function(Handle), void Function(Object)>(
+        "RegisterClosureCallback");
+
+final invokeClosureCallback = testLibrary
+    .lookupFunction<Void Function(), void Function()>("InvokeClosureCallback");
+
+final releaseClosureCallback = testLibrary
+    .lookupFunction<Void Function(), void Function()>("ReleaseClosureCallback");
+
+void doClosureCallback(Object callback) {
+  final callback_as_function = callback as void Function();
+  callback_as_function();
+}
+
+final closureCallbackPointer =
+    Pointer.fromFunction<Void Function(Handle)>(doClosureCallback);
+
+void doDynamicLinking() {
+  Expect.isTrue(NativeApi.majorVersion == 2);
+  Expect.isTrue(NativeApi.minorVersion >= 0);
+  final initializeApi = testLibrary.lookupFunction<
+      IntPtr Function(Pointer<Void>),
+      int Function(Pointer<Void>)>("InitDartApiDL");
+  Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0);
+
+  final registerClosureCallback = testLibrary.lookupFunction<
+      Void Function(Pointer),
+      void Function(Pointer)>("RegisterClosureCallbackFP");
+  registerClosureCallback(closureCallbackPointer);
+}
diff --git a/samples_2/ffi/sample_ffi_functions_structs.dart b/samples_2/ffi/sample_ffi_functions_structs.dart
new file mode 100644
index 0000000..7129658
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_functions_structs.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'coordinate.dart';
+import 'dylib_utils.dart';
+
+import 'package:ffi/ffi.dart';
+
+typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
+
+main() {
+  print('start main');
+
+  DynamicLibrary ffiTestFunctions =
+      dlopenPlatformSpecific("ffi_test_functions");
+
+  {
+    // Pass a struct to a c function and get a struct as return value.
+    Pointer<NativeFunction<NativeCoordinateOp>> p1 =
+        ffiTestFunctions.lookup("TransposeCoordinate");
+    NativeCoordinateOp f1 = p1.asFunction();
+
+    Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
+    Coordinate c2 = Coordinate.allocate(42.0, 84.0, c1.addressOf);
+    c1.next = c2.addressOf;
+
+    Coordinate result = f1(c1.addressOf).ref;
+
+    print(c1.x);
+    print(c1.y);
+
+    print(result.runtimeType);
+
+    print(result.x);
+    print(result.y);
+  }
+
+  {
+    // Pass an array of structs to a c funtion.
+    Pointer<NativeFunction<NativeCoordinateOp>> p1 =
+        ffiTestFunctions.lookup("CoordinateElemAt1");
+    NativeCoordinateOp f1 = p1.asFunction();
+
+    Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
+    Pointer<Coordinate> c2 = c1.elementAt(1);
+    Pointer<Coordinate> c3 = c1.elementAt(2);
+    c1.ref.x = 10.0;
+    c1.ref.y = 10.0;
+    c1.ref.next = c3;
+    c2.ref.x = 20.0;
+    c2.ref.y = 20.0;
+    c2.ref.next = c1;
+    c3.ref.x = 30.0;
+    c3.ref.y = 30.0;
+    c3.ref.next = c2;
+
+    Coordinate result = f1(c1).ref;
+
+    print(result.x);
+    print(result.y);
+  }
+
+  print("end main");
+}
diff --git a/samples_2/ffi/sample_ffi_structs.dart b/samples_2/ffi/sample_ffi_structs.dart
new file mode 100644
index 0000000..d5af59e
--- /dev/null
+++ b/samples_2/ffi/sample_ffi_structs.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+
+import 'package:ffi/ffi.dart';
+
+import 'coordinate.dart';
+
+main() {
+  print('start main');
+
+  {
+    // Allocates each coordinate separately in c memory.
+    Coordinate c1 = Coordinate.allocate(10.0, 10.0, nullptr);
+    Coordinate c2 = Coordinate.allocate(20.0, 20.0, c1.addressOf);
+    Coordinate c3 = Coordinate.allocate(30.0, 30.0, c2.addressOf);
+    c1.next = c3.addressOf;
+
+    Coordinate currentCoordinate = c1;
+    for (var i in [0, 1, 2, 3, 4]) {
+      currentCoordinate = currentCoordinate.next.ref;
+      print("${currentCoordinate.x}; ${currentCoordinate.y}");
+    }
+
+    free(c1.addressOf);
+    free(c2.addressOf);
+    free(c3.addressOf);
+  }
+
+  {
+    // Allocates coordinates consecutively in c memory.
+    Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
+    Pointer<Coordinate> c2 = c1.elementAt(1);
+    Pointer<Coordinate> c3 = c1.elementAt(2);
+    c1.ref.x = 10.0;
+    c1.ref.y = 10.0;
+    c1.ref.next = c3;
+    c2.ref.x = 20.0;
+    c2.ref.y = 20.0;
+    c2.ref.next = c1;
+    c3.ref.x = 30.0;
+    c3.ref.y = 30.0;
+    c3.ref.next = c2;
+
+    Coordinate currentCoordinate = c1.ref;
+    for (var i in [0, 1, 2, 3, 4]) {
+      currentCoordinate = currentCoordinate.next.ref;
+      print("${currentCoordinate.x}; ${currentCoordinate.y}");
+    }
+
+    free(c1);
+  }
+
+  {
+    Coordinate c = Coordinate.allocate(10, 10, nullptr);
+    print(c is Coordinate);
+    print(c is Pointer<Void>);
+    print(c is Pointer);
+    free(c.addressOf);
+  }
+
+  print("end main");
+}
diff --git a/samples_2/ffi/samples_test.dart b/samples_2/ffi/samples_test.dart
new file mode 100644
index 0000000..a3674a4
--- /dev/null
+++ b/samples_2/ffi/samples_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2019, 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.
+//
+// This file exercises the sample files so that they are tested.
+//
+// SharedObjects=ffi_test_dynamic_library ffi_test_functions
+
+// @dart = 2.9
+
+import 'sample_ffi_bitfield.dart' as bitfield;
+import 'sample_ffi_data.dart' as data;
+import 'sample_ffi_dynamic_library.dart' as dynamic_library;
+import 'sample_ffi_functions_callbacks_closures.dart'
+    as functions_callbacks_closures;
+import 'sample_ffi_functions_callbacks.dart' as functions_callbacks;
+import 'sample_ffi_functions_structs.dart' as functions_structs;
+import 'sample_ffi_functions.dart' as functions;
+import 'sample_ffi_structs.dart' as structs;
+
+main() {
+  bitfield.main();
+  data.main();
+  dynamic_library.main();
+  functions_callbacks_closures.main();
+  functions_callbacks.main();
+  functions_structs.main();
+  functions.main();
+  structs.main();
+}
diff --git a/samples_2/ffi/sqlite/.gitignore b/samples_2/ffi/sqlite/.gitignore
new file mode 100644
index 0000000..7a6bc2e
--- /dev/null
+++ b/samples_2/ffi/sqlite/.gitignore
@@ -0,0 +1,7 @@
+.dart_tool
+.gdb_history
+.packages
+.vscode
+pubspec.lock
+test.db
+test.db-journal
\ No newline at end of file
diff --git a/samples_2/ffi/sqlite/README.md b/samples_2/ffi/sqlite/README.md
new file mode 100644
index 0000000..fbd5cb9
--- /dev/null
+++ b/samples_2/ffi/sqlite/README.md
@@ -0,0 +1,55 @@
+# Sample code dart:ffi
+
+This is an illustrative sample for how to use `dart:ffi`.
+
+## Prerequirement
+
+For Windows, Linux, and MacOS, you should make sure, sqlite dev lib installed on your system.
+
+Windows user can download dll from https://www.sqlite.org/download.html
+
+If you do not have any sqlite3.dll or so file, you may found error message:
+
+```
+Unhandled exception:
+Invalid argument(s): Failed to load dynamic library (126)
+#0      _open (dart:ffi-patch/ffi_dynamic_library_patch.dart:13:55)
+#1      new DynamicLibrary.open (dart:ffi-patch/ffi_dynamic_library_patch.dart:22:12)
+```
+
+## Building and Running this Sample
+
+Building and running this sample is done through pub.
+Running `pub get` and `pub run example/main` should produce the following output.
+
+```sh
+$ pub get
+Resolving dependencies... (6.8s)
++ analyzer 0.35.4
+...
++ yaml 2.1.15
+Downloading analyzer 0.35.4...
+Downloading kernel 0.3.14...
+Downloading front_end 0.1.14...
+Changed 47 dependencies!
+Precompiling executables... (18.0s)
+Precompiled test:test.
+
+```
+
+```
+$ pub run example/main
+1 Chocolade chip cookie Chocolade cookie foo
+2 Ginger cookie null 42
+3 Cinnamon roll null null
+1 Chocolade chip cookie Chocolade cookie foo
+2 Ginger cookie null 42
+expected exception on accessing result data after close: The result has already been closed.
+expected this query to fail: no such column: non_existing_column (Code 1: SQL logic error)
+```
+
+## Tutorial
+
+A tutorial walking through the code is available in [docs/sqlite-tutorial.md](docs/sqlite-tutorial.md).
+For information on how to use this package within a Flutter app, see [docs/android.md](docs/android.md).
+(Note: iOS is not yet supported).
diff --git a/samples_2/ffi/sqlite/docs/android.md b/samples_2/ffi/sqlite/docs/android.md
new file mode 100644
index 0000000..7478c17
--- /dev/null
+++ b/samples_2/ffi/sqlite/docs/android.md
@@ -0,0 +1,53 @@
+**This documentation is for demonstration/testing purposes only!**
+
+# Using FFI with Flutter
+
+## Android
+
+Before using the FFI on Android, you need to procure an Android-compatible build of the native library you want to link against.
+It's important that the shared object(s) be compatible with ABI version you wish to target (or else, that you have multiple builds for different ABIs).
+See [https://developer.android.com/ndk/guides/abis] for more details on Android ABIs.
+Within Flutter, the target ABI is controlled by the `--target-platform` parameter to the `flutter` command.
+
+The workflow for packaging a native library will depend significantly on the library itself, but to illustrate the challenges at play, we'll demonstrate how to build the SQLite library from source to use with the FFI on an Android device.
+
+### Building SQLite for Android
+
+Every Android device ships with a copy of the SQLite library (`/system/lib/libsqlite.so`).
+Unfortunately, this library cannot be loaded directly by apps (see [https://developer.android.com/about/versions/nougat/android-7.0-changes#ndk]).
+It is accessible only through Java.
+Instead, we can build SQLite directly with the NDK.
+
+First, download the SQLite "amalgamation" source from [https://www.sqlite.org/download.html].
+For the sake of brevity, we'll assume the file has been saved as `sqlite-amalgamation-XXXXXXX.zip`, the Android SDK (with NDK extension) is available in `~/Android`, and we're on a Linux workstation.
+
+```sh
+unzip sqlite-amalgamation-XXXXXXX.zip
+cd sqlite-amalgamation-XXXXXXX
+~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang -c sqlite3.c -o sqlite3.o
+~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld -shared sqlite3.o -o libsqlite3.so
+```
+
+Note the use of the `aarch64` prefix to the compiler: this indicates that we're building a shared object for the `arm64-v8a` ABI.
+This will be important later.
+
+### Update Gradle script
+
+Next we need to instruct Gradle to package this library with the app, so it will be available to load off the Android device at runtime.
+Create a folder `native-libraries` in the root folder of the app, and update the `android/app/build.gradle` file:
+
+```groovy
+android {
+    // ...
+    sourceSets {
+        main {
+            jniLibs.srcDir "${project.projectDir.path}/../../native-libraries"
+        }
+    }
+}
+```
+
+Within the `native-libraries` folder, the libraries are organized by ABI.
+Therefore, we must copy the compiled `libsqlite3.so` into `native-libraries/arm64-v8a/libsqlite3.so`.
+If multiple sub-directories are present, the libraries from the sub-directory corresponding to the target ABI will be available in the application's linking path, so the library can be loaded with `DynamicLibrary.open("libsqlite3.so")` in Dart.
+Finally, pass `--target-platform=android-arm64` to the `flutter` command when running or building the app since `libsqlite3.so` was compiled for the `arm64-v8a` ABI.
diff --git a/samples_2/ffi/sqlite/docs/lib/scenario-default.svg b/samples_2/ffi/sqlite/docs/lib/scenario-default.svg
new file mode 100644
index 0000000..6ffa8a3
--- /dev/null
+++ b/samples_2/ffi/sqlite/docs/lib/scenario-default.svg
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="27.846457 27.846457 426.19686 227.77166" width="426.19686" height="227.77166">
+  <defs>
+    <font-face font-family="Helvetica Neue" font-size="16" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
+      <font-face-src>
+        <font-face-name name="HelveticaNeue"/>
+      </font-face-src>
+    </font-face>
+    <font-face font-family="Helvetica Neue" font-size="10" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
+      <font-face-src>
+        <font-face-name name="HelveticaNeue"/>
+      </font-face-src>
+    </font-face>
+  </defs>
+  <metadata> Produced by OmniGraffle 7.9.4 
+    <dc:date>2019-03-13 09:56:08 +0000</dc:date>
+  </metadata>
+  <g id="Canvas_1" fill="none" fill-opacity="1" stroke="none" stroke-dasharray="none" stroke-opacity="1">
+    <title>Canvas 1</title>
+    <rect fill="white" x="27.846457" y="27.846457" width="426.19686" height="227.77166"/>
+    <g id="Canvas_1: Layer 1">
+      <title>Layer 1</title>
+      <g id="Graphic_3">
+        <rect x="28.346457" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="28.346457" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 110.00952)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.703685" y="15">Flutter </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="22.847685" y="33.448">App</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="8.031685" y="69.896">(Imports </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="4.775685" y="88.34399">package)</tspan>
+        </text>
+      </g>
+      <g id="Graphic_5">
+        <rect x="368.50395" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="368.50395" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(373.50395 137.45752)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.855685" y="15">Native </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="12.927685" y="33.448">Library</tspan>
+        </text>
+      </g>
+      <g id="Graphic_6">
+        <rect x="311.81103" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="311.81103" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(316.81103 146.68152)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x=".5064575" y="15">dart:ffi</tspan>
+        </text>
+      </g>
+      <g id="Graphic_7">
+        <rect x="113.38583" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="113.38583" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(118.38583 119.20552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="3.9014575" y="10">Package </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="15.571457" y="22.28">API</tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="1.8614575" y="46.56">(Does not </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.0514575" y="58.839996">expose </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.7764575" y="71.119995">dart:ffi)</tspan>
+        </text>
+      </g>
+      <g id="Graphic_8">
+        <rect x="28.346457" y="226.77166" width="311.81103" height="28.346457" fill="white"/>
+        <rect x="28.346457" y="226.77166" width="311.81103" height="28.346457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 231.7209)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="135.79352" y="15">Dart</tspan>
+        </text>
+      </g>
+      <g id="Graphic_9">
+        <rect x="340.1575" y="226.77166" width="113.38583" height="28.346457" fill="white"/>
+        <rect x="340.1575" y="226.77166" width="113.38583" height="28.346457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(345.1575 231.7209)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="23.428915" y="15">C / C++</tspan>
+        </text>
+      </g>
+      <g id="Graphic_10">
+        <rect x="28.346457" y="28.346457" width="85.03937" height="56.692915" fill="white"/>
+        <rect x="28.346457" y="28.346457" width="85.03937" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 38.244917)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="22.847685" y="15">App </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="1.2236848" y="33.448">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_11">
+        <rect x="113.38583" y="28.346457" width="198.4252" height="56.692915" fill="white"/>
+        <rect x="113.38583" y="28.346457" width="198.4252" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(118.38583 38.244917)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="63.1006" y="15">Package</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="57.9166" y="33.448">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_12">
+        <rect x="311.81103" y="28.346457" width="56.692915" height="56.692915" fill="white"/>
+        <rect x="311.81103" y="28.346457" width="56.692915" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(316.81103 29.020918)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="8.234457" y="15">Dart </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.490457" y="33.448">VM </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="4.2264575" y="51.895996">Team</tspan>
+        </text>
+      </g>
+      <g id="Graphic_13">
+        <rect x="255.11812" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="255.11812" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(260.11812 149.76552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="3.8064575" y="10">Bindings</tspan>
+        </text>
+      </g>
+      <g id="Graphic_14">
+        <rect x="368.50395" y="28.346457" width="85.03937" height="56.692915" fill="white"/>
+        <rect x="368.50395" y="28.346457" width="85.03937" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(373.50395 29.020918)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.855686" y="15">Native </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="12.927686" y="33.448">Library </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="1.2236862" y="51.895996">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_15">
+        <rect x="170.07874" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="170.07874" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(175.07874 106.92552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="18.074685" y="10">Package </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="2.8746848" y="22.28">Implementation</tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="9.559685" y="46.56">(Code which </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.259685" y="58.839996">converts C++ </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x=".1996848" y="71.119995">abstractions into </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="28.074685" y="83.39999">Dart </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="8.629685" y="95.67999">abstractions)</tspan>
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/samples_2/ffi/sqlite/docs/lib/scenario-full.svg b/samples_2/ffi/sqlite/docs/lib/scenario-full.svg
new file mode 100644
index 0000000..4ae18c5
--- /dev/null
+++ b/samples_2/ffi/sqlite/docs/lib/scenario-full.svg
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="27.846457 27.846457 511.23623 227.77166" width="511.23623" height="227.77166">
+  <defs>
+    <font-face font-family="Helvetica Neue" font-size="16" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
+      <font-face-src>
+        <font-face-name name="HelveticaNeue"/>
+      </font-face-src>
+    </font-face>
+    <font-face font-family="Helvetica Neue" font-size="10" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
+      <font-face-src>
+        <font-face-name name="HelveticaNeue"/>
+      </font-face-src>
+    </font-face>
+  </defs>
+  <metadata> Produced by OmniGraffle 7.9.4 
+    <dc:date>2019-03-13 09:53:08 +0000</dc:date>
+  </metadata>
+  <g id="Canvas_1" stroke-opacity="1" stroke="none" stroke-dasharray="none" fill-opacity="1" fill="none">
+    <title>Canvas 1</title>
+    <rect fill="white" x="27.846457" y="27.846457" width="511.23623" height="227.77166"/>
+    <g id="Canvas_1: Layer 1">
+      <title>Layer 1</title>
+      <g id="Graphic_3">
+        <rect x="28.346457" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="28.346457" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 110.00952)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.703685" y="15">Flutter </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="22.847685" y="33.448">App</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="8.031685" y="69.896">(Imports </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="4.775685" y="88.34399">package)</tspan>
+        </text>
+      </g>
+      <g id="Graphic_5">
+        <rect x="453.5433" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="453.5433" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(458.5433 137.45752)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.855685" y="15">Native </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="12.927685" y="33.448">Library</tspan>
+        </text>
+      </g>
+      <g id="Graphic_6">
+        <rect x="311.81103" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="311.81103" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(316.81103 146.68152)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x=".5064575" y="15">dart:ffi</tspan>
+        </text>
+      </g>
+      <g id="Graphic_7">
+        <rect x="113.38583" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="113.38583" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(118.38583 119.20552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="3.9014575" y="10">Package </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="15.571457" y="22.28">API</tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="1.8614575" y="46.56">(Does not </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.0514575" y="58.839996">expose </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.7764575" y="71.119995">dart:ffi)</tspan>
+        </text>
+      </g>
+      <g id="Graphic_8">
+        <rect x="28.346457" y="226.77166" width="311.81103" height="28.346457" fill="white"/>
+        <rect x="28.346457" y="226.77166" width="311.81103" height="28.346457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 231.7209)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="135.79352" y="15">Dart</tspan>
+        </text>
+      </g>
+      <g id="Graphic_9">
+        <rect x="340.1575" y="226.77166" width="198.4252" height="28.346457" fill="white"/>
+        <rect x="340.1575" y="226.77166" width="198.4252" height="28.346457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(345.1575 231.7209)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="65.9486" y="15">C / C++</tspan>
+        </text>
+      </g>
+      <g id="Graphic_10">
+        <rect x="28.346457" y="28.346457" width="85.03937" height="56.692915" fill="white"/>
+        <rect x="28.346457" y="28.346457" width="85.03937" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(33.346457 38.244917)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="22.847685" y="15">App </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="1.2236848" y="33.448">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_11">
+        <rect x="113.38583" y="28.346457" width="198.4252" height="56.692915" fill="white"/>
+        <rect x="113.38583" y="28.346457" width="198.4252" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(118.38583 38.244917)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="63.1006" y="15">Package</tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="57.9166" y="33.448">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_12">
+        <rect x="311.81103" y="28.346457" width="56.692915" height="56.692915" fill="white"/>
+        <rect x="311.81103" y="28.346457" width="56.692915" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(316.81103 29.020918)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="8.234457" y="15">Dart </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.490457" y="33.448">VM </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="4.2264575" y="51.895996">Team</tspan>
+        </text>
+      </g>
+      <g id="Graphic_13">
+        <rect x="255.11812" y="85.03937" width="56.692915" height="141.73229" fill="white"/>
+        <rect x="255.11812" y="85.03937" width="56.692915" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(260.11812 149.76552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="3.8064575" y="10">Bindings</tspan>
+        </text>
+      </g>
+      <g id="Graphic_14">
+        <rect x="453.5433" y="28.346457" width="85.03937" height="56.692915" fill="white"/>
+        <rect x="453.5433" y="28.346457" width="85.03937" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(458.5433 29.020918)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="14.855686" y="15">Native </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="12.927686" y="33.448">Library </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="1.2236862" y="51.895996">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_15">
+        <rect x="170.07874" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="170.07874" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(175.07874 106.92552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="18.074685" y="10">Package </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="2.8746848" y="22.28">Implementation</tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="9.559685" y="46.56">(Code which </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="7.259685" y="58.839996">converts C++ </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x=".1996848" y="71.119995">abstractions into </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="28.074685" y="83.39999">Dart </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="8.629685" y="95.67999">abstractions)</tspan>
+        </text>
+      </g>
+      <g id="Graphic_16">
+        <rect x="368.50395" y="28.346457" width="85.03937" height="56.692915" fill="white"/>
+        <rect x="368.50395" y="28.346457" width="85.03937" height="56.692915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(373.50395 38.244917)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="6.407685" y="15">Package </tspan>
+          <tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="1.2236848" y="33.448">Developer</tspan>
+        </text>
+      </g>
+      <g id="Graphic_17">
+        <rect x="368.50395" y="85.03937" width="85.03937" height="141.73229" fill="white"/>
+        <rect x="368.50395" y="85.03937" width="85.03937" height="141.73229" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
+        <text transform="translate(373.50395 119.20552)" fill="black">
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="14.554685" y="10">Glue code</tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="9.559685" y="34.28">(Code which </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="8.719685" y="46.56">takes care of </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x="5.194685" y="58.839996">things such as </tspan>
+          <tspan font-family="Helvetica Neue" font-size="10" font-weight="400" fill="black" x=".7796848" y="71.119995">C++ exceptions)</tspan>
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/samples_2/ffi/sqlite/docs/sqlite-tutorial.md b/samples_2/ffi/sqlite/docs/sqlite-tutorial.md
new file mode 100644
index 0000000..c7ea858
--- /dev/null
+++ b/samples_2/ffi/sqlite/docs/sqlite-tutorial.md
@@ -0,0 +1,234 @@
+# dart:ffi SQLite mini tutorial
+
+In this mini tutorial we learn how to bind SQLite, a native library, in Dart using Dart's new foreign function interface `dart:ffi`.
+We build a package which provides a Dartlike SQLite API using objects and `Iterator`s.
+Inside the package we write Dart code which directly invokes C functions and manipulates C memory.
+
+## Binding C Functions to Dart
+
+The first step is to load a Native Library:
+
+```dart
+import "dart:ffi";
+
+DynamicLibrary sqlite = dlopenPlatformSpecific("sqlite3");
+```
+
+In a `DynamicLibrary` we can `lookup` functions.
+Let's lookup the function `sqlite3_prepare_v2` in the SQLite library.
+That function has the following signature in the library header file.
+
+```c++
+SQLITE_API int sqlite3_prepare_v2(
+  sqlite3 *db,            /* Database handle */
+  const char *zSql,       /* SQL statement, UTF-8 encoded */
+  int nByte,              /* Maximum length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
+  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+```
+
+In order to lookup a function, we need a _C signature_ and a _Dart signature_.
+
+```dart
+typedef sqlite3_prepare_v2_native_t = Int32 Function(
+    DatabasePointer database,
+    CString query,
+    Int32 nbytes,
+    Pointer<StatementPointer> statementOut,
+    Pointer<CString> tail);
+
+typedef Sqlite3_prepare_v2_t = int Function(
+    DatabasePointer database,
+    CString query,
+    int nbytes,
+    Pointer<StatementPointer> statementOut,
+    Pointer<CString> tail);
+```
+
+With these two signatures we can `lookup` the C function and expose it as a Dart function with `asFunction`.
+
+```dart
+Sqlite3_prepare_v2_t sqlite3_prepare_v2 = sqlite
+    .lookup<NativeFunction<sqlite3_prepare_v2_native_t>>("sqlite3_prepare_v2")
+    .asFunction();
+```
+
+Browse the code: [platform specific dynamic library loading](../lib/src/ffi/dylib_utils.dart), [C signatures](../lib/src/bindings/signatures.dart), [Dart signatures and bindings](../lib/src/bindings/bindings.dart), and [dart:ffi dynamic library interface](../../../../sdk/lib/ffi/dynamic_library.dart).
+
+## Managing C Memory
+
+In order to call `sqlite3_prepare_v2` to prepare a SQLite statement before executing, we need to be able to pass C pointers to C functions.
+
+Database and Statement pointers are opaque pointers in the SQLite C API.
+We specify these as classes extending `Pointer<Void>`.
+
+```dart
+class DatabasePointer extends Pointer<Void> {}
+class StatementPointer extends Pointer<Void> {}
+```
+
+Strings in C are pointers to character arrays.
+
+```dart
+class CString extends Pointer<Uint8> {}
+```
+
+Pointers to C integers, floats, an doubles can be read from and written through to `dart:ffi`.
+However, before we can write to C memory from dart, we need to `allocate` some memory.
+
+```dart
+Pointer<Uint8> p = allocate(); // Infers type argument allocate<Uint8>(), and allocates 1 byte.
+p.value = 123;                 // Stores a Dart int into this C int8.
+int v = p.value;               // Loads a value from C memory.
+```
+
+Note that you can only load a Dart `int` from a C `Uint8`.
+Trying to load a Dart `double` will result in a runtime exception.
+
+We've almost modeled C Strings.
+The last thing we need is to use this `Pointer` as an array.
+We can do this by using `elementAt`.
+
+```dart
+CString string = allocate(count: 4).cast(); // Allocates 4 bytes and casts it to a string.
+string.value = 70;                          // Stores 'F' at index 0.
+string[1] = 70;                             // Stores 'F' at index 1.
+string[2] = 73;                             // Stores 'I' at index 2.
+string[3] = 0;                              // Null terminates the string.
+```
+
+We wrap the above logic of allocating strings in the constructor `CString.allocate`.
+
+Now we have all ingredients to call `sqlite3_prepare_v2`.
+
+```dart
+Pointer<StatementPointer> statementOut = allocate();
+CString queryC = CString.allocate(query);
+int resultCode = sqlite3_prepare_v2(
+    _database, queryC, -1, statementOut, nullptr);
+```
+
+With `dart:ffi` we are responsible for freeing C memory that we allocate.
+So after calling `sqlite3_prepare_v2` we read out the statement pointer, and free the statement pointer pointer and `CString` which held the query string.
+
+```
+StatementPointer statement = statementOut.value;
+statementOut.free();
+queryC.free();
+```
+
+Browse the code: [CString class](../lib/src/ffi/utf8.dart), [code calling sqlite3_prepare_v2](../lib/src/database.dart#57), and [dart:ffi pointer interface](../../../../sdk/lib/ffi/ffi.dart).
+
+## Dart API
+
+We would like to present the users of our package with an object oriented API - not exposing any `dart:ffi` objects to them.
+
+The SQLite C API returns a cursor to the first row of a result after executing a query.
+We can read out the columns of this row and move the cursor to the next row.
+The most natural way to expose this in Dart is through an `Iterable`.
+We provide our package users with the following API.
+
+```dart
+class Result implements Iterable<Row> {}
+
+class Row {
+  dynamic readColumnByIndex(int columnIndex) {}
+  dynamic readColumn(String columnName) {}
+}
+```
+
+However, this interface does not completely match the semantics of the C API.
+When we start reading the next `Row`, we do no longer have access to the previous `Row`.
+We can model this by letting a `Row` keep track if its current or not.
+
+```dart
+class Row {
+  bool _isCurrentRow = true;
+
+  dynamic readColumnByIndex(int columnIndex) {
+    if (!_isCurrentRow) {
+      throw Exception(
+          "This row is not the current row, reading data from the non-current"
+          " row is not supported by sqlite.");
+    }
+    // ...
+    }
+}
+```
+
+A second mismatch between Dart and C is that in C we have to manually release resources.
+After executing a query and reading its results we need to call `sqlite3_finalize(statement)`.
+
+We can take two approaches here, either we structure the API in such a way that users of our package (implicitly) release resources, or we use finalizers to release resources.
+In this tutorial we take the first approach.
+
+If our users iterate over all `Row`s, we can implicitly finalize the statement after they are done with the last row.
+However, if they decide they do not want to iterate over the whole result, they need to explicitly state this.
+In this tutorial, we use the `ClosableIterator` abstraction for `Iterators` with backing resources that need to be `close`d.
+
+```dart
+Result result = d.query("""
+  select id, name
+  from Cookies
+  ;""");
+for (Row r in result) {
+  String name = r.readColumn("name");
+  print(name);
+}
+// Implicitly closes the iterator.
+
+result = d.query("""
+  select id, name
+  from Cookies
+  ;""");
+for (Row r in result) {
+  int id = r.readColumn("id");
+  if (id == 1) {
+    result.close(); // Explicitly closes the iterator, releasing underlying resources.
+    break;
+  }
+}
+```
+
+Browse the code: [Database, Result, Row](../lib/src/database.dart), and [CloseableIterator](../lib/src/collections/closable_iterator.dart).
+
+## Architecture Overview
+
+The following diagram summarized what we have implemented as _package developers_ in this tutorial.
+
+![architecture](lib/scenario-default.svg)
+
+As the package developers wrapping an existing native library, we have only written Dart code - not any C/C++ code.
+We specified bindings to the native library.
+We have provided our package users with an object oriented API without exposing any `dart:ffi` objects.
+And finally, we have implemented the package API by calling the C API.
+
+## Current dart:ffi Development Status
+
+In this minitutorial we used these `dart:ffi` features:
+
+* Loading dynamic libararies and looking up C functions in these dynamic libraries.
+* Calling C functions, with `dart:ffi` automatically marshalling arguments and return value.
+* Manipulating C memory through `Pointer`s with `allocate`, `free`, `load`, `store`, and `elementAt`.
+
+Features which we did not use in this tutorial:
+
+* `@struct` on subtypes of `Pointer` to define a struct with fields. (However, this feature is likely to change in the future.)
+
+Features which `dart:ffi` does not support yet:
+
+* Callbacks from C back into Dart.
+* Finalizers
+* C++ Exceptions (Not on roadmap yet.)
+
+Platform limitations:
+
+* `dart:ffi` is only enabled on 64 bit Windows, Linux, and MacOS. (Arm64 and 32 bit Intel are under review.)
+* `dart:ffi` only works in JIT mode, not in AOT.
+
+It is possible to work around some of the current limitations by adding a C/C++ layer.
+For example we could catch C++ exceptions in a C++ layer, and rethrow them in Dart.
+The architecture diagram would change to the following in that case.
+
+![architecture2](lib/scenario-full.svg)
diff --git a/samples_2/ffi/sqlite/example/main.dart b/samples_2/ffi/sqlite/example/main.dart
new file mode 100644
index 0000000..6af01d4
--- /dev/null
+++ b/samples_2/ffi/sqlite/example/main.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "../lib/sqlite.dart";
+
+void main() {
+  Database d = Database("test.db");
+  d.execute("drop table if exists Cookies;");
+  d.execute("""
+      create table Cookies (
+        id integer primary key,
+        name text not null,
+        alternative_name text
+      );""");
+  d.execute("""
+      insert into Cookies (id, name, alternative_name)
+      values
+        (1,'Chocolade chip cookie', 'Chocolade cookie'),
+        (2,'Ginger cookie', null),
+        (3,'Cinnamon roll', null)
+      ;""");
+  Result result = d.query("""
+      select
+        id,
+        name,
+        alternative_name,
+        case
+          when id=1 then 'foo'
+          when id=2 then 42
+          when id=3 then null
+        end as multi_typed_column
+      from Cookies
+      ;""");
+  for (Row r in result) {
+    int id = r.readColumnAsInt("id");
+    String name = r.readColumnByIndex(1);
+    String alternativeName = r.readColumn("alternative_name");
+    dynamic multiTypedValue = r.readColumn("multi_typed_column");
+    print("$id $name $alternativeName $multiTypedValue");
+  }
+  result = d.query("""
+      select
+        id,
+        name,
+        alternative_name,
+        case
+          when id=1 then 'foo'
+          when id=2 then 42
+          when id=3 then null
+        end as multi_typed_column
+      from Cookies
+      ;""");
+  for (Row r in result) {
+    int id = r.readColumnAsInt("id");
+    String name = r.readColumnByIndex(1);
+    String alternativeName = r.readColumn("alternative_name");
+    dynamic multiTypedValue = r.readColumn("multi_typed_column");
+    print("$id $name $alternativeName $multiTypedValue");
+    if (id == 2) {
+      result.close();
+      break;
+    }
+  }
+  try {
+    result.iterator.moveNext();
+  } on SQLiteException catch (e) {
+    print("expected exception on accessing result data after close: $e");
+  }
+  try {
+    d.query("""
+      select
+        id,
+        non_existing_column
+      from Cookies
+      ;""");
+  } on SQLiteException catch (e) {
+    print("expected this query to fail: $e");
+  }
+  d.execute("drop table Cookies;");
+  d.close();
+}
diff --git a/samples_2/ffi/sqlite/lib/sqlite.dart b/samples_2/ffi/sqlite/lib/sqlite.dart
new file mode 100644
index 0000000..825e434
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/sqlite.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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 = 2.9
+
+/// A synchronous SQLite wrapper.
+///
+/// Written using dart:ffi.
+library sqlite;
+
+export "src/database.dart";
diff --git a/samples_2/ffi/sqlite/lib/src/bindings/bindings.dart b/samples_2/ffi/sqlite/lib/src/bindings/bindings.dart
new file mode 100644
index 0000000..a1cdba7
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/bindings/bindings.dart
@@ -0,0 +1,396 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "dart:ffi";
+import "package:ffi/ffi.dart";
+
+import "../ffi/dylib_utils.dart";
+
+import "signatures.dart";
+import "types.dart";
+
+class _SQLiteBindings {
+  DynamicLibrary sqlite;
+
+  /// Opening A New Database Connection
+  ///
+  /// ^These routines open an SQLite database file as specified by the
+  /// filename argument. ^The filename argument is interpreted as UTF-8 for
+  /// sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
+  /// order for sqlite3_open16(). ^(A database connection handle is usually
+  /// returned in *ppDb, even if an error occurs.  The only exception is that
+  /// if SQLite is unable to allocate memory to hold the sqlite3 object,
+  /// a NULL will be written into *ppDb instead of a pointer to the sqlite3
+  /// object.)^ ^(If the database is opened (and/or created) successfully, then
+  /// [SQLITE_OK] is returned.  Otherwise an error code is returned.)^ ^The
+  /// [sqlite3_errmsg] or sqlite3_errmsg16() routines can be used to obtain
+  /// an English language description of the error following a failure of any
+  /// of the sqlite3_open() routines.
+  int Function(Pointer<Utf8> filename, Pointer<Pointer<Database>> databaseOut,
+      int flags, Pointer<Utf8> vfs) sqlite3_open_v2;
+
+  int Function(Pointer<Database> database) sqlite3_close_v2;
+
+  /// Compiling An SQL Statement
+  ///
+  /// To execute an SQL query, it must first be compiled into a byte-code
+  /// program using one of these routines.
+  ///
+  /// The first argument, "db", is a database connection obtained from a
+  /// prior successful call to sqlite3_open, [sqlite3_open_v2] or
+  /// sqlite3_open16.  The database connection must not have been closed.
+  ///
+  /// The second argument, "zSql", is the statement to be compiled, encoded
+  /// as either UTF-8 or UTF-16.  The sqlite3_prepare() and sqlite3_prepare_v2()
+  /// interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
+  /// use UTF-16.
+  ///
+  /// ^If the nByte argument is less than zero, then zSql is read up to the
+  /// first zero terminator. ^If nByte is non-negative, then it is the maximum
+  /// number of  bytes read from zSql.  ^When nByte is non-negative, the
+  /// zSql string ends at either the first '\000' or '\u0000' character or
+  /// the nByte-th byte, whichever comes first. If the caller knows
+  /// that the supplied string is nul-terminated, then there is a small
+  /// performance advantage to be gained by passing an nByte parameter that
+  /// is equal to the number of bytes in the input string <i>including</i>
+  /// the nul-terminator bytes.
+  ///
+  /// ^If pzTail is not NULL then *pzTail is made to point to the first byte
+  /// past the end of the first SQL statement in zSql.  These routines only
+  /// compile the first statement in zSql, so *pzTail is left pointing to
+  /// what remains uncompiled.
+  ///
+  /// ^*ppStmt is left pointing to a compiled prepared statement that can be
+  /// executed using sqlite3_step.  ^If there is an error, *ppStmt is set
+  /// to NULL.  ^If the input text contains no SQL (if the input is an empty
+  /// string or a comment) then *ppStmt is set to NULL.
+  /// The calling procedure is responsible for deleting the compiled
+  /// SQL statement using [sqlite3_finalize] after it has finished with it.
+  /// ppStmt may not be NULL.
+  ///
+  /// ^On success, the sqlite3_prepare family of routines return [SQLITE_OK];
+  /// otherwise an error code is returned.
+  ///
+  /// The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are
+  /// recommended for all new programs. The two older interfaces are retained
+  /// for backwards compatibility, but their use is discouraged.
+  /// ^In the "v2" interfaces, the prepared statement
+  /// that is returned (the sqlite3_stmt object) contains a copy of the
+  /// original SQL text. This causes the [sqlite3_step] interface to
+  /// behave differently in three ways:
+  int Function(
+      Pointer<Database> database,
+      Pointer<Utf8> query,
+      int nbytes,
+      Pointer<Pointer<Statement>> statementOut,
+      Pointer<Pointer<Utf8>> tail) sqlite3_prepare_v2;
+
+  /// Evaluate An SQL Statement
+  ///
+  /// After a prepared statement has been prepared using either
+  /// [sqlite3_prepare_v2] or sqlite3_prepare16_v2() or one of the legacy
+  /// interfaces sqlite3_prepare() or sqlite3_prepare16(), this function
+  /// must be called one or more times to evaluate the statement.
+  ///
+  /// The details of the behavior of the sqlite3_step() interface depend
+  /// on whether the statement was prepared using the newer "v2" interface
+  /// [sqlite3_prepare_v2] and sqlite3_prepare16_v2() or the older legacy
+  /// interface sqlite3_prepare() and sqlite3_prepare16().  The use of the
+  /// new "v2" interface is recommended for new applications but the legacy
+  /// interface will continue to be supported.
+  ///
+  /// ^In the legacy interface, the return value will be either [SQLITE_BUSY],
+  /// [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].
+  /// ^With the "v2" interface, any of the other [result codes] or
+  /// [extended result codes] might be returned as well.
+  ///
+  /// ^[SQLITE_BUSY] means that the database engine was unable to acquire the
+  /// database locks it needs to do its job.  ^If the statement is a [COMMIT]
+  /// or occurs outside of an explicit transaction, then you can retry the
+  /// statement.  If the statement is not a [COMMIT] and occurs within an
+  /// explicit transaction then you should rollback the transaction before
+  /// continuing.
+  ///
+  /// ^[SQLITE_DONE] means that the statement has finished executing
+  /// successfully.  sqlite3_step() should not be called again on this virtual
+  /// machine without first calling [sqlite3_reset()] to reset the virtual
+  /// machine back to its initial state.
+  ///
+  /// ^If the SQL statement being executed returns any data, then [SQLITE_ROW]
+  /// is returned each time a new row of data is ready for processing by the
+  /// caller. The values may be accessed using the [column access functions].
+  /// sqlite3_step() is called again to retrieve the next row of data.
+  ///
+  /// ^[SQLITE_ERROR] means that a run-time error (such as a constraint
+  /// violation) has occurred.  sqlite3_step() should not be called again on
+  /// the VM. More information may be found by calling [sqlite3_errmsg()].
+  /// ^With the legacy interface, a more specific error code (for example,
+  /// [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)
+  /// can be obtained by calling [sqlite3_reset()] on the
+  /// prepared statement.  ^In the "v2" interface,
+  /// the more specific error code is returned directly by sqlite3_step().
+  ///
+  /// [SQLITE_MISUSE] means that the this routine was called inappropriately.
+  /// Perhaps it was called on a prepared statement that has
+  /// already been [sqlite3_finalize | finalized] or on one that had
+  /// previously returned [SQLITE_ERROR] or [SQLITE_DONE].  Or it could
+  /// be the case that the same database connection is being used by two or
+  /// more threads at the same moment in time.
+  ///
+  /// For all versions of SQLite up to and including 3.6.23.1, a call to
+  /// [sqlite3_reset] was required after sqlite3_step() returned anything
+  /// other than [Errors.SQLITE_ROW] before any subsequent invocation of
+  /// sqlite3_step().  Failure to reset the prepared statement using
+  /// [sqlite3_reset()] would result in an [Errors.SQLITE_MISUSE] return from
+  /// sqlite3_step().  But after version 3.6.23.1, sqlite3_step() began
+  /// calling [sqlite3_reset] automatically in this circumstance rather
+  /// than returning [Errors.SQLITE_MISUSE]. This is not considered a
+  /// compatibility break because any application that ever receives an
+  /// [Errors.SQLITE_MISUSE] error is broken by definition.  The
+  /// [SQLITE_OMIT_AUTORESET] compile-time option
+  /// can be used to restore the legacy behavior.
+  ///
+  /// <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()
+  /// API always returns a generic error code, [SQLITE_ERROR], following any
+  /// error other than [SQLITE_BUSY] and [SQLITE_MISUSE].  You must call
+  /// [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the
+  /// specific [error codes] that better describes the error.
+  /// We admit that this is a goofy design.  The problem has been fixed
+  /// with the "v2" interface.  If you prepare all of your SQL statements
+  /// using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead
+  /// of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,
+  /// then the more specific [error codes] are returned directly
+  /// by sqlite3_step().  The use of the "v2" interface is recommended.
+  int Function(Pointer<Statement> statement) sqlite3_step;
+
+  /// CAPI3REF: Reset A Prepared Statement Object
+  ///
+  /// The sqlite3_reset() function is called to reset a prepared statement
+  /// object back to its initial state, ready to be re-executed.
+  /// ^Any SQL statement variables that had values bound to them using
+  /// the sqlite3_bind_blob | sqlite3_bind_*() API retain their values.
+  /// Use sqlite3_clear_bindings() to reset the bindings.
+  ///
+  /// ^The [sqlite3_reset] interface resets the prepared statement S
+  /// back to the beginning of its program.
+  ///
+  /// ^If the most recent call to [sqlite3_step] for the
+  /// prepared statement S returned [Errors.SQLITE_ROW] or [Errors.SQLITE_DONE],
+  /// or if [sqlite3_step] has never before been called on S,
+  /// then [sqlite3_reset] returns [Errors.SQLITE_OK].
+  ///
+  /// ^If the most recent call to [sqlite3_step(S)] for the
+  /// prepared statement S indicated an error, then
+  /// [sqlite3_reset] returns an appropriate [Errors].
+  ///
+  /// ^The [sqlite3_reset] interface does not change the values
+  int Function(Pointer<Statement> statement) sqlite3_reset;
+
+  /// Destroy A Prepared Statement Object
+  ///
+  /// ^The sqlite3_finalize() function is called to delete a prepared statement.
+  /// ^If the most recent evaluation of the statement encountered no errors
+  /// or if the statement is never been evaluated, then sqlite3_finalize()
+  /// returns SQLITE_OK.  ^If the most recent evaluation of statement S failed,
+  /// then sqlite3_finalize(S) returns the appropriate error code or extended
+  /// error code.
+  ///
+  /// ^The sqlite3_finalize(S) routine can be called at any point during
+  /// the life cycle of prepared statement S:
+  /// before statement S is ever evaluated, after
+  /// one or more calls to [sqlite3_reset], or after any call
+  /// to [sqlite3_step] regardless of whether or not the statement has
+  /// completed execution.
+  ///
+  /// ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op.
+  ///
+  /// The application must finalize every prepared statement in order to avoid
+  /// resource leaks.  It is a grievous error for the application to try to use
+  /// a prepared statement after it has been finalized.  Any use of a prepared
+  /// statement after it has been finalized can result in undefined and
+  /// undesirable behavior such as segfaults and heap corruption.
+  int Function(Pointer<Statement> statement) sqlite3_finalize;
+
+  /// Number Of Columns In A Result Set
+  ///
+  /// ^Return the number of columns in the result set returned by the
+  /// prepared statement. ^This routine returns 0 if pStmt is an SQL
+  /// statement that does not return data (for example an [UPDATE]).
+  int Function(Pointer<Statement> statement) sqlite3_column_count;
+
+  /// Column Names In A Result Set
+  ///
+  /// ^These routines return the name assigned to a particular column
+  /// in the result set of a SELECT statement.  ^The sqlite3_column_name()
+  /// interface returns a pointer to a zero-terminated UTF-8 string
+  /// and sqlite3_column_name16() returns a pointer to a zero-terminated
+  /// UTF-16 string.  ^The first parameter is the prepared statement
+  /// that implements the SELECT statement. ^The second parameter is the
+  /// column number.  ^The leftmost column is number 0.
+  ///
+  /// ^The returned string pointer is valid until either the prepared statement
+  /// is destroyed by [sqlite3_finalize] or until the statement is automatically
+  /// reprepared by the first call to [sqlite3_step] for a particular run
+  /// or until the next call to
+  /// sqlite3_column_name() or sqlite3_column_name16() on the same column.
+  ///
+  /// ^If sqlite3_malloc() fails during the processing of either routine
+  /// (for example during a conversion from UTF-8 to UTF-16) then a
+  /// NULL pointer is returned.
+  ///
+  /// ^The name of a result column is the value of the "AS" clause for
+  /// that column, if there is an AS clause.  If there is no AS clause
+  /// then the name of the column is unspecified and may change from
+  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_name;
+
+  /// CAPI3REF: Declared Datatype Of A Query Result
+  ///
+  /// ^(The first parameter is a prepared statement.
+  /// If this statement is a SELECT statement and the Nth column of the
+  /// returned result set of that SELECT is a table column (not an
+  /// expression or subquery) then the declared type of the table
+  /// column is returned.)^  ^If the Nth column of the result set is an
+  /// expression or subquery, then a NULL pointer is returned.
+  /// ^The returned string is always UTF-8 encoded.
+  ///
+  /// ^(For example, given the database schema:
+  ///
+  /// CREATE TABLE t1(c1 VARIANT);
+  ///
+  /// and the following statement to be compiled:
+  ///
+  /// SELECT c1 + 1, c1 FROM t1;
+  ///
+  /// this routine would return the string "VARIANT" for the second result
+  /// column (i==1), and a NULL pointer for the first result column (i==0).)^
+  ///
+  /// ^SQLite uses dynamic run-time typing.  ^So just because a column
+  /// is declared to contain a particular type does not mean that the
+  /// data stored in that column is of the declared type.  SQLite is
+  /// strongly typed, but the typing is dynamic not static.  ^Type
+  /// is associated with individual values, not with the containers
+  /// used to hold those values.
+  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_decltype;
+
+  int Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_type;
+
+  Pointer<Value> Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_value;
+
+  double Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_double;
+
+  int Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_int;
+
+  Pointer<Utf8> Function(Pointer<Statement> statement, int columnIndex)
+      sqlite3_column_text;
+
+  /// The sqlite3_errstr() interface returns the English-language text that
+  /// describes the result code, as UTF-8. Memory to hold the error message
+  /// string is managed internally and must not be freed by the application.
+  Pointer<Utf8> Function(int code) sqlite3_errstr;
+
+  /// Error Codes And Messages
+  ///
+  /// ^The sqlite3_errcode() interface returns the numeric [result code] or
+  /// [extended result code] for the most recent failed sqlite3_* API call
+  /// associated with a [database connection]. If a prior API call failed
+  /// but the most recent API call succeeded, the return value from
+  /// sqlite3_errcode() is undefined.  ^The sqlite3_extended_errcode()
+  /// interface is the same except that it always returns the
+  /// [extended result code] even when extended result codes are
+  /// disabled.
+  ///
+  /// ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
+  /// text that describes the error, as either UTF-8 or UTF-16 respectively.
+  /// ^(Memory to hold the error message string is managed internally.
+  /// The application does not need to worry about freeing the result.
+  /// However, the error string might be overwritten or deallocated by
+  /// subsequent calls to other SQLite interface functions.)^
+  ///
+  /// When the serialized [threading mode] is in use, it might be the
+  /// case that a second error occurs on a separate thread in between
+  /// the time of the first error and the call to these interfaces.
+  /// When that happens, the second error will be reported since these
+  /// interfaces always report the most recent result.  To avoid
+  /// this, each thread can obtain exclusive use of the [database connection] D
+  /// by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning
+  /// to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after
+  /// all calls to the interfaces listed here are completed.
+  ///
+  /// If an interface fails with SQLITE_MISUSE, that means the interface
+  /// was invoked incorrectly by the application.  In that case, the
+  /// error code and message may or may not be set.
+  Pointer<Utf8> Function(Pointer<Database> database) sqlite3_errmsg;
+
+  _SQLiteBindings() {
+    sqlite = dlopenPlatformSpecific("sqlite3");
+    sqlite3_open_v2 = sqlite
+        .lookup<NativeFunction<sqlite3_open_v2_native_t>>("sqlite3_open_v2")
+        .asFunction();
+    sqlite3_close_v2 = sqlite
+        .lookup<NativeFunction<sqlite3_close_v2_native_t>>("sqlite3_close_v2")
+        .asFunction();
+    sqlite3_prepare_v2 = sqlite
+        .lookup<NativeFunction<sqlite3_prepare_v2_native_t>>(
+            "sqlite3_prepare_v2")
+        .asFunction();
+    sqlite3_step = sqlite
+        .lookup<NativeFunction<sqlite3_step_native_t>>("sqlite3_step")
+        .asFunction();
+    sqlite3_reset = sqlite
+        .lookup<NativeFunction<sqlite3_reset_native_t>>("sqlite3_reset")
+        .asFunction();
+    sqlite3_finalize = sqlite
+        .lookup<NativeFunction<sqlite3_finalize_native_t>>("sqlite3_finalize")
+        .asFunction();
+    sqlite3_errstr = sqlite
+        .lookup<NativeFunction<sqlite3_errstr_native_t>>("sqlite3_errstr")
+        .asFunction();
+    sqlite3_errmsg = sqlite
+        .lookup<NativeFunction<sqlite3_errmsg_native_t>>("sqlite3_errmsg")
+        .asFunction();
+    sqlite3_column_count = sqlite
+        .lookup<NativeFunction<sqlite3_column_count_native_t>>(
+            "sqlite3_column_count")
+        .asFunction();
+    sqlite3_column_name = sqlite
+        .lookup<NativeFunction<sqlite3_column_name_native_t>>(
+            "sqlite3_column_name")
+        .asFunction();
+    sqlite3_column_decltype = sqlite
+        .lookup<NativeFunction<sqlite3_column_decltype_native_t>>(
+            "sqlite3_column_decltype")
+        .asFunction();
+    sqlite3_column_type = sqlite
+        .lookup<NativeFunction<sqlite3_column_type_native_t>>(
+            "sqlite3_column_type")
+        .asFunction();
+    sqlite3_column_value = sqlite
+        .lookup<NativeFunction<sqlite3_column_value_native_t>>(
+            "sqlite3_column_value")
+        .asFunction();
+    sqlite3_column_double = sqlite
+        .lookup<NativeFunction<sqlite3_column_double_native_t>>(
+            "sqlite3_column_double")
+        .asFunction();
+    sqlite3_column_int = sqlite
+        .lookup<NativeFunction<sqlite3_column_int_native_t>>(
+            "sqlite3_column_int")
+        .asFunction();
+    sqlite3_column_text = sqlite
+        .lookup<NativeFunction<sqlite3_column_text_native_t>>(
+            "sqlite3_column_text")
+        .asFunction();
+  }
+}
+
+_SQLiteBindings _cachedBindings;
+_SQLiteBindings get bindings => _cachedBindings ??= _SQLiteBindings();
diff --git a/samples_2/ffi/sqlite/lib/src/bindings/constants.dart b/samples_2/ffi/sqlite/lib/src/bindings/constants.dart
new file mode 100644
index 0000000..120d567
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/bindings/constants.dart
@@ -0,0 +1,184 @@
+// Copyright (c) 2019, 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 = 2.9
+
+/// Result Codes
+///
+/// Many SQLite functions return an integer result code from the set shown
+/// here in order to indicates success or failure.
+///
+/// New error codes may be added in future versions of SQLite.
+///
+/// See also: SQLITE_IOERR_READ | extended result codes,
+/// sqlite3_vtab_on_conflict() SQLITE_ROLLBACK | result codes.
+class Errors {
+  /// Successful result
+  static const int SQLITE_OK = 0;
+
+  /// Generic error
+  static const int SQLITE_ERROR = 1;
+
+  /// Internal logic error in SQLite
+  static const int SQLITE_INTERNAL = 2;
+
+  /// Access permission denied
+  static const int SQLITE_PERM = 3;
+
+  /// Callback routine requested an abort
+  static const int SQLITE_ABORT = 4;
+
+  /// The database file is locked
+  static const int SQLITE_BUSY = 5;
+
+  /// A table in the database is locked
+  static const int SQLITE_LOCKED = 6;
+
+  /// A malloc() failed
+  static const int SQLITE_NOMEM = 7;
+
+  /// Attempt to write a readonly database
+  static const int SQLITE_READONLY = 8;
+
+  /// Operation terminated by sqlite3_interrupt()
+  static const int SQLITE_INTERRUPT = 9;
+
+  /// Some kind of disk I/O error occurred
+  static const int SQLITE_IOERR = 10;
+
+  /// The database disk image is malformed
+  static const int SQLITE_CORRUPT = 11;
+
+  /// Unknown opcode in sqlite3_file_control()
+  static const int SQLITE_NOTFOUND = 12;
+
+  /// Insertion failed because database is full
+  static const int SQLITE_FULL = 13;
+
+  /// Unable to open the database file
+  static const int SQLITE_CANTOPEN = 14;
+
+  /// Database lock protocol error
+  static const int SQLITE_PROTOCOL = 15;
+
+  /// Internal use only
+  static const int SQLITE_EMPTY = 16;
+
+  /// The database schema changed
+  static const int SQLITE_SCHEMA = 17;
+
+  /// String or BLOB exceeds size limit
+  static const int SQLITE_TOOBIG = 18;
+
+  /// Abort due to constraint violation
+  static const int SQLITE_CONSTRAINT = 19;
+
+  /// Data type mismatch
+  static const int SQLITE_MISMATCH = 20;
+
+  /// Library used incorrectly
+  static const int SQLITE_MISUSE = 21;
+
+  /// Uses OS features not supported on host
+  static const int SQLITE_NOLFS = 22;
+
+  /// Authorization denied
+  static const int SQLITE_AUTH = 23;
+
+  /// Not used
+  static const int SQLITE_FORMAT = 24;
+
+  /// 2nd parameter to sqlite3_bind out of range
+  static const int SQLITE_RANGE = 25;
+
+  /// File opened that is not a database file
+  static const int SQLITE_NOTADB = 26;
+
+  /// Notifications from sqlite3_log()
+  static const int SQLITE_NOTICE = 27;
+
+  /// Warnings from sqlite3_log()
+  static const int SQLITE_WARNING = 28;
+
+  /// sqlite3_step() has another row ready
+  static const int SQLITE_ROW = 100;
+
+  /// sqlite3_step() has finished executing
+  static const int SQLITE_DONE = 101;
+}
+
+/// Flags For File Open Operations
+///
+/// These bit values are intended for use in the
+/// 3rd parameter to the [sqlite3_open_v2()] interface and
+/// in the 4th parameter to the [sqlite3_vfs.xOpen] method.
+class Flags {
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_READONLY = 0x00000001;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_READWRITE = 0x00000002;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_CREATE = 0x00000004;
+
+  /// VFS only
+  static const int SQLITE_OPEN_DELETEONCLOSE = 0x00000008;
+
+  /// VFS only
+  static const int SQLITE_OPEN_EXCLUSIVE = 0x00000010;
+
+  /// VFS only
+  static const int SQLITE_OPEN_AUTOPROXY = 0x00000020;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_URI = 0x00000040;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_MEMORY = 0x00000080;
+
+  /// VFS only
+  static const int SQLITE_OPEN_MAIN_DB = 0x00000100;
+
+  /// VFS only
+  static const int SQLITE_OPEN_TEMP_DB = 0x00000200;
+
+  /// VFS only
+  static const int SQLITE_OPEN_TRANSIENT_DB = 0x00000400;
+
+  /// VFS only
+  static const int SQLITE_OPEN_MAIN_JOURNAL = 0x00000800;
+
+  /// VFS only
+  static const int SQLITE_OPEN_TEMP_JOURNAL = 0x00001000;
+
+  /// VFS only
+  static const int SQLITE_OPEN_SUBJOURNAL = 0x00002000;
+
+  /// VFS only
+  static const int SQLITE_OPEN_MASTER_JOURNAL = 0x00004000;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_NOMUTEX = 0x00008000;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_FULLMUTEX = 0x00010000;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_SHAREDCACHE = 0x00020000;
+
+  /// Ok for sqlite3_open_v2()
+  static const int SQLITE_OPEN_PRIVATECACHE = 0x00040000;
+
+  /// VFS only
+  static const int SQLITE_OPEN_WAL = 0x00080000;
+}
+
+class Types {
+  static const int SQLITE_INTEGER = 1;
+  static const int SQLITE_FLOAT = 2;
+  static const int SQLITE_TEXT = 3;
+  static const int SQLITE_BLOB = 4;
+  static const int SQLITE_NULL = 5;
+}
diff --git a/samples_2/ffi/sqlite/lib/src/bindings/signatures.dart b/samples_2/ffi/sqlite/lib/src/bindings/signatures.dart
new file mode 100644
index 0000000..8e5d6e6
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/bindings/signatures.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "dart:ffi";
+
+import "package:ffi/ffi.dart";
+
+import "types.dart";
+
+typedef sqlite3_open_v2_native_t = Int32 Function(Pointer<Utf8> filename,
+    Pointer<Pointer<Database>> ppDb, Int32 flags, Pointer<Utf8> vfs);
+
+typedef sqlite3_close_v2_native_t = Int32 Function(Pointer<Database> database);
+
+typedef sqlite3_prepare_v2_native_t = Int32 Function(
+    Pointer<Database> database,
+    Pointer<Utf8> query,
+    Int32 nbytes,
+    Pointer<Pointer<Statement>> statementOut,
+    Pointer<Pointer<Utf8>> tail);
+
+typedef sqlite3_step_native_t = Int32 Function(Pointer<Statement> statement);
+
+typedef sqlite3_reset_native_t = Int32 Function(Pointer<Statement> statement);
+
+typedef sqlite3_finalize_native_t = Int32 Function(
+    Pointer<Statement> statement);
+
+typedef sqlite3_errstr_native_t = Pointer<Utf8> Function(Int32 error);
+
+typedef sqlite3_errmsg_native_t = Pointer<Utf8> Function(
+    Pointer<Database> database);
+
+typedef sqlite3_column_count_native_t = Int32 Function(
+    Pointer<Statement> statement);
+
+typedef sqlite3_column_name_native_t = Pointer<Utf8> Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_decltype_native_t = Pointer<Utf8> Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_type_native_t = Int32 Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_value_native_t = Pointer<Value> Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_double_native_t = Double Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_int_native_t = Int32 Function(
+    Pointer<Statement> statement, Int32 columnIndex);
+
+typedef sqlite3_column_text_native_t = Pointer<Utf8> Function(
+    Pointer<Statement> statement, Int32 columnIndex);
diff --git a/samples_2/ffi/sqlite/lib/src/bindings/types.dart b/samples_2/ffi/sqlite/lib/src/bindings/types.dart
new file mode 100644
index 0000000..f6a1736
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/bindings/types.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "dart:ffi";
+
+/// Database Connection Handle
+///
+/// Each open SQLite database is represented by a pointer to an instance of
+/// the opaque structure named "sqlite3".  It is useful to think of an sqlite3
+/// pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and
+/// [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
+/// is its destructor.  There are many other interfaces (such as
+/// [sqlite3_prepare_v2()], [sqlite3_create_function()], and
+/// [sqlite3_busy_timeout()] to name but three) that are methods on an
+class Database extends Struct {}
+
+/// SQL Statement Object
+///
+/// An instance of this object represents a single SQL statement.
+/// This object is variously known as a "prepared statement" or a
+/// "compiled SQL statement" or simply as a "statement".
+///
+/// The life of a statement object goes something like this:
+///
+/// <ol>
+/// <li> Create the object using [sqlite3_prepare_v2()] or a related
+///      function.
+/// <li> Bind values to [host parameters] using the sqlite3_bind_*()
+///      interfaces.
+/// <li> Run the SQL by calling [sqlite3_step()] one or more times.
+/// <li> Reset the statement using [sqlite3_reset()] then go back
+///      to step 2.  Do this zero or more times.
+/// <li> Destroy the object using [sqlite3_finalize()].
+/// </ol>
+///
+/// Refer to documentation on individual methods above for additional
+/// information.
+class Statement extends Struct {}
+
+/// Dynamically Typed Value Object
+///
+/// SQLite uses the sqlite3_value object to represent all values
+/// that can be stored in a database table. SQLite uses dynamic typing
+/// for the values it stores.  ^Values stored in sqlite3_value objects
+/// can be integers, floating point values, strings, BLOBs, or NULL.
+///
+/// An sqlite3_value object may be either "protected" or "unprotected".
+/// Some interfaces require a protected sqlite3_value.  Other interfaces
+/// will accept either a protected or an unprotected sqlite3_value.
+/// Every interface that accepts sqlite3_value arguments specifies
+/// whether or not it requires a protected sqlite3_value.
+///
+/// The terms "protected" and "unprotected" refer to whether or not
+/// a mutex is held.  An internal mutex is held for a protected
+/// sqlite3_value object but no mutex is held for an unprotected
+/// sqlite3_value object.  If SQLite is compiled to be single-threaded
+/// (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
+/// or if SQLite is run in one of reduced mutex modes
+/// [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
+/// then there is no distinction between protected and unprotected
+/// sqlite3_value objects and they can be used interchangeably.  However,
+/// for maximum code portability it is recommended that applications
+/// still make the distinction between protected and unprotected
+/// sqlite3_value objects even when not strictly required.
+///
+/// ^The sqlite3_value objects that are passed as parameters into the
+/// implementation of [application-defined SQL functions] are protected.
+/// ^The sqlite3_value object returned by
+/// [sqlite3_column_value()] is unprotected.
+/// Unprotected sqlite3_value objects may only be used with
+/// [sqlite3_result_value()] and [sqlite3_bind_value()].
+/// The [sqlite3_value_blob | sqlite3_value_type()] family of
+/// interfaces require protected sqlite3_value objects.
+class Value extends Struct {}
diff --git a/samples_2/ffi/sqlite/lib/src/collections/closable_iterator.dart b/samples_2/ffi/sqlite/lib/src/collections/closable_iterator.dart
new file mode 100644
index 0000000..83ca2ba
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/collections/closable_iterator.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2019, 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 = 2.9
+
+/// This iterator should be [close]d after use.
+///
+/// [ClosableIterator]s often use resources which should be freed after use.
+/// The consumer of the iterator can either manually [close] the iterator, or
+/// consume all elements on which the iterator will automatically be closed.
+abstract class ClosableIterator<T> extends Iterator<T> {
+  /// Close this iterator.
+  void close();
+
+  /// Moves to the next element and [close]s the iterator if it was the last
+  /// element.
+  bool moveNext();
+}
+
+/// This iterable's iterator should be [close]d after use.
+///
+/// Companion class of [ClosableIterator].
+abstract class ClosableIterable<T> extends Iterable<T> {
+  /// Close this iterables iterator.
+  void close();
+
+  /// Returns a [ClosableIterator] that allows iterating the elements of this
+  /// [ClosableIterable].
+  ClosableIterator<T> get iterator;
+}
diff --git a/samples_2/ffi/sqlite/lib/src/database.dart b/samples_2/ffi/sqlite/lib/src/database.dart
new file mode 100644
index 0000000..38121a9
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/database.dart
@@ -0,0 +1,314 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "dart:collection";
+import "dart:ffi";
+
+import "package:ffi/ffi.dart";
+
+import "bindings/bindings.dart";
+
+import "bindings/types.dart" as types;
+import "bindings/types.dart" hide Database;
+
+import "bindings/constants.dart";
+import "collections/closable_iterator.dart";
+
+/// [Database] represents an open connection to a SQLite database.
+///
+/// All functions against a database may throw [SQLiteError].
+///
+/// This database interacts with SQLite synchonously.
+class Database {
+  Pointer<types.Database> _database;
+  bool _open = false;
+
+  /// Open a database located at the file [path].
+  Database(String path,
+      [int flags = Flags.SQLITE_OPEN_READWRITE | Flags.SQLITE_OPEN_CREATE]) {
+    Pointer<Pointer<types.Database>> dbOut = allocate();
+    final pathC = Utf8.toUtf8(path);
+    final int resultCode =
+        bindings.sqlite3_open_v2(pathC, dbOut, flags, nullptr);
+    _database = dbOut.value;
+    free(dbOut);
+    free(pathC);
+
+    if (resultCode == Errors.SQLITE_OK) {
+      _open = true;
+    } else {
+      // Even if "open" fails, sqlite3 will still create a database object. We
+      // can just destroy it.
+      SQLiteException exception = _loadError(resultCode);
+      close();
+      throw exception;
+    }
+  }
+
+  /// Close the database.
+  ///
+  /// This should only be called once on a database unless an exception is
+  /// thrown. It should be called at least once to finalize the database and
+  /// avoid resource leaks.
+  void close() {
+    assert(_open);
+    final int resultCode = bindings.sqlite3_close_v2(_database);
+    if (resultCode == Errors.SQLITE_OK) {
+      _open = false;
+    } else {
+      throw _loadError(resultCode);
+    }
+  }
+
+  /// Execute a query, discarding any returned rows.
+  void execute(String query) {
+    Pointer<Pointer<Statement>> statementOut = allocate();
+    Pointer<Utf8> queryC = Utf8.toUtf8(query);
+    int resultCode = bindings.sqlite3_prepare_v2(
+        _database, queryC, -1, statementOut, nullptr);
+    Pointer<Statement> statement = statementOut.value;
+    free(statementOut);
+    free(queryC);
+
+    while (resultCode == Errors.SQLITE_ROW || resultCode == Errors.SQLITE_OK) {
+      resultCode = bindings.sqlite3_step(statement);
+    }
+    bindings.sqlite3_finalize(statement);
+    if (resultCode != Errors.SQLITE_DONE) {
+      throw _loadError(resultCode);
+    }
+  }
+
+  /// Evaluate a query and return the resulting rows as an iterable.
+  Result query(String query) {
+    Pointer<Pointer<Statement>> statementOut = allocate();
+    Pointer<Utf8> queryC = Utf8.toUtf8(query);
+    int resultCode = bindings.sqlite3_prepare_v2(
+        _database, queryC, -1, statementOut, nullptr);
+    Pointer<Statement> statement = statementOut.value;
+    free(statementOut);
+    free(queryC);
+
+    if (resultCode != Errors.SQLITE_OK) {
+      bindings.sqlite3_finalize(statement);
+      throw _loadError(resultCode);
+    }
+
+    Map<String, int> columnIndices = {};
+    int columnCount = bindings.sqlite3_column_count(statement);
+    for (int i = 0; i < columnCount; i++) {
+      String columnName =
+          bindings.sqlite3_column_name(statement, i).ref.toString();
+      columnIndices[columnName] = i;
+    }
+
+    return Result._(this, statement, columnIndices);
+  }
+
+  SQLiteException _loadError([int errorCode]) {
+    String errorMessage = bindings.sqlite3_errmsg(_database).ref.toString();
+    if (errorCode == null) {
+      return SQLiteException(errorMessage);
+    }
+    String errorCodeExplanation =
+        bindings.sqlite3_errstr(errorCode).ref.toString();
+    return SQLiteException(
+        "$errorMessage (Code $errorCode: $errorCodeExplanation)");
+  }
+}
+
+/// [Result] represents a [Database.query]'s result and provides an [Iterable]
+/// interface for the results to be consumed.
+///
+/// Please note that this iterator should be [close]d manually if not all [Row]s
+/// are consumed.
+class Result extends IterableBase<Row> implements ClosableIterable<Row> {
+  final Database _database;
+  final ClosableIterator<Row> _iterator;
+  final Pointer<Statement> _statement;
+  final Map<String, int> _columnIndices;
+
+  Row _currentRow = null;
+
+  Result._(
+    this._database,
+    this._statement,
+    this._columnIndices,
+  ) : _iterator = _ResultIterator(_statement, _columnIndices) {}
+
+  void close() => _iterator.close();
+
+  ClosableIterator<Row> get iterator => _iterator;
+}
+
+class _ResultIterator implements ClosableIterator<Row> {
+  final Pointer<Statement> _statement;
+  final Map<String, int> _columnIndices;
+
+  Row _currentRow = null;
+  bool _closed = false;
+
+  _ResultIterator(this._statement, this._columnIndices) {}
+
+  bool moveNext() {
+    if (_closed) {
+      throw SQLiteException("The result has already been closed.");
+    }
+    _currentRow?._setNotCurrent();
+    int stepResult = bindings.sqlite3_step(_statement);
+    if (stepResult == Errors.SQLITE_ROW) {
+      _currentRow = Row._(_statement, _columnIndices);
+      return true;
+    } else {
+      close();
+      return false;
+    }
+  }
+
+  Row get current {
+    if (_closed) {
+      throw SQLiteException("The result has already been closed.");
+    }
+    return _currentRow;
+  }
+
+  void close() {
+    _currentRow?._setNotCurrent();
+    _closed = true;
+    bindings.sqlite3_finalize(_statement);
+  }
+}
+
+class Row {
+  final Pointer<Statement> _statement;
+  final Map<String, int> _columnIndices;
+
+  bool _isCurrentRow = true;
+
+  Row._(this._statement, this._columnIndices) {}
+
+  /// Reads column [columnName].
+  ///
+  /// By default it returns a dynamically typed value. If [convert] is set to
+  /// [Convert.StaticType] the value is converted to the static type computed
+  /// for the column by the query compiler.
+  dynamic readColumn(String columnName,
+      {Convert convert = Convert.DynamicType}) {
+    return readColumnByIndex(_columnIndices[columnName], convert: convert);
+  }
+
+  /// Reads column [columnName].
+  ///
+  /// By default it returns a dynamically typed value. If [convert] is set to
+  /// [Convert.StaticType] the value is converted to the static type computed
+  /// for the column by the query compiler.
+  dynamic readColumnByIndex(int columnIndex,
+      {Convert convert = Convert.DynamicType}) {
+    _checkIsCurrentRow();
+
+    Type dynamicType;
+    if (convert == Convert.DynamicType) {
+      dynamicType =
+          _typeFromCode(bindings.sqlite3_column_type(_statement, columnIndex));
+    } else {
+      dynamicType = _typeFromText(bindings
+          .sqlite3_column_decltype(_statement, columnIndex)
+          .ref
+          .toString());
+    }
+
+    switch (dynamicType) {
+      case Type.Integer:
+        return readColumnByIndexAsInt(columnIndex);
+      case Type.Text:
+        return readColumnByIndexAsText(columnIndex);
+      case Type.Null:
+        return null;
+        break;
+      default:
+    }
+  }
+
+  /// Reads column [columnName] and converts to [Type.Integer] if not an
+  /// integer.
+  int readColumnAsInt(String columnName) {
+    return readColumnByIndexAsInt(_columnIndices[columnName]);
+  }
+
+  /// Reads column [columnIndex] and converts to [Type.Integer] if not an
+  /// integer.
+  int readColumnByIndexAsInt(int columnIndex) {
+    _checkIsCurrentRow();
+    return bindings.sqlite3_column_int(_statement, columnIndex);
+  }
+
+  /// Reads column [columnName] and converts to [Type.Text] if not text.
+  String readColumnAsText(String columnName) {
+    return readColumnByIndexAsText(_columnIndices[columnName]);
+  }
+
+  /// Reads column [columnIndex] and converts to [Type.Text] if not text.
+  String readColumnByIndexAsText(int columnIndex) {
+    _checkIsCurrentRow();
+    return bindings.sqlite3_column_text(_statement, columnIndex).ref.toString();
+  }
+
+  void _checkIsCurrentRow() {
+    if (!_isCurrentRow) {
+      throw Exception(
+          "This row is not the current row, reading data from the non-current"
+          " row is not supported by sqlite.");
+    }
+  }
+
+  void _setNotCurrent() {
+    _isCurrentRow = false;
+  }
+}
+
+Type _typeFromCode(int code) {
+  switch (code) {
+    case Types.SQLITE_INTEGER:
+      return Type.Integer;
+    case Types.SQLITE_FLOAT:
+      return Type.Float;
+    case Types.SQLITE_TEXT:
+      return Type.Text;
+    case Types.SQLITE_BLOB:
+      return Type.Blob;
+    case Types.SQLITE_NULL:
+      return Type.Null;
+  }
+  throw Exception("Unknown type [$code]");
+}
+
+Type _typeFromText(String textRepresentation) {
+  switch (textRepresentation) {
+    case "integer":
+      return Type.Integer;
+    case "float":
+      return Type.Float;
+    case "text":
+      return Type.Text;
+    case "blob":
+      return Type.Blob;
+    case "null":
+      return Type.Null;
+  }
+  if (textRepresentation == null) return Type.Null;
+  throw Exception("Unknown type [$textRepresentation]");
+}
+
+enum Type { Integer, Float, Text, Blob, Null }
+
+enum Convert { DynamicType, StaticType }
+
+class SQLiteException {
+  final String message;
+  SQLiteException(this.message);
+
+  String toString() => message;
+}
diff --git a/samples_2/ffi/sqlite/lib/src/ffi/arena.dart b/samples_2/ffi/sqlite/lib/src/ffi/arena.dart
new file mode 100644
index 0000000..a26e807
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/ffi/arena.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import "dart:async";
+import "dart:ffi";
+
+import 'package:ffi/ffi.dart';
+
+/// [Arena] manages allocated C memory.
+///
+/// Arenas are zoned.
+class Arena {
+  Arena();
+
+  List<Pointer<Void>> _allocations = [];
+
+  /// Bound the lifetime of [ptr] to this [Arena].
+  T scoped<T extends Pointer>(T ptr) {
+    _allocations.add(ptr.cast());
+    return ptr;
+  }
+
+  /// Frees all memory pointed to by [Pointer]s in this arena.
+  void finalize() {
+    for (final ptr in _allocations) {
+      free(ptr);
+    }
+  }
+
+  /// The last [Arena] in the zone.
+  factory Arena.current() {
+    return Zone.current[#_currentArena];
+  }
+}
+
+/// Bound the lifetime of [ptr] to the current [Arena].
+T scoped<T extends Pointer>(T ptr) => Arena.current().scoped(ptr);
+
+class RethrownError {
+  dynamic original;
+  StackTrace originalStackTrace;
+  RethrownError(this.original, this.originalStackTrace);
+  toString() => """RethrownError(${original})
+${originalStackTrace}""";
+}
+
+/// Runs the [body] in an [Arena] freeing all memory which is [scoped] during
+/// execution of [body] at the end of the execution.
+R runArena<R>(R Function(Arena) body) {
+  Arena arena = Arena();
+  try {
+    return runZoned(() => body(arena),
+        zoneValues: {#_currentArena: arena},
+        onError: (error, st) => throw RethrownError(error, st));
+  } finally {
+    arena.finalize();
+  }
+}
diff --git a/samples_2/ffi/sqlite/lib/src/ffi/dylib_utils.dart b/samples_2/ffi/sqlite/lib/src/ffi/dylib_utils.dart
new file mode 100644
index 0000000..39653fa
--- /dev/null
+++ b/samples_2/ffi/sqlite/lib/src/ffi/dylib_utils.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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 = 2.9
+
+import 'dart:ffi';
+import 'dart:io' show Platform;
+
+String _platformPath(String name, {String path}) {
+  if (path == null) path = "";
+  if (Platform.isLinux || Platform.isAndroid)
+    return path + "lib" + name + ".so";
+  if (Platform.isMacOS) return path + "lib" + name + ".dylib";
+  if (Platform.isWindows) return path + name + ".dll";
+  throw Exception("Platform not implemented");
+}
+
+DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
+  String fullPath = _platformPath(name, path: path);
+  return DynamicLibrary.open(fullPath);
+}
diff --git a/samples_2/ffi/sqlite/pubspec.yaml b/samples_2/ffi/sqlite/pubspec.yaml
new file mode 100644
index 0000000..dcc5d40
--- /dev/null
+++ b/samples_2/ffi/sqlite/pubspec.yaml
@@ -0,0 +1,11 @@
+name: sqlite3
+version: 0.0.1
+description: >-
+  Sqlite3 wrapper. Demo for dart:ffi.
+author: Daco Harkes <dacoharkes@google.com>, Samir Jindel <sjindel@google.com>
+environment:
+  sdk: '>=2.1.0 <3.0.0'
+dependencies:
+  ffi: ^0.1.3
+dev_dependencies:
+  test: ^1.5.3
diff --git a/samples_2/ffi/sqlite/test/sqlite_test.dart b/samples_2/ffi/sqlite/test/sqlite_test.dart
new file mode 100644
index 0000000..f7d3744
--- /dev/null
+++ b/samples_2/ffi/sqlite/test/sqlite_test.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2019, 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 = 2.9
+
+// VMOptions=--optimization-counter-threshold=5
+
+import "dart:ffi";
+import "dart:io";
+
+import "package:ffi/ffi.dart";
+import "package:test/test.dart";
+
+import '../lib/sqlite.dart';
+
+void main() {
+  final dbPath = Platform.script.resolve("test.db").path;
+  test("sqlite integration test", () {
+    Database d = Database(dbPath);
+    d.execute("drop table if exists Cookies;");
+    d.execute("""
+      create table Cookies (
+        id integer primary key,
+        name text not null,
+        alternative_name text
+      );""");
+    d.execute("""
+      insert into Cookies (id, name, alternative_name)
+      values
+        (1,'Chocolade chip cookie', 'Chocolade cookie'),
+        (2,'Ginger cookie', null),
+        (3,'Cinnamon roll', null)
+      ;""");
+    Result result = d.query("""
+      select
+        id,
+        name,
+        alternative_name,
+        case
+          when id=1 then 'foo'
+          when id=2 then 42
+          when id=3 then null
+        end as multi_typed_column
+      from Cookies
+      ;""");
+    for (Row r in result) {
+      int id = r.readColumnAsInt("id");
+      expect(true, 1 <= id && id <= 3);
+      String name = r.readColumnByIndex(1);
+      expect(true, name is String);
+      String alternativeName = r.readColumn("alternative_name");
+      expect(true, alternativeName is String || alternativeName == null);
+      dynamic multiTypedValue = r.readColumn("multi_typed_column");
+      expect(
+          true,
+          multiTypedValue == 42 ||
+              multiTypedValue == 'foo' ||
+              multiTypedValue == null);
+      print("$id $name $alternativeName $multiTypedValue");
+    }
+    result = d.query("""
+      select
+        id,
+        name,
+        alternative_name,
+        case
+          when id=1 then 'foo'
+          when id=2 then 42
+          when id=3 then null
+        end as multi_typed_column
+      from Cookies
+      ;""");
+    for (Row r in result) {
+      int id = r.readColumnAsInt("id");
+      expect(true, 1 <= id && id <= 3);
+      String name = r.readColumnByIndex(1);
+      expect(true, name is String);
+      String alternativeName = r.readColumn("alternative_name");
+      expect(true, alternativeName is String || alternativeName == null);
+      dynamic multiTypedValue = r.readColumn("multi_typed_column");
+      expect(
+          true,
+          multiTypedValue == 42 ||
+              multiTypedValue == 'foo' ||
+              multiTypedValue == null);
+      print("$id $name $alternativeName $multiTypedValue");
+      if (id == 2) {
+        result.close();
+        break;
+      }
+    }
+    try {
+      result.iterator.moveNext();
+    } on SQLiteException catch (e) {
+      print("expected exception on accessing result data after close: $e");
+    }
+    try {
+      d.query("""
+      select
+        id,
+        non_existing_column
+      from Cookies
+      ;""");
+    } on SQLiteException catch (e) {
+      print("expected this query to fail: $e");
+    }
+    d.execute("drop table Cookies;");
+    d.close();
+  });
+
+  test("concurrent db open and queries", () {
+    Database d = Database(dbPath);
+    Database d2 = Database(dbPath);
+    d.execute("drop table if exists Cookies;");
+    d.execute("""
+      create table Cookies (
+        id integer primary key,
+        name text not null,
+        alternative_name text
+      );""");
+    d.execute("""
+      insert into Cookies (id, name, alternative_name)
+      values
+        (1,'Chocolade chip cookie', 'Chocolade cookie'),
+        (2,'Ginger cookie', null),
+        (3,'Cinnamon roll', null)
+      ;""");
+    Result r = d.query("select * from Cookies;");
+    Result r2 = d2.query("select * from Cookies;");
+    r.iterator..moveNext();
+    r2.iterator..moveNext();
+    r.iterator..moveNext();
+    Result r3 = d2.query("select * from Cookies;");
+    r3.iterator..moveNext();
+    expect(2, r.iterator.current.readColumn("id"));
+    expect(1, r2.iterator.current.readColumn("id"));
+    expect(1, r3.iterator.current.readColumn("id"));
+    r.close();
+    r2.close();
+    r3.close();
+    d.close();
+    d2.close();
+  });
+
+  test("stress test", () {
+    Database d = Database(dbPath);
+    d.execute("drop table if exists Cookies;");
+    d.execute("""
+      create table Cookies (
+        id integer primary key,
+        name text not null,
+        alternative_name text
+      );""");
+    int repeats = 100;
+    for (int i = 0; i < repeats; i++) {
+      d.execute("""
+      insert into Cookies (name, alternative_name)
+      values
+        ('Chocolade chip cookie', 'Chocolade cookie'),
+        ('Ginger cookie', null),
+        ('Cinnamon roll', null)
+      ;""");
+    }
+    Result r = d.query("select count(*) from Cookies;");
+    int count = r.first.readColumnByIndexAsInt(0);
+    expect(count, 3 * repeats);
+    r.close();
+    d.close();
+  });
+  test("Utf8 unit test", () {
+    final String test = 'Hasta Mañana';
+    final medium = Utf8.toUtf8(test);
+    expect(test, medium.ref.toString());
+    free(medium);
+  });
+}
diff --git a/samples_2/sample_extension/.gitignore b/samples_2/sample_extension/.gitignore
new file mode 100644
index 0000000..518f7dd
--- /dev/null
+++ b/samples_2/sample_extension/.gitignore
@@ -0,0 +1,9 @@
+/Makefile
+/*.Makefile
+/*.sln
+/*.target.mk
+/*.vcproj
+/*.vcxproj
+/*.vcxproj.filters
+/*.vcxproj.user
+/*.xcodeproj
diff --git a/samples_2/sample_extension/README.md b/samples_2/sample_extension/README.md
new file mode 100644
index 0000000..2e13091
--- /dev/null
+++ b/samples_2/sample_extension/README.md
@@ -0,0 +1,13 @@
+This directory contains samples of native extensions.
+
+To run the samples, first build both the Dart SDK and the runtime. For example:
+
+```
+$ ./tools/build.py create_sdk runtime
+```
+
+Then execute the sample programs. For example:
+
+```
+$ xcodebuild/ReleaseX64/dart samples/sample_extension/test/sample_extension_test.dart
+```
diff --git a/samples_2/sample_extension/sample_asynchronous_extension.dart b/samples_2/sample_extension/sample_asynchronous_extension.dart
new file mode 100644
index 0000000..541652d
--- /dev/null
+++ b/samples_2/sample_extension/sample_asynchronous_extension.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2012, 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 = 2.9
+
+library sample_asynchronous_extension;
+
+import 'dart:async';
+import 'dart:isolate';
+import 'dart-ext:sample_extension';
+
+// A class caches the native port used to call an asynchronous extension.
+class RandomArray {
+  static SendPort _port;
+
+  Future<List<int>> randomArray(int seed, int length) {
+    var completer = new Completer<List<int>>();
+    var replyPort = new RawReceivePort();
+    var args = new List(3);
+    args[0] = seed;
+    args[1] = length;
+    args[2] = replyPort.sendPort;
+    _servicePort.send(args);
+    replyPort.handler = (result) {
+      replyPort.close();
+      if (result != null) {
+        completer.complete(result);
+      } else {
+        completer.completeError(new Exception("Random array creation failed"));
+      }
+    };
+    return completer.future;
+  }
+
+  SendPort get _servicePort {
+    if (_port == null) {
+      _port = _newServicePort();
+    }
+    return _port;
+  }
+
+  SendPort _newServicePort() native "RandomArray_ServicePort";
+}
diff --git a/samples_2/sample_extension/sample_extension.cc b/samples_2/sample_extension/sample_extension.cc
new file mode 100644
index 0000000..2e04cec
--- /dev/null
+++ b/samples_2/sample_extension/sample_extension.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012, 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.
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/dart_api.h"
+#include "include/dart_native_api.h"
+
+Dart_NativeFunction ResolveName(Dart_Handle name,
+                                int argc,
+                                bool* auto_setup_scope);
+
+DART_EXPORT Dart_Handle sample_extension_Init(Dart_Handle parent_library) {
+  if (Dart_IsError(parent_library)) {
+    return parent_library;
+  }
+
+  Dart_Handle result_code =
+      Dart_SetNativeResolver(parent_library, ResolveName, NULL);
+  if (Dart_IsError(result_code)) {
+    return result_code;
+  }
+
+  return Dart_Null();
+}
+
+Dart_Handle HandleError(Dart_Handle handle) {
+  if (Dart_IsError(handle)) {
+    Dart_PropagateError(handle);
+  }
+  return handle;
+}
+
+void SystemRand(Dart_NativeArguments arguments) {
+  Dart_EnterScope();
+  Dart_Handle result = HandleError(Dart_NewInteger(rand()));
+  Dart_SetReturnValue(arguments, result);
+  Dart_ExitScope();
+}
+
+void SystemSrand(Dart_NativeArguments arguments) {
+  Dart_EnterScope();
+  bool success = false;
+  Dart_Handle seed_object = HandleError(Dart_GetNativeArgument(arguments, 0));
+  if (Dart_IsInteger(seed_object)) {
+    bool fits;
+    HandleError(Dart_IntegerFitsIntoInt64(seed_object, &fits));
+    if (fits) {
+      int64_t seed;
+      HandleError(Dart_IntegerToInt64(seed_object, &seed));
+      srand(static_cast<unsigned>(seed));
+      success = true;
+    }
+  }
+  Dart_SetReturnValue(arguments, HandleError(Dart_NewBoolean(success)));
+  Dart_ExitScope();
+}
+
+uint8_t* randomArray(int seed, int length) {
+  if (length <= 0 || length > 10000000) {
+    return NULL;
+  }
+  uint8_t* values = reinterpret_cast<uint8_t*>(malloc(length));
+  if (NULL == values) {
+    return NULL;
+  }
+  srand(seed);
+  for (int i = 0; i < length; ++i) {
+    values[i] = rand() % 256;
+  }
+  return values;
+}
+
+void wrappedRandomArray(Dart_Port dest_port_id, Dart_CObject* message) {
+  Dart_Port reply_port_id = ILLEGAL_PORT;
+  if (message->type == Dart_CObject_kArray &&
+      3 == message->value.as_array.length) {
+    // Use .as_array and .as_int32 to access the data in the Dart_CObject.
+    Dart_CObject* param0 = message->value.as_array.values[0];
+    Dart_CObject* param1 = message->value.as_array.values[1];
+    Dart_CObject* param2 = message->value.as_array.values[2];
+    if (param0->type == Dart_CObject_kInt32 &&
+        param1->type == Dart_CObject_kInt32 &&
+        param2->type == Dart_CObject_kSendPort) {
+      int seed = param0->value.as_int32;
+      int length = param1->value.as_int32;
+      reply_port_id = param2->value.as_send_port.id;
+      uint8_t* values = randomArray(seed, length);
+
+      if (values != NULL) {
+        Dart_CObject result;
+        result.type = Dart_CObject_kTypedData;
+        result.value.as_typed_data.type = Dart_TypedData_kUint8;
+        result.value.as_typed_data.values = values;
+        result.value.as_typed_data.length = length;
+        if (Dart_PostCObject(reply_port_id, &result)) {
+          Dart_CObject error;
+          error.type = Dart_CObject_kNull;
+          Dart_PostCObject(reply_port_id, &error);
+        }
+        free(values);
+        // It is OK that result is destroyed when function exits.
+        // Dart_PostCObject has copied its data.
+        return;
+      }
+    }
+  }
+  fprintf(stderr,
+          "Invalid message received, cannot proceed. Aborting the process.\n");
+  abort();
+}
+
+void randomArrayServicePort(Dart_NativeArguments arguments) {
+  Dart_EnterScope();
+  Dart_SetReturnValue(arguments, Dart_Null());
+  Dart_Port service_port =
+      Dart_NewNativePort("RandomArrayService", wrappedRandomArray, true);
+  if (service_port != ILLEGAL_PORT) {
+    Dart_Handle send_port = HandleError(Dart_NewSendPort(service_port));
+    Dart_SetReturnValue(arguments, send_port);
+  }
+  Dart_ExitScope();
+}
+
+struct FunctionLookup {
+  const char* name;
+  Dart_NativeFunction function;
+};
+
+FunctionLookup function_list[] = {
+    {"SystemRand", SystemRand},
+    {"SystemSrand", SystemSrand},
+    {"RandomArray_ServicePort", randomArrayServicePort},
+    {NULL, NULL}};
+
+FunctionLookup no_scope_function_list[] = {{"NoScopeSystemRand", SystemRand},
+                                           {NULL, NULL}};
+
+Dart_NativeFunction ResolveName(Dart_Handle name,
+                                int argc,
+                                bool* auto_setup_scope) {
+  if (!Dart_IsString(name)) {
+    return NULL;
+  }
+  Dart_NativeFunction result = NULL;
+  if (auto_setup_scope == NULL) {
+    return NULL;
+  }
+
+  Dart_EnterScope();
+  const char* cname;
+  HandleError(Dart_StringToCString(name, &cname));
+
+  for (int i = 0; function_list[i].name != NULL; ++i) {
+    if (strcmp(function_list[i].name, cname) == 0) {
+      *auto_setup_scope = true;
+      result = function_list[i].function;
+      break;
+    }
+  }
+
+  if (result != NULL) {
+    Dart_ExitScope();
+    return result;
+  }
+
+  for (int i = 0; no_scope_function_list[i].name != NULL; ++i) {
+    if (strcmp(no_scope_function_list[i].name, cname) == 0) {
+      *auto_setup_scope = false;
+      result = no_scope_function_list[i].function;
+      break;
+    }
+  }
+
+  Dart_ExitScope();
+  return result;
+}
diff --git a/samples_2/sample_extension/sample_extension_dllmain_win.cc b/samples_2/sample_extension/sample_extension_dllmain_win.cc
new file mode 100644
index 0000000..b7b7c57
--- /dev/null
+++ b/samples_2/sample_extension/sample_extension_dllmain_win.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2012, 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.
+
+#if defined(_WIN32)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
+  return true;
+}
+
+#endif  // defined(_WIN32)
diff --git a/samples_2/sample_extension/sample_synchronous_extension.dart b/samples_2/sample_extension/sample_synchronous_extension.dart
new file mode 100644
index 0000000..e5ad660
--- /dev/null
+++ b/samples_2/sample_extension/sample_synchronous_extension.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2012, 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 = 2.9
+
+library sample_synchronous_extension;
+
+import 'dart-ext:sample_extension';
+
+// The simplest way to call native code: top-level functions.
+int systemRand() native "SystemRand";
+int noScopeSystemRand() native "NoScopeSystemRand";
+bool systemSrand(int seed) native "SystemSrand";
diff --git a/samples_2/sample_extension/test/sample_extension_app_snapshot_test.dart b/samples_2/sample_extension/test/sample_extension_app_snapshot_test.dart
new file mode 100644
index 0000000..b2eb204
--- /dev/null
+++ b/samples_2/sample_extension/test/sample_extension_app_snapshot_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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 native extensions.
+
+// @dart = 2.9
+
+// OtherResources=../sample_synchronous_extension.dart
+// OtherResources=../sample_asynchronous_extension.dart
+// OtherResources=../test_sample_synchronous_extension.dart
+// OtherResources=../test_sample_asynchronous_extension.dart
+
+import 'sample_extension_test_helper.dart';
+
+void main() {
+  testNativeExtensions("app-jit");
+}
diff --git a/samples_2/sample_extension/test/sample_extension_test.dart b/samples_2/sample_extension/test/sample_extension_test.dart
new file mode 100644
index 0000000..393463b
--- /dev/null
+++ b/samples_2/sample_extension/test/sample_extension_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 native extensions.
+
+// @dart = 2.9
+
+import 'sample_extension_test_helper.dart';
+
+void main() {
+  testNativeExtensions(null /* no snapshot */);
+}
diff --git a/samples_2/sample_extension/test/sample_extension_test_helper.dart b/samples_2/sample_extension/test/sample_extension_test_helper.dart
new file mode 100644
index 0000000..9c701bf
--- /dev/null
+++ b/samples_2/sample_extension/test/sample_extension_test_helper.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2013, 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 native extensions.
+
+// @dart = 2.9
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:isolate';
+
+import "package:expect/expect.dart";
+import "package:path/path.dart";
+
+Future copyFileToDirectory(String file, String directory) async {
+  String src = file;
+  String dst = directory;
+  ProcessResult result;
+  switch (Platform.operatingSystem) {
+    case 'linux':
+    case 'macos':
+      result = await Process.run('cp', [src, dst]);
+      break;
+    case 'windows':
+      result = await Process.run('cmd.exe', ['/C', 'copy $src $dst']);
+      break;
+    default:
+      Expect.fail('Unknown operating system ${Platform.operatingSystem}');
+  }
+  if (result.exitCode != 0) {
+    print(result.stdout);
+    print(result.stderr);
+    throw "Failed to copy test file ($file) to temporary directory ($directory)";
+  }
+}
+
+Future run(String program, List<String> arguments) async {
+  print("+ $program ${arguments.join(' ')}");
+  ProcessResult result = await Process.run(program, arguments);
+  if (result.exitCode != 0) {
+    print('Failing process stdout: ${result.stdout}');
+    print('Failing process stderr: ${result.stderr}');
+    print('End failing process stderr');
+    Expect.fail('Test failed with exit code ${result.exitCode}');
+  }
+}
+
+Future testNativeExtensions(String snapshotKind) async {
+  String buildDirectory = dirname(Platform.executable);
+  Directory tempDirectory =
+      Directory.systemTemp.createTempSync('sample_extension_');
+  try {
+    String testDirectory = tempDirectory.path;
+    String sourceDirectory = Platform.script.resolve('..').toFilePath();
+
+    // Copy sample_extension dart files and sample_extension tests to the
+    // temporary test directory.
+    for (var file in [
+      'sample_synchronous_extension.dart',
+      'sample_asynchronous_extension.dart',
+      'test_sample_synchronous_extension.dart',
+      'test_sample_asynchronous_extension.dart'
+    ]) {
+      await copyFileToDirectory(join(sourceDirectory, file), testDirectory);
+    }
+
+    for (var test in [
+      'test_sample_synchronous_extension.dart',
+      'test_sample_asynchronous_extension.dart'
+    ]) {
+      String script = join(testDirectory, test);
+      String snapshot;
+      if (snapshotKind == null) {
+        snapshot = script;
+      } else {
+        snapshot = join(testDirectory, "$test.snapshot");
+        List<String> args = new List<String>.from(Platform.executableArguments);
+        args.add('--snapshot=$snapshot');
+        args.add('--snapshot-kind=$snapshotKind');
+        args.add(script);
+        await run(Platform.executable, args);
+      }
+
+      List<String> args = new List<String>.from(Platform.executableArguments);
+      args.add(snapshot);
+      await run(Platform.executable, args);
+    }
+  } finally {
+    tempDirectory.deleteSync(recursive: true);
+  }
+}
diff --git a/samples_2/sample_extension/test_sample_asynchronous_extension.dart b/samples_2/sample_extension/test_sample_asynchronous_extension.dart
new file mode 100644
index 0000000..5ceb7c8
--- /dev/null
+++ b/samples_2/sample_extension/test_sample_asynchronous_extension.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2012, 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 = 2.9
+
+library test_sample_extension;
+
+import 'sample_asynchronous_extension.dart';
+
+void check(bool condition, String message) {
+  if (!condition) {
+    throw new StateError(message);
+  }
+}
+
+void main() {
+  RandomArray r = new RandomArray();
+  r.randomArray(17, 100).then((list_100) {
+    r.randomArray(17, 200).then((list_200) {
+      for (var i = 0; i < 100; ++i) {
+        check(list_100[i] == list_200[i], "list_100[i] == list_200[i]");
+      }
+    });
+  });
+
+  // Gets a list of 256000 random uint8 values, using seed 19, and
+  // runs checkNormal on that list.
+  r.randomArray(19, 256000).then(checkNormal);
+}
+
+void checkNormal(List l) {
+  // Count how many times each byte value occurs.  Assert that the counts
+  // are all within a reasonable (six-sigma) range.
+  List counts = new List<int>.filled(256, 0);
+  for (var e in l) {
+    counts[e]++;
+  }
+  new RandomArray().randomArray(18, 256000).then(checkCorrelation(counts));
+}
+
+Function checkCorrelation(List counts) {
+  return (List l) {
+    List counts_2 = new List<int>.filled(256, 0);
+    for (var e in l) {
+      counts_2[e]++;
+    }
+    var product = 0;
+    for (var i = 0; i < 256; ++i) {
+      check(counts[i] < 1200, "counts[i] < 1200");
+      check(counts_2[i] < 1200, "counts_2[i] < 1200");
+      check(counts[i] > 800, "counts[i] > 800");
+      check(counts[i] > 800, "counts[i] > 800");
+
+      product += counts[i] * counts_2[i];
+    }
+    check(product < 256000000 * 1.001, "product < 256000000 * 1.001");
+    check(product > 256000000 * 0.999, "product > 256000000 * 0.999");
+  };
+}
diff --git a/samples_2/sample_extension/test_sample_synchronous_extension.dart b/samples_2/sample_extension/test_sample_synchronous_extension.dart
new file mode 100644
index 0000000..77d2fc3
--- /dev/null
+++ b/samples_2/sample_extension/test_sample_synchronous_extension.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2012, 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 = 2.9
+
+library test_sample_extension;
+
+import 'sample_synchronous_extension.dart';
+
+void check(bool condition, String message) {
+  if (!condition) {
+    throw new StateError(message);
+  }
+}
+
+void checkSystemRand() {
+  systemSrand(17);
+  var x1 = systemRand();
+  var x2 = systemRand();
+  var x3 = systemRand();
+  check(x1 != x2, "x1 != x2");
+  check(x1 != x3, "x1 != x3");
+  check(x2 != x3, "x2 != x3");
+  systemSrand(17);
+  check(x1 == systemRand(), "x1 == systemRand()");
+  check(x2 == systemRand(), "x2 == systemRand()");
+  check(x3 == systemRand(), "x3 == systemRand()");
+  systemSrand(18);
+  check(x1 != systemRand(), "x1 != systemRand()");
+  check(x2 != systemRand(), "x2 != systemRand()");
+  check(x3 != systemRand(), "x3 != systemRand()");
+}
+
+void checkNoScopeSystemRand() {
+  systemSrand(17);
+  var x1 = noScopeSystemRand();
+  var x2 = noScopeSystemRand();
+  var x3 = noScopeSystemRand();
+  check(x1 != x2, "x1 != x2");
+  check(x1 != x3, "x1 != x3");
+  check(x2 != x3, "x2 != x3");
+  systemSrand(17);
+  check(x1 == noScopeSystemRand(), "x1 == noScopeSystemRand()");
+  check(x2 == noScopeSystemRand(), "x2 == noScopeSystemRand()");
+  check(x3 == noScopeSystemRand(), "x3 == noScopeSystemRand()");
+  systemSrand(18);
+  check(x1 != noScopeSystemRand(), "x1 != noScopeSystemRand()");
+  check(x2 != noScopeSystemRand(), "x2 != noScopeSystemRand()");
+  check(x3 != noScopeSystemRand(), "x3 != noScopeSystemRand()");
+}
+
+void main() {
+  checkSystemRand();
+  checkNoScopeSystemRand();
+}
diff --git a/samples_2/samples_2.status b/samples_2/samples_2.status
new file mode 100644
index 0000000..2fe1de2
--- /dev/null
+++ b/samples_2/samples_2.status
@@ -0,0 +1,38 @@
+# Copyright (c) 2012, 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.
+
+[ $arch == arm ]
+sample_extension/test/*: Skip # Issue 14705
+
+[ $builder_tag == asan ]
+ffi/samples_test: SkipByDesign # FFI skips, see ffi.status
+
+[ $builder_tag == optimization_counter_threshold ]
+sample_extension/test/sample_extension_app_snapshot_test: SkipByDesign # This test is too slow for testing with low optimization counter threshold.
+
+[ $compiler == dart2js && $runtime == none ]
+*: Fail, Pass # TODO(ahe): Triage these tests.
+
+[ $compiler == none && $mode == debug && $runtime == vm && $system == windows ]
+sample_extension/test/sample_extension_app_snapshot_test: Pass, RuntimeError # Issue 28842
+
+[ $compiler == none && $runtime == vm && $system == fuchsia ]
+*: Skip # Not yet triaged.
+
+[ $arch == simarm || $arch == simarm64 ]
+ffi/*: SkipByDesign # FFI skips, see ffi.status
+
+[ $arch != x64 || $compiler != dartk || $system != linux || $hot_reload || $hot_reload_rollback ]
+ffi/sqlite/test/sqlite_test: SkipByDesign # FFI not supported or libsqlite3.so not available.
+
+[ $compiler == app_jitk || $compiler == dartkp ]
+sample_extension/test/sample_extension_app_snapshot_test: SkipByDesign
+sample_extension/test/sample_extension_test: SkipByDesign
+
+[ $runtime == d8 || $browser ]
+ffi/*: SkipByDesign # Skip tests that use dart:ffi.
+sample_extension/*: SkipByDesign # Skip tests that use dart:io.
+
+[ $hot_reload || $hot_reload_rollback ]
+sample_extension/test/sample_extension_app_snapshot_test: SkipByDesign # Cannot reload with URI pointing to app snapshot.
diff --git a/sdk/lib/collection/maps.dart b/sdk/lib/collection/maps.dart
index 90ecb6a..b0ab270 100644
--- a/sdk/lib/collection/maps.dart
+++ b/sdk/lib/collection/maps.dart
@@ -285,7 +285,7 @@
   }
 
   /// This operation is not supported by an unmodifiable map.
-  V remove(Object? key) {
+  V? remove(Object? key) {
     throw UnsupportedError("Cannot modify unmodifiable map");
   }
 
diff --git a/tools/VERSION b/tools/VERSION
index 438d08f..2827b90 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 74
+PRERELEASE 75
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 6cca3d0..fadcd91 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -26,7 +26,7 @@
       "pkg/",
       "runtime/tests/",
       "samples-dev/",
-      "samples/",
+      "samples_2/",
       "sdk/",
       "tests/.dart_tool/package_config.json",
       "tests/angular/",
@@ -113,7 +113,7 @@
       "pkg/",
       "runtime/tests/",
       "samples-dev/",
-      "samples/",
+      "samples_2/",
       "sdk/",
       "tests/.dart_tool/package_config.json",
       "tests/angular/",
@@ -200,6 +200,7 @@
       "runtime/tests/",
       "samples-dev/",
       "samples/",
+      "samples_2/",
       "sdk/",
       "tests/.dart_tool/package_config.json",
       "tests/angular/",
@@ -303,6 +304,7 @@
       "xcodebuild/ReleaseSIMARM64/",
       "xcodebuild/ReleaseX64/",
       "samples/",
+      "samples_2/",
       "samples-dev/",
       "tools/",
       "third_party/android_tools/sdk/platform-tools/adb",
@@ -510,7 +512,9 @@
     "dart2js-(linux|mac|win)-chrome-unsound": {
       "options": {
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-weak-(linux|mac|win)-x64-d8": {
@@ -527,7 +531,9 @@
     "dart2js-(linux|win)-firefox-unsound": {
       "options": {
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-win-ie11": {
@@ -540,7 +546,9 @@
       "options": {
         "use-sdk": true,
         "babel": "{\"presets\":[\"es2015\"]}",
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-win-edge": {
@@ -551,7 +559,9 @@
     "dart2js-win-edge-unsound": {
       "options": {
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-mac-safari": {
@@ -562,7 +572,9 @@
     "dart2js-mac-safari-unsound": {
       "options": {
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-minified-csp-(linux|mac|win)-chrome": {
@@ -577,7 +589,9 @@
         "minified": true,
         "csp": true,
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-minified-(linux|mac|win)-d8": {
@@ -590,7 +604,9 @@
       "options": {
         "minified": true,
         "use-sdk": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-production-(linux|mac|win)-d8": {
@@ -610,7 +626,9 @@
     "dart2js-hostasserts-(linux|mac|win)-(ia32|x64)-d8-unsound": {
       "options": {
         "host-checked": true,
-        "dart2js-options": ["--no-sound-null-safety"]
+        "dart2js-options": [
+          "--no-sound-null-safety"
+        ]
       }
     },
     "dart2js-hostasserts-weak-(linux|win)-x64-(d8|chrome)": {