Version 2.15.0-236.0.dev

Merge commit '93370cb8b6bdecf2e00170be477ad96c742a1d6a' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
index d0d1f5f..b7e5f96 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_comparison.dart
@@ -16,12 +16,10 @@
 
 class RemoveComparison extends CorrectionProducer {
   @override
-  bool canBeAppliedInBulk;
+  bool get canBeAppliedInBulk => true;
 
   @override
-  bool canBeAppliedToFile;
-
-  RemoveComparison(this.canBeAppliedInBulk, this.canBeAppliedToFile);
+  bool get canBeAppliedToFile => true;
 
   @override
   FixKind get fixKind => DartFixKind.REMOVE_COMPARISON;
@@ -122,10 +120,5 @@
   }
 
   /// Return an instance of this class. Used as a tear-off in `FixProcessor`.
-  static RemoveComparison newInstance() => RemoveComparison(false, false);
-
-  /// Return an instance of this class that can apply bulk and in-file fixes.
-  /// Used as a tear-off in `FixProcessor`.
-  static RemoveComparison newInstanceBulkFixable() =>
-      RemoveComparison(true, true);
+  static RemoveComparison newInstance() => RemoveComparison();
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/use_effective_integer_division.dart b/pkg/analysis_server/lib/src/services/correction/dart/use_effective_integer_division.dart
index 5a0839d..56c4583 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/use_effective_integer_division.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/use_effective_integer_division.dart
@@ -12,9 +12,18 @@
 
 class UseEffectiveIntegerDivision extends CorrectionProducer {
   @override
+  bool get canBeAppliedInBulk => true;
+
+  @override
+  bool get canBeAppliedToFile => true;
+
+  @override
   FixKind get fixKind => DartFixKind.USE_EFFECTIVE_INTEGER_DIVISION;
 
   @override
+  FixKind get multiFixKind => DartFixKind.USE_EFFECTIVE_INTEGER_DIVISION_MULTI;
+
+  @override
   Future<void> compute(ChangeBuilder builder) async {
     for (var n in node.withParents) {
       if (n is MethodInvocation) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
index ff863ed..075f326 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
@@ -11,7 +11,7 @@
 
 class UseEqEqNull extends CorrectionProducer {
   @override
-  bool get canBeAppliedInBulk => false;
+  bool get canBeAppliedInBulk => true;
 
   @override
   bool get canBeAppliedToFile => true;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/use_not_eq_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/use_not_eq_null.dart
index 3ae6b45..e7f633c 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/use_not_eq_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/use_not_eq_null.dart
@@ -11,7 +11,7 @@
 
 class UseNotEqNull extends CorrectionProducer {
   @override
-  bool get canBeAppliedInBulk => false;
+  bool get canBeAppliedInBulk => true;
 
   @override
   bool get canBeAppliedToFile => true;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 7dd6fff..c0f4fbb 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -1386,6 +1386,11 @@
     DartFixKindPriority.DEFAULT,
     'Use effective integer division ~/',
   );
+  static const USE_EFFECTIVE_INTEGER_DIVISION_MULTI = FixKind(
+    'dart.fix.use.effectiveIntegerDivision.multi',
+    DartFixKindPriority.IN_FILE,
+    'Use effective integer division ~/ everywhere in file',
+  );
   static const USE_EQ_EQ_NULL = FixKind(
     'dart.fix.use.eqEqNull',
     DartFixKindPriority.DEFAULT,
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 3b5ed58..e6be85e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -355,7 +355,7 @@
       RemoveInitializer.newInstance,
     ],
     LintNames.avoid_null_checks_in_equality_operators: [
-      RemoveComparison.newInstanceBulkFixable,
+      RemoveComparison.newInstance,
     ],
     LintNames.avoid_print: [
       MakeConditionalOnDebugMode.newInstance,
diff --git a/pkg/analysis_server/test/src/services/correction/fix/use_effective_integer_division_test.dart b/pkg/analysis_server/test/src/services/correction/fix/use_effective_integer_division_test.dart
index d8a6c78..b8b788d 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/use_effective_integer_division_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/use_effective_integer_division_test.dart
@@ -11,10 +11,31 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(UseEffectiveIntegerDivisionTest);
+    defineReflectiveTests(UseEffectiveIntegerDivisionMultiTest);
   });
 }
 
 @reflectiveTest
+class UseEffectiveIntegerDivisionMultiTest extends BulkFixProcessorTest {
+  Future<void> test_singleFile() async {
+    await resolveTestCode('''
+main() {
+  var a = 5;
+  var b = 2;
+  print((a / ((a / b).toInt())).toInt());
+}
+''');
+    await assertHasFix('''
+main() {
+  var a = 5;
+  var b = 2;
+  print(a ~/ (a ~/ b));
+}
+''');
+  }
+}
+
+@reflectiveTest
 class UseEffectiveIntegerDivisionTest extends FixProcessorTest {
   @override
   FixKind get kind => DartFixKind.USE_EFFECTIVE_INTEGER_DIVISION;
diff --git a/pkg/vm/lib/transformations/ffi/native.dart b/pkg/vm/lib/transformations/ffi/native.dart
index bf8a531..dd28967 100644
--- a/pkg/vm/lib/transformations/ffi/native.dart
+++ b/pkg/vm/lib/transformations/ffi/native.dart
@@ -99,24 +99,43 @@
 
   // Replaces parameters with Pointer if:
   // 1) they extend NativeFieldWrapperClass1, and
-  // 2) the corresponding native parameter is Pointer.
-  FunctionType _pointerizeFunctionType(
-      FunctionType dartType, FunctionType nativeType) {
-    final parameters = <DartType>[];
-    for (var i = 0; i < dartType.positionalParameters.length; i++) {
-      final parameter = dartType.positionalParameters[i];
-      if (parameter is InterfaceType) {
-        final nativeParameter = nativeType.positionalParameters[i];
-        if (_extendsNativeFieldWrapperClass1(parameter) &&
-            env.isSubtypeOf(nativeParameter, pointerVoidType,
-                SubtypeCheckMode.ignoringNullabilities)) {
-          parameters.add(pointerVoidType);
-          continue;
-        }
+  // 2) the corresponding FFI parameter is Pointer.
+  DartType _wrapArgumentType(
+      DartType dartParameterType, DartType ffiParameterType) {
+    if (dartParameterType is InterfaceType) {
+      if (_extendsNativeFieldWrapperClass1(dartParameterType) &&
+          env.isSubtypeOf(ffiParameterType, pointerVoidType,
+              SubtypeCheckMode.ignoringNullabilities)) {
+        return pointerVoidType;
       }
-      parameters.add(parameter);
     }
-    return FunctionType(parameters, dartType.returnType, dartType.nullability);
+    return dartParameterType;
+  }
+
+  // Replaces return type with Object if it is Handle.
+  DartType _wrapReturnType(DartType dartReturnType, DartType ffiReturnType) {
+    if (env.isSubtypeOf(
+        ffiReturnType,
+        handleClass.getThisType(coreTypes, Nullability.nonNullable),
+        SubtypeCheckMode.ignoringNullabilities)) {
+      return objectClass.getThisType(coreTypes, Nullability.nonNullable);
+    }
+    return dartReturnType;
+  }
+
+  // Compute synthetic FFI function type, accounting for Objects passed as
+  // Pointer, and Objects returned as Handles.
+  FunctionType _wrapFunctionType(
+      FunctionType dartFunctionType, FunctionType ffiFunctionType) {
+    return FunctionType(
+      [
+        for (var i = 0; i < dartFunctionType.positionalParameters.length; i++)
+          _wrapArgumentType(dartFunctionType.positionalParameters[i],
+              ffiFunctionType.positionalParameters[i]),
+      ],
+      _wrapReturnType(dartFunctionType.returnType, ffiFunctionType.returnType),
+      dartFunctionType.nullability,
+    );
   }
 
   // Create field holding the resolved native function pointer.
@@ -131,22 +150,15 @@
   //           .fromAddress(_ffi_resolver('..', 'DoXYZ', 1))
   //           .asFunction<int Function(Pointer<Void>)>(isLeaf:true);
   Field _createResolvedFfiNativeField(
-      InstanceConstant annotationConst,
       String dartFunctionName,
-      FunctionType dartType,
-      FunctionType nativeType,
-      TreeNode? parent,
-      int fileOffset) {
-    final nativeFunctionName = annotationConst
-        .fieldValues[ffiNativeNameField.fieldReference] as StringConstant;
-    final isLeaf = annotationConst
-        .fieldValues[ffiNativeIsLeafField.fieldReference] as BoolConstant;
-
-    // int Function(Pointer<Void>)
-    final dartTypePointerized = _pointerizeFunctionType(dartType, nativeType);
-
+      StringConstant nativeFunctionName,
+      bool isLeaf,
+      FunctionType dartFunctionType,
+      FunctionType ffiFunctionType,
+      int fileOffset,
+      Uri fileUri) {
     // Derive number of arguments from the native function signature.
-    final numberNativeArgs = nativeType.positionalParameters.length;
+    final numberNativeArgs = ffiFunctionType.positionalParameters.length;
 
     // _ffi_resolver('...', 'DoXYZ', 1)
     final resolverInvocation = FunctionInvocation(
@@ -167,7 +179,8 @@
         Arguments([
           resolverInvocation
         ], types: [
-          InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeType])
+          InterfaceType(
+              nativeFunctionClass, Nullability.legacy, [ffiFunctionType])
         ]))
       ..fileOffset = fileOffset;
 
@@ -176,22 +189,15 @@
     final asFunctionInvocation = StaticInvocation(
         asFunctionMethod,
         Arguments([fromAddressInvocation],
-            types: [nativeType, dartTypePointerized],
-            named: [NamedExpression('isLeaf', BoolLiteral(isLeaf.value))]))
+            types: [ffiFunctionType, dartFunctionType],
+            named: [NamedExpression('isLeaf', BoolLiteral(isLeaf))]))
       ..fileOffset = fileOffset;
 
-    var fileUri = currentLibrary.fileUri;
-    if (parent is Class) {
-      fileUri = parent.fileUri;
-    } else if (parent is Library) {
-      fileUri = parent.fileUri;
-    }
-
     // static final _doXyz$FfiNative$Ptr = ...
     final fieldName =
         Name('_$dartFunctionName\$FfiNative\$Ptr', currentLibrary);
     final functionPointerField = Field.immutable(fieldName,
-        type: dartTypePointerized,
+        type: dartFunctionType,
         initializer: asFunctionInvocation,
         isStatic: true,
         isFinal: true,
@@ -199,19 +205,45 @@
         getterReference: currentLibraryIndex?.lookupGetterReference(fieldName))
       ..fileOffset = fileOffset;
 
-    // Add field to the parent the FfiNative function belongs to.
-    if (parent is Class) {
-      parent.addField(functionPointerField);
-    } else if (parent is Library) {
-      parent.addField(functionPointerField);
-    } else {
-      throw 'Unexpected parent of @FfiNative function. '
-          'Expected Class or Library, but found ${parent}.';
-    }
-
     return functionPointerField;
   }
 
+  // Whether a parameter of [dartParameterType], passed as [ffiParameterType],
+  // needs to be converted to Pointer.
+  bool _requiresPointerConversion(
+      DartType dartParameterType, DartType ffiParameterType) {
+    return (env.isSubtypeOf(ffiParameterType, pointerVoidType,
+            SubtypeCheckMode.ignoringNullabilities) &&
+        !env.isSubtypeOf(dartParameterType, pointerVoidType,
+            SubtypeCheckMode.ignoringNullabilities));
+  }
+
+  VariableDeclaration _declareTemporary(Expression initializer,
+      DartType dartParameterType, DartType ffiParameterType) {
+    final wrappedType =
+        (_requiresPointerConversion(dartParameterType, ffiParameterType)
+            ? nativeFieldWrapperClass1Type
+            : dartParameterType);
+    return VariableDeclaration(variableDeclarationTemporaryName,
+        initializer: initializer, type: wrappedType, isFinal: true);
+  }
+
+  Expression _getTemporary(VariableDeclaration temporary,
+      DartType dartParameterType, DartType ffiParameterType) {
+    if (_requiresPointerConversion(dartParameterType, ffiParameterType)) {
+      // Pointer.fromAddress(_getNativeField(#t0))
+      return StaticInvocation(
+          fromAddressInternal,
+          Arguments([
+            StaticInvocation(
+                getNativeFieldFunction, Arguments([VariableGet(temporary)]))
+          ], types: [
+            voidType
+          ]));
+    }
+    return VariableGet(temporary);
+  }
+
   // FfiNative calls that pass objects extending NativeFieldWrapperClass1
   // should be passed as Pointer instead so we don't have the overhead of
   // converting Handles.
@@ -228,80 +260,51 @@
   //     final #t1 = passAsPointer(Pointer.fromAddress(_getNativeField(#t0)));
   //     reachabilityFence(#t0);
   //   } => #t1
-  Expression _convertArgumentsNativeFieldWrapperClass1ToPointer(
-      FunctionInvocation invocation,
-      List<DartType> ffiParameters,
-      List<VariableDeclaration> dartParameters) {
+  Expression _wrapArgumentsAndReturn(FunctionInvocation invocation,
+      FunctionType dartFunctionType, FunctionType ffiFunctionType) {
+    List<DartType> ffiParameters = ffiFunctionType.positionalParameters;
+    List<DartType> dartParameters = dartFunctionType.positionalParameters;
     // Create lists of temporary variables for arguments potentially being
     // wrapped, and the (potentially) wrapped arguments to be passed.
     final temporariesForArguments = [];
     final callArguments = <Expression>[];
     final fencedArguments = [];
-    bool hasPointer = false;
     for (int i = 0; i < invocation.arguments.positional.length; i++) {
-      if (env.isSubtypeOf(ffiParameters[i], pointerVoidType,
-              SubtypeCheckMode.ignoringNullabilities) &&
-          !env.isSubtypeOf(dartParameters[i].type, pointerVoidType,
-              SubtypeCheckMode.ignoringNullabilities)) {
-        // Only NativeFieldWrapperClass1 instances can be passed as pointer.
-        if (!_extendsNativeFieldWrapperClass1(dartParameters[i].type)) {
-          diagnosticReporter.report(
-              messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
-              dartParameters[i].fileOffset,
-              1,
-              invocation.location?.file);
-        }
-
-        hasPointer = true;
-
-        // final NativeFieldWrapperClass1 #t0 = ClassWithNativeField();
-        final argument = VariableDeclaration(variableDeclarationTemporaryName,
-            initializer: invocation.arguments.positional[i],
-            type: nativeFieldWrapperClass1Type,
-            isFinal: true);
-        temporariesForArguments.add(argument);
-        fencedArguments.add(argument);
-
-        // Pointer.fromAddress(_getNativeField(#t0))
-        final ptr = StaticInvocation(
-            fromAddressInternal,
-            Arguments([
-              StaticInvocation(
-                  getNativeFieldFunction, Arguments([VariableGet(argument)]))
-            ], types: [
-              voidType
-            ]));
-        callArguments.add(ptr);
-
-        continue;
-      }
+      final temporary = _declareTemporary(invocation.arguments.positional[i],
+          dartParameters[i], ffiParameters[i]);
       // Note: We also evaluate, and assign temporaries for, non-wrapped
       // arguments as we need to preserve the original evaluation order.
-      final argument = VariableDeclaration(variableDeclarationTemporaryName,
-          initializer: invocation.arguments.positional[i],
-          type: dartParameters[i].type,
-          isFinal: true);
-      temporariesForArguments.add(argument);
-      callArguments.add(VariableGet(argument));
+      temporariesForArguments.add(temporary);
+      callArguments
+          .add(_getTemporary(temporary, dartParameters[i], ffiParameters[i]));
+      if (_requiresPointerConversion(dartParameters[i], ffiParameters[i])) {
+        fencedArguments.add(temporary);
+        continue;
+      }
     }
 
-    // If there are no arguments to convert then we can drop the whole wrap.
-    if (!hasPointer) {
-      return invocation;
+    Expression resultInitializer = invocation;
+    if (env.isSubtypeOf(
+        ffiFunctionType.returnType,
+        handleClass.getThisType(coreTypes, Nullability.nonNullable),
+        SubtypeCheckMode.ignoringNullabilities)) {
+      resultInitializer = AsExpression(invocation, dartFunctionType.returnType);
     }
 
+    //   final T #t1 = foo(Pointer.fromAddress(_getNativeField(#t0)));
+    final result = VariableDeclaration(variableDeclarationTemporaryName,
+        initializer: resultInitializer,
+        type: dartFunctionType.returnType,
+        isFinal: true);
+
     invocation.arguments = Arguments(callArguments);
 
     // {
     //   final NativeFieldWrapperClass1 #t0 = ClassWithNativeField();
-    //   final T #t1 = foo(Pointer.fromAddress(_getNativeField(#t0)));
+    //   .. #t1 ..
     //   reachabilityFence(#t0);
     // } => #t1
-    final result = VariableDeclaration(variableDeclarationTemporaryName,
-        initializer: invocation,
-        type: invocation.functionType!.returnType,
-        isFinal: true);
-    return BlockExpression(
+    final resultBlock = BlockExpression(
       Block([
         ...temporariesForArguments,
         result,
@@ -311,6 +314,125 @@
       ]),
       VariableGet(result),
     );
+
+    return resultBlock;
+  }
+
+  // Verify the Dart and FFI parameter types are compatible.
+  bool _verifyParameter(DartType dartParameterType, DartType ffiParameterType,
+      int annotationOffset, Uri? file) {
+    // Only NativeFieldWrapperClass1 instances can be passed as pointer.
+    if (_requiresPointerConversion(dartParameterType, ffiParameterType) &&
+        !_extendsNativeFieldWrapperClass1(dartParameterType)) {
+      diagnosticReporter.report(
+          messageFfiNativeOnlyNativeFieldWrapperClassCanBePointer,
+          annotationOffset,
+          1,
+          file);
+      return false;
+    }
+    return true;
+  }
+
+  // Verify the signatures of the Dart function and the accompanying FfiNative
+  // annotation matches.
+  bool _verifySignatures(Procedure node, FunctionType dartFunctionType,
+      FunctionType ffiFunctionType, int annotationOffset) {
+    if (dartFunctionType.positionalParameters.length !=
+        ffiFunctionType.positionalParameters.length) {
+      final template = (node.isStatic
+          ? templateFfiNativeUnexpectedNumberOfParameters
+          : templateFfiNativeUnexpectedNumberOfParametersWithReceiver);
+      diagnosticReporter.report(
+          template.withArguments(dartFunctionType.positionalParameters.length,
+              ffiFunctionType.positionalParameters.length),
+          annotationOffset,
+          1,
+          node.location?.file);
+      return false;
+    }
+
+    var validSignature = true;
+    for (var i = 0; i < dartFunctionType.positionalParameters.length; i++) {
+      final dartParameterType = dartFunctionType.positionalParameters[i];
+      if (dartParameterType is InterfaceType) {
+        if (!_verifyParameter(
+            dartParameterType,
+            ffiFunctionType.positionalParameters[i],
+            annotationOffset,
+            node.location?.file)) {
+          validSignature = false;
+        }
+      }
+    }
+
+    return validSignature;
+  }
+
+  Procedure _transformProcedure(
+      Procedure node,
+      StringConstant nativeFunctionName,
+      bool isLeaf,
+      int annotationOffset,
+      FunctionType dartFunctionType,
+      FunctionType ffiFunctionType,
+      List<Expression> argumentList) {
+    if (!_verifySignatures(
+        node, ffiFunctionType, dartFunctionType, annotationOffset)) {
+      return node;
+    }
+
+    // int Function(Pointer<Void>)
+    final wrappedDartFunctionType =
+        _wrapFunctionType(dartFunctionType, ffiFunctionType);
+
+    final parent = node.parent;
+
+    var fileUri = currentLibrary.fileUri;
+    if (parent is Class) {
+      fileUri = parent.fileUri;
+    } else if (parent is Library) {
+      fileUri = parent.fileUri;
+    }
+
+    // static final _myMethod$FfiNative$Ptr = ..
+    final resolvedField = _createResolvedFfiNativeField(
+        node.name.text,
+        nativeFunctionName,
+        isLeaf,
+        wrappedDartFunctionType,
+        ffiFunctionType,
+        node.fileOffset,
+        fileUri);
+
+    // Add field to the parent the FfiNative function belongs to.
+    if (parent is Class) {
+      parent.addField(resolvedField);
+    } else if (parent is Library) {
+      parent.addField(resolvedField);
+    } else {
+      throw 'Unexpected parent of @FfiNative function. '
+          'Expected Class or Library, but found ${parent}.';
+    }
+
+    // _myFunction$FfiNative$Ptr(obj, x)
+    final functionPointerInvocation = FunctionInvocation(
+        FunctionAccessKind.FunctionType,
+        StaticGet(resolvedField),
+        Arguments(argumentList),
+        functionType: wrappedDartFunctionType)
+      ..fileOffset = node.fileOffset;
+
+    Expression result = (wrappedDartFunctionType == dartFunctionType
+        ? functionPointerInvocation
+        : _wrapArgumentsAndReturn(
+            functionPointerInvocation, dartFunctionType, ffiFunctionType));
+
+    //   => _myFunction$FfiNative$Ptr(
+    //     Pointer<Void>.fromAddress(_getNativeField(obj)), x)
+    node.function.body = ReturnStatement(result)..parent = node.function;
+
+    return node;
   }
 
   // Transform FfiNative instance methods.
@@ -327,84 +449,32 @@
   //         Pointer<Void>.fromAddress(_getNativeField(self)), x);
   //     int myMethod(int x) => _myMethod$FfiNative(this, x);
   //   }
+  //
+  //   ... {
+  //     static final _myMethod$FfiNative$Ptr = ...
+  //     int myMethod(int x)
+  //       => _myMethod$FfiNative$Ptr(
+  //         Pointer<Void>.fromAddress(_getNativeField(this)), x);
+  //   }
   Procedure _transformInstanceMethod(
-      Procedure node, InstanceConstant ffiConstant, int annotationOffset) {
-    final ffiSignature = ffiConstant.typeArguments[0] as FunctionType;
+      Procedure node,
+      FunctionType ffiFunctionType,
+      StringConstant nativeFunctionName,
+      bool isLeaf,
+      int annotationOffset) {
+    final dartFunctionType = FunctionType([
+      (node.parent as Class).getThisType(coreTypes, Nullability.nonNullable),
+      for (final parameter in node.function.positionalParameters) parameter.type
+    ], node.function.returnType, Nullability.nonNullable);
 
-    // The FfiNative annotation should have an extra parameter for `self`.
-    if (node.function.positionalParameters.length + 1 !=
-        ffiSignature.positionalParameters.length) {
-      diagnosticReporter.report(
-          templateFfiNativeUnexpectedNumberOfParametersWithReceiver
-              .withArguments(node.function.positionalParameters.length + 1,
-                  ffiSignature.positionalParameters.length),
-          annotationOffset,
-          1,
-          node.location?.file);
-      return node;
-    }
-
-    final cls = node.parent as Class;
-
-    final staticParameters = [
-      // Add the implicit `self` for the inner glue functions.
-      VariableDeclaration('self',
-          type: cls.getThisType(coreTypes, Nullability.nonNullable)),
+    final argumentList = [
+      ThisExpression(),
       for (final parameter in node.function.positionalParameters)
-        VariableDeclaration(parameter.name, type: parameter.type)
+        VariableGet(parameter)
     ];
 
-    final staticFunctionType = FunctionType(
-        staticParameters.map((e) => e.type).toList(),
-        node.function.returnType,
-        Nullability.nonNullable);
-
-    // static final _myMethod$FfiNative$Ptr = ..
-    final resolvedField = _createResolvedFfiNativeField(
-        ffiConstant,
-        node.name.text,
-        staticFunctionType,
-        ffiSignature,
-        node.parent,
-        node.fileOffset);
-
-    //   _myMethod$FfiNative$Ptr(self, x)
-    final functionPointerInvocation = FunctionInvocation(
-        FunctionAccessKind.FunctionType,
-        StaticGet(resolvedField),
-        Arguments(
-            [for (final parameter in staticParameters) VariableGet(parameter)]),
-        functionType: staticFunctionType)
-      ..fileOffset = node.fileOffset;
-
-    //   static _myMethod$FfiNative(MyNativeClass self, int x)
-    //     => _myMethod$FfiNative$Ptr(
-    //       Pointer<Void>.fromAddress(_getNativeField(self)), x)
-    final ffiProcedure = Procedure(
-        Name('_${node.name.text}\$FfiNative', currentLibrary),
-        ProcedureKind.Method,
-        FunctionNode(
-            ReturnStatement(_convertArgumentsNativeFieldWrapperClass1ToPointer(
-                functionPointerInvocation,
-                ffiSignature.positionalParameters,
-                staticParameters)),
-            positionalParameters: staticParameters),
-        isStatic: true,
-        fileUri: node.fileUri)
-      ..fileOffset = node.fileOffset;
-    cls.addProcedure(ffiProcedure);
-
-    //   => _myMethod$FfiNative(this, x)
-    node.function.body = ReturnStatement(StaticInvocation(
-        ffiProcedure,
-        Arguments([
-          ThisExpression(),
-          for (var parameter in node.function.positionalParameters)
-            VariableGet(parameter)
-        ])))
-      ..parent = node.function;
-
-    return node;
+    return _transformProcedure(node, nativeFunctionName, isLeaf,
+        annotationOffset, dartFunctionType, ffiFunctionType, argumentList);
   }
 
   // Transform FfiNative static functions.
@@ -417,51 +487,21 @@
   //     => myFunction$FfiNative$Ptr(
   //       Pointer<Void>.fromAddress(_getNativeField(obj)), x);
   Procedure _transformStaticFunction(
-      Procedure node, InstanceConstant ffiConstant, int annotationOffset) {
-    final ffiSignature = ffiConstant.typeArguments[0] as FunctionType;
+      Procedure node,
+      FunctionType ffiFunctionType,
+      StringConstant nativeFunctionName,
+      bool isLeaf,
+      int annotationOffset) {
+    final dartFunctionType =
+        node.function.computeThisFunctionType(Nullability.nonNullable);
 
-    if (node.function.positionalParameters.length !=
-        ffiSignature.positionalParameters.length) {
-      diagnosticReporter.report(
-          templateFfiNativeUnexpectedNumberOfParameters.withArguments(
-              node.function.positionalParameters.length,
-              ffiSignature.positionalParameters.length),
-          annotationOffset,
-          1,
-          node.location?.file);
-      return node;
-    }
+    final argumentList = [
+      for (final parameter in node.function.positionalParameters)
+        VariableGet(parameter)
+    ];
 
-    // _myFunction$FfiNative$Ptr = ..
-    final resolvedField = _createResolvedFfiNativeField(
-        ffiConstant,
-        node.name.text,
-        node.function.computeThisFunctionType(Nullability.nonNullable),
-        ffiSignature,
-        node.parent,
-        node.fileOffset);
-
-    // _myFunction$FfiNative$Ptr(obj, x)
-    final functionPointerInvocation = FunctionInvocation(
-        FunctionAccessKind.FunctionType,
-        StaticGet(resolvedField),
-        Arguments([
-          for (final parameter in node.function.positionalParameters)
-            VariableGet(parameter)
-        ]),
-        functionType: resolvedField.type as FunctionType)
-      ..fileOffset = node.fileOffset;
-
-    //   => _myFunction$FfiNative$Ptr(
-    //     Pointer<Void>.fromAddress(_getNativeField(obj)), x)
-    node.function.body = ReturnStatement(
-        _convertArgumentsNativeFieldWrapperClass1ToPointer(
-            functionPointerInvocation,
-            ffiSignature.positionalParameters,
-            node.function.positionalParameters))
-      ..parent = node.function;
-
-    return node;
+    return _transformProcedure(node, nativeFunctionName, isLeaf,
+        annotationOffset, dartFunctionType, ffiFunctionType, argumentList);
   }
 
   @override
@@ -483,16 +523,20 @@
 
     node.annotations.remove(ffiNativeAnnotation);
 
+    final ffiConstant = ffiNativeAnnotation.constant as InstanceConstant;
+    final ffiFunctionType = ffiConstant.typeArguments[0] as FunctionType;
+    final nativeFunctionName = ffiConstant
+        .fieldValues[ffiNativeNameField.fieldReference] as StringConstant;
+    final isLeaf = (ffiConstant.fieldValues[ffiNativeIsLeafField.fieldReference]
+            as BoolConstant)
+        .value;
+
     if (!node.isStatic) {
-      return _transformInstanceMethod(
-          node,
-          ffiNativeAnnotation.constant as InstanceConstant,
-          ffiNativeAnnotation.fileOffset);
+      return _transformInstanceMethod(node, ffiFunctionType, nativeFunctionName,
+          isLeaf, ffiNativeAnnotation.fileOffset);
     }
 
-    return _transformStaticFunction(
-        node,
-        ffiNativeAnnotation.constant as InstanceConstant,
-        ffiNativeAnnotation.fileOffset);
+    return _transformStaticFunction(node, ffiFunctionType, nativeFunctionName,
+        isLeaf, ffiNativeAnnotation.fileOffset);
   }
 }
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart b/pkg/vm/testcases/transformations/ffi/ffinative.dart
index c611214..347ba00 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart
@@ -35,6 +35,12 @@
 
   @FfiNative<Void Function(Pointer<Void>, Handle)>('doesntmatter')
   external void goodHasReceiverPtrAndHandle(NativeClassy v);
+
+  @FfiNative<Handle Function(Pointer<Void>, Bool)>('doesntmatter')
+  external String? meh(bool blah);
+
+  @FfiNative<Bool Function(Pointer<Void>)>('doesntmatter')
+  external bool blah();
 }
 
 void main() {
@@ -46,4 +52,6 @@
   NativeClassy().goodHasReceiverHandleAndPtr(NativeClassy());
   NativeClassy().goodHasReceiverHandleAndHandle(NativeClassy());
   NativeClassy().goodHasReceiverPtrAndHandle(NativeClassy());
+  NativeClassy().meh(true);
+  NativeClassy().blah();
 }
diff --git a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
index eec7f88..557aa76 100644
--- a/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
+++ b/pkg/vm/testcases/transformations/ffi/ffinative.dart.expect
@@ -22,44 +22,49 @@
   static final field (self::NativeClassy, ffi::Pointer<ffi::Void>) → void _goodHasReceiverHandleAndPtr$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, ffi::Pointer<ffi::Void>) → void, (ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Pointer<ffi::Void*>*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
   static final field (self::NativeClassy, self::NativeClassy) → void _goodHasReceiverHandleAndHandle$FfiNative$Ptr = ffi::_asFunctionInternal<(self::NativeClassy, self::NativeClassy) → void, (ffi::Handle*, ffi::Handle*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Handle*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
   static final field (ffi::Pointer<ffi::Void>, self::NativeClassy) → void _goodHasReceiverPtrAndHandle$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, self::NativeClassy) → void, (ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::Handle*) →* ffi::Void*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+  static final field (ffi::Pointer<ffi::Void>, core::bool) → core::Object _meh$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>, core::bool) → core::Object, (ffi::Pointer<ffi::Void*>*, ffi::Bool*) →* ffi::Handle*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*, ffi::Bool*) →* ffi::Handle*>*>(ffi::_ffi_resolver(#C1, #C4, #C5){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
+  static final field (ffi::Pointer<ffi::Void>) → core::bool _blah$FfiNative$Ptr = ffi::_asFunctionInternal<(ffi::Pointer<ffi::Void>) → core::bool, (ffi::Pointer<ffi::Void*>*) →* ffi::Bool*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::Pointer<ffi::Void*>*) →* ffi::Bool*>*>(ffi::_ffi_resolver(#C1, #C4, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
   synthetic constructor •() → self::NativeClassy
     : super nat::NativeFieldWrapperClass1::•()
     ;
   method goodHasReceiverPointer(core::int v) → void
-    return self::NativeClassy::_goodHasReceiverPointer$FfiNative(this, v);
-  method goodHasReceiverHandle(core::int v) → void
-    return self::NativeClassy::_goodHasReceiverHandle$FfiNative(this, v);
-  method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
-    return self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative(this, v);
-  method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void
-    return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative(this, v);
-  method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
-    return self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative(this, v);
-  static method /*isLegacy*/ _goodHasReceiverPointer$FfiNative(self::NativeClassy self, core::int v) → dynamic
     return block {
-      final nat::NativeFieldWrapperClass1 #t1 = self;
+      final nat::NativeFieldWrapperClass1 #t1 = this;
       final core::int #t2 = v;
-      final void #t3 = self::NativeClassy::_goodHasReceiverPointer$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t1)), #t2){(self::NativeClassy, core::int) → void};
+      final void #t3 = self::NativeClassy::_goodHasReceiverPointer$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t1)), #t2){(ffi::Pointer<ffi::Void>, core::int) → void};
       _in::reachabilityFence(#t1);
     } =>#t3;
-  static method /*isLegacy*/ _goodHasReceiverHandle$FfiNative(self::NativeClassy self, core::int v) → dynamic
-    return self::NativeClassy::_goodHasReceiverHandle$FfiNative$Ptr(self, v){(self::NativeClassy, core::int) → void};
-  static method /*isLegacy*/ _goodHasReceiverHandleAndPtr$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
+  method goodHasReceiverHandle(core::int v) → void
+    return self::NativeClassy::_goodHasReceiverHandle$FfiNative$Ptr(this, v){(self::NativeClassy, core::int) → void};
+  method goodHasReceiverHandleAndPtr(self::NativeClassy v) → void
     return block {
-      final self::NativeClassy #t4 = self;
+      final self::NativeClassy #t4 = this;
       final nat::NativeFieldWrapperClass1 #t5 = v;
-      final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5))){(self::NativeClassy, self::NativeClassy) → void};
+      final void #t6 = self::NativeClassy::_goodHasReceiverHandleAndPtr$FfiNative$Ptr(#t4, ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t5))){(self::NativeClassy, ffi::Pointer<ffi::Void>) → void};
       _in::reachabilityFence(#t5);
     } =>#t6;
-  static method /*isLegacy*/ _goodHasReceiverHandleAndHandle$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
-    return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative$Ptr(self, v){(self::NativeClassy, self::NativeClassy) → void};
-  static method /*isLegacy*/ _goodHasReceiverPtrAndHandle$FfiNative(self::NativeClassy self, self::NativeClassy v) → dynamic
+  method goodHasReceiverHandleAndHandle(self::NativeClassy v) → void
+    return self::NativeClassy::_goodHasReceiverHandleAndHandle$FfiNative$Ptr(this, v){(self::NativeClassy, self::NativeClassy) → void};
+  method goodHasReceiverPtrAndHandle(self::NativeClassy v) → void
     return block {
-      final nat::NativeFieldWrapperClass1 #t7 = self;
+      final nat::NativeFieldWrapperClass1 #t7 = this;
       final self::NativeClassy #t8 = v;
-      final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t7)), #t8){(self::NativeClassy, self::NativeClassy) → void};
+      final void #t9 = self::NativeClassy::_goodHasReceiverPtrAndHandle$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t7)), #t8){(ffi::Pointer<ffi::Void>, self::NativeClassy) → void};
       _in::reachabilityFence(#t7);
     } =>#t9;
+  method meh(core::bool blah) → core::String?
+    return block {
+      final nat::NativeFieldWrapperClass1 #t10 = this;
+      final core::bool #t11 = blah;
+      final core::String? #t12 = self::NativeClassy::_meh$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t10)), #t11){(ffi::Pointer<ffi::Void>, core::bool) → core::Object} as core::String?;
+      _in::reachabilityFence(#t10);
+    } =>#t12;
+  method blah() → core::bool
+    return block {
+      final nat::NativeFieldWrapperClass1 #t13 = this;
+      final core::bool #t14 = self::NativeClassy::_blah$FfiNative$Ptr(ffi::_fromAddress<ffi::Void>(nat::_getNativeField(#t13))){(ffi::Pointer<ffi::Void>) → core::bool};
+      _in::reachabilityFence(#t13);
+    } =>#t14;
 }
 static final field (core::int) → core::int _returnIntPtr$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), false)/*isLegacy*/;
 static final field (core::int) → core::int _returnIntPtrLeaf$FfiNative$Ptr = ffi::_asFunctionInternal<(core::int) → core::int, (ffi::IntPtr*) →* ffi::IntPtr*>(ffi::_fromAddress<ffi::NativeFunction<(ffi::IntPtr*) →* ffi::IntPtr*>*>(ffi::_ffi_resolver(#C1, #C2, #C3){(core::Object, core::Object, core::int) → core::int}), true)/*isLegacy*/;
@@ -76,6 +81,8 @@
   new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndPtr}(new self::NativeClassy::•()){(self::NativeClassy) → void};
   new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverHandleAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
   new self::NativeClassy::•().{self::NativeClassy::goodHasReceiverPtrAndHandle}(new self::NativeClassy::•()){(self::NativeClassy) → void};
+  new self::NativeClassy::•().{self::NativeClassy::meh}(true){(core::bool) → core::String?};
+  new self::NativeClassy::•().{self::NativeClassy::blah}(){() → core::bool};
 }
 constants  {
   #C1 = "#lib"
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart
index 288f66e..fe52156 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/math_patch.dart
@@ -67,14 +67,14 @@
 
 @patch
 class Random {
-  static Random? _secureRandom;
+  static final Random _secureRandom = _JSSecureRandom();
 
   @patch
   factory Random([int? seed]) =>
       (seed == null) ? const _JSRandom() : _Random(seed);
 
   @patch
-  factory Random.secure() => _secureRandom ??= _JSSecureRandom();
+  factory Random.secure() => _secureRandom;
 }
 
 class _JSRandom implements Random {
diff --git a/sdk/lib/_internal/js_runtime/lib/math_patch.dart b/sdk/lib/_internal/js_runtime/lib/math_patch.dart
index 585f4c5..64236b3 100644
--- a/sdk/lib/_internal/js_runtime/lib/math_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/math_patch.dart
@@ -63,14 +63,14 @@
 
 @patch
 class Random {
-  static Random? _secureRandom;
+  static final Random _secureRandom = _JSSecureRandom();
 
   @patch
   factory Random([int? seed]) =>
-      (seed == null) ? const _JSRandom() : new _Random(seed);
+      (seed == null) ? const _JSRandom() : _Random(seed);
 
   @patch
-  factory Random.secure() => _secureRandom ??= _JSSecureRandom();
+  factory Random.secure() => _secureRandom;
 }
 
 class _JSRandom implements Random {
diff --git a/tools/VERSION b/tools/VERSION
index d699e32..3c5112c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 235
+PRERELEASE 236
 PRERELEASE_PATCH 0
\ No newline at end of file