Version 2.14.0-175.0.dev

Merge commit 'be893fdf275788d3e65487fa67084f7f301a3b2b' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13329a7..069fea5e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,11 @@
     daylight saving changes that are not precisely one hour.
     (No change on the Web which uses the JavaScript `Date` object.)
 
+#### `dart:ffi`
+
+*   Adds the `DynamicLibrary.providesSymbol` function to check whether a symbol
+    is available in a dynamic library.
+
 #### `dart:io`
 
 *   BREAKING CHANGE (for pre-migrated null safe code):
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
index b92f5af..385e300 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.expect
@@ -25,4 +25,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index d480c5f..157a41b 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -52,4 +52,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
index 792f6e0..8e37bb2 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.expect
@@ -25,4 +25,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index 1097538..dedc171 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -52,4 +52,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
index 7c27f0b..0275472 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.expect
@@ -33,4 +33,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
index 6978f30..bb7ab61a 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -84,4 +84,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
index 7831122..8045c2f 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.expect
@@ -33,4 +33,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
index 3cdfa2e..1ba5718 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -84,4 +84,4 @@
 
 Constructor coverage from constants:
 org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
-- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:135:9)
+- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:136:9)
diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc
index 3935a54..eb8e74b 100644
--- a/runtime/lib/ffi_dynamic_library.cc
+++ b/runtime/lib/ffi_dynamic_library.cc
@@ -99,8 +99,8 @@
     defined(HOST_OS_ANDROID) || defined(HOST_OS_FUCHSIA)
   dlerror();  // Clear any errors.
   void* pointer = dlsym(handle, symbol);
-  if (pointer == nullptr) {
-    char* error = dlerror();
+  char* error = dlerror();
+  if (error != nullptr) {
     const String& msg = String::Handle(
         String::NewFormatted("Failed to lookup symbol (%s)", error));
     Exceptions::ThrowArgumentError(msg);
@@ -147,4 +147,32 @@
   return Integer::NewFromUint64(handle);
 }
 
+static bool SymbolExists(void* handle, const char* symbol) {
+#if defined(HOST_OS_LINUX) || defined(HOST_OS_MACOS) ||                        \
+    defined(HOST_OS_ANDROID) || defined(HOST_OS_FUCHSIA)
+  dlerror();  // Clear previous error, if any.
+  dlsym(handle, symbol);
+  // Checking whether dlsym returns a nullptr is not enough, as the value of
+  // the symbol could actually be NULL. Check the error condition instead.
+  return dlerror() == nullptr;
+#elif defined(HOST_OS_WINDOWS)
+  return GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol) != nullptr;
+#else
+  const Array& args = Array::Handle(Array::New(1));
+  args.SetAt(0,
+             String::Handle(String::New(
+                 "The dart:ffi library is not available on this platform.")));
+  Exceptions::ThrowByType(Exceptions::kUnsupported, args);
+#endif
+}
+
+DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
+  GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
+  GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
+                               arguments->NativeArgAt(1));
+
+  void* handle = dlib.GetHandle();
+  return Bool::Get(SymbolExists(handle, argSymbolName.ToCString())).ptr();
+}
+
 }  // namespace dart
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index 5965209..5237e60 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -402,6 +402,7 @@
   V(Ffi_dl_open, 1)                                                            \
   V(Ffi_dl_lookup, 2)                                                          \
   V(Ffi_dl_getHandle, 1)                                                       \
+  V(Ffi_dl_providesSymbol, 2)                                                  \
   V(Ffi_asExternalTypedData, 2)                                                \
   V(Ffi_dl_processLibrary, 0)                                                  \
   V(Ffi_dl_executableLibrary, 0)                                               \
diff --git a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
index 5482890..2966149 100644
--- a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
@@ -30,6 +30,9 @@
   Pointer<T> lookup<T extends NativeType>(String symbolName)
       native "Ffi_dl_lookup";
 
+  @patch
+  bool providesSymbol(String symbolName) native "Ffi_dl_providesSymbol";
+
   // TODO(dacoharkes): Expose this to users, or extend Pointer?
   // https://github.com/dart-lang/sdk/issues/35881
   int getHandle() native "Ffi_dl_getHandle";
diff --git a/sdk/lib/ffi/dynamic_library.dart b/sdk/lib/ffi/dynamic_library.dart
index e39e53c..0a4ee2e 100644
--- a/sdk/lib/ffi/dynamic_library.dart
+++ b/sdk/lib/ffi/dynamic_library.dart
@@ -39,9 +39,15 @@
   /// [dlsym(3)](https://man7.org/linux/man-pages/man3/dlsym.3.html) system
   /// call.
   ///
-  /// The symbol must be provided by the dynamic library.
+  /// The symbol must be provided by the dynamic library. To check whether
+  /// the library provides such symbol, use [hasSymbol].
   external Pointer<T> lookup<T extends NativeType>(String symbolName);
 
+  /// Checks whether this dynamic library provides a symbol with the given
+  /// name.
+  @Since('2.14')
+  external bool providesSymbol(String symbolName);
+
   /// Dynamic libraries are equal if they load the same library.
   external bool operator ==(Object other);
 
diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart
index 26177e8..e82e567 100644
--- a/sdk/lib/ffi/ffi.dart
+++ b/sdk/lib/ffi/ffi.dart
@@ -11,6 +11,7 @@
  */
 library dart.ffi;
 
+import 'dart:_internal' show Since;
 import 'dart:isolate';
 import 'dart:typed_data';
 
diff --git a/tests/ffi/has_symbol_test.dart b/tests/ffi/has_symbol_test.dart
new file mode 100644
index 0000000..858de9c
--- /dev/null
+++ b/tests/ffi/has_symbol_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+import 'ffi_test_helpers.dart';
+
+void main() {
+  testHasSymbol();
+}
+
+void testHasSymbol() {
+  Expect.isTrue(ffiTestFunctions.providesSymbol('ReturnMaxUint8'));
+  Expect.isFalse(ffiTestFunctions.providesSymbol('SymbolNotInLibrary'));
+
+  if (Platform.isMacOS ||
+      Platform.isIOS ||
+      Platform.isAndroid ||
+      Platform.isLinux) {
+    DynamicLibrary p = DynamicLibrary.process();
+    Expect.isTrue(p.providesSymbol('dlopen'));
+    Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
+  }
+
+  DynamicLibrary e = DynamicLibrary.executable();
+  Expect.isTrue(e.providesSymbol('Dart_Invoke'));
+  Expect.isFalse(e.providesSymbol('symbol_that_does_not_exist_in_executable'));
+}
diff --git a/tests/ffi_2/has_symbol_test.dart b/tests/ffi_2/has_symbol_test.dart
new file mode 100644
index 0000000..11c1de10e
--- /dev/null
+++ b/tests/ffi_2/has_symbol_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+// @dart=2.9
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+import 'ffi_test_helpers.dart';
+
+void main() {
+  testHasSymbol();
+}
+
+void testHasSymbol() {
+  Expect.isTrue(ffiTestFunctions.providesSymbol('ReturnMaxUint8'));
+  Expect.isFalse(ffiTestFunctions.providesSymbol('SymbolNotInLibrary'));
+
+  if (Platform.isMacOS ||
+      Platform.isIOS ||
+      Platform.isAndroid ||
+      Platform.isLinux) {
+    DynamicLibrary p = DynamicLibrary.process();
+    Expect.isTrue(p.providesSymbol('dlopen'));
+    Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
+  }
+
+  DynamicLibrary e = DynamicLibrary.executable();
+  Expect.isTrue(e.providesSymbol('Dart_Invoke'));
+  Expect.isFalse(e.providesSymbol('symbol_that_does_not_exist_in_executable'));
+}
diff --git a/tools/VERSION b/tools/VERSION
index 4e385a8..931f10d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 174
+PRERELEASE 175
 PRERELEASE_PATCH 0
\ No newline at end of file