/* * [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 = Opcodes.forApi(apiLevel); private DexBuilder dexBuilder; public void setDexBuilder(DexBuilder dexBuilder) { this.dexBuilder = dexBuilder; } public void setApiLevel(int apiLevel, boolean experimental) { this.opcodes = new Opcodes(apiLevel, experimental); 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 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 implementsList] @init { List 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 fields] @init {$fields = Lists.newArrayList();} : ^(I_FIELDS (field { $fields.add($field.field); })*); methods returns[List 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 elements] : {$elements = Lists.newArrayList();} ^(I_ARRAY_ELEMENTS (fixed_64bit_literal_number { $elements.add($fixed_64bit_literal_number.value); })*); packed_switch_elements returns[List