[vm/bytecode] Check number of arguments in closures without optional arguments

This CL adds EntryFixed bytecode instruction in bytecode generator and
interpreter. The new instruction is used instead of Entry in prologues of
closures with fixed arguments in order to check number of arguments.

Fixes language_2/closure_call_wrong_argument_count_test in bytecode mode.

Change-Id: I873166a73c5f33e7faca8739cd6f210370e81b2e
Reviewed-on: https://dart-review.googlesource.com/71231
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index bc3f8ac..0df6742 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -944,6 +944,10 @@
     emitWord(_encode0(Opcode.kDeoptRewind));
   }
 
+  void emitEntryFixed(int ra, int rd) {
+    emitWord(_encodeAD(Opcode.kEntryFixed, ra, rd));
+  }
+
   void emitEntryOptional(int ra, int rb, int rc) {
     emitWord(_encodeABC(Opcode.kEntryOptional, ra, rb, rc));
   }
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 5ca0253..0d8f197 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -37,6 +37,9 @@
 // 10. InstanceCall1 and InstanceCall2 instructions are superseded by
 //     InstanceCall which works for any number of checked arguments.
 //
+// 11. EntryFixed instruction works like Entry. In addition, it checks number
+//     of fixed arguments.
+//
 
 enum Opcode {
   kTrap,
@@ -210,6 +213,7 @@
   kBooleanNegate,
   kThrow,
   kEntry,
+  kEntryFixed,
   kEntryOptional,
   kEntryOptimized,
   kFrame,
@@ -614,6 +618,10 @@
       Encoding.kA, const [Operand.imm, Operand.none, Operand.none]),
   Opcode.kEntry: const Format(
       Encoding.kD, const [Operand.imm, Operand.none, Operand.none]),
+  Opcode.kEntryFixed: const Format(
+      Encoding.kAD, const [Operand.imm, Operand.imm, Operand.none]),
+  Opcode.kEntryOptional: const Format(
+      Encoding.kABC, const [Operand.imm, Operand.imm, Operand.imm]),
   Opcode.kEntryOptimized: const Format(
       Encoding.kAD, const [Operand.imm, Operand.imm, Operand.none]),
   Opcode.kFrame: const Format(
@@ -674,8 +682,6 @@
       Encoding.kAD, const [Operand.imm, Operand.imm, Operand.none]),
   Opcode.kDeoptRewind: const Format(
       Encoding.k0, const [Operand.none, Operand.none, Operand.none]),
-  Opcode.kEntryOptional: const Format(
-      Encoding.kABC, const [Operand.imm, Operand.imm, Operand.imm]),
 };
 
 // Should match constant in runtime/vm/stack_frame_dbc.h.
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 932217b..6346b7b 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -716,6 +716,9 @@
   }
 
   void _genPrologue(Node node, FunctionNode function) {
+    final bool isClosure =
+        node is FunctionDeclaration || node is FunctionExpression;
+
     if (locals.hasOptionalParameters) {
       final int numOptionalPositional = function.positionalParameters.length -
           function.requiredParameterCount;
@@ -743,14 +746,13 @@
       }
 
       asm.emitFrame(locals.frameSize - locals.numParameters);
+    } else if (isClosure) {
+      asm.emitEntryFixed(locals.numParameters, locals.frameSize);
     } else {
       asm.emitEntry(locals.frameSize);
     }
     asm.emitCheckStack();
 
-    final bool isClosure =
-        node is FunctionDeclaration || node is FunctionExpression;
-
     if (isClosure) {
       asm.emitPush(locals.closureVarIndexInFrame);
       asm.emitLoadFieldTOS(cp.add(new ConstantInstanceField(closureContext)));
diff --git a/pkg/vm/testcases/bytecode/async.dart.expect b/pkg/vm/testcases/bytecode/async.dart.expect
index 79176de..45fdde0 100644
--- a/pkg/vm/testcases/bytecode/async.dart.expect
+++ b/pkg/vm/testcases/bytecode/async.dart.expect
@@ -158,7 +158,7 @@
 }
 
 Closure CP#0 {
-  Entry                4
+  EntryFixed           2, 4
   CheckStack
   Push                 FP[-6]
   LoadFieldTOS         CP#1
@@ -2121,7 +2121,7 @@
 }
 
 Closure CP#1 {
-  Entry                4
+  EntryFixed           1, 4
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#2
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index 7959152..6912332 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -293,7 +293,7 @@
   [57] = ICData target-name 'call', arg-desc CP#49
 }
 Closure CP#12 {
-  Entry                4
+  EntryFixed           1, 4
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#1
@@ -378,7 +378,7 @@
 }
 
 Closure CP#9 {
-  Entry                5
+  EntryFixed           1, 5
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#1
@@ -421,7 +421,7 @@
 }
 
 Closure CP#0 {
-  Entry                5
+  EntryFixed           1, 5
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#1
@@ -638,7 +638,7 @@
   [46] = ICData target-name 'call', arg-desc CP#18
 }
 Closure CP#16 {
-  Entry                3
+  EntryFixed           1, 3
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#4
@@ -666,7 +666,7 @@
 }
 
 Closure CP#3 {
-  Entry                4
+  EntryFixed           2, 4
   CheckStack
   Push                 FP[-6]
   LoadFieldTOS         CP#4
@@ -739,7 +739,7 @@
 }
 
 Closure CP#43 {
-  Entry                3
+  EntryFixed           1, 3
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#4
@@ -954,7 +954,7 @@
   [33] = ICData target-name '+', arg-desc CP#2
 }
 Closure CP#8 {
-  Entry                2
+  EntryFixed           1, 2
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#9
@@ -972,7 +972,7 @@
 }
 
 Closure CP#25 {
-  Entry                3
+  EntryFixed           2, 3
   CheckStack
   Push                 FP[-6]
   LoadFieldTOS         CP#9
@@ -1098,7 +1098,7 @@
   [27] = StaticICData target 'dart.core::print', arg-desc CP#4
 }
 Closure CP#9 {
-  Entry                3
+  EntryFixed           1, 3
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#10
@@ -1204,7 +1204,7 @@
   [18] = Reserved
 }
 Closure CP#5 {
-  Entry                2
+  EntryFixed           1, 2
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#6
@@ -1286,7 +1286,7 @@
   [22] = ICData target-name 'call', arg-desc CP#8
 }
 Closure CP#1 {
-  Entry                3
+  EntryFixed           2, 3
   CheckStack
   Push                 FP[-6]
   LoadFieldTOS         CP#2
@@ -1549,7 +1549,7 @@
   [22] = TypeArgs [dart.core::int]
 }
 Closure CP#0 {
-  Entry                3
+  EntryFixed           2, 3
   CheckStack
   Push                 FP[-6]
   LoadFieldTOS         CP#1
diff --git a/pkg/vm/testcases/bytecode/try_blocks.dart.expect b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
index 1917977..696f42d 100644
--- a/pkg/vm/testcases/bytecode/try_blocks.dart.expect
+++ b/pkg/vm/testcases/bytecode/try_blocks.dart.expect
@@ -405,7 +405,7 @@
   [42] = EndClosureFunctionScope
 }
 Closure CP#2 {
-  Entry                6
+  EntryFixed           1, 6
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#3
@@ -443,7 +443,7 @@
 }
 
 Closure CP#31 {
-  Entry                6
+  EntryFixed           1, 6
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#3
@@ -934,7 +934,7 @@
   [42] = StaticICData target 'dart.core::print', arg-desc CP#7
 }
 Closure CP#12 {
-  Entry                2
+  EntryFixed           1, 2
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#13
@@ -1103,7 +1103,7 @@
   [39] = ICData target-name 'call', arg-desc CP#5
 }
 Closure CP#2 {
-  Entry                6
+  EntryFixed           1, 6
   CheckStack
   Push                 FP[-5]
   LoadFieldTOS         CP#3
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 9beae96..b850f5c 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -563,6 +563,13 @@
 //    Function prologue for the function
 //        rD - number of local slots to reserve;
 //
+//  - EntryFixed A, D
+//
+//    Function prologue for functions without optional arguments.
+//    Checks number of arguments.
+//        A - expected number of positional arguments;
+//        D - number of local slots to reserve;
+//
 //  - EntryOptional A, B, C
 //
 //    Function prologue for the function with optional or named arguments:
@@ -969,6 +976,7 @@
   V(BooleanNegate,                       A_D, reg, reg, ___)                   \
   V(Throw,                                 A, num, ___, ___)                   \
   V(Entry,                                 D, num, ___, ___)                   \
+  V(EntryFixed,                          A_D, num, num, ___)                   \
   V(EntryOptional,                     A_B_C, num, num, num)                   \
   V(EntryOptimized,                      A_D, num, num, ___)                   \
   V(Frame,                                 D, num, ___, ___)                   \
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index 1c2c099..90a6f16 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -1841,6 +1841,26 @@
   }
 
   {
+    BYTECODE(EntryFixed, A_D);
+    const uint16_t num_fixed_params = rA;
+    const uint16_t num_locals = rD;
+
+    const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
+    const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
+    if ((arg_count != num_fixed_params) || (pos_count != num_fixed_params)) {
+      goto ClosureNoSuchMethod;
+    }
+
+    // Initialize locals with null & set SP.
+    for (intptr_t i = 0; i < num_locals; i++) {
+      FP[i] = null_value;
+    }
+    SP = FP + num_locals - 1;
+
+    DISPATCH();
+  }
+
+  {
     BYTECODE(EntryOptional, A_B_C);
     const uint16_t num_fixed_params = rA;
     const uint16_t num_opt_pos_params = rB;