diff options
Diffstat (limited to 'smali/src/main/antlr/smaliTreeWalker.g')
-rw-r--r-- | smali/src/main/antlr/smaliTreeWalker.g | 1289 |
1 files changed, 1289 insertions, 0 deletions
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; + }; |