aboutsummaryrefslogtreecommitdiff
path: root/smali/src/main/antlr/smaliTreeWalker.g
blob: 171756ec29342dcf91ff72f8c663532a9855d11d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
/*
 * [The "BSD licence"]
 * Copyright (c) 2010 Ben Gruver
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

tree grammar smaliTreeWalker;

options {
  tokenVocab=smaliParser;
  ASTLabelType=CommonTree;
}

@header {
package org.jf.smali;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.antlr.runtime.BitSet;
import org.antlr.runtime.*;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.TreeNodeStream;
import org.antlr.runtime.tree.TreeParser;
import org.antlr.runtime.tree.TreeRuleReturnScope;
import org.jf.dexlib2.*;
import org.jf.dexlib2.builder.Label;
import org.jf.dexlib2.builder.MethodImplementationBuilder;
import org.jf.dexlib2.builder.SwitchLabelElement;
import org.jf.dexlib2.builder.instruction.*;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.AnnotationElement;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.MethodImplementation;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.dexlib2.immutable.ImmutableAnnotation;
import org.jf.dexlib2.immutable.ImmutableAnnotationElement;
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
import org.jf.dexlib2.immutable.reference.ImmutableMethodProtoReference;
import org.jf.dexlib2.immutable.reference.ImmutableReference;
import org.jf.dexlib2.immutable.reference.ImmutableTypeReference;
import org.jf.dexlib2.immutable.value.*;
import org.jf.dexlib2.util.MethodUtil;
import org.jf.dexlib2.writer.InstructionFactory;
import org.jf.dexlib2.writer.builder.*;
import org.jf.util.LinearSearch;

import java.util.*;
}

@members {
  public String classType;
  private boolean verboseErrors = false;
  private int apiLevel = 15;
  private Opcodes opcodes = Opcodes.forApi(apiLevel);
  private DexBuilder dexBuilder;

  public void setDexBuilder(DexBuilder dexBuilder) {
      this.dexBuilder = dexBuilder;
  }

  public void setApiLevel(int apiLevel) {
      this.opcodes = Opcodes.forApi(apiLevel);
      this.apiLevel = apiLevel;
  }

  public void setVerboseErrors(boolean verboseErrors) {
    this.verboseErrors = verboseErrors;
  }

  private byte parseRegister_nibble(String register)
      throws SemanticException {
    int totalMethodRegisters = method_stack.peek().totalMethodRegisters;
    int methodParameterRegisters = method_stack.peek().methodParameterRegisters;

    //register should be in the format "v12"
    int val = Byte.parseByte(register.substring(1));
    if (register.charAt(0) == 'p') {
      val = totalMethodRegisters - methodParameterRegisters + val;
    }
    if (val >= 2<<4) {
      throw new SemanticException(input, "The maximum allowed register in this context is list of registers is v15");
    }
    //the parser wouldn't have accepted a negative register, i.e. v-1, so we don't have to check for val<0;
    return (byte)val;
  }

  //return a short, because java's byte is signed
  private short parseRegister_byte(String register)
      throws SemanticException {
    int totalMethodRegisters = method_stack.peek().totalMethodRegisters;
    int methodParameterRegisters = method_stack.peek().methodParameterRegisters;
    //register should be in the format "v123"
    int val = Short.parseShort(register.substring(1));
    if (register.charAt(0) == 'p') {
      val = totalMethodRegisters - methodParameterRegisters + val;
    }
    if (val >= 2<<8) {
      throw new SemanticException(input, "The maximum allowed register in this context is v255");
    }
    return (short)val;
  }

  //return an int because java's short is signed
  private int parseRegister_short(String register)
      throws SemanticException {
    int totalMethodRegisters = method_stack.peek().totalMethodRegisters;
    int methodParameterRegisters = method_stack.peek().methodParameterRegisters;
    //register should be in the format "v12345"
    int val = Integer.parseInt(register.substring(1));
    if (register.charAt(0) == 'p') {
      val = totalMethodRegisters - methodParameterRegisters + val;
    }
    if (val >= 2<<16) {
      throw new SemanticException(input, "The maximum allowed register in this context is v65535");
    }
    //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0;
    return val;
  }

  public String getErrorMessage(RecognitionException e, String[] tokenNames) {
    if ( e instanceof SemanticException ) {
      return e.getMessage();
    } else {
      return super.getErrorMessage(e, tokenNames);
    }
  }

  public String getErrorHeader(RecognitionException e) {
    return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]";
  }
}

smali_file returns[ClassDef classDef]
  : ^(I_CLASS_DEF header methods fields annotations)
  {
    $classDef = dexBuilder.internClassDef($header.classType, $header.accessFlags, $header.superType,
            $header.implementsList, $header.sourceSpec, $annotations.annotations, $fields.fields, $methods.methods);
  };
  catch [Exception ex] {
    if (verboseErrors) {
      ex.printStackTrace(System.err);
    }
    reportError(new SemanticException(input, ex));
  }


header returns[String classType, int accessFlags, String superType, List<String> implementsList, String sourceSpec]
: class_spec super_spec? implements_list source_spec
  {
    classType = $class_spec.type;
    $classType = classType;
    $accessFlags = $class_spec.accessFlags;
    $superType = $super_spec.type;
    $implementsList = $implements_list.implementsList;
    $sourceSpec = $source_spec.source;
  };


class_spec returns[String type, int accessFlags]
  : CLASS_DESCRIPTOR access_list
  {
    $type = $CLASS_DESCRIPTOR.text;
    $accessFlags = $access_list.value;
  };

super_spec returns[String type]
  : ^(I_SUPER CLASS_DESCRIPTOR)
  {
    $type = $CLASS_DESCRIPTOR.text;
  };


implements_spec returns[String type]
  : ^(I_IMPLEMENTS CLASS_DESCRIPTOR)
  {
    $type = $CLASS_DESCRIPTOR.text;
  };

implements_list returns[List<String> implementsList]
@init { List<String> typeList; }
  : {typeList = Lists.newArrayList();}
    (implements_spec {typeList.add($implements_spec.type);} )*
  {
    if (typeList.size() > 0) {
      $implementsList = typeList;
    } else {
      $implementsList = null;
    }
  };

source_spec returns[String source]
  : {$source = null;}
    ^(I_SOURCE string_literal {$source = $string_literal.value;})
  | /*epsilon*/;

access_list returns [int value]
  @init
  {
    $value = 0;
  }
  : ^(I_ACCESS_LIST
      (
        ACCESS_SPEC
        {
          $value |= AccessFlags.getAccessFlag($ACCESS_SPEC.getText()).getValue();
        }
      )*);


fields returns[List<BuilderField> fields]
  @init {$fields = Lists.newArrayList();}
  : ^(I_FIELDS
      (field
      {
        $fields.add($field.field);
      })*);

methods returns[List<BuilderMethod> methods]
  @init {$methods = Lists.newArrayList();}
  : ^(I_METHODS
      (method
      {
        $methods.add($method.ret);
      })*);

field returns [BuilderField field]
  :^(I_FIELD SIMPLE_NAME access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?)
  {
    int accessFlags = $access_list.value;


    if (!AccessFlags.STATIC.isSet(accessFlags) && $field_initial_value.encodedValue != null) {
        throw new SemanticException(input, "Initial field values can only be specified for static fields.");
    }

    $field = dexBuilder.internField(classType, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type, $access_list.value,
            $field_initial_value.encodedValue, $annotations.annotations);
  };


field_initial_value returns[EncodedValue encodedValue]
  : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;}
  | /*epsilon*/;

literal returns[EncodedValue encodedValue]
  : integer_literal { $encodedValue = new ImmutableIntEncodedValue($integer_literal.value); }
  | long_literal { $encodedValue = new ImmutableLongEncodedValue($long_literal.value); }
  | short_literal { $encodedValue = new ImmutableShortEncodedValue($short_literal.value); }
  | byte_literal { $encodedValue = new ImmutableByteEncodedValue($byte_literal.value); }
  | float_literal { $encodedValue = new ImmutableFloatEncodedValue($float_literal.value); }
  | double_literal { $encodedValue = new ImmutableDoubleEncodedValue($double_literal.value); }
  | char_literal { $encodedValue = new ImmutableCharEncodedValue($char_literal.value); }
  | string_literal { $encodedValue = new ImmutableStringEncodedValue($string_literal.value); }
  | bool_literal { $encodedValue = ImmutableBooleanEncodedValue.forBoolean($bool_literal.value); }
  | NULL_LITERAL { $encodedValue = ImmutableNullEncodedValue.INSTANCE; }
  | type_descriptor { $encodedValue = new ImmutableTypeEncodedValue($type_descriptor.type); }
  | array_literal { $encodedValue = new ImmutableArrayEncodedValue($array_literal.elements); }
  | subannotation { $encodedValue = new ImmutableAnnotationEncodedValue($subannotation.annotationType, $subannotation.elements); }
  | field_literal { $encodedValue = new ImmutableFieldEncodedValue($field_literal.value); }
  | method_literal { $encodedValue = new ImmutableMethodEncodedValue($method_literal.value); }
  | enum_literal { $encodedValue = new ImmutableEnumEncodedValue($enum_literal.value); };

//everything but string
fixed_64bit_literal_number returns[Number value]
  : integer_literal { $value = $integer_literal.value; }
  | long_literal { $value = $long_literal.value; }
  | short_literal { $value = $short_literal.value; }
  | byte_literal { $value = $byte_literal.value; }
  | float_literal { $value = Float.floatToRawIntBits($float_literal.value); }
  | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); }
  | char_literal { $value = (int)$char_literal.value; }
  | bool_literal { $value = $bool_literal.value?1:0; };

fixed_64bit_literal returns[long value]
  : integer_literal { $value = $integer_literal.value; }
  | long_literal { $value = $long_literal.value; }
  | short_literal { $value = $short_literal.value; }
  | byte_literal { $value = $byte_literal.value; }
  | float_literal { $value = Float.floatToRawIntBits($float_literal.value); }
  | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); }
  | char_literal { $value = $char_literal.value; }
  | bool_literal { $value = $bool_literal.value?1:0; };

//everything but string and double
//long is allowed, but it must fit into an int
fixed_32bit_literal returns[int value]
  : integer_literal { $value = $integer_literal.value; }
  | long_literal { LiteralTools.checkInt($long_literal.value); $value = (int)$long_literal.value; }
  | short_literal { $value = $short_literal.value; }
  | byte_literal { $value = $byte_literal.value; }
  | float_literal { $value = Float.floatToRawIntBits($float_literal.value); }
  | char_literal { $value = $char_literal.value; }
  | bool_literal { $value = $bool_literal.value?1:0; };

array_elements returns[List<Number> elements]
  : {$elements = Lists.newArrayList();}
    ^(I_ARRAY_ELEMENTS
      (fixed_64bit_literal_number
      {
        $elements.add($fixed_64bit_literal_number.value);
      })*);

packed_switch_elements returns[List<Label> elements]
  @init {$elements = Lists.newArrayList();}
  :
    ^(I_PACKED_SWITCH_ELEMENTS
      (label_ref { $elements.add($label_ref.label); })*
    );

sparse_switch_elements returns[List<SwitchLabelElement> elements]
  @init {$elements = Lists.newArrayList();}
  :
    ^(I_SPARSE_SWITCH_ELEMENTS
       (fixed_32bit_literal label_ref
       {
         $elements.add(new SwitchLabelElement($fixed_32bit_literal.value, $label_ref.label));
       })*
    );

method returns[BuilderMethod ret]
  scope
  {
    boolean isStatic;
    int totalMethodRegisters;
    int methodParameterRegisters;
    MethodImplementationBuilder methodBuilder;
  }
  @init
  {
    $method::totalMethodRegisters = 0;
    $method::methodParameterRegisters = 0;
    int accessFlags = 0;
    $method::isStatic = false;
  }
  :
    ^(I_METHOD
      method_name_and_prototype
      access_list
      {
        accessFlags = $access_list.value;
        $method::isStatic = AccessFlags.STATIC.isSet(accessFlags);
        $method::methodParameterRegisters =
                MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic);
      }
      (
        (registers_directive
        {
          if ($registers_directive.isLocalsDirective) {
            $method::totalMethodRegisters = $registers_directive.registers + $method::methodParameterRegisters;
          } else {
            $method::totalMethodRegisters = $registers_directive.registers;
          }

          $method::methodBuilder = new MethodImplementationBuilder($method::totalMethodRegisters);

        })
        |
        /* epsilon */
        {
          $method::methodBuilder = new MethodImplementationBuilder(0);
        }
      )
      ordered_method_items
      catches
      parameters[$method_name_and_prototype.parameters]
      annotations
    )
  {
    MethodImplementation methodImplementation = null;
    List<BuilderTryBlock> tryBlocks = $catches.tryBlocks;

    boolean isAbstract = false;
    boolean isNative = false;

    if ((accessFlags & AccessFlags.ABSTRACT.getValue()) != 0) {
      isAbstract = true;
    } else if ((accessFlags & AccessFlags.NATIVE.getValue()) != 0) {
      isNative = true;
    }

    methodImplementation = $method::methodBuilder.getMethodImplementation();

    if (Iterables.isEmpty(methodImplementation.getInstructions())) {
      if (!isAbstract && !isNative) {
        throw new SemanticException(input, $I_METHOD, "A non-abstract/non-native method must have at least 1 instruction");
      }

      String methodType;
      if (isAbstract) {
        methodType = "an abstract";
      } else {
        methodType = "a native";
      }

      if ($registers_directive.start != null) {
        if ($registers_directive.isLocalsDirective) {
          throw new SemanticException(input, $registers_directive.start, "A .locals directive is not valid in \%s method", methodType);
        } else {
          throw new SemanticException(input, $registers_directive.start, "A .registers directive is not valid in \%s method", methodType);
        }
      }

      if (methodImplementation.getTryBlocks().size() > 0) {
        throw new SemanticException(input, $I_METHOD, "try/catch blocks cannot be present in \%s method", methodType);
      }

      if (!Iterables.isEmpty(methodImplementation.getDebugItems())) {
        throw new SemanticException(input, $I_METHOD, "debug directives cannot be present in \%s method", methodType);
      }

      methodImplementation = null;
    } else {
      if (isAbstract) {
        throw new SemanticException(input, $I_METHOD, "An abstract method cannot have any instructions");
      }
      if (isNative) {
        throw new SemanticException(input, $I_METHOD, "A native method cannot have any instructions");
      }

      if ($registers_directive.start == null) {
        throw new SemanticException(input, $I_METHOD, "A .registers or .locals directive must be present for a non-abstract/non-final method");
      }

      if ($method::totalMethodRegisters < $method::methodParameterRegisters) {
        throw new SemanticException(input, $registers_directive.start, "This method requires at least " +
                Integer.toString($method::methodParameterRegisters) +
                " registers, for the method parameters");
      }
    }

    $ret = dexBuilder.internMethod(
            classType,
            $method_name_and_prototype.name,
            $method_name_and_prototype.parameters,
            $method_name_and_prototype.returnType,
            accessFlags,
            $annotations.annotations,
            methodImplementation);
  };

method_prototype returns[List<String> parameters, String returnType]
  : ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) method_type_list)
  {
    $returnType = $type_descriptor.type;
    $parameters = $method_type_list.types;
  };

method_name_and_prototype returns[String name, List<SmaliMethodParameter> parameters, String returnType]
  : SIMPLE_NAME method_prototype
  {
    $name = $SIMPLE_NAME.text;
    $parameters = Lists.newArrayList();

    int paramRegister = 0;
    for (String type: $method_prototype.parameters) {
        $parameters.add(new SmaliMethodParameter(paramRegister++, type));
        char c = type.charAt(0);
        if (c == 'D' || c == 'J') {
            paramRegister++;
        }
    }
    $returnType = $method_prototype.returnType;
  };

method_type_list returns[List<String> types]
  @init
  {
    $types = Lists.newArrayList();
  }
  : (
      nonvoid_type_descriptor
      {
        $types.add($nonvoid_type_descriptor.type);
      }
    )*;


method_reference returns[ImmutableMethodReference methodReference]
  : reference_type_descriptor? SIMPLE_NAME method_prototype
  {
    String type;
    if ($reference_type_descriptor.type == null) {
        type = classType;
    } else {
        type = $reference_type_descriptor.type;
    }
    $methodReference = new ImmutableMethodReference(type, $SIMPLE_NAME.text,
             $method_prototype.parameters, $method_prototype.returnType);
  };

field_reference returns[ImmutableFieldReference fieldReference]
  : reference_type_descriptor? SIMPLE_NAME nonvoid_type_descriptor
  {
    String type;
    if ($reference_type_descriptor.type == null) {
        type = classType;
    } else {
        type = $reference_type_descriptor.type;
    }
    $fieldReference = new ImmutableFieldReference(type, $SIMPLE_NAME.text,
            $nonvoid_type_descriptor.type);
  };

registers_directive returns[boolean isLocalsDirective, int registers]
  : {$registers = 0;}
    ^(( I_REGISTERS {$isLocalsDirective = false;}
      | I_LOCALS {$isLocalsDirective = true;}
      )
      short_integral_literal {$registers = $short_integral_literal.value & 0xFFFF;}
     );

label_def
  : ^(I_LABEL SIMPLE_NAME)
  {
    $method::methodBuilder.addLabel($SIMPLE_NAME.text);
  };

catches returns[List<BuilderTryBlock> tryBlocks]
  @init {tryBlocks = Lists.newArrayList();}
  : ^(I_CATCHES catch_directive* catchall_directive*);

catch_directive
  : ^(I_CATCH nonvoid_type_descriptor from=label_ref to=label_ref using=label_ref)
  {
    $method::methodBuilder.addCatch(dexBuilder.internTypeReference($nonvoid_type_descriptor.type),
        $from.label, $to.label, $using.label);
  };

catchall_directive
  : ^(I_CATCHALL from=label_ref to=label_ref using=label_ref)
  {
    $method::methodBuilder.addCatch($from.label, $to.label, $using.label);
  };

parameters[List<SmaliMethodParameter> parameters]
  : ^(I_PARAMETERS (parameter[parameters])*);

parameter[List<SmaliMethodParameter> parameters]
  : ^(I_PARAMETER REGISTER string_literal? annotations)
    {
        final int registerNumber = parseRegister_short($REGISTER.text);
        int totalMethodRegisters = $method::totalMethodRegisters;
        int methodParameterRegisters = $method::methodParameterRegisters;

        if (registerNumber >= totalMethodRegisters) {
            throw new SemanticException(input, $I_PARAMETER, "Register \%s is larger than the maximum register v\%d " +
                    "for this method", $REGISTER.text, totalMethodRegisters-1);
        }
        final int indexGuess = registerNumber - (totalMethodRegisters - methodParameterRegisters) - ($method::isStatic?0:1);

        if (indexGuess < 0) {
            throw new SemanticException(input, $I_PARAMETER, "Register \%s is not a parameter register.",
                    $REGISTER.text);
        }

        int parameterIndex = LinearSearch.linearSearch(parameters, SmaliMethodParameter.COMPARATOR,
            new WithRegister() { public int getRegister() { return indexGuess; } },
                indexGuess);

        if (parameterIndex < 0) {
            throw new SemanticException(input, $I_PARAMETER, "Register \%s is the second half of a wide parameter.",
                                $REGISTER.text);
        }

        SmaliMethodParameter methodParameter = parameters.get(parameterIndex);
        methodParameter.name = $string_literal.value;
        if ($annotations.annotations != null && $annotations.annotations.size() > 0) {
            methodParameter.annotations = $annotations.annotations;
        }
    };

debug_directive
  : line
  | local
  | end_local
  | restart_local
  | prologue
  | epilogue
  | source;

line
  : ^(I_LINE integral_literal)
    {
        $method::methodBuilder.addLineNumber($integral_literal.value);
    };

local
  : ^(I_LOCAL REGISTER ((NULL_LITERAL | name=string_literal) nonvoid_type_descriptor? signature=string_literal?)?)
    {
      int registerNumber = parseRegister_short($REGISTER.text);
      $method::methodBuilder.addStartLocal(registerNumber,
              dexBuilder.internNullableStringReference($name.value),
              dexBuilder.internNullableTypeReference($nonvoid_type_descriptor.type),
              dexBuilder.internNullableStringReference($signature.value));
    };

end_local
  : ^(I_END_LOCAL REGISTER)
    {
      int registerNumber = parseRegister_short($REGISTER.text);
      $method::methodBuilder.addEndLocal(registerNumber);
    };

restart_local
  : ^(I_RESTART_LOCAL REGISTER)
    {
      int registerNumber = parseRegister_short($REGISTER.text);
      $method::methodBuilder.addRestartLocal(registerNumber);
    };

prologue
  : I_PROLOGUE
    {
      $method::methodBuilder.addPrologue();
    };

epilogue
  : I_EPILOGUE
    {
      $method::methodBuilder.addEpilogue();
    };

source
  : ^(I_SOURCE string_literal?)
    {
      $method::methodBuilder.addSetSourceFile(dexBuilder.internNullableStringReference($string_literal.value));
    };

ordered_method_items
  : ^(I_ORDERED_METHOD_ITEMS (label_def | instruction | debug_directive)*);

label_ref returns[Label label]
  : SIMPLE_NAME { $label = $method::methodBuilder.getLabel($SIMPLE_NAME.text); };

register_list returns[byte[\] registers, byte registerCount]
  @init
  {
    $registers = new byte[5];
    $registerCount = 0;
  }
  : ^(I_REGISTER_LIST
      (REGISTER
      {
        if ($registerCount == 5) {
          throw new SemanticException(input, $I_REGISTER_LIST, "A list of registers can only have a maximum of 5 " +
                  "registers. Use the <op>/range alternate opcode instead.");
        }
        $registers[$registerCount++] = parseRegister_nibble($REGISTER.text);
      })*);

register_range returns[int startRegister, int endRegister]
  : ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?)
    {
        if ($startReg == null) {
            $startRegister = 0;
            $endRegister = -1;
        } else {
                $startRegister  = parseRegister_short($startReg.text);
                if ($endReg == null) {
                    $endRegister = $startRegister;
                } else {
                    $endRegister = parseRegister_short($endReg.text);
                }

                int registerCount = $endRegister-$startRegister+1;
                if (registerCount < 1) {
                    throw new SemanticException(input, $I_REGISTER_RANGE, "A register range must have the lower register listed first");
                }
            }
    };

verification_error_reference returns[ImmutableReference reference]
  : CLASS_DESCRIPTOR
  {
    $reference = new ImmutableTypeReference($CLASS_DESCRIPTOR.text);
  }
  | field_reference
  {
    $reference = $field_reference.fieldReference;
  }
  | method_reference
  {
    $reference = $method_reference.methodReference;
  };

verification_error_type returns[int verificationError]
  : VERIFICATION_ERROR_TYPE
  {
    $verificationError = VerificationError.getVerificationError($VERIFICATION_ERROR_TYPE.text);
  };

instruction
  : insn_format10t
  | insn_format10x
  | insn_format11n
  | insn_format11x
  | insn_format12x
  | insn_format20bc
  | insn_format20t
  | insn_format21c_field
  | insn_format21c_string
  | insn_format21c_type
  | insn_format21ih
  | insn_format21lh
  | insn_format21s
  | insn_format21t
  | insn_format22b
  | insn_format22c_field
  | insn_format22c_type
  | insn_format22s
  | insn_format22t
  | insn_format22x
  | insn_format23x
  | insn_format30t
  | insn_format31c
  | insn_format31i
  | insn_format31t
  | insn_format32x
  | insn_format35c_method
  | insn_format35c_type
  | insn_format3rc_method
  | insn_format3rc_type
  | insn_format45cc_method
  | insn_format4rcc_method
  | insn_format51l_type
  | insn_array_data_directive
  | insn_packed_switch_directive
  | insn_sparse_switch_directive;
  catch [Exception ex] {
    reportError(new SemanticException(input, $start, ex.getMessage()));
    recover(input, null);
  }

insn_format10t
  : //e.g. goto endloop:
    ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT10t.text);
      $method::methodBuilder.addInstruction(new BuilderInstruction10t(opcode, $label_ref.label));
    };

insn_format10x
  : //e.g. return
    ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT10x.text);
      $method::methodBuilder.addInstruction(new BuilderInstruction10x(opcode));
    };

insn_format11n
  : //e.g. const/4 v0, 5
    ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT11n.text);
      byte regA = parseRegister_nibble($REGISTER.text);

      short litB = $short_integral_literal.value;
      LiteralTools.checkNibble(litB);

      $method::methodBuilder.addInstruction(new BuilderInstruction11n(opcode, regA, litB));
    };

insn_format11x
  : //e.g. move-result-object v1
    ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT11x.text);
      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction11x(opcode, regA));
    };

insn_format12x
  : //e.g. move v1 v2
    ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT12x.text);
      byte regA = parseRegister_nibble($registerA.text);
      byte regB = parseRegister_nibble($registerB.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction12x(opcode, regA, regB));
    };

insn_format20bc
  : //e.g. throw-verification-error generic-error, Lsome/class;
    ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc verification_error_type verification_error_reference)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT20bc.text);

      int verificationError = $verification_error_type.verificationError;
      ImmutableReference referencedItem = $verification_error_reference.reference;

      $method::methodBuilder.addInstruction(new BuilderInstruction20bc(opcode, verificationError,
              dexBuilder.internReference(referencedItem)));
    };

insn_format20t
  : //e.g. goto/16 endloop:
    ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT20t.text);
      $method::methodBuilder.addInstruction(new BuilderInstruction20t(opcode, $label_ref.label));
    };

insn_format21c_field
  : //e.g. sget_object v0, java/lang/System/out LJava/io/PrintStream;
    ^(I_STATEMENT_FORMAT21c_FIELD inst=(INSTRUCTION_FORMAT21c_FIELD | INSTRUCTION_FORMAT21c_FIELD_ODEX) REGISTER field_reference)
    {
      Opcode opcode = opcodes.getOpcodeByName($inst.text);
      short regA = parseRegister_byte($REGISTER.text);

      ImmutableFieldReference fieldReference = $field_reference.fieldReference;

      $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
              dexBuilder.internFieldReference(fieldReference)));
    };

insn_format21c_string
  : //e.g. const-string v1, "Hello World!"
    ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text);
      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
              dexBuilder.internStringReference($string_literal.value)));
    };

insn_format21c_type
  : //e.g. const-class v2, org/jf/HelloWorld2/HelloWorld2
    ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text);
      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
              dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
    };

insn_format21ih
  : //e.g. const/high16 v1, 1234
    ^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21ih.text);
      short regA = parseRegister_byte($REGISTER.text);

      int litB = $fixed_32bit_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction21ih(opcode, regA, litB));
    };

insn_format21lh
  : //e.g. const-wide/high16 v1, 1234
    ^(I_STATEMENT_FORMAT21lh INSTRUCTION_FORMAT21lh REGISTER fixed_64bit_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21lh.text);
      short regA = parseRegister_byte($REGISTER.text);

      long litB = $fixed_64bit_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction21lh(opcode, regA, litB));
    };

insn_format21s
  : //e.g. const/16 v1, 1234
    ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21s.text);
      short regA = parseRegister_byte($REGISTER.text);

      short litB = $short_integral_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction21s(opcode, regA, litB));
    };

insn_format21t
  : //e.g. if-eqz v0, endloop:
    ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21t.text);
      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction21t(opcode, regA, $label_ref.label));
    };

insn_format22b
  : //e.g. add-int v0, v1, 123
    ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22b.text);
      short regA = parseRegister_byte($registerA.text);
      short regB = parseRegister_byte($registerB.text);

      short litC = $short_integral_literal.value;
      LiteralTools.checkByte(litC);

      $method::methodBuilder.addInstruction(new BuilderInstruction22b(opcode, regA, regB, litC));
    };

insn_format22c_field
  : //e.g. iput-object v1, v0, org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
    ^(I_STATEMENT_FORMAT22c_FIELD inst=(INSTRUCTION_FORMAT22c_FIELD | INSTRUCTION_FORMAT22c_FIELD_ODEX) registerA=REGISTER registerB=REGISTER field_reference)
    {
      Opcode opcode = opcodes.getOpcodeByName($inst.text);
      byte regA = parseRegister_nibble($registerA.text);
      byte regB = parseRegister_nibble($registerB.text);

      ImmutableFieldReference fieldReference = $field_reference.fieldReference;

      $method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB,
              dexBuilder.internFieldReference(fieldReference)));
    };

insn_format22c_type
  : //e.g. instance-of v0, v1, Ljava/lang/String;
    ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER nonvoid_type_descriptor)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_TYPE.text);
      byte regA = parseRegister_nibble($registerA.text);
      byte regB = parseRegister_nibble($registerB.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB,
              dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
    };

insn_format22s
  : //e.g. add-int/lit16 v0, v1, 12345
    ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22s.text);
      byte regA = parseRegister_nibble($registerA.text);
      byte regB = parseRegister_nibble($registerB.text);

      short litC = $short_integral_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction22s(opcode, regA, regB, litC));
    };

insn_format22t
  : //e.g. if-eq v0, v1, endloop:
    ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22t.text);
      byte regA = parseRegister_nibble($registerA.text);
      byte regB = parseRegister_nibble($registerB.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction22t(opcode, regA, regB, $label_ref.label));
    };

insn_format22x
  : //e.g. move/from16 v1, v1234
    ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22x.text);
      short regA = parseRegister_byte($registerA.text);
      int regB = parseRegister_short($registerB.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction22x(opcode, regA, regB));
    };

insn_format23x
  : //e.g. add-int v1, v2, v3
    ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT23x.text);
      short regA = parseRegister_byte($registerA.text);
      short regB = parseRegister_byte($registerB.text);
      short regC = parseRegister_byte($registerC.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction23x(opcode, regA, regB, regC));
    };

insn_format30t
  : //e.g. goto/32 endloop:
    ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT30t.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction30t(opcode, $label_ref.label));
    };

insn_format31c
  : //e.g. const-string/jumbo v1 "Hello World!"
    ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31c.text);
      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction31c(opcode, regA,
              dexBuilder.internStringReference($string_literal.value)));
    };

insn_format31i
  : //e.g. const v0, 123456
    ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31i.text);
      short regA = parseRegister_byte($REGISTER.text);

      int litB = $fixed_32bit_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction31i(opcode, regA, litB));
    };

insn_format31t
  : //e.g. fill-array-data v0, ArrayData:
    ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER label_ref)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31t.text);

      short regA = parseRegister_byte($REGISTER.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction31t(opcode, regA, $label_ref.label));
    };

insn_format32x
  : //e.g. move/16 v5678, v1234
    ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT32x.text);
      int regA = parseRegister_short($registerA.text);
      int regB = parseRegister_short($registerB.text);

      $method::methodBuilder.addInstruction(new BuilderInstruction32x(opcode, regA, regB));
    };

insn_format35c_method
  : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V
    ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list method_reference)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT35c_METHOD.text);

      //this depends on the fact that register_list returns a byte[5]
      byte[] registers = $register_list.registers;
      byte registerCount = $register_list.registerCount;

      ImmutableMethodReference methodReference = $method_reference.methodReference;

      $method::methodBuilder.addInstruction(new BuilderInstruction35c(opcode, registerCount, registers[0], registers[1],
              registers[2], registers[3], registers[4], dexBuilder.internMethodReference(methodReference)));
    };

insn_format35c_type
  : //e.g. filled-new-array {v0,v1}, I
    ^(I_STATEMENT_FORMAT35c_TYPE INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT35c_TYPE.text);

      //this depends on the fact that register_list returns a byte[5]
      byte[] registers = $register_list.registers;
      byte registerCount = $register_list.registerCount;

      $method::methodBuilder.addInstruction(new BuilderInstruction35c(opcode, registerCount, registers[0], registers[1],
              registers[2], registers[3], registers[4], dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
    };

insn_format3rc_method
  : //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range method_reference)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT3rc_METHOD.text);
      int startRegister = $register_range.startRegister;
      int endRegister = $register_range.endRegister;

      int registerCount = endRegister-startRegister+1;

      ImmutableMethodReference methodReference = $method_reference.methodReference;

      $method::methodBuilder.addInstruction(new BuilderInstruction3rc(opcode, startRegister, registerCount,
              dexBuilder.internMethodReference(methodReference)));
    };

insn_format3rc_type
  : //e.g. filled-new-array/range {v0..v6} I
    ^(I_STATEMENT_FORMAT3rc_TYPE INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT3rc_TYPE.text);
      int startRegister = $register_range.startRegister;
      int endRegister = $register_range.endRegister;

      int registerCount = endRegister-startRegister+1;

      $method::methodBuilder.addInstruction(new BuilderInstruction3rc(opcode, startRegister, registerCount,
              dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
    };

insn_format45cc_method
  : //e.g. invoke-polymorphic {v0, v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J
    ^(I_STATEMENT_FORMAT45cc_METHOD INSTRUCTION_FORMAT45cc_METHOD register_list method_reference method_prototype)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT45cc_METHOD.text);

      //this depends on the fact that register_list returns a byte[5]
      byte[] registers = $register_list.registers;
      byte registerCount = $register_list.registerCount;

      ImmutableMethodReference methodReference = $method_reference.methodReference;
      ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference(
              $method_prototype.parameters,
              $method_prototype.returnType);

      $method::methodBuilder.addInstruction(new BuilderInstruction45cc(opcode, registerCount, registers[0], registers[1],
              registers[2], registers[3], registers[4],
              dexBuilder.internMethodReference(methodReference),
              dexBuilder.internMethodProtoReference(methodProtoReference)));
    };

insn_format4rcc_method
  : //e.g. invoke-polymorphic {v0..v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J
    ^(I_STATEMENT_FORMAT4rcc_METHOD INSTRUCTION_FORMAT4rcc_METHOD register_range method_reference method_prototype)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT4rcc_METHOD.text);
      int startRegister = $register_range.startRegister;
      int endRegister = $register_range.endRegister;

      int registerCount = endRegister-startRegister+1;

      ImmutableMethodReference methodReference = $method_reference.methodReference;
      ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference(
              $method_prototype.parameters,
              $method_prototype.returnType);

      $method::methodBuilder.addInstruction(new BuilderInstruction4rcc(opcode, startRegister, registerCount,
              dexBuilder.internMethodReference(methodReference),
              dexBuilder.internMethodProtoReference(methodProtoReference)));
    };

insn_format51l_type
  : //e.g. const-wide v0, 5000000000L
    ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal)
    {
      Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT51l.text);
      short regA = parseRegister_byte($REGISTER.text);

      long litB = $fixed_64bit_literal.value;

      $method::methodBuilder.addInstruction(new BuilderInstruction51l(opcode, regA, litB));
    };

insn_array_data_directive
  : //e.g. .array-data 4 1000000 .end array-data
    ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements)
    {
      int elementWidth = $short_integral_literal.value;
      List<Number> elements = $array_elements.elements;

      $method::methodBuilder.addInstruction(new BuilderArrayPayload(elementWidth, $array_elements.elements));
    };

insn_packed_switch_directive
  :
    ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_elements)
      {
        int startKey = $fixed_32bit_literal.value;
        $method::methodBuilder.addInstruction(new BuilderPackedSwitchPayload(startKey,
            $packed_switch_elements.elements));
      };

insn_sparse_switch_directive
  :
    ^(I_STATEMENT_SPARSE_SWITCH sparse_switch_elements)
    {
      $method::methodBuilder.addInstruction(new BuilderSparseSwitchPayload($sparse_switch_elements.elements));
    };

array_descriptor returns [String type]
  : ARRAY_TYPE_PREFIX ( PRIMITIVE_TYPE { $type = $ARRAY_TYPE_PREFIX.text + $PRIMITIVE_TYPE.text; }
                      | CLASS_DESCRIPTOR { $type = $ARRAY_TYPE_PREFIX.text + $CLASS_DESCRIPTOR.text; });

nonvoid_type_descriptor returns [String type]
  : (PRIMITIVE_TYPE { $type = $text; }
  | CLASS_DESCRIPTOR { $type = $text; }
  | array_descriptor { $type = $array_descriptor.type; })
  ;

reference_type_descriptor returns [String type]
  : (CLASS_DESCRIPTOR { $type = $text; }
  | array_descriptor { $type = $array_descriptor.type; })
  ;

type_descriptor returns [String type]
  : VOID_TYPE {$type = "V";}
  | nonvoid_type_descriptor {$type = $nonvoid_type_descriptor.type;}
  ;

short_integral_literal returns[short value]
  : long_literal
    {
      LiteralTools.checkShort($long_literal.value);
      $value = (short)$long_literal.value;
    }
  | integer_literal
    {
      LiteralTools.checkShort($integer_literal.value);
      $value = (short)$integer_literal.value;
    }
  | short_literal {$value = $short_literal.value;}
  | char_literal {$value = (short)$char_literal.value;}
  | byte_literal {$value = $byte_literal.value;};

integral_literal returns[int value]
  : long_literal
    {
      LiteralTools.checkInt($long_literal.value);
      $value = (int)$long_literal.value;
    }
  | integer_literal {$value = $integer_literal.value;}
  | short_literal {$value = $short_literal.value;}
  | byte_literal {$value = $byte_literal.value;};


integer_literal returns[int value]
  : INTEGER_LITERAL { $value = LiteralTools.parseInt($INTEGER_LITERAL.text); };

long_literal returns[long value]
  : LONG_LITERAL { $value = LiteralTools.parseLong($LONG_LITERAL.text); };

short_literal returns[short value]
  : SHORT_LITERAL { $value = LiteralTools.parseShort($SHORT_LITERAL.text); };

byte_literal returns[byte value]
  : BYTE_LITERAL { $value = LiteralTools.parseByte($BYTE_LITERAL.text); };

float_literal returns[float value]
  : FLOAT_LITERAL { $value = LiteralTools.parseFloat($FLOAT_LITERAL.text); };

double_literal returns[double value]
  : DOUBLE_LITERAL { $value = LiteralTools.parseDouble($DOUBLE_LITERAL.text); };

char_literal returns[char value]
  : CHAR_LITERAL { $value = $CHAR_LITERAL.text.charAt(1); };

string_literal returns[String value]
  : STRING_LITERAL
    {
      $value = $STRING_LITERAL.text;
      $value = $value.substring(1,$value.length()-1);
    };

bool_literal returns[boolean value]
  : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); };

array_literal returns[List<EncodedValue> elements]
  : {$elements = Lists.newArrayList();}
    ^(I_ENCODED_ARRAY (literal {$elements.add($literal.encodedValue);})*);

annotations returns[Set<Annotation> annotations]
  : {HashMap<String, Annotation> annotationMap = Maps.newHashMap();}
    ^(I_ANNOTATIONS (annotation
    {
        Annotation anno = $annotation.annotation;
        Annotation old = annotationMap.put(anno.getType(), anno);
        if (old != null) {
            throw new SemanticException(input, "Multiple annotations of type \%s", anno.getType());
        }
    })*)
    {
      if (annotationMap.size() > 0) {
        $annotations = ImmutableSet.copyOf(annotationMap.values());
      }
    };

annotation returns[Annotation annotation]
  : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation)
    {
      int visibility = AnnotationVisibility.getVisibility($ANNOTATION_VISIBILITY.text);
      $annotation = new ImmutableAnnotation(visibility, $subannotation.annotationType, $subannotation.elements);
    };

annotation_element returns[AnnotationElement element]
  : ^(I_ANNOTATION_ELEMENT SIMPLE_NAME literal)
    {
      $element = new ImmutableAnnotationElement($SIMPLE_NAME.text, $literal.encodedValue);
    };

subannotation returns[String annotationType, List<AnnotationElement> elements]
  : {ArrayList<AnnotationElement> elements = Lists.newArrayList();}
    ^(I_SUBANNOTATION
        CLASS_DESCRIPTOR
        (annotation_element
        {
           elements.add($annotation_element.element);
        })*
     )
    {
      $annotationType = $CLASS_DESCRIPTOR.text;
      $elements = elements;
    };

field_literal returns[FieldReference value]
  : ^(I_ENCODED_FIELD field_reference)
    {
      $value = $field_reference.fieldReference;
    };

method_literal returns[MethodReference value]
  : ^(I_ENCODED_METHOD method_reference)
    {
      $value = $method_reference.methodReference;
    };

enum_literal returns[FieldReference value]
  : ^(I_ENCODED_ENUM field_reference)
    {
      $value = $field_reference.fieldReference;
    };