[dartdevc/nnbd] warn on null param in weak mode

This warns only the first time per (callee/param) location, which reduces the noise significantly.

Change-Id: I9674c366ca38a097fcc93fd2a0716366da57c9d1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136400
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Vijay Menon <vsm@google.com>
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
index 398cbf6..0d62265 100644
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
@@ -24,10 +24,10 @@
   throw AssertionErrorImpl(message, fileUri, line, column, conditionSource);
 }
 
+final _nullFailedSet = JS('!', 'new Set()');
 // Run-time null safety assertion per:
 // https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md#automatic-debug-assertion-insertion
 nullFailed(String? fileUri, int? line, int? column, String? variable) {
-  // TODO(vsm): Consider a weak mode warning cached by location.
   if (_strictSubtypeChecks) {
     throw AssertionErrorImpl(
         'A null value was passed into a non-nullable parameter $variable',
@@ -35,6 +35,13 @@
         line,
         column,
         '$variable != null');
+  } else {
+    var key = '$fileUri:$line:$column';
+    if (!JS('!', '#.has(#)', _nullFailedSet, key)) {
+      JS('', '#.add(#)', _nullFailedSet, key);
+      _nullWarn(
+          'A null value was passed into a non-nullable parameter $variable');
+    }
   }
 }
 
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index a4a6e21..320afa0 100644
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -186,7 +186,7 @@
       var error = "Dynamic call with missing required named arguments: "
           "${missingRequired.join(', ')}.";
       if (!_strictSubtypeChecks) {
-        _warn("$error This will be an error when strict mode is enabled.");
+        _nullWarn(error);
       } else {
         return error;
       }
@@ -438,8 +438,7 @@
     bool result = JS('', '#.get(#)', _nullComparisonMap, type);
     if (JS('!', '# === void 0', result)) {
       JS('', '#.set(#, #)', _nullComparisonMap, type, false);
-      _warn("Null is not a subtype of $type.\n"
-          "This will be a runtime failure when strict mode is enabled.");
+      _nullWarn("Null is not a subtype of $type.");
     }
     return obj;
   }
diff --git a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 54d5e63..a69f6fe 100644
--- a/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk_nnbd/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -215,6 +215,11 @@
   JS('void', 'console.warn(#)', arg);
 }
 
+void _nullWarn(arg) {
+  _warn('$arg\n'
+      'This will become a failure when runtime null safety is enabled.');
+}
+
 /// Tracks objects that have been compared against null (i.e., null is Type).
 /// Separating this null map out from _cacheMaps lets us fast-track common
 /// legacy type checks.
@@ -1191,9 +1196,8 @@
     if (validSubtype) {
       // TODO(nshahan) Need more information to be helpful here.
       // File and line number that caused the subtype check?
-      // Possibly break into debuger?
-      _warn("$t1 is not a subtype of $t2.\n"
-          "This will be a runtime failure when strict mode is enabled.");
+      // Possibly break into debugger?
+      _nullWarn("$t1 is not a subtype of $t2.");
     }
   }
   JS('', '#.set(#, #)', map, t2, validSubtype);