Version 2.15.0-82.0.dev

Merge commit 'f21b7cafbcc6fe8ce29bb1ebbbc33b175d9cc1f4' into 'dev'
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index b4b8664..f28d741 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -778,8 +778,7 @@
   // the inlining ID to that; instead, treat it as unset.
   explicit Instruction(const InstructionSource& source,
                        intptr_t deopt_id = DeoptId::kNone)
-      : deopt_id_(deopt_id),
-        inlining_id_(source.inlining_id) {}
+      : deopt_id_(deopt_id), inlining_id_(source.inlining_id) {}
 
   explicit Instruction(intptr_t deopt_id = DeoptId::kNone)
       : Instruction(InstructionSource(), deopt_id) {}
@@ -1508,7 +1507,6 @@
 
   DEFINE_INSTRUCTION_TYPE_CHECK(BlockEntry)
 
-
  protected:
   BlockEntryInstr(intptr_t block_id,
                   intptr_t try_index,
@@ -5301,7 +5299,6 @@
   virtual bool HasUnknownSideEffects() const { return true; }
   virtual Instruction* Canonicalize(FlowGraph* flow_graph);
 
-
  private:
   const TokenPosition token_pos_;
   const UntaggedPcDescriptors::Kind stub_kind_;
@@ -8803,7 +8800,6 @@
 
   virtual Value* RedefinedValue() const;
 
-
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index e4666e2..a34ae49 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1054,11 +1054,14 @@
   return instructions;
 }
 
-Fragment BaseFlowGraphBuilder::CheckNullOptimized(TokenPosition position,
-                                                  const String& function_name) {
+Fragment BaseFlowGraphBuilder::CheckNullOptimized(
+    const String& function_name,
+    CheckNullInstr::ExceptionType exception_type,
+    TokenPosition position) {
   Value* value = Pop();
-  CheckNullInstr* check_null = new (Z) CheckNullInstr(
-      value, function_name, GetNextDeoptId(), InstructionSource(position));
+  CheckNullInstr* check_null =
+      new (Z) CheckNullInstr(value, function_name, GetNextDeoptId(),
+                             InstructionSource(position), exception_type);
   Push(check_null);  // Use the redefinition.
   return Fragment(check_null);
 }
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index 6a8b1b3..93875e0 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -384,13 +384,20 @@
 
   // Pops the top of the stack, checks it for null, and pushes the result on
   // the stack to create a data dependency.
-  // 'function_name' is a selector which is being called (reported in
-  // NoSuchMethod message).
+  //
   // Note that the result can currently only be used in optimized code, because
   // optimized code uses FlowGraph::RemoveRedefinitions to remove the
   // redefinitions, while unoptimized code does not.
-  Fragment CheckNullOptimized(TokenPosition position,
-                              const String& function_name);
+  Fragment CheckNullOptimized(
+      const String& name,
+      CheckNullInstr::ExceptionType exception_type,
+      TokenPosition position = TokenPosition::kNoSource);
+  Fragment CheckNullOptimized(
+      const String& function_name,
+      TokenPosition position = TokenPosition::kNoSource) {
+    return CheckNullOptimized(function_name, CheckNullInstr::kNoSuchMethod,
+                              position);
+  }
 
   // Records extra unchecked entry point 'unchecked_entry' in 'graph_entry'.
   void RecordUncheckedEntryPoint(GraphEntryInstr* graph_entry,
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 115caf4..ad2f103 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1280,12 +1280,10 @@
       body += LoadLocal(parsed_function_->RawParameterVariable(0));  // decoder
       body += LoadLocal(parsed_function_->RawParameterVariable(1));  // bytes
       body += LoadLocal(parsed_function_->RawParameterVariable(2));  // start
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       body += UnboxTruncate(kUnboxedIntPtr);
       body += LoadLocal(parsed_function_->RawParameterVariable(3));  // end
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       body += UnboxTruncate(kUnboxedIntPtr);
       body += LoadLocal(parsed_function_->RawParameterVariable(4));  // table
       body += Utf8Scan();
@@ -1327,13 +1325,11 @@
       LocalVariable* arg_offset = parsed_function_->RawParameterVariable(1);
 
       body += LoadLocal(arg_offset);
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       LocalVariable* arg_offset_not_null = MakeTemporary();
 
       body += LoadLocal(arg_pointer);
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       // No GC from here til LoadIndexed.
       body += LoadUntagged(compiler::target::PointerBase::data_field_offset());
       body += LoadLocal(arg_offset_not_null);
@@ -1430,8 +1426,7 @@
         // Pointer<Pointer<X>> as argument, and (2) the bound on the pointer
         // type parameter guarantees X is an interface type.
         body += LoadLocal(arg_pointer);
-        body += CheckNullOptimized(TokenPosition::kNoSource,
-                                   String::ZoneHandle(Z, function.name()));
+        body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
         body += LoadNativeField(
             Slot::GetTypeArgumentsSlotFor(thread_, pointer_class));
         body += NullConstant();  // function_type_args.
@@ -1441,17 +1436,14 @@
 
       ASSERT_EQUAL(function.NumParameters(), 3);
       body += LoadLocal(arg_offset);
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       LocalVariable* arg_offset_not_null = MakeTemporary();
       body += LoadLocal(arg_value);
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       LocalVariable* arg_value_not_null = MakeTemporary();
 
       body += LoadLocal(arg_pointer);  // Pointer.
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       // No GC from here til StoreIndexed.
       body += LoadUntagged(compiler::target::PointerBase::data_field_offset());
       body += LoadLocal(arg_offset_not_null);
@@ -1489,16 +1481,14 @@
       body += AllocateObject(TokenPosition::kNoSource, pointer_class, 1);
       body += LoadLocal(MakeTemporary());  // Duplicate Pointer.
       body += LoadLocal(parsed_function_->RawParameterVariable(0));  // Address.
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       body += UnboxTruncate(kUnboxedFfiIntPtr);
       body += StoreNativeField(Slot::Pointer_data_field());
     } break;
     case MethodRecognizer::kFfiGetAddress: {
       ASSERT_EQUAL(function.NumParameters(), 1);
       body += LoadLocal(parsed_function_->RawParameterVariable(0));  // Pointer.
-      body += CheckNullOptimized(TokenPosition::kNoSource,
-                                 String::ZoneHandle(Z, function.name()));
+      body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
       // This can only be Pointer, so it is always safe to LoadUntagged.
       body += LoadUntagged(compiler::target::Pointer::data_field_offset());
       body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
@@ -1517,9 +1507,9 @@
       // Load TypedDataArray from Instance Handle implementing
       // NativeFieldWrapper.
       body += LoadLocal(parsed_function_->RawParameterVariable(0));  // Object.
-      body += CheckNullOptimized(TokenPosition::kNoSource, name);
+      body += CheckNullOptimized(name);
       body += LoadNativeField(Slot::Instance_native_fields_array());  // Fields.
-      body += CheckNullOptimized(TokenPosition::kNoSource, name);
+      body += CheckNullOptimized(name);
       // Load the native field at index.
       body += IntConstant(0);  // Index.
       body += LoadIndexed(kIntPtrCid);
@@ -4362,10 +4352,10 @@
     function_body += LoadLocal(
         parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
     // Check for 'null'.
-    // TODO(36780): Mention the param name instead of function reciever.
-    function_body +=
-        CheckNullOptimized(TokenPosition::kNoSource,
-                           String::ZoneHandle(Z, marshaller.function_name()));
+    function_body += CheckNullOptimized(
+        String::ZoneHandle(
+            Z, function.ParameterNameAt(kFirstArgumentParameterOffset + i)),
+        CheckNullInstr::kArgumentError);
     function_body += StoreLocal(
         TokenPosition::kNoSource,
         parsed_function_->ParameterVariable(kFirstArgumentParameterOffset + i));
@@ -4544,8 +4534,8 @@
     body += IntConstant(0);
   } else if (!marshaller.IsHandle(compiler::ffi::kResultIndex)) {
     body +=
-        CheckNullOptimized(TokenPosition::kNoSource,
-                           String::ZoneHandle(Z, marshaller.function_name()));
+        CheckNullOptimized(String::ZoneHandle(Z, String::New("return_value")),
+                           CheckNullInstr::kArgumentError);
   }
 
   if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index fb90a6e..5ef43b9 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -144,7 +144,19 @@
   Exceptions::ThrowByType(Exceptions::kRange, args);
 }
 
-static void NullErrorHelper(Zone* zone, const String& selector) {
+static void NullErrorHelper(Zone* zone,
+                            const String& selector,
+                            bool is_param_name = false) {
+  if (is_param_name) {
+    const String& error = String::Handle(
+        selector.IsNull()
+            ? String::New("argument value is null")
+            : String::NewFormatted("argument value for '%s' is null",
+                                   selector.ToCString()));
+    Exceptions::ThrowArgumentError(error);
+    return;
+  }
+
   // If the selector is null, this must be a null check that wasn't due to a
   // method invocation, so was due to the null check operator.
   if (selector.IsNull()) {
@@ -178,7 +190,10 @@
   Exceptions::ThrowByType(Exceptions::kNoSuchMethod, args);
 }
 
-static void DoThrowNullError(Isolate* isolate, Thread* thread, Zone* zone) {
+static void DoThrowNullError(Isolate* isolate,
+                             Thread* thread,
+                             Zone* zone,
+                             bool is_param) {
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
   const StackFrame* caller_frame = iterator.NextFrame();
@@ -205,11 +220,11 @@
     member_name = Symbols::OptimizedOut().ptr();
   }
 
-  NullErrorHelper(zone, member_name);
+  NullErrorHelper(zone, member_name, is_param);
 }
 
 DEFINE_RUNTIME_ENTRY(NullError, 0) {
-  DoThrowNullError(isolate, thread, zone);
+  DoThrowNullError(isolate, thread, zone, /*is_param=*/false);
 }
 
 // Collects information about pointers within the top |kMaxSlotsCollected|
@@ -258,7 +273,7 @@
     RELEASE_ASSERT(caller_frame->IsDartFrame());
     ReportImpossibleNullError(cid.Value(), caller_frame, thread);
   }
-  DoThrowNullError(isolate, thread, zone);
+  DoThrowNullError(isolate, thread, zone, /*is_param=*/false);
 }
 
 DEFINE_RUNTIME_ENTRY(NullErrorWithSelector, 1) {
@@ -271,8 +286,7 @@
 }
 
 DEFINE_RUNTIME_ENTRY(ArgumentNullError, 0) {
-  const String& error = String::Handle(String::New("argument value is null"));
-  Exceptions::ThrowArgumentError(error);
+  DoThrowNullError(isolate, thread, zone, /*is_param=*/true);
 }
 
 DEFINE_RUNTIME_ENTRY(ArgumentError, 1) {
diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart
index d5fd3c0..6d4c77e 100644
--- a/tests/ffi/function_test.dart
+++ b/tests/ffi/function_test.dart
@@ -39,6 +39,7 @@
     testFloatRounding();
     testVoidReturn();
     testNoArgs();
+    testNativeFunctionNullableInt();
   }
 }
 
@@ -457,3 +458,16 @@
   double result = inventFloatValue();
   Expect.approxEquals(1337.0, result);
 }
+
+void testNativeFunctionNullableInt() {
+  final sumPlus42 = ffiTestFunctions.lookupFunction<
+      Int32 Function(Int32, Int32), int Function(int, int?)>("SumPlus42");
+
+  try {
+    sumPlus42(3, null);
+  } catch (e) {
+    // TODO(http://dartbug.com/47098): Save param names to dwarf.
+    Expect.isTrue(e.toString().contains('ffi_param2') ||
+        e.toString().contains('<optimized out>'));
+  }
+}
diff --git a/tests/ffi_2/function_test.dart b/tests/ffi_2/function_test.dart
index f955bbd..c0f9657 100644
--- a/tests/ffi_2/function_test.dart
+++ b/tests/ffi_2/function_test.dart
@@ -41,6 +41,7 @@
     testFloatRounding();
     testVoidReturn();
     testNoArgs();
+    testNativeFunctionNullableInt();
   }
 }
 
@@ -459,3 +460,16 @@
   double result = inventFloatValue();
   Expect.approxEquals(1337.0, result);
 }
+
+void testNativeFunctionNullableInt() {
+  final sumPlus42 = ffiTestFunctions.lookupFunction<
+      Int32 Function(Int32, Int32), int Function(int, int)>("SumPlus42");
+
+  try {
+    sumPlus42(3, null);
+  } catch (e) {
+    // TODO(http://dartbug.com/47098): Save param names to dwarf.
+    Expect.isTrue(e.toString().contains('ffi_param2') ||
+        e.toString().contains('<optimized out>'));
+  }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 5394c86..8866cbd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 81
+PRERELEASE 82
 PRERELEASE_PATCH 0
\ No newline at end of file