Add support for hidden api restrictions to smali and baksmali

The hidden api restrictions are exposed in the smali language as
additional access flags for fields and methods
This commit is contained in:
Ben Gruver 2020-02-02 16:15:11 -08:00
parent 721fffc60e
commit c3bbc771f5
11 changed files with 317 additions and 26 deletions

View File

@ -31,6 +31,7 @@ package org.jf.baksmali.Adaptors;
import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor; import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor;
import org.jf.baksmali.BaksmaliOptions; import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.HiddenApiRestriction;
import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.value.EncodedValue; import org.jf.dexlib2.iface.value.EncodedValue;
@ -39,6 +40,7 @@ import org.jf.util.IndentingWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Set;
public class FieldDefinition { public class FieldDefinition {
public static void writeTo(BaksmaliOptions options, IndentingWriter writer, Field field, public static void writeTo(BaksmaliOptions options, IndentingWriter writer, Field field,
@ -60,7 +62,7 @@ public class FieldDefinition {
} }
writer.write(".field "); writer.write(".field ");
writeAccessFlags(writer, field.getAccessFlags()); writeAccessFlagsAndRestrictions(writer, field.getAccessFlags(), field.getHiddenApiRestrictions());
writer.write(field.getName()); writer.write(field.getName());
writer.write(':'); writer.write(':');
writer.write(field.getType()); writer.write(field.getType());
@ -92,10 +94,16 @@ public class FieldDefinition {
} }
} }
private static void writeAccessFlags(IndentingWriter writer, int accessFlags) throws IOException { private static void writeAccessFlagsAndRestrictions(
IndentingWriter writer, int accessFlags, Set<HiddenApiRestriction> hiddenApiRestrictions)
throws IOException {
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(accessFlags)) { for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(accessFlags)) {
writer.write(accessFlag.toString()); writer.write(accessFlag.toString());
writer.write(' '); writer.write(' ');
} }
for (HiddenApiRestriction hiddenApiRestriction : hiddenApiRestrictions) {
writer.write(hiddenApiRestriction.toString());
writer.write(' ');
}
} }
} }

View File

@ -33,10 +33,7 @@ import com.google.common.collect.Lists;
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem; import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory; import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
import org.jf.baksmali.BaksmaliOptions; import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.*;
import org.jf.dexlib2.Format;
import org.jf.dexlib2.Opcode;
import org.jf.dexlib2.ReferenceType;
import org.jf.dexlib2.analysis.AnalysisException; import org.jf.dexlib2.analysis.AnalysisException;
import org.jf.dexlib2.analysis.AnalyzedInstruction; import org.jf.dexlib2.analysis.AnalyzedInstruction;
import org.jf.dexlib2.analysis.MethodAnalyzer; import org.jf.dexlib2.analysis.MethodAnalyzer;
@ -165,7 +162,7 @@ public class MethodDefinition {
public static void writeEmptyMethodTo(IndentingWriter writer, Method method, public static void writeEmptyMethodTo(IndentingWriter writer, Method method,
BaksmaliOptions options) throws IOException { BaksmaliOptions options) throws IOException {
writer.write(".method "); writer.write(".method ");
writeAccessFlags(writer, method.getAccessFlags()); writeAccessFlagsAndRestrictions(writer, method.getAccessFlags(), method.getHiddenApiRestrictions());
writer.write(method.getName()); writer.write(method.getName());
writer.write("("); writer.write("(");
ImmutableList<MethodParameter> methodParameters = ImmutableList.copyOf(method.getParameters()); ImmutableList<MethodParameter> methodParameters = ImmutableList.copyOf(method.getParameters());
@ -196,7 +193,7 @@ public class MethodDefinition {
} }
writer.write(".method "); writer.write(".method ");
writeAccessFlags(writer, method.getAccessFlags()); writeAccessFlagsAndRestrictions(writer, method.getAccessFlags(), method.getHiddenApiRestrictions());
writer.write(method.getName()); writer.write(method.getName());
writer.write("("); writer.write("(");
for (MethodParameter parameter: methodParameters) { for (MethodParameter parameter: methodParameters) {
@ -303,12 +300,17 @@ public class MethodDefinition {
} }
} }
private static void writeAccessFlags(IndentingWriter writer, int accessFlags) private static void writeAccessFlagsAndRestrictions(
IndentingWriter writer, int accessFlags, Set<HiddenApiRestriction> hiddenApiRestrictions)
throws IOException { throws IOException {
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) { for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) {
writer.write(accessFlag.toString()); writer.write(accessFlag.toString());
writer.write(' '); writer.write(' ');
} }
for (HiddenApiRestriction hiddenApiRestriction : hiddenApiRestrictions) {
writer.write(hiddenApiRestriction.toString());
writer.write(' ');
}
} }
private static void writeParameters(IndentingWriter writer, Method method, private static void writeParameters(IndentingWriter writer, Method method,

View File

@ -0,0 +1,43 @@
/*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
package org.jf.baksmali;
import org.junit.Test;
public class HiddenApiRestrictionsRoundtripTest extends RoundtripTest {
@Test
public void testHiddenApiRestrictions() {
BaksmaliOptions options = new BaksmaliOptions();
options.apiLevel = 29;
runTest("HiddenApiRestrictions", options);
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2020, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
package org.jf.baksmali;
import org.antlr.runtime.RecognitionException;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.dexbacked.raw.ItemType;
import org.jf.smali.SmaliTestUtils;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
public class HiddenApiRestrictionsTest {
@Test
public void testNoHiddenApiRestrictions() throws IOException, RecognitionException {
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" return-void\n" +
".end method";
DexBackedClassDef classDef = SmaliTestUtils.compileSmali(source, 29);
Assert.assertNull(classDef.dexFile.getMapItemForSection(ItemType.HIDDENAPI_CLASS_DATA_ITEM));
}
@Test
public void testWithHiddenApiRestrictions() throws IOException, RecognitionException {
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public whitelist static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" return-void\n" +
".end method";
DexBackedClassDef classDef = SmaliTestUtils.compileSmali(source, 29);
Assert.assertNotNull(classDef.dexFile.getMapItemForSection(ItemType.HIDDENAPI_CLASS_DATA_ITEM));
}
@Test
public void testWithHiddenApiRestrictionsWithLowerApi() throws IOException, RecognitionException {
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public whitelist static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" return-void\n" +
".end method";
try {
SmaliTestUtils.compileSmali(source, 28);
Assert.fail();
} catch (RuntimeException ex) {
// expected exception
}
}
}

View File

@ -0,0 +1,30 @@
.class public LHiddenApiRestrictions;
.super Ljava/lang/Object;
.field public static whitelist staticField:I
.field public core-platform-api domainSpecificFlagTest:I
.field public blacklist instanceField:I
.method public blacklist virtualMethod()V
.registers 1
return-void
.end method
.method private greylist-max-o directMethod()V
.registers 1
return-void
.end method
.method private core-platform-api corePlatformApiTest()V
.registers 1
return-void
.end method
.method greylist-max-q private core-platform-api corePlatformApiAndHiddenApiTest()V
.registers 1
return-void
.end method

View File

@ -0,0 +1,40 @@
.class public LHiddenApiRestrictions;
.super Ljava/lang/Object;
# static fields
.field public static whitelist staticField:I
# instance fields
.field public whitelist core-platform-api domainSpecificFlagTest:I
.field public blacklist instanceField:I
# direct methods
.method private greylist-max-q core-platform-api corePlatformApiAndHiddenApiTest()V
.registers 1
return-void
.end method
.method private whitelist core-platform-api corePlatformApiTest()V
.registers 1
return-void
.end method
.method private greylist-max-o directMethod()V
.registers 1
return-void
.end method
# virtual methods
.method public blacklist virtualMethod()V
.registers 1
return-void
.end method

View File

@ -33,6 +33,8 @@ package org.jf.dexlib2;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner; import java.util.StringJoiner;
@ -58,6 +60,15 @@ public enum HiddenApiRestriction {
CORE_PLATFORM_API CORE_PLATFORM_API
}; };
private static final Map<String, HiddenApiRestriction> hiddenApiRestrictionsByName;
static {
hiddenApiRestrictionsByName = new HashMap<>();
for (HiddenApiRestriction hiddenApiRestriction : HiddenApiRestriction.values()) {
hiddenApiRestrictionsByName.put(hiddenApiRestriction.toString(), hiddenApiRestriction);
}
}
private static final int HIDDENAPI_FLAG_MASK = 0x7; private static final int HIDDENAPI_FLAG_MASK = 0x7;
private final int value; private final int value;
@ -134,4 +145,8 @@ public enum HiddenApiRestriction {
return value; return value;
} }
public static HiddenApiRestriction forName(String name) {
return hiddenApiRestrictionsByName.get(name);
}
} }

View File

@ -72,6 +72,7 @@ tokens {
FIELD_OFFSET; FIELD_OFFSET;
FLOAT_LITERAL; FLOAT_LITERAL;
FLOAT_LITERAL_OR_ID; FLOAT_LITERAL_OR_ID;
HIDDENAPI_RESTRICTION;
IMPLEMENTS_DIRECTIVE; IMPLEMENTS_DIRECTIVE;
INLINE_INDEX; INLINE_INDEX;
INSTRUCTION_FORMAT10t; INSTRUCTION_FORMAT10t;
@ -167,6 +168,7 @@ tokens {
I_IMPLEMENTS; I_IMPLEMENTS;
I_SOURCE; I_SOURCE;
I_ACCESS_LIST; I_ACCESS_LIST;
I_ACCESS_OR_RESTRICTION_LIST;
I_METHODS; I_METHODS;
I_FIELDS; I_FIELDS;
I_FIELD; I_FIELD;
@ -479,6 +481,13 @@ source_spec
access_list access_list
: ACCESS_SPEC* -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC*); : ACCESS_SPEC* -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC*);
access_or_restriction
: ACCESS_SPEC | HIDDENAPI_RESTRICTION;
access_or_restriction_list
: access_or_restriction*
-> ^(I_ACCESS_OR_RESTRICTION_LIST[$start,"I_ACCESS_AND_RESTRICTION_LIST"] access_or_restriction*);
/*When there are annotations immediately after a field definition, we don't know whether they are field annotations /*When there are annotations immediately after a field definition, we don't know whether they are field annotations
or class annotations until we determine if there is an .end field directive. In either case, we still "consume" and parse or class annotations until we determine if there is an .end field directive. In either case, we still "consume" and parse
@ -486,19 +495,19 @@ the annotations. If it turns out that they are field annotations, we include the
add them to the $smali_file::classAnnotations list*/ add them to the $smali_file::classAnnotations list*/
field field
@init {List<CommonTree> annotations = new ArrayList<CommonTree>();} @init {List<CommonTree> annotations = new ArrayList<CommonTree>();}
: FIELD_DIRECTIVE access_list member_name COLON nonvoid_type_descriptor (EQUAL literal)? : FIELD_DIRECTIVE access_or_restriction_list member_name COLON nonvoid_type_descriptor (EQUAL literal)?
( ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* ( ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})*
( END_FIELD_DIRECTIVE ( END_FIELD_DIRECTIVE
-> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*)) -> ^(I_FIELD[$start, "I_FIELD"] member_name access_or_restriction_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*))
| /*epsilon*/ {$smali_file::classAnnotations.addAll(annotations);} | /*epsilon*/ {$smali_file::classAnnotations.addAll(annotations);}
-> ^(I_FIELD[$start, "I_FIELD"] member_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS)) -> ^(I_FIELD[$start, "I_FIELD"] member_name access_or_restriction_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS))
) )
); );
method method
: METHOD_DIRECTIVE access_list member_name method_prototype statements_and_directives : METHOD_DIRECTIVE access_or_restriction_list member_name method_prototype statements_and_directives
END_METHOD_DIRECTIVE END_METHOD_DIRECTIVE
-> ^(I_METHOD[$start, "I_METHOD"] member_name method_prototype access_list statements_and_directives); -> ^(I_METHOD[$start, "I_METHOD"] member_name method_prototype access_or_restriction_list statements_and_directives);
statements_and_directives statements_and_directives
scope scope

View File

@ -223,7 +223,7 @@ source_spec returns[String source]
^(I_SOURCE string_literal {$source = $string_literal.value;}) ^(I_SOURCE string_literal {$source = $string_literal.value;})
| /*epsilon*/; | /*epsilon*/;
access_list returns [int value] access_list returns[int value]
@init @init
{ {
$value = 0; $value = 0;
@ -236,6 +236,51 @@ access_list returns [int value]
} }
)*); )*);
access_or_restriction_list returns[int value, Set<HiddenApiRestriction> hiddenApiRestrictions]
@init
{
$value = 0;
HiddenApiRestriction hiddenApiRestriction = null;
HiddenApiRestriction domainSpecificApiRestriction = null;
}
: ^(I_ACCESS_OR_RESTRICTION_LIST
(
ACCESS_SPEC
{
$value |= AccessFlags.getAccessFlag($ACCESS_SPEC.getText()).getValue();
}
|
HIDDENAPI_RESTRICTION
{
if (opcodes.api < 29) {
throw new SemanticException(input, $HIDDENAPI_RESTRICTION, "Hidden API restrictions are only supported on api 29 and above.");
}
HiddenApiRestriction restriction = HiddenApiRestriction.forName($HIDDENAPI_RESTRICTION.getText());
if (restriction.isDomainSpecificApiFlag()) {
if (domainSpecificApiRestriction != null) {
throw new SemanticException(input, $HIDDENAPI_RESTRICTION, "Only one domain-specific api restriction may be specified.");
}
domainSpecificApiRestriction = restriction;
} else {
if (hiddenApiRestriction != null) {
throw new SemanticException(input, $HIDDENAPI_RESTRICTION, "Only one hidden api restriction may be specified.");
}
hiddenApiRestriction = restriction;
}
}
)*)
{
List<HiddenApiRestriction> restrictions = new ArrayList<>(2);
if (hiddenApiRestriction != null) {
restrictions.add(hiddenApiRestriction);
}
if (domainSpecificApiRestriction != null) {
restrictions.add(domainSpecificApiRestriction);
}
$hiddenApiRestrictions = ImmutableSet.copyOf(restrictions);
};
fields returns[List<BuilderField> fields] fields returns[List<BuilderField> fields]
@init {$fields = Lists.newArrayList();} @init {$fields = Lists.newArrayList();}
@ -254,17 +299,17 @@ methods returns[List<BuilderMethod> methods]
})*); })*);
field returns [BuilderField field] field returns [BuilderField field]
:^(I_FIELD SIMPLE_NAME access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?) :^(I_FIELD SIMPLE_NAME access_or_restriction_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?)
{ {
int accessFlags = $access_list.value; int accessFlags = $access_or_restriction_list.value;
Set<HiddenApiRestriction> hiddenApiRestrictions = $access_or_restriction_list.hiddenApiRestrictions;
if (!AccessFlags.STATIC.isSet(accessFlags) && $field_initial_value.encodedValue != null) { if (!AccessFlags.STATIC.isSet(accessFlags) && $field_initial_value.encodedValue != null) {
throw new SemanticException(input, "Initial field values can only be specified for static fields."); 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 = dexBuilder.internField(classType, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type, accessFlags,
$field_initial_value.encodedValue, $annotations.annotations, ImmutableSet.of()); $field_initial_value.encodedValue, $annotations.annotations, hiddenApiRestrictions);
}; };
@ -363,13 +408,15 @@ method returns[BuilderMethod ret]
$method::methodParameterRegisters = 0; $method::methodParameterRegisters = 0;
int accessFlags = 0; int accessFlags = 0;
$method::isStatic = false; $method::isStatic = false;
Set<HiddenApiRestriction> hiddenApiRestrictions = null;
} }
: :
^(I_METHOD ^(I_METHOD
method_name_and_prototype method_name_and_prototype
access_list access_or_restriction_list
{ {
accessFlags = $access_list.value; accessFlags = $access_or_restriction_list.value;
hiddenApiRestrictions = $access_or_restriction_list.hiddenApiRestrictions;
$method::isStatic = AccessFlags.STATIC.isSet(accessFlags); $method::isStatic = AccessFlags.STATIC.isSet(accessFlags);
$method::methodParameterRegisters = $method::methodParameterRegisters =
MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic); MethodUtil.getParameterRegisterCount($method_name_and_prototype.parameters, $method::isStatic);
@ -467,7 +514,7 @@ method returns[BuilderMethod ret]
$method_name_and_prototype.returnType, $method_name_and_prototype.returnType,
accessFlags, accessFlags,
$annotations.annotations, $annotations.annotations,
ImmutableSet.of(), hiddenApiRestrictions,
methodImplementation); methodImplementation);
}; };

View File

@ -38,8 +38,8 @@ import org.antlr.runtime.TokenSource;
import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.writer.builder.DexBuilder; import org.jf.dexlib2.writer.builder.DexBuilder;
import org.jf.dexlib2.writer.io.MemoryDataStore; import org.jf.dexlib2.writer.io.MemoryDataStore;
@ -49,11 +49,11 @@ import java.io.StringReader;
public class SmaliTestUtils { public class SmaliTestUtils {
public static ClassDef compileSmali(String smaliText) throws RecognitionException, IOException { public static DexBackedClassDef compileSmali(String smaliText) throws RecognitionException, IOException {
return compileSmali(smaliText, 15); return compileSmali(smaliText, 15);
} }
public static ClassDef compileSmali(String smaliText, int apiLevel) public static DexBackedClassDef compileSmali(String smaliText, int apiLevel)
throws RecognitionException, IOException { throws RecognitionException, IOException {
CommonTokenStream tokens; CommonTokenStream tokens;
LexerErrorInterface lexer; LexerErrorInterface lexer;

View File

@ -470,6 +470,11 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayPrefix} ({ClassDescriptor} |
return newToken(ACCESS_SPEC); return newToken(ACCESS_SPEC);
} }
"whitelist" | "greylist" | "blacklist" | "greylist-max-o" | "greylist-max-p" | "greylist-max-q" |
"core-platform-api" {
return newToken(HIDDENAPI_RESTRICTION);
}
"no-error" | "generic-error" | "no-such-class" | "no-such-field" | "no-such-method" | "illegal-class-access" | "no-error" | "generic-error" | "no-such-class" | "no-such-field" | "no-such-method" | "illegal-class-access" |
"illegal-field-access" | "illegal-method-access" | "class-change-error" | "instantiation-error" { "illegal-field-access" | "illegal-method-access" | "class-change-error" | "instantiation-error" {
return newToken(VERIFICATION_ERROR_TYPE); return newToken(VERIFICATION_ERROR_TYPE);