[ddc] Fix generic method constant tearoffs

Previously all functions objects returned by `dart.gbind()` would
contain the same 3 properties: name, length, and runtimeType. For
two distinct functions, if these three properties were the same
our constant canonicalization would incorrectly canonicalize to the
same value. Attaching the original function and the type arguments
used in instantiation ensures the const canonicalization will treat
different functions or instantiations as different values.

These values will also be used for equality checks proposed here:
https://github.com/dart-lang/language/issues/1712

Fixes: https://github.com/dart-lang/sdk/issues/46568
Issue: https://github.com/dart-lang/sdk/issues/46486
Change-Id: I65111df9af80d878eb3127c5e3dfac1ffba95535
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206423
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index 54576b0..33854c7 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -85,10 +85,14 @@
 gbind(f, @rest List<Object> typeArgs) {
   GenericFunctionType type = JS('!', '#[#]', f, _runtimeType);
   type.checkBounds(typeArgs);
-  // Create a JS wrapper function that will also pass the type arguments, and
-  // tag it with the instantiated function type.
+  // Create a JS wrapper function that will also pass the type arguments.
   var result =
       JS('', '(...args) => #.apply(null, #.concat(args))', f, typeArgs);
+  // Tag the wrapper with the original function to be used for equality checks.
+  JS('', '#["_originalFn"] = #', result, f);
+  JS('', '#["_typeArgs"] = #', result, constList(typeArgs, Object));
+
+  // Tag the wrapper with the instantiated function type.
   return fn(result, type.instantiate(typeArgs));
 }