| /* |
| * [The "BSD licence"] |
| * Copyright (c) 2010 Ben Gruver |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| tree grammar smaliTreeWalker; |
| |
| options { |
| tokenVocab=smaliParser; |
| ASTLabelType=CommonTree; |
| } |
| |
| @header { |
| package org.jf.smali; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import org.antlr.runtime.BitSet; |
| import org.antlr.runtime.*; |
| import org.antlr.runtime.tree.CommonTree; |
| import org.antlr.runtime.tree.TreeNodeStream; |
| import org.antlr.runtime.tree.TreeParser; |
| import org.antlr.runtime.tree.TreeRuleReturnScope; |
| import org.jf.dexlib2.*; |
| import org.jf.dexlib2.builder.Label; |
| import org.jf.dexlib2.builder.MethodImplementationBuilder; |
| import org.jf.dexlib2.builder.SwitchLabelElement; |
| import org.jf.dexlib2.builder.instruction.*; |
| import org.jf.dexlib2.iface.Annotation; |
| import org.jf.dexlib2.iface.AnnotationElement; |
| import org.jf.dexlib2.iface.ClassDef; |
| import org.jf.dexlib2.iface.MethodImplementation; |
| import org.jf.dexlib2.iface.reference.FieldReference; |
| import org.jf.dexlib2.iface.reference.MethodReference; |
| import org.jf.dexlib2.iface.value.EncodedValue; |
| import org.jf.dexlib2.immutable.ImmutableAnnotation; |
| import org.jf.dexlib2.immutable.ImmutableAnnotationElement; |
| import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; |
| import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; |
| import org.jf.dexlib2.immutable.reference.ImmutableMethodProtoReference; |
| import org.jf.dexlib2.immutable.reference.ImmutableReference; |
| import org.jf.dexlib2.immutable.reference.ImmutableTypeReference; |
| import org.jf.dexlib2.immutable.value.*; |
| import org.jf.dexlib2.util.MethodUtil; |
| import org.jf.dexlib2.writer.InstructionFactory; |
| import org.jf.dexlib2.writer.builder.*; |
| import org.jf.util.LinearSearch; |
| |
| import java.util.*; |
| } |
| |
| @members { |
| public String classType; |
| private boolean verboseErrors = false; |
| private int apiLevel = 15; |
| private Opcodes opcodes = Opcodes.forApi(apiLevel); |
| private DexBuilder dexBuilder; |
| |
| public void setDexBuilder(DexBuilder dexBuilder) { |
| this.dexBuilder = dexBuilder; |
| } |
| |
| public void setApiLevel(int apiLevel) { |
| this.opcodes = Opcodes.forApi(apiLevel); |
| this.apiLevel = apiLevel; |
| } |
| |
| public void setVerboseErrors(boolean verboseErrors) { |
| this.verboseErrors = verboseErrors; |
| } |
| |
| private byte parseRegister_nibble(String register) |
| throws SemanticException { |
| int totalMethodRegisters = method_stack.peek().totalMethodRegisters; |
| int methodParameterRegisters = method_stack.peek().methodParameterRegisters; |
| |
| //register should be in the format "v12" |
| int val = Byte.parseByte(register.substring(1)); |
| if (register.charAt(0) == 'p') { |
| val = totalMethodRegisters - methodParameterRegisters + val; |
| } |
| if (val >= 2<<4) { |
| throw new SemanticException(input, "The maximum allowed register in this context is list of registers is v15"); |
| } |
| //the parser wouldn't have accepted a negative register, i.e. v-1, so we don't have to check for val<0; |
| return (byte)val; |
| } |
| |
| //return a short, because java's byte is signed |
| private short parseRegister_byte(String register) |
| throws SemanticException { |
| int totalMethodRegisters = method_stack.peek().totalMethodRegisters; |
| int methodParameterRegisters = method_stack.peek().methodParameterRegisters; |
| //register should be in the format "v123" |
| int val = Short.parseShort(register.substring(1)); |
| if (register.charAt(0) == 'p') { |
| val = totalMethodRegisters - methodParameterRegisters + val; |
| } |
| if (val >= 2<<8) { |
| throw new SemanticException(input, "The maximum allowed register in this context is v255"); |
| } |
| return (short)val; |
| } |
| |
| //return an int because java's short is signed |
| private int parseRegister_short(String register) |
| throws SemanticException { |
| int totalMethodRegisters = method_stack.peek().totalMethodRegisters; |
| int methodParameterRegisters = method_stack.peek().methodParameterRegisters; |
| //register should be in the format "v12345" |
| int val = Integer.parseInt(register.substring(1)); |
| if (register.charAt(0) == 'p') { |
| val = totalMethodRegisters - methodParameterRegisters + val; |
| } |
| if (val >= 2<<16) { |
| throw new SemanticException(input, "The maximum allowed register in this context is v65535"); |
| } |
| //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; |
| return val; |
| } |
| |
| public String getErrorMessage(RecognitionException e, String[] tokenNames) { |
| if ( e instanceof SemanticException ) { |
| return e.getMessage(); |
| } else { |
| return super.getErrorMessage(e, tokenNames); |
| } |
| } |
| |
| public String getErrorHeader(RecognitionException e) { |
| return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; |
| } |
| } |
| |
| smali_file returns[ClassDef classDef] |
| : ^(I_CLASS_DEF header methods fields annotations) |
| { |
| $classDef = dexBuilder.internClassDef($header.classType, $header.accessFlags, $header.superType, |
| $header.implementsList, $header.sourceSpec, $annotations.annotations, $fields.fields, $methods.methods); |
| }; |
| catch [Exception ex] { |
| if (verboseErrors) { |
| ex.printStackTrace(System.err); |
| } |
| reportError(new SemanticException(input, ex)); |
| } |
| |
| |
| header returns[String classType, int accessFlags, String superType, List<String> implementsList, String sourceSpec] |
| : class_spec super_spec? implements_list source_spec |
| { |
| classType = $class_spec.type; |
| $classType = classType; |
| $accessFlags = $class_spec.accessFlags; |
| $superType = $super_spec.type; |
| $implementsList = $implements_list.implementsList; |
| $sourceSpec = $source_spec.source; |
| }; |
| |
| |
| class_spec returns[String type, int accessFlags] |
| : CLASS_DESCRIPTOR access_list |
| { |
| $type = $CLASS_DESCRIPTOR.text; |
| $accessFlags = $access_list.value; |
| }; |
| |
| super_spec returns[String type] |
| : ^(I_SUPER CLASS_DESCRIPTOR) |
| { |
| $type = $CLASS_DESCRIPTOR.text; |
| }; |
| |
| |
| implements_spec returns[String type] |
| : ^(I_IMPLEMENTS CLASS_DESCRIPTOR) |
| { |
| $type = $CLASS_DESCRIPTOR.text; |
| }; |
| |
| implements_list returns[List<String> implementsList] |
| @init { List<String> typeList; } |
| : {typeList = Lists.newArrayList();} |
| (implements_spec {typeList.add($implements_spec.type);} )* |
| { |
| if (typeList.size() > 0) { |
| $implementsList = typeList; |
| } else { |
| $implementsList = null; |
| } |
| }; |
| |
| source_spec returns[String source] |
| : {$source = null;} |
| ^(I_SOURCE string_literal {$source = $string_literal.value;}) |
| | /*epsilon*/; |
| |
| access_list returns [int value] |
| @init |
| { |
| $value = 0; |
| } |
| : ^(I_ACCESS_LIST |
| ( |
| ACCESS_SPEC |
| { |
| $value |= AccessFlags.getAccessFlag($ACCESS_SPEC.getText()).getValue(); |
| } |
| )*); |
| |
| |
| fields returns[List<BuilderField> fields] |
| @init {$fields = Lists.newArrayList();} |
| : ^(I_FIELDS |
| (field |
| { |
| $fields.add($field.field); |
| })*); |
| |
| methods returns[List<BuilderMethod> methods] |
| @init {$methods = Lists.newArrayList();} |
| : ^(I_METHODS |
| (method |
| { |
| $methods.add($method.ret); |
| })*); |
| |
| field returns [BuilderField field] |
| :^(I_FIELD SIMPLE_NAME access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?) |
| { |
| int accessFlags = $access_list.value; |
| |
| |
| if (!AccessFlags.STATIC.isSet(accessFlags) && $field_initial_value.encodedValue != null) { |
| throw new SemanticException(input, "Initial field values can only be specified for static fields."); |
| } |
| |
| $field = dexBuilder.internField(classType, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type, $access_list.value, |
| $field_initial_value.encodedValue, $annotations.annotations); |
| }; |
| |
| |
| field_initial_value returns[EncodedValue encodedValue] |
| : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;} |
| | /*epsilon*/; |
| |
| literal returns[EncodedValue encodedValue] |
| : integer_literal { $encodedValue = new ImmutableIntEncodedValue($integer_literal.value); } |
| | long_literal { $encodedValue = new ImmutableLongEncodedValue($long_literal.value); } |
| | short_literal { $encodedValue = new ImmutableShortEncodedValue($short_literal.value); } |
| | byte_literal { $encodedValue = new ImmutableByteEncodedValue($byte_literal.value); } |
| | float_literal { $encodedValue = new ImmutableFloatEncodedValue($float_literal.value); } |
| | double_literal { $encodedValue = new ImmutableDoubleEncodedValue($double_literal.value); } |
| | char_literal { $encodedValue = new ImmutableCharEncodedValue($char_literal.value); } |
| | string_literal { $encodedValue = new ImmutableStringEncodedValue($string_literal.value); } |
| | bool_literal { $encodedValue = ImmutableBooleanEncodedValue.forBoolean($bool_literal.value); } |
| | NULL_LITERAL { $encodedValue = ImmutableNullEncodedValue.INSTANCE; } |
| | type_descriptor { $encodedValue = new ImmutableTypeEncodedValue($type_descriptor.type); } |
| | array_literal { $encodedValue = new ImmutableArrayEncodedValue($array_literal.elements); } |
| | subannotation { $encodedValue = new ImmutableAnnotationEncodedValue($subannotation.annotationType, $subannotation.elements); } |
| | field_literal { $encodedValue = new ImmutableFieldEncodedValue($field_literal.value); } |
| | method_literal { $encodedValue = new ImmutableMethodEncodedValue($method_literal.value); } |
| | enum_literal { $encodedValue = new ImmutableEnumEncodedValue($enum_literal.value); }; |
| |
| //everything but string |
| fixed_64bit_literal_number returns[Number value] |
| : integer_literal { $value = $integer_literal.value; } |
| | long_literal { $value = $long_literal.value; } |
| | short_literal { $value = $short_literal.value; } |
| | byte_literal { $value = $byte_literal.value; } |
| | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } |
| | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); } |
| | char_literal { $value = (int)$char_literal.value; } |
| | bool_literal { $value = $bool_literal.value?1:0; }; |
| |
| fixed_64bit_literal returns[long value] |
| : integer_literal { $value = $integer_literal.value; } |
| | long_literal { $value = $long_literal.value; } |
| | short_literal { $value = $short_literal.value; } |
| | byte_literal { $value = $byte_literal.value; } |
| | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } |
| | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); } |
| | char_literal { $value = $char_literal.value; } |
| | bool_literal { $value = $bool_literal.value?1:0; }; |
| |
| //everything but string and double |
| //long is allowed, but it must fit into an int |
| fixed_32bit_literal returns[int value] |
| : integer_literal { $value = $integer_literal.value; } |
| | long_literal { LiteralTools.checkInt($long_literal.value); $value = (int)$long_literal.value; } |
| | short_literal { $value = $short_literal.value; } |
| | byte_literal { $value = $byte_literal.value; } |
| | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } |
| | char_literal { $value = $char_literal.value; } |
| | bool_literal { $value = $bool_literal.value?1:0; }; |
| |
| array_elements returns[List<Number> elements] |
| : {$elements = Lists.newArrayList();} |
| ^(I_ARRAY_ELEMENTS |
| (fixed_64bit_literal_number |
| { |
| $elements.add($fixed_64bit_literal_number.value); |
| })*); |
| |
| packed_switch_elements returns[List<Label> elements] |
| @init {$elements = Lists.newArrayList();} |
| : |
| ^(I_PACKED_SWITCH_ELEMENTS |
| (label_ref { $elements.add($label_ref.label); })* |
| ); |
| |
| sparse_switch_elements returns[List<SwitchLabelElement> elements] |
| @init {$elements = Lists.newArrayList();} |
| : |
| ^(I_SPARSE_SWITCH_ELEMENTS |
| (fixed_32bit_literal label_ref |
| { |
| $elements.add(new SwitchLabelElement($fixed_32bit_literal.value, $label_ref.label)); |
| })* |
| ); |
| |
| method returns[BuilderMethod ret] |
| scope |
| { |
| boolean isStatic; |
| int totalMethodRegisters; |
| int methodParameterRegisters; |
| MethodImplementationBuilder methodBuilder; |
| } |
| @init |
| { |
| $method::totalMethodRegisters = 0; |
| $method::methodParameterRegisters = 0; |
| int accessFlags = 0; |
| $method::isStatic = false; |
| } |
| : |
| ^(I_METHOD |
| method_name_and_prototype |
| access_list |
| { |
| accessFlags = $access_list.value; |
| $method::isStatic = AccessFlags.STATIC.isSet(accessFlags); |
| $method::methodParameterRegisters = |
| MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic); |
| } |
| ( |
| (registers_directive |
| { |
| if ($registers_directive.isLocalsDirective) { |
| $method::totalMethodRegisters = $registers_directive.registers + $method::methodParameterRegisters; |
| } else { |
| $method::totalMethodRegisters = $registers_directive.registers; |
| } |
| |
| $method::methodBuilder = new MethodImplementationBuilder($method::totalMethodRegisters); |
| |
| }) |
| | |
| /* epsilon */ |
| { |
| $method::methodBuilder = new MethodImplementationBuilder(0); |
| } |
| ) |
| ordered_method_items |
| catches |
| parameters[$method_name_and_prototype.parameters] |
| annotations |
| ) |
| { |
| MethodImplementation methodImplementation = null; |
| List<BuilderTryBlock> tryBlocks = $catches.tryBlocks; |
| |
| boolean isAbstract = false; |
| boolean isNative = false; |
| |
| if ((accessFlags & AccessFlags.ABSTRACT.getValue()) != 0) { |
| isAbstract = true; |
| } else if ((accessFlags & AccessFlags.NATIVE.getValue()) != 0) { |
| isNative = true; |
| } |
| |
| methodImplementation = $method::methodBuilder.getMethodImplementation(); |
| |
| if (Iterables.isEmpty(methodImplementation.getInstructions())) { |
| if (!isAbstract && !isNative) { |
| throw new SemanticException(input, $I_METHOD, "A non-abstract/non-native method must have at least 1 instruction"); |
| } |
| |
| String methodType; |
| if (isAbstract) { |
| methodType = "an abstract"; |
| } else { |
| methodType = "a native"; |
| } |
| |
| if ($registers_directive.start != null) { |
| if ($registers_directive.isLocalsDirective) { |
| throw new SemanticException(input, $registers_directive.start, "A .locals directive is not valid in \%s method", methodType); |
| } else { |
| throw new SemanticException(input, $registers_directive.start, "A .registers directive is not valid in \%s method", methodType); |
| } |
| } |
| |
| if (methodImplementation.getTryBlocks().size() > 0) { |
| throw new SemanticException(input, $I_METHOD, "try/catch blocks cannot be present in \%s method", methodType); |
| } |
| |
| if (!Iterables.isEmpty(methodImplementation.getDebugItems())) { |
| throw new SemanticException(input, $I_METHOD, "debug directives cannot be present in \%s method", methodType); |
| } |
| |
| methodImplementation = null; |
| } else { |
| if (isAbstract) { |
| throw new SemanticException(input, $I_METHOD, "An abstract method cannot have any instructions"); |
| } |
| if (isNative) { |
| throw new SemanticException(input, $I_METHOD, "A native method cannot have any instructions"); |
| } |
| |
| if ($registers_directive.start == null) { |
| throw new SemanticException(input, $I_METHOD, "A .registers or .locals directive must be present for a non-abstract/non-final method"); |
| } |
| |
| if ($method::totalMethodRegisters < $method::methodParameterRegisters) { |
| throw new SemanticException(input, $registers_directive.start, "This method requires at least " + |
| Integer.toString($method::methodParameterRegisters) + |
| " registers, for the method parameters"); |
| } |
| } |
| |
| $ret = dexBuilder.internMethod( |
| classType, |
| $method_name_and_prototype.name, |
| $method_name_and_prototype.parameters, |
| $method_name_and_prototype.returnType, |
| accessFlags, |
| $annotations.annotations, |
| methodImplementation); |
| }; |
| |
| method_prototype returns[List<String> parameters, String returnType] |
| : ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) method_type_list) |
| { |
| $returnType = $type_descriptor.type; |
| $parameters = $method_type_list.types; |
| }; |
| |
| method_name_and_prototype returns[String name, List<SmaliMethodParameter> parameters, String returnType] |
| : SIMPLE_NAME method_prototype |
| { |
| $name = $SIMPLE_NAME.text; |
| $parameters = Lists.newArrayList(); |
| |
| int paramRegister = 0; |
| for (String type: $method_prototype.parameters) { |
| $parameters.add(new SmaliMethodParameter(paramRegister++, type)); |
| char c = type.charAt(0); |
| if (c == 'D' || c == 'J') { |
| paramRegister++; |
| } |
| } |
| $returnType = $method_prototype.returnType; |
| }; |
| |
| method_type_list returns[List<String> types] |
| @init |
| { |
| $types = Lists.newArrayList(); |
| } |
| : ( |
| nonvoid_type_descriptor |
| { |
| $types.add($nonvoid_type_descriptor.type); |
| } |
| )*; |
| |
| |
| method_reference returns[ImmutableMethodReference methodReference] |
| : reference_type_descriptor? SIMPLE_NAME method_prototype |
| { |
| String type; |
| if ($reference_type_descriptor.type == null) { |
| type = classType; |
| } else { |
| type = $reference_type_descriptor.type; |
| } |
| $methodReference = new ImmutableMethodReference(type, $SIMPLE_NAME.text, |
| $method_prototype.parameters, $method_prototype.returnType); |
| }; |
| |
| field_reference returns[ImmutableFieldReference fieldReference] |
| : reference_type_descriptor? SIMPLE_NAME nonvoid_type_descriptor |
| { |
| String type; |
| if ($reference_type_descriptor.type == null) { |
| type = classType; |
| } else { |
| type = $reference_type_descriptor.type; |
| } |
| $fieldReference = new ImmutableFieldReference(type, $SIMPLE_NAME.text, |
| $nonvoid_type_descriptor.type); |
| }; |
| |
| registers_directive returns[boolean isLocalsDirective, int registers] |
| : {$registers = 0;} |
| ^(( I_REGISTERS {$isLocalsDirective = false;} |
| | I_LOCALS {$isLocalsDirective = true;} |
| ) |
| short_integral_literal {$registers = $short_integral_literal.value & 0xFFFF;} |
| ); |
| |
| label_def |
| : ^(I_LABEL SIMPLE_NAME) |
| { |
| $method::methodBuilder.addLabel($SIMPLE_NAME.text); |
| }; |
| |
| catches returns[List<BuilderTryBlock> tryBlocks] |
| @init {tryBlocks = Lists.newArrayList();} |
| : ^(I_CATCHES catch_directive* catchall_directive*); |
| |
| catch_directive |
| : ^(I_CATCH nonvoid_type_descriptor from=label_ref to=label_ref using=label_ref) |
| { |
| $method::methodBuilder.addCatch(dexBuilder.internTypeReference($nonvoid_type_descriptor.type), |
| $from.label, $to.label, $using.label); |
| }; |
| |
| catchall_directive |
| : ^(I_CATCHALL from=label_ref to=label_ref using=label_ref) |
| { |
| $method::methodBuilder.addCatch($from.label, $to.label, $using.label); |
| }; |
| |
| parameters[List<SmaliMethodParameter> parameters] |
| : ^(I_PARAMETERS (parameter[parameters])*); |
| |
| parameter[List<SmaliMethodParameter> parameters] |
| : ^(I_PARAMETER REGISTER string_literal? annotations) |
| { |
| final int registerNumber = parseRegister_short($REGISTER.text); |
| int totalMethodRegisters = $method::totalMethodRegisters; |
| int methodParameterRegisters = $method::methodParameterRegisters; |
| |
| if (registerNumber >= totalMethodRegisters) { |
| throw new SemanticException(input, $I_PARAMETER, "Register \%s is larger than the maximum register v\%d " + |
| "for this method", $REGISTER.text, totalMethodRegisters-1); |
| } |
| final int indexGuess = registerNumber - (totalMethodRegisters - methodParameterRegisters) - ($method::isStatic?0:1); |
| |
| if (indexGuess < 0) { |
| throw new SemanticException(input, $I_PARAMETER, "Register \%s is not a parameter register.", |
| $REGISTER.text); |
| } |
| |
| int parameterIndex = LinearSearch.linearSearch(parameters, SmaliMethodParameter.COMPARATOR, |
| new WithRegister() { public int getRegister() { return indexGuess; } }, |
| indexGuess); |
| |
| if (parameterIndex < 0) { |
| throw new SemanticException(input, $I_PARAMETER, "Register \%s is the second half of a wide parameter.", |
| $REGISTER.text); |
| } |
| |
| SmaliMethodParameter methodParameter = parameters.get(parameterIndex); |
| methodParameter.name = $string_literal.value; |
| if ($annotations.annotations != null && $annotations.annotations.size() > 0) { |
| methodParameter.annotations = $annotations.annotations; |
| } |
| }; |
| |
| debug_directive |
| : line |
| | local |
| | end_local |
| | restart_local |
| | prologue |
| | epilogue |
| | source; |
| |
| line |
| : ^(I_LINE integral_literal) |
| { |
| $method::methodBuilder.addLineNumber($integral_literal.value); |
| }; |
| |
| local |
| : ^(I_LOCAL REGISTER ((NULL_LITERAL | name=string_literal) nonvoid_type_descriptor? signature=string_literal?)?) |
| { |
| int registerNumber = parseRegister_short($REGISTER.text); |
| $method::methodBuilder.addStartLocal(registerNumber, |
| dexBuilder.internNullableStringReference($name.value), |
| dexBuilder.internNullableTypeReference($nonvoid_type_descriptor.type), |
| dexBuilder.internNullableStringReference($signature.value)); |
| }; |
| |
| end_local |
| : ^(I_END_LOCAL REGISTER) |
| { |
| int registerNumber = parseRegister_short($REGISTER.text); |
| $method::methodBuilder.addEndLocal(registerNumber); |
| }; |
| |
| restart_local |
| : ^(I_RESTART_LOCAL REGISTER) |
| { |
| int registerNumber = parseRegister_short($REGISTER.text); |
| $method::methodBuilder.addRestartLocal(registerNumber); |
| }; |
| |
| prologue |
| : I_PROLOGUE |
| { |
| $method::methodBuilder.addPrologue(); |
| }; |
| |
| epilogue |
| : I_EPILOGUE |
| { |
| $method::methodBuilder.addEpilogue(); |
| }; |
| |
| source |
| : ^(I_SOURCE string_literal?) |
| { |
| $method::methodBuilder.addSetSourceFile(dexBuilder.internNullableStringReference($string_literal.value)); |
| }; |
| |
| ordered_method_items |
| : ^(I_ORDERED_METHOD_ITEMS (label_def | instruction | debug_directive)*); |
| |
| label_ref returns[Label label] |
| : SIMPLE_NAME { $label = $method::methodBuilder.getLabel($SIMPLE_NAME.text); }; |
| |
| register_list returns[byte[\] registers, byte registerCount] |
| @init |
| { |
| $registers = new byte[5]; |
| $registerCount = 0; |
| } |
| : ^(I_REGISTER_LIST |
| (REGISTER |
| { |
| if ($registerCount == 5) { |
| throw new SemanticException(input, $I_REGISTER_LIST, "A list of registers can only have a maximum of 5 " + |
| "registers. Use the <op>/range alternate opcode instead."); |
| } |
| $registers[$registerCount++] = parseRegister_nibble($REGISTER.text); |
| })*); |
| |
| register_range returns[int startRegister, int endRegister] |
| : ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?) |
| { |
| if ($startReg == null) { |
| $startRegister = 0; |
| $endRegister = -1; |
| } else { |
| $startRegister = parseRegister_short($startReg.text); |
| if ($endReg == null) { |
| $endRegister = $startRegister; |
| } else { |
| $endRegister = parseRegister_short($endReg.text); |
| } |
| |
| int registerCount = $endRegister-$startRegister+1; |
| if (registerCount < 1) { |
| throw new SemanticException(input, $I_REGISTER_RANGE, "A register range must have the lower register listed first"); |
| } |
| } |
| }; |
| |
| verification_error_reference returns[ImmutableReference reference] |
| : CLASS_DESCRIPTOR |
| { |
| $reference = new ImmutableTypeReference($CLASS_DESCRIPTOR.text); |
| } |
| | field_reference |
| { |
| $reference = $field_reference.fieldReference; |
| } |
| | method_reference |
| { |
| $reference = $method_reference.methodReference; |
| }; |
| |
| verification_error_type returns[int verificationError] |
| : VERIFICATION_ERROR_TYPE |
| { |
| $verificationError = VerificationError.getVerificationError($VERIFICATION_ERROR_TYPE.text); |
| }; |
| |
| instruction |
| : insn_format10t |
| | insn_format10x |
| | insn_format11n |
| | insn_format11x |
| | insn_format12x |
| | insn_format20bc |
| | insn_format20t |
| | insn_format21c_field |
| | insn_format21c_string |
| | insn_format21c_type |
| | insn_format21ih |
| | insn_format21lh |
| | insn_format21s |
| | insn_format21t |
| | insn_format22b |
| | insn_format22c_field |
| | insn_format22c_type |
| | insn_format22s |
| | insn_format22t |
| | insn_format22x |
| | insn_format23x |
| | insn_format30t |
| | insn_format31c |
| | insn_format31i |
| | insn_format31t |
| | insn_format32x |
| | insn_format35c_method |
| | insn_format35c_type |
| | insn_format3rc_method |
| | insn_format3rc_type |
| | insn_format45cc_method |
| | insn_format4rcc_method |
| | insn_format51l_type |
| | insn_array_data_directive |
| | insn_packed_switch_directive |
| | insn_sparse_switch_directive; |
| catch [Exception ex] { |
| reportError(new SemanticException(input, $start, ex.getMessage())); |
| recover(input, null); |
| } |
| |
| insn_format10t |
| : //e.g. goto endloop: |
| ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT10t.text); |
| $method::methodBuilder.addInstruction(new BuilderInstruction10t(opcode, $label_ref.label)); |
| }; |
| |
| insn_format10x |
| : //e.g. return |
| ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT10x.text); |
| $method::methodBuilder.addInstruction(new BuilderInstruction10x(opcode)); |
| }; |
| |
| insn_format11n |
| : //e.g. const/4 v0, 5 |
| ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT11n.text); |
| byte regA = parseRegister_nibble($REGISTER.text); |
| |
| short litB = $short_integral_literal.value; |
| LiteralTools.checkNibble(litB); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction11n(opcode, regA, litB)); |
| }; |
| |
| insn_format11x |
| : //e.g. move-result-object v1 |
| ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT11x.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction11x(opcode, regA)); |
| }; |
| |
| insn_format12x |
| : //e.g. move v1 v2 |
| ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT12x.text); |
| byte regA = parseRegister_nibble($registerA.text); |
| byte regB = parseRegister_nibble($registerB.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction12x(opcode, regA, regB)); |
| }; |
| |
| insn_format20bc |
| : //e.g. throw-verification-error generic-error, Lsome/class; |
| ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc verification_error_type verification_error_reference) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT20bc.text); |
| |
| int verificationError = $verification_error_type.verificationError; |
| ImmutableReference referencedItem = $verification_error_reference.reference; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction20bc(opcode, verificationError, |
| dexBuilder.internReference(referencedItem))); |
| }; |
| |
| insn_format20t |
| : //e.g. goto/16 endloop: |
| ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT20t.text); |
| $method::methodBuilder.addInstruction(new BuilderInstruction20t(opcode, $label_ref.label)); |
| }; |
| |
| insn_format21c_field |
| : //e.g. sget_object v0, java/lang/System/out LJava/io/PrintStream; |
| ^(I_STATEMENT_FORMAT21c_FIELD inst=(INSTRUCTION_FORMAT21c_FIELD | INSTRUCTION_FORMAT21c_FIELD_ODEX) REGISTER field_reference) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($inst.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| ImmutableFieldReference fieldReference = $field_reference.fieldReference; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA, |
| dexBuilder.internFieldReference(fieldReference))); |
| }; |
| |
| insn_format21c_string |
| : //e.g. const-string v1, "Hello World!" |
| ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA, |
| dexBuilder.internStringReference($string_literal.value))); |
| }; |
| |
| insn_format21c_type |
| : //e.g. const-class v2, org/jf/HelloWorld2/HelloWorld2 |
| ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA, |
| dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); |
| }; |
| |
| insn_format21ih |
| : //e.g. const/high16 v1, 1234 |
| ^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21ih.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| int litB = $fixed_32bit_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21ih(opcode, regA, litB)); |
| }; |
| |
| insn_format21lh |
| : //e.g. const-wide/high16 v1, 1234 |
| ^(I_STATEMENT_FORMAT21lh INSTRUCTION_FORMAT21lh REGISTER fixed_64bit_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21lh.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| long litB = $fixed_64bit_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21lh(opcode, regA, litB)); |
| }; |
| |
| insn_format21s |
| : //e.g. const/16 v1, 1234 |
| ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21s.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| short litB = $short_integral_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21s(opcode, regA, litB)); |
| }; |
| |
| insn_format21t |
| : //e.g. if-eqz v0, endloop: |
| ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21t.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction21t(opcode, regA, $label_ref.label)); |
| }; |
| |
| insn_format22b |
| : //e.g. add-int v0, v1, 123 |
| ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22b.text); |
| short regA = parseRegister_byte($registerA.text); |
| short regB = parseRegister_byte($registerB.text); |
| |
| short litC = $short_integral_literal.value; |
| LiteralTools.checkByte(litC); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22b(opcode, regA, regB, litC)); |
| }; |
| |
| insn_format22c_field |
| : //e.g. iput-object v1, v0, org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; |
| ^(I_STATEMENT_FORMAT22c_FIELD inst=(INSTRUCTION_FORMAT22c_FIELD | INSTRUCTION_FORMAT22c_FIELD_ODEX) registerA=REGISTER registerB=REGISTER field_reference) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($inst.text); |
| byte regA = parseRegister_nibble($registerA.text); |
| byte regB = parseRegister_nibble($registerB.text); |
| |
| ImmutableFieldReference fieldReference = $field_reference.fieldReference; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB, |
| dexBuilder.internFieldReference(fieldReference))); |
| }; |
| |
| insn_format22c_type |
| : //e.g. instance-of v0, v1, Ljava/lang/String; |
| ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER nonvoid_type_descriptor) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_TYPE.text); |
| byte regA = parseRegister_nibble($registerA.text); |
| byte regB = parseRegister_nibble($registerB.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB, |
| dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); |
| }; |
| |
| insn_format22s |
| : //e.g. add-int/lit16 v0, v1, 12345 |
| ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22s.text); |
| byte regA = parseRegister_nibble($registerA.text); |
| byte regB = parseRegister_nibble($registerB.text); |
| |
| short litC = $short_integral_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22s(opcode, regA, regB, litC)); |
| }; |
| |
| insn_format22t |
| : //e.g. if-eq v0, v1, endloop: |
| ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22t.text); |
| byte regA = parseRegister_nibble($registerA.text); |
| byte regB = parseRegister_nibble($registerB.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22t(opcode, regA, regB, $label_ref.label)); |
| }; |
| |
| insn_format22x |
| : //e.g. move/from16 v1, v1234 |
| ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22x.text); |
| short regA = parseRegister_byte($registerA.text); |
| int regB = parseRegister_short($registerB.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction22x(opcode, regA, regB)); |
| }; |
| |
| insn_format23x |
| : //e.g. add-int v1, v2, v3 |
| ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT23x.text); |
| short regA = parseRegister_byte($registerA.text); |
| short regB = parseRegister_byte($registerB.text); |
| short regC = parseRegister_byte($registerC.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction23x(opcode, regA, regB, regC)); |
| }; |
| |
| insn_format30t |
| : //e.g. goto/32 endloop: |
| ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT30t.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction30t(opcode, $label_ref.label)); |
| }; |
| |
| insn_format31c |
| : //e.g. const-string/jumbo v1 "Hello World!" |
| ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31c.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction31c(opcode, regA, |
| dexBuilder.internStringReference($string_literal.value))); |
| }; |
| |
| insn_format31i |
| : //e.g. const v0, 123456 |
| ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31i.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| int litB = $fixed_32bit_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction31i(opcode, regA, litB)); |
| }; |
| |
| insn_format31t |
| : //e.g. fill-array-data v0, ArrayData: |
| ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER label_ref) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31t.text); |
| |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction31t(opcode, regA, $label_ref.label)); |
| }; |
| |
| insn_format32x |
| : //e.g. move/16 v5678, v1234 |
| ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT32x.text); |
| int regA = parseRegister_short($registerA.text); |
| int regB = parseRegister_short($registerB.text); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction32x(opcode, regA, regB)); |
| }; |
| |
| insn_format35c_method |
| : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V |
| ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list method_reference) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT35c_METHOD.text); |
| |
| //this depends on the fact that register_list returns a byte[5] |
| byte[] registers = $register_list.registers; |
| byte registerCount = $register_list.registerCount; |
| |
| ImmutableMethodReference methodReference = $method_reference.methodReference; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction35c(opcode, registerCount, registers[0], registers[1], |
| registers[2], registers[3], registers[4], dexBuilder.internMethodReference(methodReference))); |
| }; |
| |
| insn_format35c_type |
| : //e.g. filled-new-array {v0,v1}, I |
| ^(I_STATEMENT_FORMAT35c_TYPE INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT35c_TYPE.text); |
| |
| //this depends on the fact that register_list returns a byte[5] |
| byte[] registers = $register_list.registers; |
| byte registerCount = $register_list.registerCount; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction35c(opcode, registerCount, registers[0], registers[1], |
| registers[2], registers[3], registers[4], dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); |
| }; |
| |
| insn_format3rc_method |
| : //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; |
| ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range method_reference) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT3rc_METHOD.text); |
| int startRegister = $register_range.startRegister; |
| int endRegister = $register_range.endRegister; |
| |
| int registerCount = endRegister-startRegister+1; |
| |
| ImmutableMethodReference methodReference = $method_reference.methodReference; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction3rc(opcode, startRegister, registerCount, |
| dexBuilder.internMethodReference(methodReference))); |
| }; |
| |
| insn_format3rc_type |
| : //e.g. filled-new-array/range {v0..v6} I |
| ^(I_STATEMENT_FORMAT3rc_TYPE INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT3rc_TYPE.text); |
| int startRegister = $register_range.startRegister; |
| int endRegister = $register_range.endRegister; |
| |
| int registerCount = endRegister-startRegister+1; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction3rc(opcode, startRegister, registerCount, |
| dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); |
| }; |
| |
| insn_format45cc_method |
| : //e.g. invoke-polymorphic {v0, v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J |
| ^(I_STATEMENT_FORMAT45cc_METHOD INSTRUCTION_FORMAT45cc_METHOD register_list method_reference method_prototype) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT45cc_METHOD.text); |
| |
| //this depends on the fact that register_list returns a byte[5] |
| byte[] registers = $register_list.registers; |
| byte registerCount = $register_list.registerCount; |
| |
| ImmutableMethodReference methodReference = $method_reference.methodReference; |
| ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference( |
| $method_prototype.parameters, |
| $method_prototype.returnType); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction45cc(opcode, registerCount, registers[0], registers[1], |
| registers[2], registers[3], registers[4], |
| dexBuilder.internMethodReference(methodReference), |
| dexBuilder.internMethodProtoReference(methodProtoReference))); |
| }; |
| |
| insn_format4rcc_method |
| : //e.g. invoke-polymorphic {v0..v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J |
| ^(I_STATEMENT_FORMAT4rcc_METHOD INSTRUCTION_FORMAT4rcc_METHOD register_range method_reference method_prototype) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT4rcc_METHOD.text); |
| int startRegister = $register_range.startRegister; |
| int endRegister = $register_range.endRegister; |
| |
| int registerCount = endRegister-startRegister+1; |
| |
| ImmutableMethodReference methodReference = $method_reference.methodReference; |
| ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference( |
| $method_prototype.parameters, |
| $method_prototype.returnType); |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction4rcc(opcode, startRegister, registerCount, |
| dexBuilder.internMethodReference(methodReference), |
| dexBuilder.internMethodProtoReference(methodProtoReference))); |
| }; |
| |
| insn_format51l_type |
| : //e.g. const-wide v0, 5000000000L |
| ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) |
| { |
| Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT51l.text); |
| short regA = parseRegister_byte($REGISTER.text); |
| |
| long litB = $fixed_64bit_literal.value; |
| |
| $method::methodBuilder.addInstruction(new BuilderInstruction51l(opcode, regA, litB)); |
| }; |
| |
| insn_array_data_directive |
| : //e.g. .array-data 4 1000000 .end array-data |
| ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements) |
| { |
| int elementWidth = $short_integral_literal.value; |
| List<Number> elements = $array_elements.elements; |
| |
| $method::methodBuilder.addInstruction(new BuilderArrayPayload(elementWidth, $array_elements.elements)); |
| }; |
| |
| insn_packed_switch_directive |
| : |
| ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_elements) |
| { |
| int startKey = $fixed_32bit_literal.value; |
| $method::methodBuilder.addInstruction(new BuilderPackedSwitchPayload(startKey, |
| $packed_switch_elements.elements)); |
| }; |
| |
| insn_sparse_switch_directive |
| : |
| ^(I_STATEMENT_SPARSE_SWITCH sparse_switch_elements) |
| { |
| $method::methodBuilder.addInstruction(new BuilderSparseSwitchPayload($sparse_switch_elements.elements)); |
| }; |
| |
| array_descriptor returns [String type] |
| : ARRAY_TYPE_PREFIX ( PRIMITIVE_TYPE { $type = $ARRAY_TYPE_PREFIX.text + $PRIMITIVE_TYPE.text; } |
| | CLASS_DESCRIPTOR { $type = $ARRAY_TYPE_PREFIX.text + $CLASS_DESCRIPTOR.text; }); |
| |
| nonvoid_type_descriptor returns [String type] |
| : (PRIMITIVE_TYPE { $type = $text; } |
| | CLASS_DESCRIPTOR { $type = $text; } |
| | array_descriptor { $type = $array_descriptor.type; }) |
| ; |
| |
| reference_type_descriptor returns [String type] |
| : (CLASS_DESCRIPTOR { $type = $text; } |
| | array_descriptor { $type = $array_descriptor.type; }) |
| ; |
| |
| type_descriptor returns [String type] |
| : VOID_TYPE {$type = "V";} |
| | nonvoid_type_descriptor {$type = $nonvoid_type_descriptor.type;} |
| ; |
| |
| short_integral_literal returns[short value] |
| : long_literal |
| { |
| LiteralTools.checkShort($long_literal.value); |
| $value = (short)$long_literal.value; |
| } |
| | integer_literal |
| { |
| LiteralTools.checkShort($integer_literal.value); |
| $value = (short)$integer_literal.value; |
| } |
| | short_literal {$value = $short_literal.value;} |
| | char_literal {$value = (short)$char_literal.value;} |
| | byte_literal {$value = $byte_literal.value;}; |
| |
| integral_literal returns[int value] |
| : long_literal |
| { |
| LiteralTools.checkInt($long_literal.value); |
| $value = (int)$long_literal.value; |
| } |
| | integer_literal {$value = $integer_literal.value;} |
| | short_literal {$value = $short_literal.value;} |
| | byte_literal {$value = $byte_literal.value;}; |
| |
| |
| integer_literal returns[int value] |
| : INTEGER_LITERAL { $value = LiteralTools.parseInt($INTEGER_LITERAL.text); }; |
| |
| long_literal returns[long value] |
| : LONG_LITERAL { $value = LiteralTools.parseLong($LONG_LITERAL.text); }; |
| |
| short_literal returns[short value] |
| : SHORT_LITERAL { $value = LiteralTools.parseShort($SHORT_LITERAL.text); }; |
| |
| byte_literal returns[byte value] |
| : BYTE_LITERAL { $value = LiteralTools.parseByte($BYTE_LITERAL.text); }; |
| |
| float_literal returns[float value] |
| : FLOAT_LITERAL { $value = LiteralTools.parseFloat($FLOAT_LITERAL.text); }; |
| |
| double_literal returns[double value] |
| : DOUBLE_LITERAL { $value = LiteralTools.parseDouble($DOUBLE_LITERAL.text); }; |
| |
| char_literal returns[char value] |
| : CHAR_LITERAL { $value = $CHAR_LITERAL.text.charAt(1); }; |
| |
| string_literal returns[String value] |
| : STRING_LITERAL |
| { |
| $value = $STRING_LITERAL.text; |
| $value = $value.substring(1,$value.length()-1); |
| }; |
| |
| bool_literal returns[boolean value] |
| : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); }; |
| |
| array_literal returns[List<EncodedValue> elements] |
| : {$elements = Lists.newArrayList();} |
| ^(I_ENCODED_ARRAY (literal {$elements.add($literal.encodedValue);})*); |
| |
| annotations returns[Set<Annotation> annotations] |
| : {HashMap<String, Annotation> annotationMap = Maps.newHashMap();} |
| ^(I_ANNOTATIONS (annotation |
| { |
| Annotation anno = $annotation.annotation; |
| Annotation old = annotationMap.put(anno.getType(), anno); |
| if (old != null) { |
| throw new SemanticException(input, "Multiple annotations of type \%s", anno.getType()); |
| } |
| })*) |
| { |
| if (annotationMap.size() > 0) { |
| $annotations = ImmutableSet.copyOf(annotationMap.values()); |
| } |
| }; |
| |
| annotation returns[Annotation annotation] |
| : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation) |
| { |
| int visibility = AnnotationVisibility.getVisibility($ANNOTATION_VISIBILITY.text); |
| $annotation = new ImmutableAnnotation(visibility, $subannotation.annotationType, $subannotation.elements); |
| }; |
| |
| annotation_element returns[AnnotationElement element] |
| : ^(I_ANNOTATION_ELEMENT SIMPLE_NAME literal) |
| { |
| $element = new ImmutableAnnotationElement($SIMPLE_NAME.text, $literal.encodedValue); |
| }; |
| |
| subannotation returns[String annotationType, List<AnnotationElement> elements] |
| : {ArrayList<AnnotationElement> elements = Lists.newArrayList();} |
| ^(I_SUBANNOTATION |
| CLASS_DESCRIPTOR |
| (annotation_element |
| { |
| elements.add($annotation_element.element); |
| })* |
| ) |
| { |
| $annotationType = $CLASS_DESCRIPTOR.text; |
| $elements = elements; |
| }; |
| |
| field_literal returns[FieldReference value] |
| : ^(I_ENCODED_FIELD field_reference) |
| { |
| $value = $field_reference.fieldReference; |
| }; |
| |
| method_literal returns[MethodReference value] |
| : ^(I_ENCODED_METHOD method_reference) |
| { |
| $value = $method_reference.methodReference; |
| }; |
| |
| enum_literal returns[FieldReference value] |
| : ^(I_ENCODED_ENUM field_reference) |
| { |
| $value = $field_reference.fieldReference; |
| }; |