mirror of
https://github.com/revanced/smali.git
synced 2025-05-27 19:30:13 +02:00
1406 lines
52 KiB
Plaintext
1406 lines
52 KiB
Plaintext
/*
|
|
* [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.ImmutableCallSiteReference;
|
|
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
|
|
import org.jf.dexlib2.immutable.reference.ImmutableMethodHandleReference;
|
|
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;
|
|
private int callSiteNameIndex = 0;
|
|
|
|
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[ImmutableEncodedValue 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); }
|
|
| method_handle_literal { $encodedValue = new ImmutableMethodHandleEncodedValue($method_handle_literal.value); }
|
|
| method_prototype { $encodedValue = new ImmutableMethodTypeEncodedValue($method_prototype.proto); };
|
|
|
|
//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[ImmutableMethodProtoReference proto]
|
|
: ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) method_type_list)
|
|
{
|
|
String returnType = $type_descriptor.type;
|
|
List<String> parameters = $method_type_list.types;
|
|
$proto = new ImmutableMethodProtoReference(parameters, returnType);
|
|
};
|
|
|
|
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 (CharSequence type: $method_prototype.proto.getParameterTypes()) {
|
|
$parameters.add(new SmaliMethodParameter(paramRegister++, type.toString()));
|
|
char c = type.charAt(0);
|
|
if (c == 'D' || c == 'J') {
|
|
paramRegister++;
|
|
}
|
|
}
|
|
$returnType = $method_prototype.proto.getReturnType();
|
|
};
|
|
|
|
method_type_list returns[List<String> types]
|
|
@init
|
|
{
|
|
$types = Lists.newArrayList();
|
|
}
|
|
: (
|
|
nonvoid_type_descriptor
|
|
{
|
|
$types.add($nonvoid_type_descriptor.type);
|
|
}
|
|
)*;
|
|
|
|
call_site_reference returns[ImmutableCallSiteReference callSiteReference]
|
|
:
|
|
^(I_CALL_SITE_REFERENCE call_site_name=SIMPLE_NAME method_name=string_literal method_prototype
|
|
call_site_extra_arguments method_reference)
|
|
{
|
|
String callSiteName = $call_site_name.text;
|
|
ImmutableMethodHandleReference methodHandleReference =
|
|
new ImmutableMethodHandleReference(MethodHandleType.STATIC_INVOKE,
|
|
$method_reference.methodReference);
|
|
$callSiteReference = new ImmutableCallSiteReference(
|
|
callSiteName, methodHandleReference, $method_name.value, $method_prototype.proto,
|
|
$call_site_extra_arguments.extraArguments);
|
|
};
|
|
|
|
method_handle_type returns[int methodHandleType]
|
|
: METHOD_HANDLE_TYPE_FIELD | METHOD_HANDLE_TYPE_METHOD {
|
|
$methodHandleType = MethodHandleType.getMethodHandleType($text);
|
|
};
|
|
|
|
method_handle_reference returns[ImmutableMethodHandleReference methodHandle]
|
|
: method_handle_type (field_reference | method_reference) {
|
|
ImmutableReference reference;
|
|
if ($field_reference.text != null) {
|
|
reference = $field_reference.fieldReference;
|
|
} else {
|
|
reference = $method_reference.methodReference;
|
|
}
|
|
$methodHandle = new ImmutableMethodHandleReference($method_handle_type.methodHandleType, reference);
|
|
};
|
|
|
|
method_handle_literal returns[ImmutableMethodHandleReference value]
|
|
: (I_ENCODED_METHOD_HANDLE method_handle_reference) {
|
|
$value = $method_handle_reference.methodHandle;
|
|
};
|
|
|
|
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.proto.getParameterTypes(), $method_prototype.proto.getReturnType());
|
|
};
|
|
|
|
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));
|
|
};
|
|
|
|
call_site_extra_arguments returns[List<ImmutableEncodedValue> extraArguments]
|
|
: { $extraArguments = Lists.newArrayList(); }
|
|
^(I_CALL_SITE_EXTRA_ARGUMENTS (literal { $extraArguments.add($literal.encodedValue); })*);
|
|
|
|
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_call_site
|
|
| insn_format35c_method
|
|
| insn_format35c_type
|
|
| insn_format3rc_call_site
|
|
| 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_call_site
|
|
: //e.g. invoke-custom {v0, v1}, call_site_name
|
|
// OR invoke-custom {v0, v1}, {"doSomething", (LCustom;Ljava/lang/String;)Ljava/lang/String;, "just testing"}, BootstrapLinker;->normalLink(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
|
|
^(I_STATEMENT_FORMAT35c_CALL_SITE INSTRUCTION_FORMAT35c_CALL_SITE register_list call_site_reference)
|
|
{
|
|
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT35c_CALL_SITE.text);
|
|
|
|
//this depends on the fact that register_list returns a byte[5]
|
|
byte[] registers = $register_list.registers;
|
|
byte registerCount = $register_list.registerCount;
|
|
|
|
ImmutableCallSiteReference callSiteReference = $call_site_reference.callSiteReference;
|
|
|
|
$method::methodBuilder.addInstruction(new BuilderInstruction35c(opcode, registerCount, registers[0],
|
|
registers[1], registers[2], registers[3], registers[4], dexBuilder.internCallSite(callSiteReference)));
|
|
};
|
|
|
|
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_call_site
|
|
: //e.g. invoke-custom/range {v0 .. v1}, call_site_name
|
|
// OR invoke-custom/range {v0 .. v1}, {"doSomething", (LCustom;Ljava/lang/String;)Ljava/lang/String;, "just testing"}, BootstrapLinker;->normalLink(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
|
|
^(I_STATEMENT_FORMAT3rc_CALL_SITE INSTRUCTION_FORMAT3rc_CALL_SITE register_range call_site_reference)
|
|
{
|
|
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT3rc_CALL_SITE.text);
|
|
int startRegister = $register_range.startRegister;
|
|
int endRegister = $register_range.endRegister;
|
|
|
|
int registerCount = endRegister - startRegister + 1;
|
|
|
|
ImmutableCallSiteReference callSiteReference = $call_site_reference.callSiteReference;
|
|
|
|
$method::methodBuilder.addInstruction(new BuilderInstruction3rc(opcode, startRegister, registerCount,
|
|
dexBuilder.internCallSite(callSiteReference)));
|
|
};
|
|
|
|
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;
|
|
|
|
$method::methodBuilder.addInstruction(new BuilderInstruction45cc(opcode, registerCount, registers[0], registers[1],
|
|
registers[2], registers[3], registers[4],
|
|
dexBuilder.internMethodReference(methodReference),
|
|
dexBuilder.internMethodProtoReference($method_prototype.proto)));
|
|
};
|
|
|
|
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;
|
|
|
|
$method::methodBuilder.addInstruction(new BuilderInstruction4rcc(opcode, startRegister, registerCount,
|
|
dexBuilder.internMethodReference(methodReference),
|
|
dexBuilder.internMethodProtoReference($method_prototype.proto)));
|
|
};
|
|
|
|
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());
|
|
}
|
|
})*)
|
|
{
|
|
$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[ImmutableFieldReference value]
|
|
: ^(I_ENCODED_FIELD field_reference)
|
|
{
|
|
$value = $field_reference.fieldReference;
|
|
};
|
|
|
|
method_literal returns[ImmutableMethodReference value]
|
|
: ^(I_ENCODED_METHOD method_reference)
|
|
{
|
|
$value = $method_reference.methodReference;
|
|
};
|
|
|
|
enum_literal returns[ImmutableFieldReference value]
|
|
: ^(I_ENCODED_ENUM field_reference)
|
|
{
|
|
$value = $field_reference.fieldReference;
|
|
};
|