[vm/bytecode] Add a compile-time error if there are too many arguments.

Issue: https://github.com/dart-lang/sdk/issues/37305
Change-Id: I9dac7c1b7b49fca8531ca2bf83b1c309d8217969
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106984
Reviewed-by: Aart Bik <ajcbik@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 77ea7b9..649e318 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -34,6 +34,8 @@
 export '../fasta/fasta_codes.dart'
     show
         LocatedMessage,
+        messageBytecodeLimitExceededTooManyArguments,
+        noLength,
         templateFfiFieldAnnotation,
         templateFfiStructAnnotation,
         templateFfiNotStatic,
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index b25813b..de913de 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -461,6 +461,15 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeBytecodeLimitExceededTooManyArguments =
+    messageBytecodeLimitExceededTooManyArguments;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageBytecodeLimitExceededTooManyArguments =
+    const MessageCode("BytecodeLimitExceededTooManyArguments",
+        message: r"""Dart bytecode limit exceeded: too many arguments.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeCandidateFound = messageCandidateFound;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 8f99626..0ea1a63 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -28,6 +28,8 @@
 BadTypeVariableInSupertype/analyzerCode: Fail
 BuiltInIdentifierAsType/example: Fail
 BuiltInIdentifierInDeclaration/example: Fail
+BytecodeLimitExceededTooManyArguments/analyzerCode: Fail
+BytecodeLimitExceededTooManyArguments/example: Fail
 CannotAssignToParenthesizedExpression/example: Fail
 CannotAssignToSuper/example: Fail
 CannotReadPackagesFile/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index d2b0dac..d94388d 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3566,3 +3566,6 @@
   script: >
     class Base<T> {}
     class Derived<T> extends Base<Derived<Derived<T>>> {}
+
+BytecodeLimitExceededTooManyArguments:
+  template: "Dart bytecode limit exceeded: too many arguments."
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 2c9f9c1..45afa97 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -645,6 +645,9 @@
 // Context IDs are referenced using 8-bit unsigned operands.
 const int contextIdLimit = 1 << 8;
 
+// Number of arguments is encoded as 8-bit unsigned operand.
+const int argumentsLimit = 1 << 8;
+
 // Base class for exceptions thrown when certain limit of bytecode
 // format is exceeded.
 abstract class BytecodeLimitExceededException {}
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 343915a..74f7ded 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -9,7 +9,12 @@
 import 'package:front_end/src/api_prototype/constant_evaluator.dart'
     show ConstantEvaluator, EvaluationEnvironment, ErrorReporter;
 import 'package:front_end/src/api_unstable/vm.dart'
-    show CompilerContext, Severity, templateIllegalRecursiveType;
+    show
+        CompilerContext,
+        Severity,
+        messageBytecodeLimitExceededTooManyArguments,
+        noLength,
+        templateIllegalRecursiveType;
 
 import 'package:kernel/ast.dart' hide MapEntry, Component, FunctionDeclaration;
 import 'package:kernel/ast.dart' as ast show Component, FunctionDeclaration;
@@ -691,9 +696,11 @@
         throw 'Unexpected member ${node.runtimeType} $node';
       }
       end(node, hasCode);
-    } on BytecodeLimitExceededException {
-      // Do not generate bytecode and fall back to using kernel AST.
-      // TODO(alexmarkov): issue compile-time error
+    } on TooManyArgumentsException catch (e) {
+      CompilerContext.current.options.report(
+          messageBytecodeLimitExceededTooManyArguments.withLocation(
+              node.fileUri, e.fileOffset, noLength),
+          Severity.error);
       hasErrors = true;
       end(node, false);
     }
@@ -970,18 +977,21 @@
   }
 
   void _genDirectCall(Member target, ObjectHandle argDesc, int totalArgCount,
-      {bool isGet: false, bool isSet: false}) {
+      {bool isGet: false, bool isSet: false, TreeNode context}) {
     assert(!isGet || !isSet);
     final kind = isGet
         ? InvocationKind.getter
         : (isSet ? InvocationKind.setter : InvocationKind.method);
     final cpIndex = cp.addDirectCall(kind, target, argDesc);
 
+    if (totalArgCount >= argumentsLimit) {
+      throw new TooManyArgumentsException(context.fileOffset);
+    }
     asm.emitDirectCall(cpIndex, totalArgCount);
   }
 
   void _genDirectCallWithArgs(Member target, Arguments args,
-      {bool hasReceiver: false, bool isFactory: false}) {
+      {bool hasReceiver: false, bool isFactory: false, TreeNode context}) {
     final argDesc = objectTable.getArgDescHandleByArguments(args,
         hasReceiver: hasReceiver, isFactory: isFactory);
 
@@ -995,7 +1005,7 @@
       totalArgCount++;
     }
 
-    _genDirectCall(target, argDesc, totalArgCount);
+    _genDirectCall(target, argDesc, totalArgCount, context: context);
   }
 
   void _genTypeArguments(List<DartType> typeArgs, {Class instantiatingClass}) {
@@ -2359,7 +2369,7 @@
         new Arguments(node.arguments.positional, named: node.arguments.named)
           ..parent = node;
     _genArguments(null, args);
-    _genDirectCallWithArgs(node.target, args, hasReceiver: true);
+    _genDirectCallWithArgs(node.target, args, hasReceiver: true, context: node);
     asm.emitDrop1();
   }
 
@@ -2369,7 +2379,7 @@
     _genArguments(node.receiver, args);
     final target = node.target;
     if (target is Procedure && !target.isGetter && !target.isSetter) {
-      _genDirectCallWithArgs(target, args, hasReceiver: true);
+      _genDirectCallWithArgs(target, args, hasReceiver: true, context: node);
     } else {
       throw new UnsupportedOperationError(
           'Unsupported DirectMethodInvocation with target ${target.runtimeType} $target');
@@ -2624,7 +2634,11 @@
   }
 
   void _genInstanceCall(
-      int totalArgCount, int callCpIndex, bool isDynamic, bool isUnchecked) {
+      int totalArgCount, int callCpIndex, bool isDynamic, bool isUnchecked,
+      [TreeNode context]) {
+    if (totalArgCount >= argumentsLimit) {
+      throw new TooManyArgumentsException(context.fileOffset);
+    }
     if (isDynamic) {
       assert(!isUnchecked);
       asm.emitDynamicCall(callCpIndex, totalArgCount);
@@ -2662,7 +2676,7 @@
         args.named.length +
         1 /* receiver */ +
         (args.types.isNotEmpty ? 1 : 0) /* type arguments */;
-    _genInstanceCall(totalArgCount, callCpIndex, isDynamic, isUnchecked);
+    _genInstanceCall(totalArgCount, callCpIndex, isDynamic, isUnchecked, node);
   }
 
   @override
@@ -2719,7 +2733,7 @@
       return;
     }
     _genArguments(new ThisExpression(), args);
-    _genDirectCallWithArgs(target, args, hasReceiver: true);
+    _genDirectCallWithArgs(target, args, hasReceiver: true, context: node);
   }
 
   @override
@@ -2872,7 +2886,8 @@
             ..parent = node;
     }
     _genArguments(null, args);
-    _genDirectCallWithArgs(target, args, isFactory: target.isFactory);
+    _genDirectCallWithArgs(target, args,
+        isFactory: target.isFactory, context: node);
   }
 
   @override
@@ -3700,7 +3715,7 @@
     final args = node.arguments;
     assert(args.types.isEmpty);
     _genArguments(new ThisExpression(), args);
-    _genDirectCallWithArgs(node.target, args, hasReceiver: true);
+    _genDirectCallWithArgs(node.target, args, hasReceiver: true, context: node);
     asm.emitDrop1();
   }
 
@@ -3718,7 +3733,7 @@
       }
     }
     assert(target != null);
-    _genDirectCallWithArgs(target, args, hasReceiver: true);
+    _genDirectCallWithArgs(target, args, hasReceiver: true, context: node);
     asm.emitDrop1();
   }
 
@@ -3746,6 +3761,11 @@
   String toString() => message;
 }
 
+class TooManyArgumentsException extends BytecodeLimitExceededException {
+  final int fileOffset;
+  TooManyArgumentsException(this.fileOffset);
+}
+
 typedef void GenerateContinuation();
 
 class FinallyBlock {