mirror of
https://github.com/revanced/Apktool.git
synced 2025-01-23 02:07:36 +01:00
Add lambda experimental dalvik opcodes
* Add new -X/--experimental flag to [dis]assemble opcodes not in art yet * Add new opcodes liberate-variable, box-lambda, unbox-lambda, capture-variable, create-lambda, invoke-lambda * Add support for encoding 25x instructions * Adds LambdaTest to check new opcodes assemble/disassemble properly TODO: invoke-lambda-range Change-Id: I5c8bcbfa8b6cb9a13ef2017fce2d1b7fda6e11c3
This commit is contained in:
parent
d54c78d966
commit
58b7c27316
@ -41,8 +41,6 @@ import org.jf.dexlib2.iface.instruction.*;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
|
||||
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.iface.reference.Reference;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
@ -300,6 +298,11 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
writer.write(", ");
|
||||
writeThirdRegister(writer);
|
||||
break;
|
||||
case Format25x:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
writeInvoke25xRegisters(writer); // vC, {vD, ...}
|
||||
break;
|
||||
case Format35c:
|
||||
writeOpcode(writer);
|
||||
writer.write(' ');
|
||||
@ -425,6 +428,43 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||
writer.write('}');
|
||||
}
|
||||
|
||||
protected void writeInvoke25xRegisters(IndentingWriter writer) throws IOException {
|
||||
OneFixedFourParameterRegisterInstruction instruction =
|
||||
(OneFixedFourParameterRegisterInstruction)this.instruction;
|
||||
final int parameterRegCount = instruction.getParameterRegisterCount();
|
||||
|
||||
writeRegister(writer, instruction.getRegisterFixedC()); // fixed register always present
|
||||
|
||||
writer.write(", {");
|
||||
switch (parameterRegCount) {
|
||||
case 1:
|
||||
writeRegister(writer, instruction.getRegisterParameterD());
|
||||
break;
|
||||
case 2:
|
||||
writeRegister(writer, instruction.getRegisterParameterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterE());
|
||||
break;
|
||||
case 3:
|
||||
writeRegister(writer, instruction.getRegisterParameterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterE());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterF());
|
||||
break;
|
||||
case 4:
|
||||
writeRegister(writer, instruction.getRegisterParameterD());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterE());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterF());
|
||||
writer.write(", ");
|
||||
writeRegister(writer, instruction.getRegisterParameterG());
|
||||
break;
|
||||
}
|
||||
writer.write('}');
|
||||
}
|
||||
|
||||
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
|
||||
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
||||
|
||||
|
@ -67,7 +67,7 @@ public class baksmali {
|
||||
|
||||
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
|
||||
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
|
||||
options.apiLevel, options.checkPackagePrivateAccess);
|
||||
options.apiLevel, options.checkPackagePrivateAccess, options.experimental);
|
||||
|
||||
if (options.customInlineDefinitions != null) {
|
||||
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,
|
||||
|
@ -73,6 +73,7 @@ public class baksmaliOptions {
|
||||
public boolean noAccessorComments = false;
|
||||
public boolean allowOdex = false;
|
||||
public boolean deodex = false;
|
||||
public boolean experimental = false;
|
||||
public boolean ignoreErrors = false;
|
||||
public boolean checkPackagePrivateAccess = false;
|
||||
public boolean useImplicitReferences = false;
|
||||
|
@ -40,7 +40,7 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class dump {
|
||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
|
||||
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel, boolean experimental) throws IOException {
|
||||
if (dumpFileName != null) {
|
||||
Writer writer = null;
|
||||
|
||||
@ -52,7 +52,7 @@ public class dump {
|
||||
consoleWidth = 120;
|
||||
}
|
||||
|
||||
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel), dexFile);
|
||||
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel, experimental), dexFile);
|
||||
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
||||
annotator.writeAnnotations(writer);
|
||||
} catch (IOException ex) {
|
||||
|
@ -192,6 +192,9 @@ public class main {
|
||||
case 'x':
|
||||
options.deodex = true;
|
||||
break;
|
||||
case 'X':
|
||||
options.experimental = true;
|
||||
break;
|
||||
case 'm':
|
||||
options.noAccessorComments = true;
|
||||
break;
|
||||
@ -253,7 +256,8 @@ public class main {
|
||||
}
|
||||
|
||||
//Read in and parse the dex file
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel);
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry,
|
||||
options.apiLevel, options.experimental);
|
||||
|
||||
if (dexFile.isOdexFile()) {
|
||||
if (!options.deodex) {
|
||||
@ -270,13 +274,15 @@ public class main {
|
||||
if (dexFile instanceof DexBackedOdexFile) {
|
||||
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||
} else {
|
||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
|
||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
|
||||
options.experimental);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
|
||||
options.inlineResolver =
|
||||
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||
InlineMethodResolver.createInlineMethodResolver(
|
||||
((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||
}
|
||||
|
||||
boolean errorOccurred = false;
|
||||
@ -288,7 +294,7 @@ public class main {
|
||||
if (dumpFileName == null) {
|
||||
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
|
||||
}
|
||||
dump.dump(dexFile, dumpFileName, options.apiLevel);
|
||||
dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
|
||||
}
|
||||
|
||||
if (errorOccurred) {
|
||||
@ -352,6 +358,10 @@ public class main {
|
||||
"odex file")
|
||||
.create("x");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("enable experimental opcodes to be disassembled, even if they aren't necessarily supported in the Android runtime yet")
|
||||
.create("X");
|
||||
|
||||
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
|
||||
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
|
||||
" than the .register directive with the total number of register")
|
||||
@ -469,6 +479,7 @@ public class main {
|
||||
basicOptions.addOption(outputDirOption);
|
||||
basicOptions.addOption(noParameterRegistersOption);
|
||||
basicOptions.addOption(deodexerantOption);
|
||||
basicOptions.addOption(experimentalOption);
|
||||
basicOptions.addOption(useLocalsOption);
|
||||
basicOptions.addOption(sequentialLabelsOption);
|
||||
basicOptions.addOption(noDebugInfoOption);
|
||||
@ -496,9 +507,9 @@ public class main {
|
||||
options.addOption((Option)option);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
|
||||
private static List<String> getDefaultBootClassPathForApi(int apiLevel, boolean experimental) {
|
||||
if (apiLevel < 9) {
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
|
@ -85,7 +85,7 @@ public class AnalysisTest {
|
||||
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
|
||||
String dexFilePath = String.format("%s%sclasses.dex", test, File.separatorChar);
|
||||
|
||||
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15);
|
||||
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15, false);
|
||||
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
if (registerInfo) {
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 junit.framework.Assert;
|
||||
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.smali.SmaliTestUtils;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.util.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class BaksmaliTestUtils {
|
||||
public static void assertSmaliCompiledEquals(String source, String expected,
|
||||
baksmaliOptions options, boolean stripComments) throws IOException,
|
||||
RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali(source, options.apiLevel,
|
||||
options.experimental);
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
// Remove unnecessary whitespace and optionally strip all comments from smali file
|
||||
String normalizedExpected = expected;
|
||||
if (stripComments) {
|
||||
normalizedExpected = TextUtils.stripComments(normalizedExpected);
|
||||
}
|
||||
normalizedExpected = TextUtils.normalizeWhitespace(normalizedExpected);
|
||||
|
||||
String normalizedActual = stringWriter.toString();
|
||||
if (stripComments) {
|
||||
normalizedActual = TextUtils.stripComments(normalizedActual);
|
||||
}
|
||||
normalizedActual = TextUtils.normalizeWhitespace(normalizedActual);
|
||||
|
||||
// Assert that normalized strings are now equal
|
||||
Assert.assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public static void assertSmaliCompiledEquals(String source, String expected,
|
||||
baksmaliOptions options) throws IOException, RecognitionException {
|
||||
assertSmaliCompiledEquals(source, expected, options, false);
|
||||
}
|
||||
|
||||
public static void assertSmaliCompiledEquals(String source, String expected)
|
||||
throws IOException, RecognitionException {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
// Static helpers class; do not instantiate.
|
||||
private BaksmaliTestUtils() { throw new AssertionError(); }
|
||||
}
|
@ -31,22 +31,15 @@
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.smali.SmaliTestUtils;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.util.TextUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class ImplicitReferenceTest {
|
||||
@Test
|
||||
public void testImplicitMethodReferences() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".method public static main([Ljava/lang/String;)V\n" +
|
||||
@ -55,7 +48,7 @@ public class ImplicitReferenceTest {
|
||||
" invoke-static {p0}, LHelloWorld;->V()V\n" +
|
||||
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
||||
" return-void\n" +
|
||||
".end method");
|
||||
".end method";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -72,19 +65,12 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = true;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitMethodReferences() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".method public static main([Ljava/lang/String;)V\n" +
|
||||
@ -93,7 +79,7 @@ public class ImplicitReferenceTest {
|
||||
" invoke-static {p0}, LHelloWorld;->V()V\n" +
|
||||
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
||||
" return-void\n" +
|
||||
".end method");
|
||||
".end method";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -110,25 +96,18 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = false;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
|
||||
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
|
||||
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
|
||||
".field public static field4:Ljava/lang/Class; = I");
|
||||
".field public static field4:Ljava/lang/Class; = I";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -142,25 +121,18 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = true;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
|
||||
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
|
||||
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
|
||||
".field public static field4:Ljava/lang/Class; = I");
|
||||
".field public static field4:Ljava/lang/Class; = I";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -174,19 +146,12 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = false;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitFieldReferences() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".method public static main([Ljava/lang/String;)V\n" +
|
||||
@ -195,7 +160,7 @@ public class ImplicitReferenceTest {
|
||||
" sget v0, LHelloWorld;->I:I\n" +
|
||||
" sget v0, LHelloWorld;->V:I\n" +
|
||||
" return-void\n" +
|
||||
".end method");
|
||||
".end method";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -212,19 +177,12 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = true;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitFieldReferences() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".method public static main([Ljava/lang/String;)V\n" +
|
||||
@ -233,7 +191,7 @@ public class ImplicitReferenceTest {
|
||||
" sget v0, LHelloWorld;->I:I\n" +
|
||||
" sget v0, LHelloWorld;->V:I\n" +
|
||||
" return-void\n" +
|
||||
".end method");
|
||||
".end method";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -250,24 +208,17 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = false;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
|
||||
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
|
||||
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
|
||||
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -280,24 +231,17 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = true;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
|
||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
||||
String source = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
".super Ljava/lang/Object;\n" +
|
||||
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
|
||||
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
|
||||
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
|
||||
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
|
||||
|
||||
String expected = "" +
|
||||
".class public LHelloWorld;\n" +
|
||||
@ -310,13 +254,7 @@ public class ImplicitReferenceTest {
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.useImplicitReferences = false;
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
IndentingWriter writer = new IndentingWriter(stringWriter);
|
||||
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
|
||||
classDefinition.writeTo(writer);
|
||||
writer.close();
|
||||
|
||||
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
|
||||
TextUtils.normalizeWhitespace(stringWriter.toString()));
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 com.google.common.io.ByteStreams;
|
||||
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class LambdaTest {
|
||||
|
||||
@Test
|
||||
public void testHelloWorldLambda() throws IOException, RecognitionException {
|
||||
runTest("HelloWorldLambda");
|
||||
}
|
||||
|
||||
private void runTest(String fileName) throws IOException, RecognitionException {
|
||||
// Load file from resources as a stream
|
||||
String smaliFile = String.format("LambdaTest%s%s.smali", File.separatorChar, fileName);
|
||||
|
||||
InputStream smaliStream = LambdaTest.class.getClassLoader().
|
||||
getResourceAsStream(smaliFile);
|
||||
if (smaliStream == null) {
|
||||
Assert.fail("Could not load " + smaliFile);
|
||||
}
|
||||
|
||||
String source = readFully(smaliStream);
|
||||
|
||||
// Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
options.apiLevel = 23; // since we need at least level 23 for lambda opcodes
|
||||
options.experimental = true; // since these opcodes aren't implemented in runtime yet
|
||||
BaksmaliTestUtils.assertSmaliCompiledEquals(source, source, options, true);
|
||||
}
|
||||
|
||||
|
||||
public static String readFully(InputStream inputStream) throws IOException {
|
||||
return readFully(inputStream, "UTF-8");
|
||||
}
|
||||
|
||||
public static String readFully(InputStream inputStream, String encoding)
|
||||
throws IOException {
|
||||
return new String(ByteStreams.toByteArray(inputStream), encoding);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
.class public LHelloWorldLambda;
|
||||
|
||||
#Ye olde hello world application (with lambdas!)
|
||||
#To assemble and run this on a phone or emulator:
|
||||
#
|
||||
#java -jar smali.jar -o classes.dex HelloWorldLambda.smali HelloWorldFunctionalInterface.smali
|
||||
#zip HelloWorld.zip classes.dex
|
||||
#adb push HelloWorld.zip /data/local
|
||||
#adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld
|
||||
#
|
||||
#if you get out of memory type errors when running smali.jar, try
|
||||
#java -Xmx512m -jar smali.jar HelloWorldLambda.smali
|
||||
#instead
|
||||
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public static doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
.registers 6 # 4 parameters, 2 locals
|
||||
liberate-variable v0, p0, "helloworld"
|
||||
|
||||
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static main([Ljava/lang/String;)V
|
||||
.registers 9 # 1 parameter, 8 locals
|
||||
|
||||
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
|
||||
const-string v1, "Hello World!"
|
||||
const-string v2, "How" # vD
|
||||
const-string v3, "are" # vE
|
||||
const-string v4, "you" # vF
|
||||
const-string v5, "doing?" # vG
|
||||
|
||||
capture-variable v1, "helloworld"
|
||||
|
||||
# TODO: do I need to pass the type of the lambda's functional interface here as a type id?
|
||||
create-lambda v1, LHelloWorldLambda;->doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
# Method descriptor is not required here, because only the single-abstract method is ever invoked.
|
||||
invoke-lambda v1, {v2, v3, v4, v5}
|
||||
|
||||
box-lambda v6, v1
|
||||
invoke-virtual {v6, v2, v3, v4, v5}, LHelloWorldFunctionalInterface;->applyFourStrings(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
# FIXME: should be \HelloWorldFunctionalInterface; instead of L...;
|
||||
|
||||
# TODO: do we really need the type descriptor here at all?
|
||||
unbox-lambda v7, v6, LHelloWorldFunctionalInterface;
|
||||
invoke-lambda v7, {v2, v3, v4, v5}
|
||||
|
||||
return-void
|
||||
.end method
|
@ -79,7 +79,7 @@ public class AccessorTest {
|
||||
public void testAccessors() throws IOException {
|
||||
URL url = AccessorTest.class.getClassLoader().getResource("accessorTest.dex");
|
||||
Assert.assertNotNull(url);
|
||||
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15);
|
||||
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15, false);
|
||||
|
||||
SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses());
|
||||
|
||||
|
@ -45,22 +45,26 @@ import java.util.zip.ZipFile;
|
||||
|
||||
public final class DexFileFactory {
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(String path, int api) throws IOException {
|
||||
return loadDexFile(new File(path), "classes.dex", new Opcodes(api));
|
||||
public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental)
|
||||
throws IOException {
|
||||
return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException {
|
||||
return loadDexFile(dexFile, "classes.dex", new Opcodes(api));
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental)
|
||||
throws IOException {
|
||||
return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api) throws IOException {
|
||||
return loadDexFile(dexFile, dexEntry, new Opcodes(api));
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api,
|
||||
boolean experimental) throws IOException {
|
||||
return loadDexFile(dexFile, dexEntry, new Opcodes(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, @Nonnull Opcodes opcodes) throws IOException {
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry,
|
||||
@Nonnull Opcodes opcodes) throws IOException {
|
||||
ZipFile zipFile = null;
|
||||
boolean isZipFile = false;
|
||||
try {
|
||||
|
@ -51,6 +51,7 @@ public enum Format {
|
||||
Format22t(4),
|
||||
Format22x(4),
|
||||
Format23x(4),
|
||||
Format25x(4),
|
||||
Format30t(6),
|
||||
Format31c(6),
|
||||
Format31i(6),
|
||||
|
@ -269,13 +269,13 @@ public enum Opcode
|
||||
INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", minApi(14), ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", minApi(11), ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
|
||||
IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT_QUICK((short)0xf5, "iput-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT_QUICK((short)0xf5, "iput-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", maxApi(22), ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", maxApi(22), ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
|
||||
@ -285,7 +285,17 @@ public enum Opcode
|
||||
|
||||
PACKED_SWITCH_PAYLOAD((short)0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
|
||||
SPARSE_SWITCH_PAYLOAD((short)0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
|
||||
ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0);
|
||||
ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
|
||||
|
||||
// Reuse the deprecated f3-ff opcodes in Art:
|
||||
INVOKE_LAMBDA((short)0xf3, "invoke-lambda", minApi(23), ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
|
||||
// TODO: What about JUMBO support if the string ID is too large?
|
||||
CAPTURE_VARIABLE((short)0xf5, "capture-variable", minApi(23), ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
|
||||
CREATE_LAMBDA((short)0xf6, "create-lambda", minApi(23), ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
// TODO: do we need a capture/liberate wide?
|
||||
LIBERATE_VARIABLE((short)0xf7, "liberate-variable", minApi(23), ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
BOX_LAMBDA((short)0xf8, "box-lambda", minApi(23), ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
UNBOX_LAMBDA((short)0xf9, "unbox-lambda", minApi(23), ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
|
||||
|
||||
//if the instruction can throw an exception
|
||||
public static final int CAN_THROW = 0x1;
|
||||
@ -309,6 +319,8 @@ public enum Opcode
|
||||
public static final int JUMBO_OPCODE = 0x200;
|
||||
//if the instruction can initialize an uninitialized object reference
|
||||
public static final int CAN_INITIALIZE_REFERENCE = 0x400;
|
||||
//if the instruction is experimental (not potentially supported by Android runtime yet)
|
||||
public static final int EXPERIMENTAL = 0x800;
|
||||
|
||||
private static final int ALL_APIS = 0xFFFF0000;
|
||||
|
||||
@ -417,4 +429,8 @@ public enum Opcode
|
||||
public final boolean canInitializeReference() {
|
||||
return (flags & CAN_INITIALIZE_REFERENCE) != 0;
|
||||
}
|
||||
|
||||
public final boolean isExperimental() {
|
||||
return (flags & EXPERIMENTAL) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,14 @@ public class Opcodes {
|
||||
private final Opcode[] opcodesByValue;
|
||||
private final HashMap<String, Opcode> opcodesByName;
|
||||
|
||||
public Opcodes(int api) {
|
||||
public Opcodes(int api, boolean experimental) {
|
||||
opcodesByValue = new Opcode[256];
|
||||
opcodesByName = Maps.newHashMap();
|
||||
|
||||
for (Opcode opcode: Opcode.values()) {
|
||||
if (!opcode.format.isPayloadFormat) {
|
||||
if (api <= opcode.getMaxApi() && api >= opcode.getMinApi()) {
|
||||
if (api <= opcode.getMaxApi() && api >= opcode.getMinApi() &&
|
||||
(experimental || !opcode.isExperimental())) {
|
||||
opcodesByValue[opcode.value] = opcode;
|
||||
opcodesByName.put(opcode.name.toLowerCase(), opcode);
|
||||
}
|
||||
|
@ -170,18 +170,18 @@ public class ClassPath {
|
||||
|
||||
@Nonnull
|
||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
||||
int api) {
|
||||
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17);
|
||||
int api, boolean experimental) {
|
||||
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17, experimental);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
||||
int api, boolean checkPackagePrivateAccess) {
|
||||
int api, boolean checkPackagePrivateAccess, boolean experimental) {
|
||||
ArrayList<DexFile> dexFiles = Lists.newArrayList();
|
||||
|
||||
for (String classPathEntry: classPath) {
|
||||
try {
|
||||
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api));
|
||||
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
|
||||
} catch (ExceptionWithContext e){}
|
||||
}
|
||||
dexFiles.add(dexFile);
|
||||
@ -192,7 +192,8 @@ public class ClassPath {
|
||||
|
||||
@Nonnull
|
||||
private static DexFile loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
|
||||
@Nonnull String bootClassPathEntry, int api) {
|
||||
@Nonnull String bootClassPathEntry, int api,
|
||||
boolean experimental) {
|
||||
File rawEntry = new File(bootClassPathEntry);
|
||||
// strip off the path - we only care about the filename
|
||||
String entryName = rawEntry.getName();
|
||||
@ -227,7 +228,7 @@ public class ClassPath {
|
||||
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
|
||||
} else {
|
||||
try {
|
||||
return DexFileFactory.loadDexFile(file, api);
|
||||
return DexFileFactory.loadDexFile(file, api, experimental);
|
||||
} catch (DexFileFactory.NoClassesDexException ex) {
|
||||
// ignore and continue
|
||||
} catch (Exception ex) {
|
||||
|
@ -73,6 +73,7 @@ public class DumpFields {
|
||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||
String outFile = "fields.txt";
|
||||
int apiLevel = 15;
|
||||
boolean experimental = false;
|
||||
|
||||
for (int i=0; i<parsedOptions.length; i++) {
|
||||
Option option = parsedOptions[i];
|
||||
@ -88,6 +89,9 @@ public class DumpFields {
|
||||
case 'a':
|
||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
case 'X':
|
||||
experimental = true;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
@ -107,9 +111,9 @@ public class DumpFields {
|
||||
}
|
||||
|
||||
try {
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
|
||||
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
|
||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel);
|
||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
|
||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
for (ClassDef classDef: dexFile.getClasses()) {
|
||||
@ -163,8 +167,14 @@ public class DumpFields {
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
|
||||
"supported by the android runtime yet.")
|
||||
.create("X");
|
||||
|
||||
options.addOption(classPathDirOption);
|
||||
options.addOption(outputFileOption);
|
||||
options.addOption(apiLevelOption);
|
||||
options.addOption(experimentalOption);
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ public class DumpVtables {
|
||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||
String outFile = "vtables.txt";
|
||||
int apiLevel = 15;
|
||||
boolean experimental = false;
|
||||
|
||||
for (int i=0; i<parsedOptions.length; i++) {
|
||||
Option option = parsedOptions[i];
|
||||
@ -86,6 +87,9 @@ public class DumpVtables {
|
||||
case 'a':
|
||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
case 'X':
|
||||
experimental = true;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
@ -105,9 +109,9 @@ public class DumpVtables {
|
||||
}
|
||||
|
||||
try {
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
|
||||
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
|
||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel);
|
||||
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
|
||||
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
for (ClassDef classDef: dexFile.getClasses()) {
|
||||
@ -167,8 +171,14 @@ public class DumpVtables {
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
|
||||
"supported by the android runtime yet.")
|
||||
.create("X");
|
||||
|
||||
options.addOption(classPathDirOption);
|
||||
options.addOption(outputFileOption);
|
||||
options.addOption(apiLevelOption);
|
||||
options.addOption(experimentalOption);
|
||||
}
|
||||
}
|
||||
|
@ -511,82 +511,90 @@ public class MutableMethodImplementation implements MethodImplementation {
|
||||
@Nonnull Instruction instruction) {
|
||||
switch (instruction.getOpcode().format) {
|
||||
case Format10t:
|
||||
setInstruction(location, newBuilderInstruction10t(location.codeAddress, codeAddressToIndex,
|
||||
(Instruction10t)instruction));
|
||||
setInstruction(location, newBuilderInstruction10t(location.codeAddress,
|
||||
codeAddressToIndex,
|
||||
(Instruction10t) instruction));
|
||||
return;
|
||||
case Format10x:
|
||||
setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction));
|
||||
setInstruction(location, newBuilderInstruction10x((Instruction10x) instruction));
|
||||
return;
|
||||
case Format11n:
|
||||
setInstruction(location, newBuilderInstruction11n((Instruction11n)instruction));
|
||||
setInstruction(location, newBuilderInstruction11n((Instruction11n) instruction));
|
||||
return;
|
||||
case Format11x:
|
||||
setInstruction(location, newBuilderInstruction11x((Instruction11x)instruction));
|
||||
setInstruction(location, newBuilderInstruction11x((Instruction11x) instruction));
|
||||
return;
|
||||
case Format12x:
|
||||
setInstruction(location, newBuilderInstruction12x((Instruction12x)instruction));
|
||||
setInstruction(location, newBuilderInstruction12x((Instruction12x) instruction));
|
||||
return;
|
||||
case Format20bc:
|
||||
setInstruction(location, newBuilderInstruction20bc((Instruction20bc)instruction));
|
||||
setInstruction(location, newBuilderInstruction20bc((Instruction20bc) instruction));
|
||||
return;
|
||||
case Format20t:
|
||||
setInstruction(location, newBuilderInstruction20t(location.codeAddress, codeAddressToIndex,
|
||||
(Instruction20t)instruction));
|
||||
setInstruction(location, newBuilderInstruction20t(location.codeAddress,
|
||||
codeAddressToIndex,
|
||||
(Instruction20t) instruction));
|
||||
return;
|
||||
case Format21c:
|
||||
setInstruction(location, newBuilderInstruction21c((Instruction21c)instruction));
|
||||
setInstruction(location, newBuilderInstruction21c((Instruction21c) instruction));
|
||||
return;
|
||||
case Format21ih:
|
||||
setInstruction(location, newBuilderInstruction21ih((Instruction21ih)instruction));
|
||||
setInstruction(location, newBuilderInstruction21ih((Instruction21ih) instruction));
|
||||
return;
|
||||
case Format21lh:
|
||||
setInstruction(location, newBuilderInstruction21lh((Instruction21lh)instruction));
|
||||
setInstruction(location, newBuilderInstruction21lh((Instruction21lh) instruction));
|
||||
return;
|
||||
case Format21s:
|
||||
setInstruction(location, newBuilderInstruction21s((Instruction21s)instruction));
|
||||
setInstruction(location, newBuilderInstruction21s((Instruction21s) instruction));
|
||||
return;
|
||||
case Format21t:
|
||||
setInstruction(location, newBuilderInstruction21t(location.codeAddress, codeAddressToIndex,
|
||||
(Instruction21t)instruction));
|
||||
setInstruction(location, newBuilderInstruction21t(location.codeAddress,
|
||||
codeAddressToIndex,
|
||||
(Instruction21t) instruction));
|
||||
return;
|
||||
case Format22b:
|
||||
setInstruction(location, newBuilderInstruction22b((Instruction22b)instruction));
|
||||
setInstruction(location, newBuilderInstruction22b((Instruction22b) instruction));
|
||||
return;
|
||||
case Format22c:
|
||||
setInstruction(location, newBuilderInstruction22c((Instruction22c)instruction));
|
||||
setInstruction(location, newBuilderInstruction22c((Instruction22c) instruction));
|
||||
return;
|
||||
case Format22s:
|
||||
setInstruction(location, newBuilderInstruction22s((Instruction22s)instruction));
|
||||
setInstruction(location, newBuilderInstruction22s((Instruction22s) instruction));
|
||||
return;
|
||||
case Format22t:
|
||||
setInstruction(location, newBuilderInstruction22t(location.codeAddress, codeAddressToIndex,
|
||||
(Instruction22t)instruction));
|
||||
setInstruction(location, newBuilderInstruction22t(location.codeAddress,
|
||||
codeAddressToIndex,
|
||||
(Instruction22t) instruction));
|
||||
return;
|
||||
case Format22x:
|
||||
setInstruction(location, newBuilderInstruction22x((Instruction22x)instruction));
|
||||
setInstruction(location, newBuilderInstruction22x((Instruction22x) instruction));
|
||||
return;
|
||||
case Format23x:
|
||||
setInstruction(location, newBuilderInstruction23x((Instruction23x)instruction));
|
||||
setInstruction(location, newBuilderInstruction23x((Instruction23x) instruction));
|
||||
return;
|
||||
case Format25x:
|
||||
setInstruction(location, newBuilderInstruction25x((Instruction25x) instruction));
|
||||
return;
|
||||
case Format30t:
|
||||
setInstruction(location, newBuilderInstruction30t(location.codeAddress, codeAddressToIndex,
|
||||
(Instruction30t)instruction));
|
||||
setInstruction(location, newBuilderInstruction30t(location.codeAddress,
|
||||
codeAddressToIndex,
|
||||
(Instruction30t) instruction));
|
||||
return;
|
||||
case Format31c:
|
||||
setInstruction(location, newBuilderInstruction31c((Instruction31c)instruction));
|
||||
setInstruction(location, newBuilderInstruction31c((Instruction31c) instruction));
|
||||
return;
|
||||
case Format31i:
|
||||
setInstruction(location, newBuilderInstruction31i((Instruction31i)instruction));
|
||||
setInstruction(location, newBuilderInstruction31i((Instruction31i) instruction));
|
||||
return;
|
||||
case Format31t:
|
||||
setInstruction(location, newBuilderInstruction31t(location, codeAddressToIndex,
|
||||
(Instruction31t)instruction));
|
||||
(Instruction31t) instruction));
|
||||
return;
|
||||
case Format32x:
|
||||
setInstruction(location, newBuilderInstruction32x((Instruction32x)instruction));
|
||||
setInstruction(location, newBuilderInstruction32x((Instruction32x) instruction));
|
||||
return;
|
||||
case Format35c:
|
||||
setInstruction(location, newBuilderInstruction35c((Instruction35c)instruction));
|
||||
setInstruction(location, newBuilderInstruction35c((Instruction35c) instruction));
|
||||
return;
|
||||
case Format3rc:
|
||||
setInstruction(location, newBuilderInstruction3rc((Instruction3rc)instruction));
|
||||
@ -820,6 +828,18 @@ public class MutableMethodImplementation implements MethodImplementation {
|
||||
instruction.getReference());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private BuilderInstruction25x newBuilderInstruction25x(@Nonnull Instruction25x instruction) {
|
||||
return new BuilderInstruction25x(
|
||||
instruction.getOpcode(),
|
||||
instruction.getParameterRegisterCount(),
|
||||
instruction.getRegisterFixedC(),
|
||||
instruction.getRegisterParameterD(),
|
||||
instruction.getRegisterParameterE(),
|
||||
instruction.getRegisterParameterF(),
|
||||
instruction.getRegisterParameterG());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) {
|
||||
return new BuilderInstruction3rc(
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.dexlib2.builder.instruction;
|
||||
|
||||
import org.jf.dexlib2.Format;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.builder.BuilderInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction25x;
|
||||
import org.jf.dexlib2.util.Preconditions;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BuilderInstruction25x extends BuilderInstruction implements Instruction25x {
|
||||
public static final Format FORMAT = Format.Format25x;
|
||||
|
||||
protected final int parameterRegisterCount;
|
||||
protected final int registerClosure;
|
||||
protected final int registerD;
|
||||
protected final int registerE;
|
||||
protected final int registerF;
|
||||
protected final int registerG;
|
||||
|
||||
public BuilderInstruction25x(@Nonnull Opcode opcode,
|
||||
int parameterRegisterCount,
|
||||
int registerClosure,
|
||||
int registerD,
|
||||
int registerE,
|
||||
int registerF,
|
||||
int registerG) {
|
||||
super(opcode);
|
||||
this.parameterRegisterCount =
|
||||
Preconditions.check25xParameterRegisterCount(parameterRegisterCount);
|
||||
this.registerClosure = Preconditions.checkNibbleRegister(registerClosure); //at least 1 reg
|
||||
this.registerD = (parameterRegisterCount>0) ?
|
||||
Preconditions.checkNibbleRegister(registerD) : 0;
|
||||
this.registerE = (parameterRegisterCount>1) ?
|
||||
Preconditions.checkNibbleRegister(registerE) : 0;
|
||||
this.registerF = (parameterRegisterCount>2) ?
|
||||
Preconditions.checkNibbleRegister(registerF) : 0;
|
||||
this.registerG = (parameterRegisterCount>3) ?
|
||||
Preconditions.checkNibbleRegister(registerG) : 0;
|
||||
}
|
||||
|
||||
@Override public int getRegisterCount() { return parameterRegisterCount + 1; }
|
||||
@Override public int getParameterRegisterCount() { return parameterRegisterCount; }
|
||||
@Override public int getRegisterFixedC() { return registerClosure; }
|
||||
@Override public int getRegisterParameterD() { return registerD; }
|
||||
@Override public int getRegisterParameterE() { return registerE; }
|
||||
@Override public int getRegisterParameterF() { return registerF; }
|
||||
@Override public int getRegisterParameterG() { return registerG; }
|
||||
|
||||
@Override public Format getFormat() { return FORMAT; }
|
||||
}
|
@ -115,6 +115,8 @@ public abstract class DexBackedInstruction implements Instruction {
|
||||
return new DexBackedInstruction22x(dexFile, opcode, instructionStartOffset);
|
||||
case Format23x:
|
||||
return new DexBackedInstruction23x(dexFile, opcode, instructionStartOffset);
|
||||
case Format25x:
|
||||
return new DexBackedInstruction25x(dexFile, opcode, instructionStartOffset);
|
||||
case Format30t:
|
||||
return new DexBackedInstruction30t(dexFile, opcode, instructionStartOffset);
|
||||
case Format31c:
|
||||
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.dexlib2.dexbacked.instruction;
|
||||
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction25x;
|
||||
import org.jf.util.NibbleUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class DexBackedInstruction25x extends DexBackedInstruction implements Instruction25x {
|
||||
public DexBackedInstruction25x(@Nonnull DexBackedDexFile dexFile,
|
||||
@Nonnull Opcode opcode,
|
||||
int instructionStart) {
|
||||
super(dexFile, opcode, instructionStart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterCount() {
|
||||
return getParameterRegisterCount() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameterRegisterCount() {
|
||||
return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterFixedC() {
|
||||
return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterParameterD() {
|
||||
return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterParameterE() {
|
||||
return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterParameterF() {
|
||||
return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRegisterParameterG() {
|
||||
return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 1));
|
||||
}
|
||||
|
||||
}
|
@ -129,6 +129,9 @@ public class CodeItem {
|
||||
case Format10x:
|
||||
annotateInstruction10x(out, instruction);
|
||||
break;
|
||||
case Format25x:
|
||||
annotateInstruction25x(out, (Instruction25x) instruction);
|
||||
break;
|
||||
case Format35c:
|
||||
annotateInstruction35c(out, (Instruction35c)instruction);
|
||||
break;
|
||||
@ -282,6 +285,30 @@ public class CodeItem {
|
||||
instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
|
||||
}
|
||||
|
||||
private void annotateInstruction25x(@Nonnull AnnotatedBytes out,
|
||||
@Nonnull Instruction25x instruction) {
|
||||
List<String> args = Lists.newArrayList();
|
||||
|
||||
int registerCount = instruction.getRegisterCount(); //at least 1.
|
||||
if (registerCount == 2) {
|
||||
args.add(formatRegister(instruction.getRegisterParameterD()));
|
||||
} else if (registerCount == 3) {
|
||||
args.add(formatRegister(instruction.getRegisterParameterD()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterE()));
|
||||
} else if (registerCount == 4) {
|
||||
args.add(formatRegister(instruction.getRegisterParameterD()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterE()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterF()));
|
||||
} else if (registerCount == 5) {
|
||||
args.add(formatRegister(instruction.getRegisterParameterD()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterE()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterF()));
|
||||
args.add(formatRegister(instruction.getRegisterParameterG()));
|
||||
}
|
||||
out.annotate(6, String.format("%s %s, {%s}",
|
||||
instruction.getOpcode().name, instruction.getRegisterFixedC(), Joiner.on(", ").join(args)));
|
||||
}
|
||||
|
||||
private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
|
||||
int startRegister = instruction.getStartRegister();
|
||||
int endRegister = startRegister + instruction.getRegisterCount() - 1;
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.dexlib2.iface.instruction;
|
||||
|
||||
public interface OneFixedFourParameterRegisterInstruction extends VariableRegisterInstruction {
|
||||
int getRegisterFixedC();
|
||||
int getRegisterParameterD();
|
||||
int getRegisterParameterE();
|
||||
int getRegisterParameterF();
|
||||
int getRegisterParameterG();
|
||||
|
||||
/** Returns the count of just the parameter register counts; in range of [0, 4] */
|
||||
int getParameterRegisterCount();
|
||||
|
||||
/** Includes the total sum of both fixed and parameter register counts; at least 1 */
|
||||
@Override
|
||||
int getRegisterCount();
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.dexlib2.iface.instruction.formats;
|
||||
|
||||
import org.jf.dexlib2.iface.instruction.OneFixedFourParameterRegisterInstruction;
|
||||
|
||||
public interface Instruction25x extends OneFixedFourParameterRegisterInstruction {
|
||||
}
|
@ -97,6 +97,8 @@ public abstract class ImmutableInstruction implements Instruction {
|
||||
return ImmutableInstruction22x.of((Instruction22x)instruction);
|
||||
case Format23x:
|
||||
return ImmutableInstruction23x.of((Instruction23x)instruction);
|
||||
case Format25x:
|
||||
return ImmutableInstruction25x.of((Instruction25x) instruction);
|
||||
case Format30t:
|
||||
return ImmutableInstruction30t.of((Instruction30t)instruction);
|
||||
case Format31c:
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.dexlib2.immutable.instruction;
|
||||
|
||||
import org.jf.dexlib2.Format;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction25x;
|
||||
import org.jf.dexlib2.util.Preconditions;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ImmutableInstruction25x extends ImmutableInstruction implements Instruction25x {
|
||||
public static final Format FORMAT = Format.Format25x;
|
||||
|
||||
protected final int parameterRegisterCount;
|
||||
protected final int registerClosure;
|
||||
protected final int registerD;
|
||||
protected final int registerE;
|
||||
protected final int registerF;
|
||||
protected final int registerG;
|
||||
|
||||
public ImmutableInstruction25x(@Nonnull Opcode opcode,
|
||||
int parameterRegisterCount,
|
||||
int registerClosure,
|
||||
int registerD,
|
||||
int registerE,
|
||||
int registerF,
|
||||
int registerG) {
|
||||
super(opcode);
|
||||
this.parameterRegisterCount =
|
||||
Preconditions.check25xParameterRegisterCount(parameterRegisterCount);
|
||||
this.registerClosure = Preconditions.checkNibbleRegister(registerClosure);
|
||||
this.registerD = (parameterRegisterCount>0) ?
|
||||
Preconditions.checkNibbleRegister(registerD) : 0;
|
||||
this.registerE = (parameterRegisterCount>1) ?
|
||||
Preconditions.checkNibbleRegister(registerE) : 0;
|
||||
this.registerF = (parameterRegisterCount>2) ?
|
||||
Preconditions.checkNibbleRegister(registerF) : 0;
|
||||
this.registerG = (parameterRegisterCount>3) ?
|
||||
Preconditions.checkNibbleRegister(registerG) : 0;
|
||||
}
|
||||
|
||||
public static ImmutableInstruction25x of(Instruction25x instruction) {
|
||||
if (instruction instanceof ImmutableInstruction25x) {
|
||||
return (ImmutableInstruction25x)instruction;
|
||||
}
|
||||
return new ImmutableInstruction25x(
|
||||
instruction.getOpcode(),
|
||||
instruction.getRegisterCount(),
|
||||
instruction.getRegisterFixedC(),
|
||||
instruction.getRegisterParameterD(),
|
||||
instruction.getRegisterParameterE(),
|
||||
instruction.getRegisterParameterF(),
|
||||
instruction.getRegisterParameterG());
|
||||
}
|
||||
|
||||
|
||||
@Override public int getParameterRegisterCount() { return parameterRegisterCount; }
|
||||
@Override public int getRegisterCount() { return parameterRegisterCount + 1; }
|
||||
|
||||
@Override public int getRegisterFixedC() { return registerClosure; }
|
||||
@Override public int getRegisterParameterD() { return registerD; }
|
||||
@Override public int getRegisterParameterE() { return registerE; }
|
||||
@Override public int getRegisterParameterF() { return registerF; }
|
||||
@Override public int getRegisterParameterG() { return registerG; }
|
||||
|
||||
@Override public Format getFormat() { return FORMAT; }
|
||||
}
|
@ -133,6 +133,15 @@ public class Preconditions {
|
||||
return registerCount;
|
||||
}
|
||||
|
||||
public static int check25xParameterRegisterCount(int registerCount) {
|
||||
if (registerCount < 0 || registerCount > 4) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid parameter register count: %d. " +
|
||||
"Must be between 0 and 4, inclusive.", registerCount));
|
||||
}
|
||||
return registerCount;
|
||||
}
|
||||
|
||||
public static int checkRegisterRangeCount(int registerCount) {
|
||||
if ((registerCount & 0xFFFFFF00) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -976,6 +976,9 @@ public abstract class DexWriter<
|
||||
case Format23x:
|
||||
instructionWriter.write((Instruction23x)instruction);
|
||||
break;
|
||||
case Format25x:
|
||||
instructionWriter.write((Instruction25x)instruction);
|
||||
break;
|
||||
case Format30t:
|
||||
instructionWriter.write((Instruction30t)instruction);
|
||||
break;
|
||||
|
@ -317,6 +317,19 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
}
|
||||
}
|
||||
|
||||
public void write(@Nonnull Instruction25x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(
|
||||
instruction.getRegisterParameterG(), instruction.getParameterRegisterCount()));
|
||||
writer.write(packNibbles(
|
||||
instruction.getRegisterFixedC(), instruction.getRegisterParameterD()));
|
||||
writer.write(packNibbles(
|
||||
instruction.getRegisterParameterE(), instruction.getRegisterParameterF()));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
public void write(@Nonnull Instruction3rc instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
|
@ -69,7 +69,7 @@ public class CustomMethodInlineTableTest {
|
||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15);
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
|
||||
@ -96,7 +96,7 @@ public class CustomMethodInlineTableTest {
|
||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15);
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
|
||||
@ -123,7 +123,7 @@ public class CustomMethodInlineTableTest {
|
||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15);
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class DexWriterTest {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dataStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
|
||||
ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(dbClassDef);
|
||||
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
|
||||
|
@ -92,7 +92,7 @@ public class JumboStringConversionTest {
|
||||
MemoryDataStore dexStore = new MemoryDataStore();
|
||||
dexBuilder.writeTo(dexStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
|
||||
|
||||
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(classDef);
|
||||
@ -189,7 +189,7 @@ public class JumboStringConversionTest {
|
||||
MemoryDataStore dexStore = new MemoryDataStore();
|
||||
dexBuilder.writeTo(dexStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
|
||||
|
||||
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(classDef);
|
||||
|
@ -0,0 +1,8 @@
|
||||
# Functional interface used by HelloWorld.smali
|
||||
# Required in order to reify the lambda with create-lambda or unbox-lambda instructions
|
||||
|
||||
.class public abstract interface LHelloWorldFunctionalInterface;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public abstract applyFourStrings(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
.end method
|
@ -0,0 +1,57 @@
|
||||
.class public LHelloWorldLambda;
|
||||
|
||||
#Ye olde hello world application (with lambdas!)
|
||||
#To assemble and run this on a phone or emulator:
|
||||
#
|
||||
#java -jar smali.jar -o classes.dex HelloWorldLambda.smali HelloWorldFunctionalInterface.smali
|
||||
#zip HelloWorld.zip classes.dex
|
||||
#adb push HelloWorld.zip /data/local
|
||||
#adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld
|
||||
#
|
||||
#if you get out of memory type errors when running smali.jar, try
|
||||
#java -Xmx512m -jar smali.jar HelloWorldLambda.smali
|
||||
#instead
|
||||
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.method public static main([Ljava/lang/String;)V
|
||||
.registers 9 # 1 parameter, 8 locals
|
||||
|
||||
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
|
||||
const-string v1, "Hello World!"
|
||||
const-string v2, "How" # vD
|
||||
const-string v3, "are" # vE
|
||||
const-string v4, "you" # vF
|
||||
const-string v5, "doing?" # vG
|
||||
|
||||
capture-variable v1, "helloworld"
|
||||
|
||||
# TODO: do I need to pass the type of the lambda's functional interface here as a type id?
|
||||
create-lambda v1, LHelloWorldLambda;->doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
# Method descriptor is not required here, because only the single-abstract method is ever invoked.
|
||||
invoke-lambda v1, {v2, v3, v4, v5}
|
||||
|
||||
box-lambda v6, v1 # The type of v6 is now 'LHelloWorldFunctionalInterface;'
|
||||
invoke-virtual {v6, v2, v3, v4, v5}, LHelloWorldFunctionalInterface;->applyFourStrings(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
# FIXME: should be \HelloWorldFunctionalInterface; instead of L...;
|
||||
|
||||
# TODO: do we really need the type descriptor here at all?
|
||||
unbox-lambda v7, v6, LHelloWorldFunctionalInterface; # The type of v7 is now \HelloWorldFunctionalInterface;
|
||||
invoke-lambda v7, {v2, v3, v4, v5}
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static doHelloWorld(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
.registers 6 # 4 parameters, 2 locals
|
||||
|
||||
# This helloworld variable is brought to you by the variable liberation front
|
||||
liberate-variable v0, p0, "helloworld"
|
||||
|
||||
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
|
||||
invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
|
||||
return-void
|
||||
.end method
|
@ -86,6 +86,8 @@ tokens {
|
||||
INSTRUCTION_FORMAT21c_FIELD_ODEX;
|
||||
INSTRUCTION_FORMAT21c_STRING;
|
||||
INSTRUCTION_FORMAT21c_TYPE;
|
||||
INSTRUCTION_FORMAT21c_LAMBDA;
|
||||
INSTRUCTION_FORMAT21c_METHOD;
|
||||
INSTRUCTION_FORMAT21ih;
|
||||
INSTRUCTION_FORMAT21lh;
|
||||
INSTRUCTION_FORMAT21s;
|
||||
@ -94,12 +96,14 @@ tokens {
|
||||
INSTRUCTION_FORMAT22c_FIELD;
|
||||
INSTRUCTION_FORMAT22c_FIELD_ODEX;
|
||||
INSTRUCTION_FORMAT22c_TYPE;
|
||||
INSTRUCTION_FORMAT22c_STRING;
|
||||
INSTRUCTION_FORMAT22cs_FIELD;
|
||||
INSTRUCTION_FORMAT22s;
|
||||
INSTRUCTION_FORMAT22s_OR_ID;
|
||||
INSTRUCTION_FORMAT22t;
|
||||
INSTRUCTION_FORMAT22x;
|
||||
INSTRUCTION_FORMAT23x;
|
||||
INSTRUCTION_FORMAT25x;
|
||||
INSTRUCTION_FORMAT30t;
|
||||
INSTRUCTION_FORMAT31c;
|
||||
INSTRUCTION_FORMAT31i;
|
||||
@ -209,6 +213,8 @@ tokens {
|
||||
I_STATEMENT_FORMAT21c_TYPE;
|
||||
I_STATEMENT_FORMAT21c_FIELD;
|
||||
I_STATEMENT_FORMAT21c_STRING;
|
||||
I_STATEMENT_FORMAT21c_LAMBDA;
|
||||
I_STATEMENT_FORMAT21c_METHOD;
|
||||
I_STATEMENT_FORMAT21ih;
|
||||
I_STATEMENT_FORMAT21lh;
|
||||
I_STATEMENT_FORMAT21s;
|
||||
@ -216,10 +222,12 @@ tokens {
|
||||
I_STATEMENT_FORMAT22b;
|
||||
I_STATEMENT_FORMAT22c_FIELD;
|
||||
I_STATEMENT_FORMAT22c_TYPE;
|
||||
I_STATEMENT_FORMAT22c_STRING;
|
||||
I_STATEMENT_FORMAT22s;
|
||||
I_STATEMENT_FORMAT22t;
|
||||
I_STATEMENT_FORMAT22x;
|
||||
I_STATEMENT_FORMAT23x;
|
||||
I_STATEMENT_FORMAT25x;
|
||||
I_STATEMENT_FORMAT30t;
|
||||
I_STATEMENT_FORMAT31c;
|
||||
I_STATEMENT_FORMAT31i;
|
||||
@ -252,7 +260,7 @@ import org.jf.dexlib2.Opcodes;
|
||||
private boolean verboseErrors = false;
|
||||
private boolean allowOdex = false;
|
||||
private int apiLevel = 15;
|
||||
private Opcodes opcodes = new Opcodes(apiLevel);
|
||||
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||
|
||||
public void setVerboseErrors(boolean verboseErrors) {
|
||||
this.verboseErrors = verboseErrors;
|
||||
@ -262,8 +270,8 @@ import org.jf.dexlib2.Opcodes;
|
||||
this.allowOdex = allowOdex;
|
||||
}
|
||||
|
||||
public void setApiLevel(int apiLevel) {
|
||||
this.opcodes = new Opcodes(apiLevel);
|
||||
public void setApiLevel(int apiLevel, boolean experimental) {
|
||||
this.opcodes = new Opcodes(apiLevel, experimental);
|
||||
this.apiLevel = apiLevel;
|
||||
}
|
||||
|
||||
@ -561,14 +569,18 @@ simple_name
|
||||
| INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX]
|
||||
| INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING]
|
||||
| INSTRUCTION_FORMAT21c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_TYPE]
|
||||
| INSTRUCTION_FORMAT21c_LAMBDA -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_LAMBDA]
|
||||
| INSTRUCTION_FORMAT21c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_METHOD]
|
||||
| INSTRUCTION_FORMAT21t -> SIMPLE_NAME[$INSTRUCTION_FORMAT21t]
|
||||
| INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD]
|
||||
| INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX]
|
||||
| INSTRUCTION_FORMAT22c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_TYPE]
|
||||
| INSTRUCTION_FORMAT22c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_STRING]
|
||||
| INSTRUCTION_FORMAT22cs_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22cs_FIELD]
|
||||
| INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID]
|
||||
| INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t]
|
||||
| INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x]
|
||||
| INSTRUCTION_FORMAT25x -> SIMPLE_NAME[$INSTRUCTION_FORMAT25x]
|
||||
| INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID]
|
||||
| INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t]
|
||||
| INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD]
|
||||
@ -806,6 +818,8 @@ instruction
|
||||
| insn_format21c_field_odex
|
||||
| insn_format21c_string
|
||||
| insn_format21c_type
|
||||
| insn_format21c_lambda
|
||||
| insn_format21c_method
|
||||
| insn_format21ih
|
||||
| insn_format21lh
|
||||
| insn_format21s
|
||||
@ -814,11 +828,13 @@ instruction
|
||||
| insn_format22c_field
|
||||
| insn_format22c_field_odex
|
||||
| insn_format22c_type
|
||||
| insn_format22c_string
|
||||
| insn_format22cs_field
|
||||
| insn_format22s
|
||||
| insn_format22t
|
||||
| insn_format22x
|
||||
| insn_format23x
|
||||
| insn_format25x
|
||||
| insn_format30t
|
||||
| insn_format31c
|
||||
| insn_format31i
|
||||
@ -912,6 +928,16 @@ insn_format21c_type
|
||||
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor
|
||||
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor);
|
||||
|
||||
insn_format21c_lambda
|
||||
: //e.g. capture-variable v1, "foobar"
|
||||
INSTRUCTION_FORMAT21c_LAMBDA REGISTER COMMA STRING_LITERAL
|
||||
-> ^(I_STATEMENT_FORMAT21c_LAMBDA[$start, "I_STATEMENT_FORMAT21c_LAMBDA"] INSTRUCTION_FORMAT21c_LAMBDA REGISTER STRING_LITERAL);
|
||||
|
||||
insn_format21c_method
|
||||
: //e.g. create-lambda v1, java/io/PrintStream/print(Ljava/lang/Stream;)V
|
||||
INSTRUCTION_FORMAT21c_METHOD REGISTER COMMA method_reference
|
||||
-> ^(I_STATEMENT_FORMAT21c_METHOD[$start, "I_STATEMENT_FORMAT21c_METHOD"] INSTRUCTION_FORMAT21c_METHOD REGISTER method_reference);
|
||||
|
||||
insn_format21ih
|
||||
: //e.g. const/high16 v1, 1234
|
||||
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal
|
||||
@ -957,6 +983,11 @@ insn_format22c_type
|
||||
INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor
|
||||
-> ^(I_STATEMENT_FORMAT22c_TYPE[$start, "I_STATEMENT_FORMAT22c_TYPE"] INSTRUCTION_FORMAT22c_TYPE REGISTER REGISTER nonvoid_type_descriptor);
|
||||
|
||||
insn_format22c_string
|
||||
: //e.g. liberate-variable v0, v1, "baz"
|
||||
INSTRUCTION_FORMAT22c_STRING REGISTER COMMA REGISTER COMMA STRING_LITERAL
|
||||
-> ^(I_STATEMENT_FORMAT22c_STRING[$start, "I_STATEMENT_FORMAT22c_STRING"] INSTRUCTION_FORMAT22c_STRING REGISTER REGISTER STRING_LITERAL);
|
||||
|
||||
insn_format22cs_field
|
||||
: //e.g. iget-quick v0, v1, field@0xc
|
||||
INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET
|
||||
@ -984,6 +1015,11 @@ insn_format23x
|
||||
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER
|
||||
-> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER REGISTER);
|
||||
|
||||
insn_format25x
|
||||
: //e.g. invoke-lambda vClosure, {vA, vB, vC, vD} -- up to 4 parameters + the closure.
|
||||
INSTRUCTION_FORMAT25x REGISTER COMMA OPEN_BRACE register_list CLOSE_BRACE
|
||||
-> ^(I_STATEMENT_FORMAT25x[$start, "I_STATEMENT_FORMAT25x"] INSTRUCTION_FORMAT25x REGISTER register_list);
|
||||
|
||||
insn_format30t
|
||||
: //e.g. goto/32 endloop:
|
||||
INSTRUCTION_FORMAT30t label_ref
|
||||
|
@ -77,15 +77,15 @@ import java.util.*;
|
||||
public String classType;
|
||||
private boolean verboseErrors = false;
|
||||
private int apiLevel = 15;
|
||||
private Opcodes opcodes = new Opcodes(apiLevel);
|
||||
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||
private DexBuilder dexBuilder;
|
||||
|
||||
public void setDexBuilder(DexBuilder dexBuilder) {
|
||||
this.dexBuilder = dexBuilder;
|
||||
}
|
||||
|
||||
public void setApiLevel(int apiLevel) {
|
||||
this.opcodes = new Opcodes(apiLevel);
|
||||
public void setApiLevel(int apiLevel, boolean experimental) {
|
||||
this.opcodes = new Opcodes(apiLevel, experimental);
|
||||
this.apiLevel = apiLevel;
|
||||
}
|
||||
|
||||
@ -674,6 +674,22 @@ register_list returns[byte[\] registers, byte registerCount]
|
||||
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text);
|
||||
})*);
|
||||
|
||||
register_list4 returns[byte[\] registers, byte registerCount]
|
||||
@init
|
||||
{
|
||||
$registers = new byte[4];
|
||||
$registerCount = 0;
|
||||
}
|
||||
: ^(I_REGISTER_LIST
|
||||
(REGISTER
|
||||
{
|
||||
if ($registerCount == 4) {
|
||||
throw new SemanticException(input, $I_REGISTER_LIST, "A list4 of registers can only have a maximum of 4 " +
|
||||
"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?)?)
|
||||
{
|
||||
@ -726,6 +742,8 @@ instruction
|
||||
| insn_format21c_field
|
||||
| insn_format21c_string
|
||||
| insn_format21c_type
|
||||
| insn_format21c_lambda
|
||||
| insn_format21c_method
|
||||
| insn_format21ih
|
||||
| insn_format21lh
|
||||
| insn_format21s
|
||||
@ -733,10 +751,12 @@ instruction
|
||||
| insn_format22b
|
||||
| insn_format22c_field
|
||||
| insn_format22c_type
|
||||
| insn_format22c_string
|
||||
| insn_format22s
|
||||
| insn_format22t
|
||||
| insn_format22x
|
||||
| insn_format23x
|
||||
| insn_format25x
|
||||
| insn_format30t
|
||||
| insn_format31c
|
||||
| insn_format31i
|
||||
@ -861,6 +881,30 @@ insn_format21c_type
|
||||
dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
|
||||
};
|
||||
|
||||
insn_format21c_lambda
|
||||
: //e.g. capture-variable v1, "foobar"
|
||||
^(I_STATEMENT_FORMAT21c_LAMBDA INSTRUCTION_FORMAT21c_LAMBDA REGISTER string_literal)
|
||||
{
|
||||
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_LAMBDA.text);
|
||||
short regA = parseRegister_byte($REGISTER.text);
|
||||
|
||||
$method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
|
||||
dexBuilder.internStringReference($string_literal.value)));
|
||||
};
|
||||
|
||||
insn_format21c_method
|
||||
: //e.g. create-lambda v1, java/io/PrintStream/print(Ljava/lang/Stream;)V
|
||||
^(I_STATEMENT_FORMAT21c_METHOD INSTRUCTION_FORMAT21c_METHOD REGISTER method_reference)
|
||||
{
|
||||
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_METHOD.text);
|
||||
short regA = parseRegister_byte($REGISTER.text);
|
||||
|
||||
ImmutableMethodReference methodReference = $method_reference.methodReference;
|
||||
|
||||
$method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
|
||||
dexBuilder.internMethodReference(methodReference)));
|
||||
};
|
||||
|
||||
insn_format21ih
|
||||
: //e.g. const/high16 v1, 1234
|
||||
^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal)
|
||||
@ -947,6 +991,18 @@ insn_format22c_type
|
||||
dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
|
||||
};
|
||||
|
||||
insn_format22c_string
|
||||
: //e.g. liberate-variable v0, v1, "baz"
|
||||
^(I_STATEMENT_FORMAT22c_STRING INSTRUCTION_FORMAT22c_STRING registerA=REGISTER registerB=REGISTER string_literal)
|
||||
{
|
||||
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_STRING.text);
|
||||
byte regA = parseRegister_nibble($registerA.text);
|
||||
byte regB = parseRegister_nibble($registerB.text);
|
||||
|
||||
$method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB,
|
||||
dexBuilder.internStringReference($string_literal.value)));
|
||||
};
|
||||
|
||||
insn_format22s
|
||||
: //e.g. add-int/lit16 v0, v1, 12345
|
||||
^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal)
|
||||
@ -994,6 +1050,23 @@ insn_format23x
|
||||
$method::methodBuilder.addInstruction(new BuilderInstruction23x(opcode, regA, regB, regC));
|
||||
};
|
||||
|
||||
insn_format25x
|
||||
: //e.g. invoke-lambda vClosure, {vD, vE, vF, vG} -- up to 4 parameters + the closure.
|
||||
^(I_STATEMENT_FORMAT25x INSTRUCTION_FORMAT25x REGISTER register_list4)
|
||||
{
|
||||
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT25x.text);
|
||||
|
||||
byte closureRegister = parseRegister_nibble($REGISTER.text);
|
||||
|
||||
//this depends on the fact that register_list4 returns a byte[4]
|
||||
byte[] registers = $register_list4.registers;
|
||||
int parameterRegisterCount = $register_list4.registerCount; // don't count closure register
|
||||
|
||||
$method::methodBuilder.addInstruction(new BuilderInstruction25x(opcode,
|
||||
parameterRegisterCount, closureRegister, registers[0], registers[1],
|
||||
registers[2], registers[3]));
|
||||
};
|
||||
|
||||
insn_format30t
|
||||
: //e.g. goto/32 endloop:
|
||||
^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref)
|
||||
|
@ -48,10 +48,16 @@ import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
public class SmaliTestUtils {
|
||||
|
||||
public static ClassDef compileSmali(String smaliText) throws RecognitionException, IOException {
|
||||
return compileSmali(smaliText, 15, false);
|
||||
}
|
||||
|
||||
public static ClassDef compileSmali(String smaliText, int apiLevel, boolean experimental)
|
||||
throws RecognitionException, IOException {
|
||||
CommonTokenStream tokens;
|
||||
LexerErrorInterface lexer;
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
|
||||
|
||||
Reader reader = new StringReader(smaliText);
|
||||
|
||||
@ -61,7 +67,7 @@ public class SmaliTestUtils {
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
parser.setVerboseErrors(true);
|
||||
parser.setAllowOdex(false);
|
||||
parser.setApiLevel(15);
|
||||
parser.setApiLevel(apiLevel, experimental);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
@ -75,6 +81,7 @@ public class SmaliTestUtils {
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
dexGen.setApiLevel(apiLevel, experimental);
|
||||
dexGen.setVerboseErrors(true);
|
||||
dexGen.setDexBuilder(dexBuilder);
|
||||
dexGen.smali_file();
|
||||
@ -87,7 +94,8 @@ public class SmaliTestUtils {
|
||||
|
||||
dexBuilder.writeTo(dataStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dataStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(
|
||||
new Opcodes(apiLevel, experimental), dataStore.getData());
|
||||
|
||||
return Iterables.getFirst(dexFile.getClasses(), null);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.antlr.runtime.Token;
|
||||
import org.antlr.runtime.TokenSource;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.antlr.runtime.tree.TreeNodeStream;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
@ -110,6 +111,7 @@ public class main {
|
||||
boolean allowOdex = false;
|
||||
boolean verboseErrors = false;
|
||||
boolean printTokens = false;
|
||||
boolean experimental = false;
|
||||
|
||||
int apiLevel = 15;
|
||||
|
||||
@ -142,6 +144,9 @@ public class main {
|
||||
case 'x':
|
||||
allowOdex = true;
|
||||
break;
|
||||
case 'X':
|
||||
experimental = true;
|
||||
break;
|
||||
case 'a':
|
||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||
break;
|
||||
@ -198,11 +203,12 @@ public class main {
|
||||
final boolean finalPrintTokens = printTokens;
|
||||
final boolean finalAllowOdex = allowOdex;
|
||||
final int finalApiLevel = apiLevel;
|
||||
final boolean finalExperimental = experimental;
|
||||
for (final File file: filesToProcess) {
|
||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||
@Override public Boolean call() throws Exception {
|
||||
return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens,
|
||||
finalAllowOdex, finalApiLevel);
|
||||
finalAllowOdex, finalApiLevel, finalExperimental);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -252,7 +258,8 @@ public class main {
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
|
||||
boolean printTokens, boolean allowOdex, int apiLevel)
|
||||
boolean printTokens, boolean allowOdex, int apiLevel,
|
||||
boolean experimental)
|
||||
throws Exception {
|
||||
CommonTokenStream tokens;
|
||||
|
||||
@ -267,7 +274,7 @@ public class main {
|
||||
|
||||
if (printTokens) {
|
||||
tokens.getTokens();
|
||||
|
||||
|
||||
for (int i=0; i<tokens.size(); i++) {
|
||||
Token token = tokens.get(i);
|
||||
if (token.getChannel() == smaliParser.HIDDEN) {
|
||||
@ -276,12 +283,14 @@ public class main {
|
||||
|
||||
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
||||
}
|
||||
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
smaliParser parser = new smaliParser(tokens);
|
||||
parser.setVerboseErrors(verboseErrors);
|
||||
parser.setAllowOdex(allowOdex);
|
||||
parser.setApiLevel(apiLevel);
|
||||
parser.setApiLevel(apiLevel, experimental);
|
||||
|
||||
smaliParser.smali_file_return result = parser.smali_file();
|
||||
|
||||
@ -294,7 +303,13 @@ public class main {
|
||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||
treeStream.setTokenStream(tokens);
|
||||
|
||||
if (printTokens) {
|
||||
System.out.println(t.toStringTree());
|
||||
}
|
||||
|
||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||
dexGen.setApiLevel(apiLevel, experimental);
|
||||
|
||||
dexGen.setVerboseErrors(verboseErrors);
|
||||
dexGen.setDexBuilder(dexBuilder);
|
||||
dexGen.smali_file();
|
||||
@ -363,6 +378,11 @@ public class main {
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("enable experimental opcodes to be assembled, even if they " +
|
||||
" aren't necessarily supported by the Android runtime yet")
|
||||
.create("X");
|
||||
|
||||
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
||||
"maximum of 6")
|
||||
@ -383,6 +403,7 @@ public class main {
|
||||
basicOptions.addOption(outputOption);
|
||||
basicOptions.addOption(allowOdexOption);
|
||||
basicOptions.addOption(apiLevelOption);
|
||||
basicOptions.addOption(experimentalOption);
|
||||
basicOptions.addOption(jobsOption);
|
||||
|
||||
debugOptions.addOption(verboseErrorsOption);
|
||||
|
@ -461,6 +461,14 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
||||
return newToken(INSTRUCTION_FORMAT21c_TYPE);
|
||||
}
|
||||
|
||||
"capture-variable" { // e.g. 'capture-variable vB, <string id>'
|
||||
return newToken(INSTRUCTION_FORMAT21c_LAMBDA);
|
||||
}
|
||||
|
||||
"create-lambda" { // e.g. 'create-lambda vClosure, <method id>'
|
||||
return newToken(INSTRUCTION_FORMAT21c_METHOD);
|
||||
}
|
||||
|
||||
"const/high16" {
|
||||
return newToken(INSTRUCTION_FORMAT21ih);
|
||||
}
|
||||
@ -492,10 +500,13 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
||||
return newToken(INSTRUCTION_FORMAT22c_FIELD_ODEX);
|
||||
}
|
||||
|
||||
"instance-of" | "new-array" {
|
||||
"instance-of" | "new-array" | "unbox-lambda" {
|
||||
return newToken(INSTRUCTION_FORMAT22c_TYPE);
|
||||
}
|
||||
|
||||
"liberate-variable" {
|
||||
return newToken(INSTRUCTION_FORMAT22c_STRING);
|
||||
}
|
||||
"iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" {
|
||||
return newToken(INSTRUCTION_FORMAT22cs_FIELD);
|
||||
}
|
||||
@ -513,7 +524,7 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
||||
return newToken(INSTRUCTION_FORMAT22t);
|
||||
}
|
||||
|
||||
"move/from16" | "move-wide/from16" | "move-object/from16" {
|
||||
"move/from16" | "move-wide/from16" | "move-object/from16" | "box-lambda" {
|
||||
return newToken(INSTRUCTION_FORMAT22x);
|
||||
}
|
||||
|
||||
@ -527,6 +538,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
||||
return newToken(INSTRUCTION_FORMAT23x);
|
||||
}
|
||||
|
||||
"invoke-lambda" { // e.g. invoke-lambda vClosure, {vD, vE, vF, vG} -- at most 4 params
|
||||
return newToken(INSTRUCTION_FORMAT25x);
|
||||
}
|
||||
|
||||
"goto/32" {
|
||||
return newToken(INSTRUCTION_FORMAT30t);
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ const-string
|
||||
check-cast
|
||||
new-instance
|
||||
const-class
|
||||
capture-variable
|
||||
create-lambda
|
||||
const/high16
|
||||
const-wide/high16
|
||||
const/16
|
||||
@ -122,6 +124,8 @@ iput-wide-volatile
|
||||
iput-object-volatile
|
||||
instance-of
|
||||
new-array
|
||||
unbox-lambda
|
||||
liberate-variable
|
||||
iget-quick
|
||||
iget-wide-quick
|
||||
iget-object-quick
|
||||
@ -144,6 +148,7 @@ if-le
|
||||
move/from16
|
||||
move-wide/from16
|
||||
move-object/from16
|
||||
box-lambda
|
||||
cmpl-float
|
||||
cmpg-float
|
||||
cmpl-double
|
||||
@ -194,6 +199,7 @@ add-double
|
||||
sub-double
|
||||
mul-double
|
||||
div-double
|
||||
invoke-lambda
|
||||
goto/32
|
||||
const-string/jumbo
|
||||
const
|
||||
|
@ -84,6 +84,8 @@ INSTRUCTION_FORMAT21c_STRING("const-string")
|
||||
INSTRUCTION_FORMAT21c_TYPE("check-cast")
|
||||
INSTRUCTION_FORMAT21c_TYPE("new-instance")
|
||||
INSTRUCTION_FORMAT21c_TYPE("const-class")
|
||||
INSTRUCTION_FORMAT21c_LAMBDA("capture-variable")
|
||||
INSTRUCTION_FORMAT21c_METHOD("create-lambda")
|
||||
INSTRUCTION_FORMAT21ih("const/high16")
|
||||
INSTRUCTION_FORMAT21lh("const-wide/high16")
|
||||
INSTRUCTION_FORMAT21s("const/16")
|
||||
@ -122,6 +124,8 @@ INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-wide-volatile")
|
||||
INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-object-volatile")
|
||||
INSTRUCTION_FORMAT22c_TYPE("instance-of")
|
||||
INSTRUCTION_FORMAT22c_TYPE("new-array")
|
||||
INSTRUCTION_FORMAT22c_TYPE("unbox-lambda")
|
||||
INSTRUCTION_FORMAT22c_STRING("liberate-variable")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iget-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iget-wide-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick")
|
||||
@ -144,6 +148,7 @@ INSTRUCTION_FORMAT22t("if-le")
|
||||
INSTRUCTION_FORMAT22x("move/from16")
|
||||
INSTRUCTION_FORMAT22x("move-wide/from16")
|
||||
INSTRUCTION_FORMAT22x("move-object/from16")
|
||||
INSTRUCTION_FORMAT22x("box-lambda")
|
||||
INSTRUCTION_FORMAT23x("cmpl-float")
|
||||
INSTRUCTION_FORMAT23x("cmpg-float")
|
||||
INSTRUCTION_FORMAT23x("cmpl-double")
|
||||
@ -194,6 +199,7 @@ INSTRUCTION_FORMAT23x("add-double")
|
||||
INSTRUCTION_FORMAT23x("sub-double")
|
||||
INSTRUCTION_FORMAT23x("mul-double")
|
||||
INSTRUCTION_FORMAT23x("div-double")
|
||||
INSTRUCTION_FORMAT25x("invoke-lambda")
|
||||
INSTRUCTION_FORMAT30t("goto/32")
|
||||
INSTRUCTION_FORMAT31c("const-string/jumbo")
|
||||
INSTRUCTION_FORMAT31i_OR_ID("const")
|
||||
|
@ -50,10 +50,28 @@ public class TextUtils {
|
||||
|
||||
@Nonnull
|
||||
public static String normalizeWhitespace(@Nonnull String source) {
|
||||
source = normalizeNewlines(source, "\n");
|
||||
// Go to native system new lines so that ^/$ work correctly
|
||||
source = normalizeNewlines(source);
|
||||
|
||||
Pattern pattern = Pattern.compile("(\n[ \t]*)+");
|
||||
// Remove all suffix/prefix whitespace
|
||||
Pattern pattern = Pattern.compile("((^[ \t]+)|([ \t]+))");
|
||||
Matcher matcher = pattern.matcher(source);
|
||||
return matcher.replaceAll("\n");
|
||||
source = matcher.replaceAll("");
|
||||
|
||||
// Remove all empty lines
|
||||
Pattern pattern2 = Pattern.compile("^\r?\n?", Pattern.MULTILINE);
|
||||
Matcher matcher2 = pattern2.matcher(source);
|
||||
source = matcher2.replaceAll("");
|
||||
|
||||
// Go back to unix-style \n newlines
|
||||
source = normalizeNewlines(source, "\n");
|
||||
return source;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String stripComments(@Nonnull String source) {
|
||||
Pattern pattern = Pattern.compile("#(.*)");
|
||||
Matcher matcher = pattern.matcher(source);
|
||||
return matcher.replaceAll("");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TextUtilsTest {
|
||||
@Test
|
||||
public void testStripComments() {
|
||||
Assert.assertEquals("", TextUtils.stripComments("#world"));
|
||||
Assert.assertEquals("hello", TextUtils.stripComments("hello#world"));
|
||||
Assert.assertEquals("multi\nline", TextUtils.stripComments("multi#hello world\nline#world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalizeWhitespace() {
|
||||
Assert.assertEquals("", TextUtils.normalizeWhitespace(" "));
|
||||
Assert.assertEquals("hello", TextUtils.normalizeWhitespace("hello "));
|
||||
Assert.assertEquals("hello", TextUtils.normalizeWhitespace(" hello"));
|
||||
Assert.assertEquals("hello", TextUtils.normalizeWhitespace(" hello "));
|
||||
Assert.assertEquals("hello\nworld", TextUtils.normalizeWhitespace("hello \n \n world"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user