diff options
Diffstat (limited to 'smali/src/main/antlr')
-rw-r--r-- | smali/src/main/antlr/smaliParser.g | 1109 | ||||
-rw-r--r-- | smali/src/main/antlr/smaliTreeWalker.g | 1289 |
2 files changed, 2398 insertions, 0 deletions
diff --git a/smali/src/main/antlr/smaliParser.g b/smali/src/main/antlr/smaliParser.g new file mode 100644 index 00000000..d057b4a4 --- /dev/null +++ b/smali/src/main/antlr/smaliParser.g @@ -0,0 +1,1109 @@ +/* + * [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. + */ + +parser grammar smaliParser; + +options { + output=AST; + ASTLabelType=CommonTree; +} + +tokens { + //Lexer tokens + ACCESS_SPEC; + ANNOTATION_DIRECTIVE; + ANNOTATION_VISIBILITY; + ARRAY_DATA_DIRECTIVE; + ARRAY_DESCRIPTOR; + ARROW; + BOOL_LITERAL; + BYTE_LITERAL; + CATCH_DIRECTIVE; + CATCHALL_DIRECTIVE; + CHAR_LITERAL; + CLASS_DESCRIPTOR; + CLASS_DIRECTIVE; + CLOSE_BRACE; + CLOSE_PAREN; + COLON; + COMMA; + DOTDOT; + DOUBLE_LITERAL; + DOUBLE_LITERAL_OR_ID; + END_ANNOTATION_DIRECTIVE; + END_ARRAY_DATA_DIRECTIVE; + END_FIELD_DIRECTIVE; + END_LOCAL_DIRECTIVE; + END_METHOD_DIRECTIVE; + END_PACKED_SWITCH_DIRECTIVE; + END_PARAMETER_DIRECTIVE; + END_SPARSE_SWITCH_DIRECTIVE; + END_SUBANNOTATION_DIRECTIVE; + ENUM_DIRECTIVE; + EPILOGUE_DIRECTIVE; + EQUAL; + FIELD_DIRECTIVE; + FIELD_OFFSET; + FLOAT_LITERAL; + FLOAT_LITERAL_OR_ID; + IMPLEMENTS_DIRECTIVE; + INLINE_INDEX; + INSTRUCTION_FORMAT10t; + INSTRUCTION_FORMAT10x; + INSTRUCTION_FORMAT10x_ODEX; + INSTRUCTION_FORMAT11n; + INSTRUCTION_FORMAT11x; + INSTRUCTION_FORMAT12x; + INSTRUCTION_FORMAT12x_OR_ID; + INSTRUCTION_FORMAT20bc; + INSTRUCTION_FORMAT20t; + INSTRUCTION_FORMAT21c_FIELD; + INSTRUCTION_FORMAT21c_FIELD_ODEX; + INSTRUCTION_FORMAT21c_STRING; + INSTRUCTION_FORMAT21c_TYPE; + INSTRUCTION_FORMAT21ih; + INSTRUCTION_FORMAT21lh; + INSTRUCTION_FORMAT21s; + INSTRUCTION_FORMAT21t; + INSTRUCTION_FORMAT22b; + INSTRUCTION_FORMAT22c_FIELD; + INSTRUCTION_FORMAT22c_FIELD_ODEX; + INSTRUCTION_FORMAT22c_TYPE; + INSTRUCTION_FORMAT22cs_FIELD; + INSTRUCTION_FORMAT22s; + INSTRUCTION_FORMAT22s_OR_ID; + INSTRUCTION_FORMAT22t; + INSTRUCTION_FORMAT22x; + INSTRUCTION_FORMAT23x; + INSTRUCTION_FORMAT30t; + INSTRUCTION_FORMAT31c; + INSTRUCTION_FORMAT31i; + INSTRUCTION_FORMAT31i_OR_ID; + INSTRUCTION_FORMAT31t; + INSTRUCTION_FORMAT32x; + INSTRUCTION_FORMAT35c_METHOD; + INSTRUCTION_FORMAT35c_METHOD_ODEX; + INSTRUCTION_FORMAT35c_TYPE; + INSTRUCTION_FORMAT35mi_METHOD; + INSTRUCTION_FORMAT35ms_METHOD; + INSTRUCTION_FORMAT3rc_METHOD; + INSTRUCTION_FORMAT3rc_METHOD_ODEX; + INSTRUCTION_FORMAT3rc_TYPE; + INSTRUCTION_FORMAT3rmi_METHOD; + INSTRUCTION_FORMAT3rms_METHOD; + INSTRUCTION_FORMAT51l; + LINE_COMMENT; + LINE_DIRECTIVE; + LOCAL_DIRECTIVE; + LOCALS_DIRECTIVE; + LONG_LITERAL; + METHOD_DIRECTIVE; + MEMBER_NAME; + NEGATIVE_INTEGER_LITERAL; + NULL_LITERAL; + OPEN_BRACE; + OPEN_PAREN; + PACKED_SWITCH_DIRECTIVE; + PARAM_LIST_END; + PARAM_LIST_START; + PARAM_LIST_OR_ID_END; + PARAM_LIST_OR_ID_START; + PARAMETER_DIRECTIVE; + POSITIVE_INTEGER_LITERAL; + PRIMITIVE_TYPE; + PROLOGUE_DIRECTIVE; + REGISTER; + REGISTERS_DIRECTIVE; + RESTART_LOCAL_DIRECTIVE; + SHORT_LITERAL; + SIMPLE_NAME; + SOURCE_DIRECTIVE; + SPARSE_SWITCH_DIRECTIVE; + STRING_LITERAL; + SUBANNOTATION_DIRECTIVE; + SUPER_DIRECTIVE; + VERIFICATION_ERROR_TYPE; + VOID_TYPE; + VTABLE_INDEX; + WHITE_SPACE; + + // misc non-lexer tokens + INTEGER_LITERAL; + INVALID_TOKEN; + + //I_* tokens are imaginary tokens used as parent AST nodes + I_CLASS_DEF; + I_SUPER; + I_IMPLEMENTS; + I_SOURCE; + I_ACCESS_LIST; + I_METHODS; + I_FIELDS; + I_FIELD; + I_FIELD_TYPE; + I_FIELD_INITIAL_VALUE; + I_METHOD; + I_METHOD_PROTOTYPE; + I_METHOD_RETURN_TYPE; + I_REGISTERS; + I_LOCALS; + I_LABEL; + I_ANNOTATIONS; + I_ANNOTATION; + I_ANNOTATION_ELEMENT; + I_SUBANNOTATION; + I_ENCODED_FIELD; + I_ENCODED_METHOD; + I_ENCODED_ENUM; + I_ENCODED_ARRAY; + I_ARRAY_ELEMENT_SIZE; + I_ARRAY_ELEMENTS; + I_PACKED_SWITCH_START_KEY; + I_PACKED_SWITCH_ELEMENTS; + I_SPARSE_SWITCH_ELEMENTS; + I_CATCH; + I_CATCHALL; + I_CATCHES; + I_PARAMETER; + I_PARAMETERS; + I_PARAMETER_NOT_SPECIFIED; + I_LINE; + I_LOCAL; + I_END_LOCAL; + I_RESTART_LOCAL; + I_PROLOGUE; + I_EPILOGUE; + I_ORDERED_METHOD_ITEMS; + I_STATEMENT_FORMAT10t; + I_STATEMENT_FORMAT10x; + I_STATEMENT_FORMAT11n; + I_STATEMENT_FORMAT11x; + I_STATEMENT_FORMAT12x; + I_STATEMENT_FORMAT20bc; + I_STATEMENT_FORMAT20t; + I_STATEMENT_FORMAT21c_TYPE; + I_STATEMENT_FORMAT21c_FIELD; + I_STATEMENT_FORMAT21c_STRING; + I_STATEMENT_FORMAT21ih; + I_STATEMENT_FORMAT21lh; + I_STATEMENT_FORMAT21s; + I_STATEMENT_FORMAT21t; + I_STATEMENT_FORMAT22b; + I_STATEMENT_FORMAT22c_FIELD; + I_STATEMENT_FORMAT22c_TYPE; + I_STATEMENT_FORMAT22s; + I_STATEMENT_FORMAT22t; + I_STATEMENT_FORMAT22x; + I_STATEMENT_FORMAT23x; + I_STATEMENT_FORMAT30t; + I_STATEMENT_FORMAT31c; + I_STATEMENT_FORMAT31i; + I_STATEMENT_FORMAT31t; + I_STATEMENT_FORMAT32x; + I_STATEMENT_FORMAT35c_METHOD; + I_STATEMENT_FORMAT35c_TYPE; + I_STATEMENT_FORMAT3rc_METHOD; + I_STATEMENT_FORMAT3rc_TYPE; + I_STATEMENT_FORMAT51l; + I_STATEMENT_ARRAY_DATA; + I_STATEMENT_PACKED_SWITCH; + I_STATEMENT_SPARSE_SWITCH; + I_REGISTER_RANGE; + I_REGISTER_LIST; +} + +@header { +package org.jf.smali; + +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.Opcodes; +} + + +@members { + public static final int ERROR_CHANNEL = 100; + + private boolean verboseErrors = false; + private boolean allowOdex = false; + private int apiLevel = 15; + private Opcodes opcodes = new Opcodes(apiLevel); + + public void setVerboseErrors(boolean verboseErrors) { + this.verboseErrors = verboseErrors; + } + + public void setAllowOdex(boolean allowOdex) { + this.allowOdex = allowOdex; + } + + public void setApiLevel(int apiLevel) { + this.opcodes = new Opcodes(apiLevel); + this.apiLevel = apiLevel; + } + + public String getErrorMessage(RecognitionException e, + String[] tokenNames) { + + if (verboseErrors) { + List stack = getRuleInvocationStack(e, this.getClass().getName()); + String msg = null; + + if (e instanceof NoViableAltException) { + NoViableAltException nvae = (NoViableAltException)e; + msg = " no viable alt; token="+getTokenErrorDisplay(e.token)+ + " (decision="+nvae.decisionNumber+ + " state "+nvae.stateNumber+")"+ + " decision=<<"+nvae.grammarDecisionDescription+">>"; + } else { + msg = super.getErrorMessage(e, tokenNames); + } + + return stack + " " + msg; + } else { + return super.getErrorMessage(e, tokenNames); + } + } + + public String getTokenErrorDisplay(Token t) { + if (!verboseErrors) { + String s = t.getText(); + if ( s==null ) { + if ( t.getType()==Token.EOF ) { + s = "<EOF>"; + } + else { + s = "<"+tokenNames[t.getType()]+">"; + } + } + s = s.replaceAll("\n","\\\\n"); + s = s.replaceAll("\r","\\\\r"); + s = s.replaceAll("\t","\\\\t"); + return "'"+s+"'"; + } + + CommonToken ct = (CommonToken)t; + + String channelStr = ""; + if (t.getChannel()>0) { + channelStr=",channel="+t.getChannel(); + } + String txt = t.getText(); + if ( txt!=null ) { + txt = txt.replaceAll("\n","\\\\n"); + txt = txt.replaceAll("\r","\\\\r"); + txt = txt.replaceAll("\t","\\\\t"); + } + else { + txt = "<no text>"; + } + return "[@"+t.getTokenIndex()+","+ct.getStartIndex()+":"+ct.getStopIndex()+"='"+txt+"',<"+tokenNames[t.getType()]+">"+channelStr+","+t.getLine()+":"+t.getCharPositionInLine()+"]"; + } + + public String getErrorHeader(RecognitionException e) { + return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; + } + + private CommonTree buildTree(int type, String text, List<CommonTree> children) { + CommonTree root = new CommonTree(new CommonToken(type, text)); + for (CommonTree child: children) { + root.addChild(child); + } + return root; + } + + private CommonToken getParamListSubToken(CommonToken baseToken, String str, int typeStartIndex) { + CommonToken token = new CommonToken(baseToken); + token.setStartIndex(baseToken.getStartIndex() + typeStartIndex); + + switch (str.charAt(typeStartIndex)) { + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + case 'J': + case 'F': + case 'D': + { + token.setType(PRIMITIVE_TYPE); + token.setText(str.substring(typeStartIndex, typeStartIndex+1)); + token.setStopIndex(baseToken.getStartIndex() + typeStartIndex); + break; + } + case 'L': + { + int i = typeStartIndex; + while (str.charAt(++i) != ';'); + + token.setType(CLASS_DESCRIPTOR); + token.setText(str.substring(typeStartIndex, i + 1)); + token.setStopIndex(baseToken.getStartIndex() + i); + break; + } + case '[': + { + int i = typeStartIndex; + while (str.charAt(++i) == '['); + + if (str.charAt(i++) == 'L') { + while (str.charAt(i++) != ';'); + } + + token.setType(ARRAY_DESCRIPTOR); + token.setText(str.substring(typeStartIndex, i)); + token.setStopIndex(baseToken.getStartIndex() + i - 1); + break; + } + default: + throw new RuntimeException(String.format("Invalid character '\%c' in param list \"\%s\" at position \%d", str.charAt(typeStartIndex), str, typeStartIndex)); + } + + return token; + } + + private CommonTree parseParamList(CommonToken paramListToken) { + String paramList = paramListToken.getText(); + CommonTree root = new CommonTree(); + + int startIndex = paramListToken.getStartIndex(); + + int i=0; + while (i<paramList.length()) { + CommonToken token = getParamListSubToken(paramListToken, paramList, i); + root.addChild(new CommonTree(token)); + i += token.getText().length(); + } + + if (root.getChildCount() == 0) { + return null; + } + return root; + } + + private void throwOdexedInstructionException(IntStream input, String odexedInstruction) + throws OdexedInstructionException { + /*this has to be done in a separate method, otherwise java will complain about the + auto-generated code in the rule after the throw not being reachable*/ + throw new OdexedInstructionException(input, odexedInstruction); + } +} + + +smali_file + scope + { + boolean hasClassSpec; + boolean hasSuperSpec; + boolean hasSourceSpec; + List<CommonTree> classAnnotations; + } + @init + { $smali_file::hasClassSpec = $smali_file::hasSuperSpec = $smali_file::hasSourceSpec = false; + $smali_file::classAnnotations = new ArrayList<CommonTree>(); + } + : + ( {!$smali_file::hasClassSpec}?=> class_spec {$smali_file::hasClassSpec = true;} + | {!$smali_file::hasSuperSpec}?=> super_spec {$smali_file::hasSuperSpec = true;} + | implements_spec + | {!$smali_file::hasSourceSpec}?=> source_spec {$smali_file::hasSourceSpec = true;} + | method + | field + | annotation {$smali_file::classAnnotations.add($annotation.tree);} + )+ + EOF + { + if (!$smali_file::hasClassSpec) { + throw new SemanticException(input, "The file must contain a .class directive"); + } + + if (!$smali_file::hasSuperSpec) { + if (!$class_spec.className.equals("Ljava/lang/Object;")) { + throw new SemanticException(input, "The file must contain a .super directive"); + } + } + } + -> ^(I_CLASS_DEF + class_spec + super_spec? + implements_spec* + source_spec? + ^(I_METHODS method*) ^(I_FIELDS field*) {buildTree(I_ANNOTATIONS, "I_ANNOTATIONS", $smali_file::classAnnotations)}); + +class_spec returns[String className] + : CLASS_DIRECTIVE access_list CLASS_DESCRIPTOR {$className = $CLASS_DESCRIPTOR.text;} -> CLASS_DESCRIPTOR access_list; + +super_spec + : SUPER_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_SUPER[$start, "I_SUPER"] CLASS_DESCRIPTOR); + +implements_spec + : IMPLEMENTS_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_IMPLEMENTS[$start, "I_IMPLEMENTS"] CLASS_DESCRIPTOR); + +source_spec + : SOURCE_DIRECTIVE STRING_LITERAL -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL); + +access_list + : ACCESS_SPEC* -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC*); + + +/*When there are annotations immediately after a field definition, we don't know whether they are field annotations +or class annotations until we determine if there is an .end field directive. In either case, we still "consume" and parse +the annotations. If it turns out that they are field annotations, we include them in the I_FIELD AST. Otherwise, we +add them to the $smali_file::classAnnotations list*/ +field + @init {List<CommonTree> annotations = new ArrayList<CommonTree>();} + : FIELD_DIRECTIVE access_list member_name COLON nonvoid_type_descriptor (EQUAL literal)? + ( ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* + ( END_FIELD_DIRECTIVE + -> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*)) + | /*epsilon*/ {$smali_file::classAnnotations.addAll(annotations);} + -> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS)) + ) + ); + +method + : METHOD_DIRECTIVE access_list member_name method_prototype statements_and_directives + END_METHOD_DIRECTIVE + -> ^(I_METHOD[$start, "I_METHOD"] member_name method_prototype access_list statements_and_directives); + +statements_and_directives + scope + { + boolean hasRegistersDirective; + List<CommonTree> methodAnnotations; + } + : { + $statements_and_directives::hasRegistersDirective = false; + $statements_and_directives::methodAnnotations = new ArrayList<CommonTree>(); + } + ( ordered_method_item + | registers_directive + | catch_directive + | catchall_directive + | parameter_directive + | annotation {$statements_and_directives::methodAnnotations.add($annotation.tree);} + )* + -> registers_directive? + ^(I_ORDERED_METHOD_ITEMS ordered_method_item*) + ^(I_CATCHES catch_directive* catchall_directive*) + ^(I_PARAMETERS parameter_directive*) + {buildTree(I_ANNOTATIONS, "I_ANNOTATIONS", $statements_and_directives::methodAnnotations)}; + +/* Method items whose order/location is important */ +ordered_method_item + : label + | instruction + | debug_directive; + +registers_directive + : ( + directive=REGISTERS_DIRECTIVE regCount=integral_literal -> ^(I_REGISTERS[$REGISTERS_DIRECTIVE, "I_REGISTERS"] $regCount) + | directive=LOCALS_DIRECTIVE regCount2=integral_literal -> ^(I_LOCALS[$LOCALS_DIRECTIVE, "I_LOCALS"] $regCount2) + ) + { + if ($statements_and_directives::hasRegistersDirective) { + throw new SemanticException(input, $directive, "There can only be a single .registers or .locals directive in a method"); + } + $statements_and_directives::hasRegistersDirective=true; + }; + +param_list_or_id + : PARAM_LIST_OR_ID_START PRIMITIVE_TYPE+ PARAM_LIST_OR_ID_END; + +/*identifiers are much more general than most languages. Any of the below can either be +the indicated type OR an identifier, depending on the context*/ +simple_name + : SIMPLE_NAME + | ACCESS_SPEC -> SIMPLE_NAME[$ACCESS_SPEC] + | VERIFICATION_ERROR_TYPE -> SIMPLE_NAME[$VERIFICATION_ERROR_TYPE] + | POSITIVE_INTEGER_LITERAL -> SIMPLE_NAME[$POSITIVE_INTEGER_LITERAL] + | NEGATIVE_INTEGER_LITERAL -> SIMPLE_NAME[$NEGATIVE_INTEGER_LITERAL] + | FLOAT_LITERAL_OR_ID -> SIMPLE_NAME[$FLOAT_LITERAL_OR_ID] + | DOUBLE_LITERAL_OR_ID -> SIMPLE_NAME[$DOUBLE_LITERAL_OR_ID] + | BOOL_LITERAL -> SIMPLE_NAME[$BOOL_LITERAL] + | NULL_LITERAL -> SIMPLE_NAME[$NULL_LITERAL] + | REGISTER -> SIMPLE_NAME[$REGISTER] + | param_list_or_id -> { adaptor.create(SIMPLE_NAME, $param_list_or_id.text) } + | PRIMITIVE_TYPE -> SIMPLE_NAME[$PRIMITIVE_TYPE] + | VOID_TYPE -> SIMPLE_NAME[$VOID_TYPE] + | ANNOTATION_VISIBILITY -> SIMPLE_NAME[$ANNOTATION_VISIBILITY] + | INSTRUCTION_FORMAT10t -> SIMPLE_NAME[$INSTRUCTION_FORMAT10t] + | INSTRUCTION_FORMAT10x -> SIMPLE_NAME[$INSTRUCTION_FORMAT10x] + | INSTRUCTION_FORMAT10x_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT10x_ODEX] + | INSTRUCTION_FORMAT11x -> SIMPLE_NAME[$INSTRUCTION_FORMAT11x] + | INSTRUCTION_FORMAT12x_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT12x_OR_ID] + | INSTRUCTION_FORMAT21c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD] + | INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX] + | INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING] + | INSTRUCTION_FORMAT21c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_TYPE] + | INSTRUCTION_FORMAT21t -> SIMPLE_NAME[$INSTRUCTION_FORMAT21t] + | INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD] + | INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX] + | INSTRUCTION_FORMAT22c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_TYPE] + | INSTRUCTION_FORMAT22cs_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22cs_FIELD] + | INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID] + | INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t] + | INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x] + | INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID] + | INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t] + | INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD] + | INSTRUCTION_FORMAT35c_METHOD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD_ODEX] + | INSTRUCTION_FORMAT35c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_TYPE] + | INSTRUCTION_FORMAT35mi_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35mi_METHOD] + | INSTRUCTION_FORMAT35ms_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35ms_METHOD] + | INSTRUCTION_FORMAT51l -> SIMPLE_NAME[$INSTRUCTION_FORMAT51l]; + +member_name + : simple_name + | MEMBER_NAME -> SIMPLE_NAME[$MEMBER_NAME]; + +method_prototype + : OPEN_PAREN param_list CLOSE_PAREN type_descriptor + -> ^(I_METHOD_PROTOTYPE[$start, "I_METHOD_PROTOTYPE"] ^(I_METHOD_RETURN_TYPE type_descriptor) param_list?); + +param_list + : PARAM_LIST_START nonvoid_type_descriptor* PARAM_LIST_END -> nonvoid_type_descriptor* + | PARAM_LIST_OR_ID_START PRIMITIVE_TYPE* PARAM_LIST_OR_ID_END -> PRIMITIVE_TYPE* + | nonvoid_type_descriptor*; + +type_descriptor + : VOID_TYPE + | PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +nonvoid_type_descriptor + : PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +reference_type_descriptor + : CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +integer_literal + : POSITIVE_INTEGER_LITERAL -> INTEGER_LITERAL[$POSITIVE_INTEGER_LITERAL] + | NEGATIVE_INTEGER_LITERAL -> INTEGER_LITERAL[$NEGATIVE_INTEGER_LITERAL]; + +float_literal + : FLOAT_LITERAL_OR_ID -> FLOAT_LITERAL[$FLOAT_LITERAL_OR_ID] + | FLOAT_LITERAL; + +double_literal + : DOUBLE_LITERAL_OR_ID -> DOUBLE_LITERAL[$DOUBLE_LITERAL_OR_ID] + | DOUBLE_LITERAL; + +literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | BYTE_LITERAL + | float_literal + | double_literal + | CHAR_LITERAL + | STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | array_literal + | subannotation + | type_field_method_literal + | enum_literal; + +parsed_integer_literal returns[int value] + : integer_literal { $value = LiteralTools.parseInt($integer_literal.text); }; + +integral_literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | CHAR_LITERAL + | BYTE_LITERAL; + +fixed_32bit_literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | BYTE_LITERAL + | float_literal + | CHAR_LITERAL + | BOOL_LITERAL; + +fixed_literal + : integer_literal + | LONG_LITERAL + | SHORT_LITERAL + | BYTE_LITERAL + | float_literal + | double_literal + | CHAR_LITERAL + | BOOL_LITERAL; + +array_literal + : OPEN_BRACE (literal (COMMA literal)* | ) CLOSE_BRACE + -> ^(I_ENCODED_ARRAY[$start, "I_ENCODED_ARRAY"] literal*); + +annotation_element + : simple_name EQUAL literal + -> ^(I_ANNOTATION_ELEMENT[$start, "I_ANNOTATION_ELEMENT"] simple_name literal); + +annotation + : ANNOTATION_DIRECTIVE ANNOTATION_VISIBILITY CLASS_DESCRIPTOR + annotation_element* END_ANNOTATION_DIRECTIVE + -> ^(I_ANNOTATION[$start, "I_ANNOTATION"] ANNOTATION_VISIBILITY ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*)); + +subannotation + : SUBANNOTATION_DIRECTIVE CLASS_DESCRIPTOR annotation_element* END_SUBANNOTATION_DIRECTIVE + -> ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*); + +enum_literal + : ENUM_DIRECTIVE reference_type_descriptor ARROW simple_name COLON reference_type_descriptor + -> ^(I_ENCODED_ENUM reference_type_descriptor simple_name reference_type_descriptor); + +type_field_method_literal + : reference_type_descriptor + | ( (reference_type_descriptor ARROW)? + ( member_name COLON nonvoid_type_descriptor -> ^(I_ENCODED_FIELD reference_type_descriptor? member_name nonvoid_type_descriptor) + | member_name method_prototype -> ^(I_ENCODED_METHOD reference_type_descriptor? member_name method_prototype) + ) + ) + | PRIMITIVE_TYPE + | VOID_TYPE; + +method_reference + : (reference_type_descriptor ARROW)? member_name method_prototype + -> reference_type_descriptor? member_name method_prototype; + +field_reference + : (reference_type_descriptor ARROW)? member_name COLON nonvoid_type_descriptor + -> reference_type_descriptor? member_name nonvoid_type_descriptor; + +label + : COLON simple_name -> ^(I_LABEL[$COLON, "I_LABEL"] simple_name); + +label_ref + : COLON simple_name -> simple_name; + +register_list + : REGISTER (COMMA REGISTER)* -> ^(I_REGISTER_LIST[$start, "I_REGISTER_LIST"] REGISTER*) + | ->^(I_REGISTER_LIST[$start, "I_REGISTER_LIST"]); + +register_range + : (startreg=REGISTER (DOTDOT endreg=REGISTER)?)? -> ^(I_REGISTER_RANGE[$start, "I_REGISTER_RANGE"] $startreg? $endreg?); + +verification_error_reference + : CLASS_DESCRIPTOR | field_reference | method_reference; + +catch_directive + : CATCH_DIRECTIVE nonvoid_type_descriptor OPEN_BRACE from=label_ref DOTDOT to=label_ref CLOSE_BRACE using=label_ref + -> ^(I_CATCH[$start, "I_CATCH"] nonvoid_type_descriptor $from $to $using); + +catchall_directive + : CATCHALL_DIRECTIVE OPEN_BRACE from=label_ref DOTDOT to=label_ref CLOSE_BRACE using=label_ref + -> ^(I_CATCHALL[$start, "I_CATCHALL"] $from $to $using); + +/*When there are annotations immediately after a parameter definition, we don't know whether they are parameter annotations +or method annotations until we determine if there is an .end parameter directive. In either case, we still "consume" and parse +the annotations. If it turns out that they are parameter annotations, we include them in the I_PARAMETER AST. Otherwise, we +add them to the $statements_and_directives::methodAnnotations list*/ +parameter_directive + @init {List<CommonTree> annotations = new ArrayList<CommonTree>();} + : PARAMETER_DIRECTIVE REGISTER (COMMA STRING_LITERAL)? + ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* + + ( END_PARAMETER_DIRECTIVE + -> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER STRING_LITERAL? ^(I_ANNOTATIONS annotation*)) + | /*epsilon*/ {$statements_and_directives::methodAnnotations.addAll(annotations);} + -> ^(I_PARAMETER[$start, "I_PARAMETER"] REGISTER STRING_LITERAL? ^(I_ANNOTATIONS)) + ); + +debug_directive + : line_directive + | local_directive + | end_local_directive + | restart_local_directive + | prologue_directive + | epilogue_directive + | source_directive; + +line_directive + : LINE_DIRECTIVE integral_literal + -> ^(I_LINE[$start, "I_LINE"] integral_literal); + +local_directive + : LOCAL_DIRECTIVE REGISTER (COMMA (NULL_LITERAL | name=STRING_LITERAL) COLON (VOID_TYPE | nonvoid_type_descriptor) + (COMMA signature=STRING_LITERAL)? )? + -> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER NULL_LITERAL? $name? nonvoid_type_descriptor? $signature?); + +end_local_directive + : END_LOCAL_DIRECTIVE REGISTER + -> ^(I_END_LOCAL[$start, "I_END_LOCAL"] REGISTER); + +restart_local_directive + : RESTART_LOCAL_DIRECTIVE REGISTER + -> ^(I_RESTART_LOCAL[$start, "I_RESTART_LOCAL"] REGISTER); + +prologue_directive + : PROLOGUE_DIRECTIVE + -> ^(I_PROLOGUE[$start, "I_PROLOGUE"]); + +epilogue_directive + : EPILOGUE_DIRECTIVE + -> ^(I_EPILOGUE[$start, "I_EPILOGUE"]); + +source_directive + : SOURCE_DIRECTIVE STRING_LITERAL? + -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL?); + +instruction_format12x + : INSTRUCTION_FORMAT12x + | INSTRUCTION_FORMAT12x_OR_ID -> INSTRUCTION_FORMAT12x[$INSTRUCTION_FORMAT12x_OR_ID]; + +instruction_format22s + : INSTRUCTION_FORMAT22s + | INSTRUCTION_FORMAT22s_OR_ID -> INSTRUCTION_FORMAT22s[$INSTRUCTION_FORMAT22s_OR_ID]; + +instruction_format31i + : INSTRUCTION_FORMAT31i + | INSTRUCTION_FORMAT31i_OR_ID -> INSTRUCTION_FORMAT31i[$INSTRUCTION_FORMAT31i_OR_ID]; + + + +instruction + : insn_format10t + | insn_format10x + | insn_format10x_odex + | insn_format11n + | insn_format11x + | insn_format12x + | insn_format20bc + | insn_format20t + | insn_format21c_field + | insn_format21c_field_odex + | insn_format21c_string + | insn_format21c_type + | insn_format21ih + | insn_format21lh + | insn_format21s + | insn_format21t + | insn_format22b + | insn_format22c_field + | insn_format22c_field_odex + | insn_format22c_type + | insn_format22cs_field + | 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_format35c_method_odex + | insn_format35mi_method + | insn_format35ms_method + | insn_format3rc_method + | insn_format3rc_method_odex + | insn_format3rc_type + | insn_format3rmi_method + | insn_format3rms_method + | insn_format51l + | insn_array_data_directive + | insn_packed_switch_directive + | insn_sparse_switch_directive; + +insn_format10t + : //e.g. goto endloop: + //e.g. goto +3 + INSTRUCTION_FORMAT10t label_ref + -> ^(I_STATEMENT_FORMAT10t[$start, "I_STATEMENT_FORMAT10t"] INSTRUCTION_FORMAT10t label_ref); + +insn_format10x + : //e.g. return-void + INSTRUCTION_FORMAT10x + -> ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x); + +insn_format10x_odex + : //e.g. return-void-barrier + INSTRUCTION_FORMAT10x_ODEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT10x_ODEX.text); + }; + +insn_format11n + : //e.g. const/4 v0, 5 + INSTRUCTION_FORMAT11n REGISTER COMMA integral_literal + -> ^(I_STATEMENT_FORMAT11n[$start, "I_STATEMENT_FORMAT11n"] INSTRUCTION_FORMAT11n REGISTER integral_literal); + +insn_format11x + : //e.g. move-result-object v1 + INSTRUCTION_FORMAT11x REGISTER + -> ^(I_STATEMENT_FORMAT11x[$start, "I_STATEMENT_FORMAT11x"] INSTRUCTION_FORMAT11x REGISTER); + +insn_format12x + : //e.g. move v1 v2 + instruction_format12x REGISTER COMMA REGISTER + -> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] instruction_format12x REGISTER REGISTER); + +insn_format20bc + : //e.g. throw-verification-error generic-error, Lsome/class; + INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA verification_error_reference + { + if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT20bc.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT20bc.text); + } + } + -> ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE verification_error_reference); + +insn_format20t + : //e.g. goto/16 endloop: + INSTRUCTION_FORMAT20t label_ref + -> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref); + +insn_format21c_field + : //e.g. sget-object v0, java/lang/System/out LJava/io/PrintStream; + INSTRUCTION_FORMAT21c_FIELD REGISTER COMMA field_reference + -> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD REGISTER field_reference); + +insn_format21c_field_odex + : //e.g. sget-object-volatile v0, java/lang/System/out LJava/io/PrintStream; + INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER COMMA field_reference + { + if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_FIELD_ODEX.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT21c_FIELD_ODEX.text); + } + } + -> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER field_reference); + +insn_format21c_string + : //e.g. const-string v1, "Hello World!" + INSTRUCTION_FORMAT21c_STRING REGISTER COMMA STRING_LITERAL + -> ^(I_STATEMENT_FORMAT21c_STRING[$start, "I_STATEMENT_FORMAT21c_STRING"] INSTRUCTION_FORMAT21c_STRING REGISTER STRING_LITERAL); + +insn_format21c_type + : //e.g. const-class v2, Lorg/jf/HelloWorld2/HelloWorld2; + INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor + -> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor); + +insn_format21ih + : //e.g. const/high16 v1, 1234 + INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal + -> ^(I_STATEMENT_FORMAT21ih[$start, "I_STATEMENT_FORMAT21ih"] INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal); + +insn_format21lh + : //e.g. const-wide/high16 v1, 1234 + INSTRUCTION_FORMAT21lh REGISTER COMMA fixed_32bit_literal + -> ^(I_STATEMENT_FORMAT21lh[$start, "I_STATEMENT_FORMAT21lh"] INSTRUCTION_FORMAT21lh REGISTER fixed_32bit_literal); + +insn_format21s + : //e.g. const/16 v1, 1234 + INSTRUCTION_FORMAT21s REGISTER COMMA integral_literal + -> ^(I_STATEMENT_FORMAT21s[$start, "I_STATEMENT_FORMAT21s"] INSTRUCTION_FORMAT21s REGISTER integral_literal); + +insn_format21t + : //e.g. if-eqz v0, endloop: + INSTRUCTION_FORMAT21t REGISTER COMMA label_ref + -> ^(I_STATEMENT_FORMAT21t[$start, "I_STATEMENT_FORMAT21t"] INSTRUCTION_FORMAT21t REGISTER label_ref); + +insn_format22b + : //e.g. add-int v0, v1, 123 + INSTRUCTION_FORMAT22b REGISTER COMMA REGISTER COMMA integral_literal + -> ^(I_STATEMENT_FORMAT22b[$start, "I_STATEMENT_FORMAT22b"] INSTRUCTION_FORMAT22b REGISTER REGISTER integral_literal); + +insn_format22c_field + : //e.g. iput-object v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + INSTRUCTION_FORMAT22c_FIELD REGISTER COMMA REGISTER COMMA field_reference + -> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD REGISTER REGISTER field_reference); + +insn_format22c_field_odex + : //e.g. iput-object-volatile v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER COMMA REGISTER COMMA field_reference + { + if (!allowOdex || opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_FIELD_ODEX.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22c_FIELD_ODEX.text); + } + } + -> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER REGISTER field_reference); + +insn_format22c_type + : //e.g. instance-of v0, v1, Ljava/lang/String; + INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor + -> ^(I_STATEMENT_FORMAT22c_TYPE[$start, "I_STATEMENT_FORMAT22c_TYPE"] INSTRUCTION_FORMAT22c_TYPE REGISTER REGISTER nonvoid_type_descriptor); + +insn_format22cs_field + : //e.g. iget-quick v0, v1, field@0xc + INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22cs_FIELD.text); + }; + +insn_format22s + : //e.g. add-int/lit16 v0, v1, 12345 + instruction_format22s REGISTER COMMA REGISTER COMMA integral_literal + -> ^(I_STATEMENT_FORMAT22s[$start, "I_STATEMENT_FORMAT22s"] instruction_format22s REGISTER REGISTER integral_literal); + +insn_format22t + : //e.g. if-eq v0, v1, endloop: + INSTRUCTION_FORMAT22t REGISTER COMMA REGISTER COMMA label_ref + -> ^(I_STATEMENT_FORMAT22t[$start, "I_STATEMENT_FFORMAT22t"] INSTRUCTION_FORMAT22t REGISTER REGISTER label_ref); + +insn_format22x + : //e.g. move/from16 v1, v1234 + INSTRUCTION_FORMAT22x REGISTER COMMA REGISTER + -> ^(I_STATEMENT_FORMAT22x[$start, "I_STATEMENT_FORMAT22x"] INSTRUCTION_FORMAT22x REGISTER REGISTER); + +insn_format23x + : //e.g. add-int v1, v2, v3 + INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER + -> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER REGISTER); + +insn_format30t + : //e.g. goto/32 endloop: + INSTRUCTION_FORMAT30t label_ref + -> ^(I_STATEMENT_FORMAT30t[$start, "I_STATEMENT_FORMAT30t"] INSTRUCTION_FORMAT30t label_ref); + +insn_format31c + : //e.g. const-string/jumbo v1 "Hello World!" + INSTRUCTION_FORMAT31c REGISTER COMMA STRING_LITERAL + ->^(I_STATEMENT_FORMAT31c[$start, "I_STATEMENT_FORMAT31c"] INSTRUCTION_FORMAT31c REGISTER STRING_LITERAL); + +insn_format31i + : //e.g. const v0, 123456 + instruction_format31i REGISTER COMMA fixed_32bit_literal + -> ^(I_STATEMENT_FORMAT31i[$start, "I_STATEMENT_FORMAT31i"] instruction_format31i REGISTER fixed_32bit_literal); + +insn_format31t + : //e.g. fill-array-data v0, ArrayData: + INSTRUCTION_FORMAT31t REGISTER COMMA label_ref + -> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER label_ref); + +insn_format32x + : //e.g. move/16 v4567, v1234 + INSTRUCTION_FORMAT32x REGISTER COMMA REGISTER + -> ^(I_STATEMENT_FORMAT32x[$start, "I_STATEMENT_FORMAT32x"] INSTRUCTION_FORMAT32x REGISTER REGISTER); + +insn_format35c_method + : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V + INSTRUCTION_FORMAT35c_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA method_reference + -> ^(I_STATEMENT_FORMAT35c_METHOD[$start, "I_STATEMENT_FORMAT35c_METHOD"] INSTRUCTION_FORMAT35c_METHOD register_list method_reference); + +insn_format35c_type + : //e.g. filled-new-array {v0,v1}, I + INSTRUCTION_FORMAT35c_TYPE OPEN_BRACE register_list CLOSE_BRACE COMMA nonvoid_type_descriptor + -> ^(I_STATEMENT_FORMAT35c_TYPE[$start, "I_STATEMENT_FORMAT35c_TYPE"] INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor); + +insn_format35c_method_odex + : //e.g. invoke-direct {p0}, Ljava/lang/Object;-><init>()V + INSTRUCTION_FORMAT35c_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA method_reference + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35c_METHOD_ODEX.text); + }; + +insn_format35mi_method + : //e.g. execute-inline {v0, v1}, inline@0x4 + INSTRUCTION_FORMAT35mi_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA INLINE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35mi_METHOD.text); + }; + +insn_format35ms_method + : //e.g. invoke-virtual-quick {v0, v1}, vtable@0x4 + INSTRUCTION_FORMAT35ms_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA VTABLE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35ms_METHOD.text); + }; + +insn_format3rc_method + : //e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; + INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA method_reference + -> ^(I_STATEMENT_FORMAT3rc_METHOD[$start, "I_STATEMENT_FORMAT3rc_METHOD"] INSTRUCTION_FORMAT3rc_METHOD register_range method_reference); + +insn_format3rc_method_odex + : //e.g. invoke-object-init/range {p0}, Ljava/lang/Object;-><init>()V + INSTRUCTION_FORMAT3rc_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA method_reference + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rc_METHOD_ODEX.text); + }; + +insn_format3rc_type + : //e.g. filled-new-array/range {v0..v6}, I + INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor + -> ^(I_STATEMENT_FORMAT3rc_TYPE[$start, "I_STATEMENT_FORMAT3rc_TYPE"] INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor); + +insn_format3rmi_method + : //e.g. execute-inline/range {v0 .. v10}, inline@0x14 + INSTRUCTION_FORMAT3rmi_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA INLINE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rmi_METHOD.text); + }; + +insn_format3rms_method + : //e.g. invoke-virtual-quick/range {v0 .. v10}, vtable@0x14 + INSTRUCTION_FORMAT3rms_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA VTABLE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rms_METHOD.text); + }; + +insn_format51l + : //e.g. const-wide v0, 5000000000L + INSTRUCTION_FORMAT51l REGISTER COMMA fixed_literal + -> ^(I_STATEMENT_FORMAT51l[$start, "I_STATEMENT_FORMAT51l"] INSTRUCTION_FORMAT51l REGISTER fixed_literal); + +insn_array_data_directive + : ARRAY_DATA_DIRECTIVE + parsed_integer_literal + { + int elementWidth = $parsed_integer_literal.value; + if (elementWidth != 4 && elementWidth != 8 && elementWidth != 1 && elementWidth != 2) { + throw new SemanticException(input, $start, "Invalid element width: \%d. Must be 1, 2, 4 or 8", elementWidth); + } + } + fixed_literal* END_ARRAY_DATA_DIRECTIVE + + -> ^(I_STATEMENT_ARRAY_DATA[$start, "I_STATEMENT_ARRAY_DATA"] ^(I_ARRAY_ELEMENT_SIZE parsed_integer_literal) + ^(I_ARRAY_ELEMENTS fixed_literal*)); + +insn_packed_switch_directive + : PACKED_SWITCH_DIRECTIVE + fixed_32bit_literal + label_ref* + END_PACKED_SWITCH_DIRECTIVE + -> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] + ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) + ^(I_PACKED_SWITCH_ELEMENTS[$start, "I_PACKED_SWITCH_ELEMENTS"] + label_ref*) + ); + +insn_sparse_switch_directive + : SPARSE_SWITCH_DIRECTIVE + (fixed_32bit_literal ARROW label_ref)* + END_SPARSE_SWITCH_DIRECTIVE + -> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] + ^(I_SPARSE_SWITCH_ELEMENTS[$start, "I_SPARSE_SWITCH_ELEMENTS"] (fixed_32bit_literal label_ref)*));
\ No newline at end of file diff --git a/smali/src/main/antlr/smaliTreeWalker.g b/smali/src/main/antlr/smaliTreeWalker.g new file mode 100644 index 00000000..f49f10ef --- /dev/null +++ b/smali/src/main/antlr/smaliTreeWalker.g @@ -0,0 +1,1289 @@ +/* + * [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.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 = new Opcodes(apiLevel); + private DexBuilder dexBuilder; + + public void setDexBuilder(DexBuilder dexBuilder) { + this.dexBuilder = dexBuilder; + } + + public void setApiLevel(int apiLevel) { + this.opcodes = new Opcodes(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;} + ); + +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_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_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)); + }; + +nonvoid_type_descriptor returns [String type] + : (PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = $start.getText(); + }; + +reference_type_descriptor returns [String type] + : (CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = $start.getText(); + }; + +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; + }; |