[cfe,dartdevc,dart2js] Use InstanceGetterInvocation for getter/field invocation

The web compilers don't support getter/field invocation encoded as a
FunctionInvocation on an InstanceGet because it doesn't work for
getter/field invocation of js-interop properties, since the InstanceGet
wouldn't result in a Dart function but just JavaScript function.

To support this in the new method invocation encoding, a special
expression, InstanceGetterInvocation, is used to encode getter/field
invocations in dart2js and ddc.

Change-Id: I21da8e8686f66ae4ce4d44245073b9e424f975b9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192181
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index cb063b6..fd85772 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -1012,16 +1012,9 @@
     // effectively final variable types and type promotion.
     ir.DartType functionType = _computeInstanceInvocationType(
         receiverType, interfaceTarget, node.arguments, argumentTypes);
-    if (functionType != node.functionType) {
+    if (functionType is ir.FunctionType && functionType != node.functionType) {
       node.functionType = functionType;
-      // TODO(johnniwinther): To provide the static guarantee that arguments
-      // of a statically typed call have been checked against the parameter
-      // types we need to call [_updateMethodInvocationTarget]. This can create
-      // uses of type variables are not registered with the closure model so
-      // we skip it for now. Note that this invariant is not currently used
-      // in later phases since it wasn't provided for function invocations in
-      // the old method invocation encoding.
-      //_updateMethodInvocationTarget(node, argumentTypes, functionType);
+      _updateMethodInvocationTarget(node, argumentTypes, functionType);
     }
     ir.DartType returnType = _getFunctionReturnType(functionType);
     receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
@@ -1046,10 +1039,14 @@
           (interfaceTarget is ir.Procedure && interfaceTarget.isGetter)) {
         // This should actually be a function invocation of an instance get but
         // this doesn't work for invocation of js-interop properties. We
-        // therefore use [ir.MethodInvocation] instead.
-        // TODO(johnniwinther): Use [ir.InstanceGetterInvocation] instead.
-        replacement = ir.MethodInvocation(
-            node.receiver, node.name, node.arguments, interfaceTarget)
+        // therefore use [ir.InstanceGetterInvocation] instead.
+        replacement = ir.InstanceGetterInvocation(
+            ir.InstanceAccessKind.Instance,
+            node.receiver,
+            node.name,
+            node.arguments,
+            interfaceTarget: interfaceTarget,
+            functionType: functionType is ir.FunctionType ? functionType : null)
           ..fileOffset = node.fileOffset;
       } else {
         replacement = ir.InstanceInvocation(ir.InstanceAccessKind.Instance,
diff --git a/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart b/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
index b930366..07fa7d4 100644
--- a/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
+++ b/pkg/dev_compiler/lib/src/kernel/nullable_inference.dart
@@ -145,6 +145,11 @@
           node.interfaceTarget, node.name.text, node, node.receiver);
 
   @override
+  bool visitInstanceGetterInvocation(InstanceGetterInvocation node) =>
+      _invocationIsNullable(
+          node.interfaceTarget, node.name.text, node, node.receiver);
+
+  @override
   bool visitDynamicInvocation(DynamicInvocation node) =>
       _invocationIsNullable(null, node.name.text, node, node.receiver);
 
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index ffc64a4..1f8d5f6 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -440,6 +440,12 @@
   }
 
   @override
+  void visitInstanceGetterInvocation(InstanceGetterInvocation node) {
+    _checkTarget(node.receiver, node.interfaceTarget);
+    super.visitInstanceGetterInvocation(node);
+  }
+
+  @override
   void visitInstanceTearOff(InstanceTearOff node) {
     _checkTearoff(node.interfaceTarget);
     super.visitInstanceTearOff(node);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index d754099..c8fbea5e 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -3330,24 +3330,38 @@
               ..fileOffset = nullAwareAction.fileOffset);
       } else if (nullAwareAction is InstanceInvocation &&
           nullAwareAction.receiver == originalPropertyGet) {
+        // TODO(johnniwinther): Remove this when [MethodInvocation] is no longer
+        // used and [originalPropertyGet] can be an [InstanceGet].
+        InstanceGet instanceGet = originalPropertyGet;
         invocationResult = new ExpressionInferenceResult(
             invocationResult.inferredType,
-            new MethodInvocation(originalReceiver, originalName,
-                nullAwareAction.arguments, originalTarget)
+            new InstanceGetterInvocation(instanceGet.kind, originalReceiver,
+                originalName, nullAwareAction.arguments,
+                interfaceTarget: originalTarget,
+                functionType: nullAwareAction.functionType)
               ..fileOffset = nullAwareAction.fileOffset);
       } else if (nullAwareAction is DynamicInvocation &&
           nullAwareAction.receiver == originalPropertyGet) {
+        // TODO(johnniwinther): Remove this when [MethodInvocation] is no longer
+        // used and [originalPropertyGet] can be an [InstanceGet].
+        InstanceGet instanceGet = originalPropertyGet;
         invocationResult = new ExpressionInferenceResult(
             invocationResult.inferredType,
-            new MethodInvocation(originalReceiver, originalName,
-                nullAwareAction.arguments, originalTarget)
+            new InstanceGetterInvocation(instanceGet.kind, originalReceiver,
+                originalName, nullAwareAction.arguments,
+                interfaceTarget: originalTarget, functionType: null)
               ..fileOffset = nullAwareAction.fileOffset);
       } else if (nullAwareAction is FunctionInvocation &&
           nullAwareAction.receiver == originalPropertyGet) {
+        // TODO(johnniwinther): Remove this when [MethodInvocation] is no longer
+        // used and [originalPropertyGet] can be an [InstanceGet].
+        InstanceGet instanceGet = originalPropertyGet;
         invocationResult = new ExpressionInferenceResult(
             invocationResult.inferredType,
-            new MethodInvocation(originalReceiver, originalName,
-                nullAwareAction.arguments, originalTarget)
+            new InstanceGetterInvocation(instanceGet.kind, originalReceiver,
+                originalName, nullAwareAction.arguments,
+                interfaceTarget: originalTarget,
+                functionType: nullAwareAction.functionType)
               ..fileOffset = nullAwareAction.fileOffset);
       }
     }
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 0863ec7..dfcff07 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -353,6 +353,7 @@
 dst
 dummy
 dupdate
+dyn
 e
 easy
 ecma
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart
new file mode 100644
index 0000000..1ef505e
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart
@@ -0,0 +1,17 @@
+// 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.
+
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {
+  Class c = new Class();
+  c.idFunction(0);
+  c.idFunction<int>(0);
+  c.dynFunction(0);
+}
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.expect
new file mode 100644
index 0000000..e29a84e
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.transformed.expect
new file mode 100644
index 0000000..6a431a4
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.strong.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:14:16 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:15:21 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:16:17 -> DoubleConstant(0.0)
+Extra constant evaluation: evaluated: 20, effectively constant: 3
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline.expect
new file mode 100644
index 0000000..9cd1271
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..9cd1271
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.expect
new file mode 100644
index 0000000..e29a84e
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.outline.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.outline.expect
new file mode 100644
index 0000000..85f75fa
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    ;
+  get dynFunction() → dynamic
+    ;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.transformed.expect
new file mode 100644
index 0000000..6a431a4
--- /dev/null
+++ b/pkg/front_end/testcases/dart2js/instance_getter_invocation.dart.weak.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:14:16 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:15:21 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:16:17 -> DoubleConstant(0.0)
+Extra constant evaluation: evaluated: 20, effectively constant: 3
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart
new file mode 100644
index 0000000..1ef505e
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart
@@ -0,0 +1,17 @@
+// 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.
+
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {
+  Class c = new Class();
+  c.idFunction(0);
+  c.idFunction<int>(0);
+  c.dynFunction(0);
+}
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.expect
new file mode 100644
index 0000000..e29a84e
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.transformed.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.transformed.expect
new file mode 100644
index 0000000..6a431a4
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.strong.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:14:16 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:15:21 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:16:17 -> DoubleConstant(0.0)
+Extra constant evaluation: evaluated: 20, effectively constant: 3
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline.expect
new file mode 100644
index 0000000..9cd1271
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..9cd1271
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+T id<T>(T t) => t;
+
+class Class {
+  S Function<S>(S) get idFunction => id;
+  dynamic get dynFunction => id;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.expect
new file mode 100644
index 0000000..e29a84e
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.expect
@@ -0,0 +1,25 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.outline.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.outline.expect
new file mode 100644
index 0000000..85f75fa
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    ;
+  get dynFunction() → dynamic
+    ;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.transformed.expect b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.transformed.expect
new file mode 100644
index 0000000..6a431a4
--- /dev/null
+++ b/pkg/front_end/testcases/dartdevc/instance_getter_invocation.dart.weak.transformed.expect
@@ -0,0 +1,31 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+  synthetic constructor •() → self::Class
+    : super core::Object::•()
+    ;
+  get idFunction() → <S extends core::Object? = dynamic>(S%) → S%
+    return #C1;
+  get dynFunction() → dynamic
+    return #C1;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% t) → self::id::T%
+  return t;
+static method main() → dynamic {
+  self::Class c = new self::Class::•();
+  let final self::Class #t1 = c in let final core::int #t2 = 0 in #t1.{self::Class::idFunction}<core::int>(#t2){(core::int) → core::int};
+  let final self::Class #t3 = c in let final core::int #t4 = 0 in #t3.{self::Class::idFunction}<core::int>(#t4){(core::int) → core::int};
+  let final self::Class #t5 = c in let final core::int #t6 = 0 in #t5.{self::Class::dynFunction}(#t6);
+}
+
+constants  {
+  #C1 = tearoff self::id
+}
+
+Extra constant evaluation status:
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:14:16 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:15:21 -> DoubleConstant(0.0)
+Evaluated: VariableGet @ org-dartlang-testcase:///instance_getter_invocation.dart:16:17 -> DoubleConstant(0.0)
+Extra constant evaluation: evaluated: 20, effectively constant: 3
diff --git a/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.expect b/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.expect
index ce47bb9..2b4c9cc 100644
--- a/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.expect
+++ b/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.expect
@@ -153,21 +153,21 @@
 }
 static method callGetter(self::Class* c) → dynamic {
   self::expect(0, c.{self::Class::getter1a}());
-  self::expect(0, c.{self::Class::getter1b}());
-  self::expect(42.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t31 = c in let final core::int* #t32 = self::read(42) in #t31.{self::Class::getter2}(#t32));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t33 = c in let final core::int* #t34 = self::read(12) in let final core::int* #t35 = self::read(23) in #t33.{self::Class::getter3}(#t34, #t35));
-  self::expect(12, let final self::Class* #t36 = c in let final core::int* #t37 = self::read(12) in #t36.{self::Class::getter4}(#t37));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t38 = c in let final core::int* #t39 = self::read(12) in let final core::int* #t40 = self::read(23) in #t38.{self::Class::getter4}(#t39, #t40));
-  self::expect(0, c.{self::Class::getter5}());
-  self::expect(12, let final self::Class* #t41 = c in let final core::int* #t42 = self::read(12) in #t41.{self::Class::getter5}(#t42));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t43 = c in let final core::int* #t44 = self::read(12) in let final core::int* #t45 = self::read(23) in #t43.{self::Class::getter5}(#t44, #t45));
-  self::expect(12, let final self::Class* #t46 = c in let final core::int* #t47 = self::read(12) in #t46.{self::Class::getter6}(#t47));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t48 = c in let final core::int* #t49 = self::read(12) in let final core::int* #t50 = self::read(23) in #t48.{self::Class::getter6}(#t49, b: #t50));
-  self::expect(0, c.{self::Class::getter7}());
-  self::expect(12, let final self::Class* #t51 = c in let final core::int* #t52 = self::read(12) in #t51.{self::Class::getter7}(a: #t52));
-  self::expect(23.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t53 = c in let final core::int* #t54 = self::read(23) in #t53.{self::Class::getter7}(b: #t54));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t55 = c in let final core::int* #t56 = self::read(12) in let final core::int* #t57 = self::read(23) in #t55.{self::Class::getter7}(a: #t56, b: #t57));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t58 = c in let final core::int* #t59 = self::read(23) in let final core::int* #t60 = self::read(12) in #t58.{self::Class::getter7}(b: #t59, a: #t60));
+  self::expect(0, c.{self::Class::getter1b}(){() →* core::int*});
+  self::expect(42.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t31 = c in let final core::int* #t32 = self::read(42) in #t31.{self::Class::getter2}(#t32){(core::int*) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t33 = c in let final core::int* #t34 = self::read(12) in let final core::int* #t35 = self::read(23) in #t33.{self::Class::getter3}(#t34, #t35){(core::int*, core::int*) →* core::int*});
+  self::expect(12, let final self::Class* #t36 = c in let final core::int* #t37 = self::read(12) in #t36.{self::Class::getter4}(#t37){(core::int*, [core::int*]) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t38 = c in let final core::int* #t39 = self::read(12) in let final core::int* #t40 = self::read(23) in #t38.{self::Class::getter4}(#t39, #t40){(core::int*, [core::int*]) →* core::int*});
+  self::expect(0, c.{self::Class::getter5}(){([core::int*, core::int*]) →* core::int*});
+  self::expect(12, let final self::Class* #t41 = c in let final core::int* #t42 = self::read(12) in #t41.{self::Class::getter5}(#t42){([core::int*, core::int*]) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t43 = c in let final core::int* #t44 = self::read(12) in let final core::int* #t45 = self::read(23) in #t43.{self::Class::getter5}(#t44, #t45){([core::int*, core::int*]) →* core::int*});
+  self::expect(12, let final self::Class* #t46 = c in let final core::int* #t47 = self::read(12) in #t46.{self::Class::getter6}(#t47){(core::int*, {b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t48 = c in let final core::int* #t49 = self::read(12) in let final core::int* #t50 = self::read(23) in #t48.{self::Class::getter6}(#t49, b: #t50){(core::int*, {b: core::int*}) →* core::int*});
+  self::expect(0, c.{self::Class::getter7}(){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(12, let final self::Class* #t51 = c in let final core::int* #t52 = self::read(12) in #t51.{self::Class::getter7}(a: #t52){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(23.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t53 = c in let final core::int* #t54 = self::read(23) in #t53.{self::Class::getter7}(b: #t54){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t55 = c in let final core::int* #t56 = self::read(12) in let final core::int* #t57 = self::read(23) in #t55.{self::Class::getter7}(a: #t56, b: #t57){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t58 = c in let final core::int* #t59 = self::read(23) in let final core::int* #t60 = self::read(12) in #t58.{self::Class::getter7}(b: #t59, a: #t60){({a: core::int*, b: core::int*}) →* core::int*});
 }
 static method expect(dynamic expected, dynamic actual) → dynamic {
   self::enableRead = true;
diff --git a/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.transformed.expect b/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.transformed.expect
index 25ceba5..3c6b74a 100644
--- a/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/implicit_getter_calls/getter_call.dart.weak.transformed.expect
@@ -153,21 +153,21 @@
 }
 static method callGetter(self::Class* c) → dynamic {
   self::expect(0, c.{self::Class::getter1a}());
-  self::expect(0, c.{self::Class::getter1b}());
-  self::expect(42.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t31 = c in let final core::int* #t32 = self::read(42) in #t31.{self::Class::getter2}(#t32));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t33 = c in let final core::int* #t34 = self::read(12) in let final core::int* #t35 = self::read(23) in #t33.{self::Class::getter3}(#t34, #t35));
-  self::expect(12, let final self::Class* #t36 = c in let final core::int* #t37 = self::read(12) in #t36.{self::Class::getter4}(#t37));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t38 = c in let final core::int* #t39 = self::read(12) in let final core::int* #t40 = self::read(23) in #t38.{self::Class::getter4}(#t39, #t40));
-  self::expect(0, c.{self::Class::getter5}());
-  self::expect(12, let final self::Class* #t41 = c in let final core::int* #t42 = self::read(12) in #t41.{self::Class::getter5}(#t42));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t43 = c in let final core::int* #t44 = self::read(12) in let final core::int* #t45 = self::read(23) in #t43.{self::Class::getter5}(#t44, #t45));
-  self::expect(12, let final self::Class* #t46 = c in let final core::int* #t47 = self::read(12) in #t46.{self::Class::getter6}(#t47));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t48 = c in let final core::int* #t49 = self::read(12) in let final core::int* #t50 = self::read(23) in #t48.{self::Class::getter6}(#t49, b: #t50));
-  self::expect(0, c.{self::Class::getter7}());
-  self::expect(12, let final self::Class* #t51 = c in let final core::int* #t52 = self::read(12) in #t51.{self::Class::getter7}(a: #t52));
-  self::expect(23.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t53 = c in let final core::int* #t54 = self::read(23) in #t53.{self::Class::getter7}(b: #t54));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t55 = c in let final core::int* #t56 = self::read(12) in let final core::int* #t57 = self::read(23) in #t55.{self::Class::getter7}(a: #t56, b: #t57));
-  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t58 = c in let final core::int* #t59 = self::read(23) in let final core::int* #t60 = self::read(12) in #t58.{self::Class::getter7}(b: #t59, a: #t60));
+  self::expect(0, c.{self::Class::getter1b}(){() →* core::int*});
+  self::expect(42.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t31 = c in let final core::int* #t32 = self::read(42) in #t31.{self::Class::getter2}(#t32){(core::int*) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t33 = c in let final core::int* #t34 = self::read(12) in let final core::int* #t35 = self::read(23) in #t33.{self::Class::getter3}(#t34, #t35){(core::int*, core::int*) →* core::int*});
+  self::expect(12, let final self::Class* #t36 = c in let final core::int* #t37 = self::read(12) in #t36.{self::Class::getter4}(#t37){(core::int*, [core::int*]) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t38 = c in let final core::int* #t39 = self::read(12) in let final core::int* #t40 = self::read(23) in #t38.{self::Class::getter4}(#t39, #t40){(core::int*, [core::int*]) →* core::int*});
+  self::expect(0, c.{self::Class::getter5}(){([core::int*, core::int*]) →* core::int*});
+  self::expect(12, let final self::Class* #t41 = c in let final core::int* #t42 = self::read(12) in #t41.{self::Class::getter5}(#t42){([core::int*, core::int*]) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t43 = c in let final core::int* #t44 = self::read(12) in let final core::int* #t45 = self::read(23) in #t43.{self::Class::getter5}(#t44, #t45){([core::int*, core::int*]) →* core::int*});
+  self::expect(12, let final self::Class* #t46 = c in let final core::int* #t47 = self::read(12) in #t46.{self::Class::getter6}(#t47){(core::int*, {b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t48 = c in let final core::int* #t49 = self::read(12) in let final core::int* #t50 = self::read(23) in #t48.{self::Class::getter6}(#t49, b: #t50){(core::int*, {b: core::int*}) →* core::int*});
+  self::expect(0, c.{self::Class::getter7}(){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(12, let final self::Class* #t51 = c in let final core::int* #t52 = self::read(12) in #t51.{self::Class::getter7}(a: #t52){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(23.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t53 = c in let final core::int* #t54 = self::read(23) in #t53.{self::Class::getter7}(b: #t54){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t55 = c in let final core::int* #t56 = self::read(12) in let final core::int* #t57 = self::read(23) in #t55.{self::Class::getter7}(a: #t56, b: #t57){({a: core::int*, b: core::int*}) →* core::int*});
+  self::expect(11.{core::int::unary-}(){() →* core::int*}, let final self::Class* #t58 = c in let final core::int* #t59 = self::read(23) in let final core::int* #t60 = self::read(12) in #t58.{self::Class::getter7}(b: #t59, a: #t60){({a: core::int*, b: core::int*}) →* core::int*});
 }
 static method expect(dynamic expected, dynamic actual) → dynamic {
   self::enableRead = true;
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 0b0de7e..3298c90 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -5253,7 +5253,7 @@
 
   int flags = 0;
 
-  /// The static type of the invocation.
+  /// The static type of the invocation, or `dynamic` is of the type is unknown.
   ///
   /// This includes substituted type parameters from the static receiver type
   /// and generic type arguments.
@@ -5261,19 +5261,21 @@
   /// For instance
   ///
   ///    class A<T> {
-  ///      Map<T, S> map<S>(S s) { ... }
+  ///      Map<T, S> Function<S>(S) get map => ...
+  ///      dynamic get dyn => ...
   ///    }
   ///    m(A<String> a) {
   ///      a.map(0); // The function type is `Map<String, int> Function(int)`.
+  ///      a.dyn(0); // The function type is `null`.
   ///    }
   ///
-  FunctionType functionType;
+  FunctionType? functionType;
 
   Reference interfaceTargetReference;
 
   InstanceGetterInvocation(InstanceAccessKind kind, Expression receiver,
       Name name, Arguments arguments,
-      {required Procedure interfaceTarget, required FunctionType functionType})
+      {required Member interfaceTarget, required FunctionType? functionType})
       : this.byReference(kind, receiver, name, arguments,
             interfaceTargetReference:
                 getNonNullableMemberReferenceGetter(interfaceTarget),
@@ -5284,14 +5286,12 @@
       {required this.interfaceTargetReference, required this.functionType})
       // ignore: unnecessary_null_comparison
       : assert(interfaceTargetReference != null),
-        // ignore: unnecessary_null_comparison
-        assert(functionType != null),
-        assert(functionType.typeParameters.isEmpty) {
+        assert(functionType == null || functionType.typeParameters.isEmpty) {
     receiver.parent = this;
     arguments.parent = this;
   }
 
-  Member get interfaceTarget => interfaceTargetReference.asProcedure;
+  Member get interfaceTarget => interfaceTargetReference.asMember;
 
   void set interfaceTarget(Member target) {
     interfaceTargetReference = getNonNullableMemberReferenceGetter(target);
@@ -5334,7 +5334,7 @@
 
   @override
   DartType getStaticTypeInternal(StaticTypeContext context) =>
-      functionType.returnType;
+      functionType?.returnType ?? const DynamicType();
 
   @override
   R accept<R>(ExpressionVisitor<R> v) => v.visitInstanceGetterInvocation(this);
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 15ebc86..2172314 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2132,10 +2132,18 @@
     InstanceAccessKind kind = InstanceAccessKind.values[readByte()];
     int flags = readByte();
     int offset = readOffset();
+    Expression receiver = readExpression();
+    Name name = readName();
+    Arguments arguments = readArguments();
+    DartType functionType = readDartType();
+    // `const DynamicType()` is used to encode a missing function type.
+    assert(functionType is FunctionType || functionType is DynamicType,
+        "Unexpected function type $functionType for InstanceGetterInvocation");
+    Reference interfaceTargetReference = readNonNullInstanceMemberReference();
     return new InstanceGetterInvocation.byReference(
-        kind, readExpression(), readName(), readArguments(),
-        functionType: readDartType() as FunctionType,
-        interfaceTargetReference: readNonNullInstanceMemberReference())
+        kind, receiver, name, arguments,
+        functionType: functionType is FunctionType ? functionType : null,
+        interfaceTargetReference: interfaceTargetReference)
       ..fileOffset = offset
       ..flags = flags;
   }
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 5b2eec7..c8c332e 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1653,7 +1653,8 @@
     writeNode(node.receiver);
     writeName(node.name);
     writeArgumentsNode(node.arguments);
-    writeDartType(node.functionType);
+    // `const DynamicType()` is used to encode a missing function type.
+    writeDartType(node.functionType ?? const DynamicType());
     writeNonNullInstanceMemberReference(node.interfaceTargetReference);
   }
 
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 930ff79..3f09922 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -695,7 +695,7 @@
   TreeNode visitInstanceGetterInvocation(InstanceGetterInvocation node) {
     return new InstanceGetterInvocation.byReference(
         node.kind, clone(node.receiver), node.name, clone(node.arguments),
-        functionType: visitType(node.functionType) as FunctionType,
+        functionType: visitOptionalType(node.functionType) as FunctionType,
         interfaceTargetReference: node.interfaceTargetReference);
   }
 
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 9482f51..0718f76 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -1498,6 +1498,19 @@
     writeSymbol('}');
   }
 
+  visitInstanceGetterInvocation(InstanceGetterInvocation node) {
+    writeExpression(node.receiver, Precedence.PRIMARY);
+    writeSymbol('.');
+    writeInterfaceTarget(node.name, node.interfaceTargetReference);
+    _writeInstanceAccessKind(node.kind);
+    writeNode(node.arguments);
+    if (node.functionType != null) {
+      writeSymbol('{');
+      writeType(node.functionType!);
+      writeSymbol('}');
+    }
+  }
+
   visitEqualsCall(EqualsCall node) {
     int precedence = Precedence.EQUALITY;
     writeExpression(node.left, precedence);
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index 0f936bf..86e8127 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -84,6 +84,8 @@
   String visitSuperPropertySet(SuperPropertySet _) => "set-super";
   String visitMethodInvocation(MethodInvocation _) => "invoke-method";
   String visitInstanceInvocation(InstanceInvocation _) => "invoke-instance";
+  String visitInstanceGetterInvocation(InstanceGetterInvocation _) =>
+      "invoke-instance-getter";
   String visitDynamicInvocation(DynamicInvocation _) => "invoke-dynamic";
   String visitFunctionInvocation(FunctionInvocation _) => "invoke-function";
   String visitLocalFunctionInvocation(LocalFunctionInvocation _) =>
@@ -656,6 +658,43 @@
       functionType: tuple.sixth as FunctionType);
 }
 
+TextSerializer<
+    InstanceGetterInvocation> instanceGetterInvocationSerializer = new Wrapped<
+        Tuple6<InstanceAccessKind, Expression, Name, Arguments, CanonicalName,
+            DartType?>,
+        InstanceGetterInvocation>(
+    unwrapInstanceGetterInvocation,
+    wrapInstanceGetterInvocation,
+    new Tuple6Serializer(
+        instanceAccessKindSerializer,
+        expressionSerializer,
+        nameSerializer,
+        argumentsSerializer,
+        const CanonicalNameSerializer(),
+        Optional(dartTypeSerializer)));
+
+Tuple6<InstanceAccessKind, Expression, Name, Arguments, CanonicalName,
+        DartType?>
+    unwrapInstanceGetterInvocation(InstanceGetterInvocation expression) {
+  return new Tuple6(
+      expression.kind,
+      expression.receiver,
+      expression.name,
+      expression.arguments,
+      expression.interfaceTargetReference.canonicalName!,
+      expression.functionType);
+}
+
+InstanceGetterInvocation wrapInstanceGetterInvocation(
+    Tuple6<InstanceAccessKind, Expression, Name, Arguments, CanonicalName,
+            DartType?>
+        tuple) {
+  return new InstanceGetterInvocation.byReference(
+      tuple.first, tuple.second, tuple.third, tuple.fourth,
+      interfaceTargetReference: tuple.fifth.reference,
+      functionType: tuple.sixth as FunctionType?);
+}
+
 const Map<DynamicAccessKind, String> dynamicAccessKindToName = const {
   DynamicAccessKind.Dynamic: "dynamic",
   DynamicAccessKind.Never: "never",
@@ -2513,6 +2552,7 @@
     "set-super": superPropertySetSerializer,
     "invoke-method": methodInvocationSerializer,
     "invoke-instance": instanceInvocationSerializer,
+    "invoke-instance-getter": instanceGetterInvocationSerializer,
     "invoke-dynamic": dynamicInvocationSerializer,
     "invoke-function": functionInvocationSerializer,
     "invoke-local-function": localFunctionInvocationSerializer,