mirror of
https://github.com/revanced/Apktool.git
synced 2024-08-15 03:40:34 +02: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.Instruction20bc;
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
|
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
|
||||||
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
|
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.iface.reference.Reference;
|
||||||
import org.jf.dexlib2.util.ReferenceUtil;
|
import org.jf.dexlib2.util.ReferenceUtil;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
@ -300,6 +298,11 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeThirdRegister(writer);
|
writeThirdRegister(writer);
|
||||||
break;
|
break;
|
||||||
|
case Format25x:
|
||||||
|
writeOpcode(writer);
|
||||||
|
writer.write(' ');
|
||||||
|
writeInvoke25xRegisters(writer); // vC, {vD, ...}
|
||||||
|
break;
|
||||||
case Format35c:
|
case Format35c:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
@ -425,6 +428,43 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write('}');
|
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 {
|
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
|
||||||
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public class baksmali {
|
|||||||
|
|
||||||
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
|
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
|
||||||
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
|
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
|
||||||
options.apiLevel, options.checkPackagePrivateAccess);
|
options.apiLevel, options.checkPackagePrivateAccess, options.experimental);
|
||||||
|
|
||||||
if (options.customInlineDefinitions != null) {
|
if (options.customInlineDefinitions != null) {
|
||||||
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,
|
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,
|
||||||
|
@ -73,6 +73,7 @@ public class baksmaliOptions {
|
|||||||
public boolean noAccessorComments = false;
|
public boolean noAccessorComments = false;
|
||||||
public boolean allowOdex = false;
|
public boolean allowOdex = false;
|
||||||
public boolean deodex = false;
|
public boolean deodex = false;
|
||||||
|
public boolean experimental = false;
|
||||||
public boolean ignoreErrors = false;
|
public boolean ignoreErrors = false;
|
||||||
public boolean checkPackagePrivateAccess = false;
|
public boolean checkPackagePrivateAccess = false;
|
||||||
public boolean useImplicitReferences = false;
|
public boolean useImplicitReferences = false;
|
||||||
|
@ -40,7 +40,7 @@ import java.io.IOException;
|
|||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
|
||||||
public class dump {
|
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) {
|
if (dumpFileName != null) {
|
||||||
Writer writer = null;
|
Writer writer = null;
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ public class dump {
|
|||||||
consoleWidth = 120;
|
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);
|
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
||||||
annotator.writeAnnotations(writer);
|
annotator.writeAnnotations(writer);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
@ -192,6 +192,9 @@ public class main {
|
|||||||
case 'x':
|
case 'x':
|
||||||
options.deodex = true;
|
options.deodex = true;
|
||||||
break;
|
break;
|
||||||
|
case 'X':
|
||||||
|
options.experimental = true;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
options.noAccessorComments = true;
|
options.noAccessorComments = true;
|
||||||
break;
|
break;
|
||||||
@ -253,7 +256,8 @@ public class main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Read in and parse the dex file
|
//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 (dexFile.isOdexFile()) {
|
||||||
if (!options.deodex) {
|
if (!options.deodex) {
|
||||||
@ -270,13 +274,15 @@ public class main {
|
|||||||
if (dexFile instanceof DexBackedOdexFile) {
|
if (dexFile instanceof DexBackedOdexFile) {
|
||||||
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||||
} else {
|
} else {
|
||||||
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
|
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
|
||||||
|
options.experimental);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
|
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
|
||||||
options.inlineResolver =
|
options.inlineResolver =
|
||||||
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
|
InlineMethodResolver.createInlineMethodResolver(
|
||||||
|
((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean errorOccurred = false;
|
boolean errorOccurred = false;
|
||||||
@ -288,7 +294,7 @@ public class main {
|
|||||||
if (dumpFileName == null) {
|
if (dumpFileName == null) {
|
||||||
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
|
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
|
||||||
}
|
}
|
||||||
dump.dump(dexFile, dumpFileName, options.apiLevel);
|
dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorOccurred) {
|
if (errorOccurred) {
|
||||||
@ -352,6 +358,10 @@ public class main {
|
|||||||
"odex file")
|
"odex file")
|
||||||
.create("x");
|
.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")
|
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
|
||||||
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
|
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
|
||||||
" than the .register directive with the total number of register")
|
" than the .register directive with the total number of register")
|
||||||
@ -469,6 +479,7 @@ public class main {
|
|||||||
basicOptions.addOption(outputDirOption);
|
basicOptions.addOption(outputDirOption);
|
||||||
basicOptions.addOption(noParameterRegistersOption);
|
basicOptions.addOption(noParameterRegistersOption);
|
||||||
basicOptions.addOption(deodexerantOption);
|
basicOptions.addOption(deodexerantOption);
|
||||||
|
basicOptions.addOption(experimentalOption);
|
||||||
basicOptions.addOption(useLocalsOption);
|
basicOptions.addOption(useLocalsOption);
|
||||||
basicOptions.addOption(sequentialLabelsOption);
|
basicOptions.addOption(sequentialLabelsOption);
|
||||||
basicOptions.addOption(noDebugInfoOption);
|
basicOptions.addOption(noDebugInfoOption);
|
||||||
@ -498,7 +509,7 @@ public class main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
|
private static List<String> getDefaultBootClassPathForApi(int apiLevel, boolean experimental) {
|
||||||
if (apiLevel < 9) {
|
if (apiLevel < 9) {
|
||||||
return Lists.newArrayList(
|
return Lists.newArrayList(
|
||||||
"/system/framework/core.jar",
|
"/system/framework/core.jar",
|
||||||
|
@ -85,7 +85,7 @@ public class AnalysisTest {
|
|||||||
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
|
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
|
||||||
String dexFilePath = String.format("%s%sclasses.dex", test, File.separatorChar);
|
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();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
if (registerInfo) {
|
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;
|
package org.jf.baksmali;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
|
||||||
import org.antlr.runtime.RecognitionException;
|
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 org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
public class ImplicitReferenceTest {
|
public class ImplicitReferenceTest {
|
||||||
@Test
|
@Test
|
||||||
public void testImplicitMethodReferences() throws IOException, RecognitionException {
|
public void testImplicitMethodReferences() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".method public static main([Ljava/lang/String;)V\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;->V()V\n" +
|
||||||
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method");
|
".end method";
|
||||||
|
|
||||||
String expected = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -72,19 +65,12 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.useImplicitReferences = true;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExplicitMethodReferences() throws IOException, RecognitionException {
|
public void testExplicitMethodReferences() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".method public static main([Ljava/lang/String;)V\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;->V()V\n" +
|
||||||
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
" invoke-static {p0}, LHelloWorld;->I()V\n" +
|
||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method");
|
".end method";
|
||||||
|
|
||||||
String expected = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -110,25 +96,18 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.useImplicitReferences = false;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
|
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\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 field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
|
||||||
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()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 = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -142,25 +121,18 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.useImplicitReferences = true;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
|
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\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 field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
|
||||||
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()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 = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -174,19 +146,12 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.useImplicitReferences = false;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testImplicitFieldReferences() throws IOException, RecognitionException {
|
public void testImplicitFieldReferences() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".method public static main([Ljava/lang/String;)V\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;->I:I\n" +
|
||||||
" sget v0, LHelloWorld;->V:I\n" +
|
" sget v0, LHelloWorld;->V:I\n" +
|
||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method");
|
".end method";
|
||||||
|
|
||||||
String expected = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -212,19 +177,12 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.useImplicitReferences = true;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExplicitFieldReferences() throws IOException, RecognitionException {
|
public void testExplicitFieldReferences() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".method public static main([Ljava/lang/String;)V\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;->I:I\n" +
|
||||||
" sget v0, LHelloWorld;->V:I\n" +
|
" sget v0, LHelloWorld;->V:I\n" +
|
||||||
" return-void\n" +
|
" return-void\n" +
|
||||||
".end method");
|
".end method";
|
||||||
|
|
||||||
String expected = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -250,24 +208,17 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.useImplicitReferences = false;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
|
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\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 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 = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -280,24 +231,17 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = true;
|
options.useImplicitReferences = true;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
|
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
|
||||||
ClassDef classDef = SmaliTestUtils.compileSmali("" +
|
String source = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
".super Ljava/lang/Object;\n" +
|
".super Ljava/lang/Object;\n" +
|
||||||
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\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 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 = "" +
|
String expected = "" +
|
||||||
".class public LHelloWorld;\n" +
|
".class public LHelloWorld;\n" +
|
||||||
@ -310,13 +254,7 @@ public class ImplicitReferenceTest {
|
|||||||
baksmaliOptions options = new baksmaliOptions();
|
baksmaliOptions options = new baksmaliOptions();
|
||||||
options.useImplicitReferences = false;
|
options.useImplicitReferences = false;
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
public void testAccessors() throws IOException {
|
||||||
URL url = AccessorTest.class.getClassLoader().getResource("accessorTest.dex");
|
URL url = AccessorTest.class.getClassLoader().getResource("accessorTest.dex");
|
||||||
Assert.assertNotNull(url);
|
Assert.assertNotNull(url);
|
||||||
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15);
|
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15, false);
|
||||||
|
|
||||||
SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses());
|
SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses());
|
||||||
|
|
||||||
|
@ -45,22 +45,26 @@ import java.util.zip.ZipFile;
|
|||||||
|
|
||||||
public final class DexFileFactory {
|
public final class DexFileFactory {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static DexBackedDexFile loadDexFile(String path, int api) throws IOException {
|
public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental)
|
||||||
return loadDexFile(new File(path), "classes.dex", new Opcodes(api));
|
throws IOException {
|
||||||
|
return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException {
|
public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental)
|
||||||
return loadDexFile(dexFile, "classes.dex", new Opcodes(api));
|
throws IOException {
|
||||||
|
return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api) throws IOException {
|
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api,
|
||||||
return loadDexFile(dexFile, dexEntry, new Opcodes(api));
|
boolean experimental) throws IOException {
|
||||||
|
return loadDexFile(dexFile, dexEntry, new Opcodes(api, experimental));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@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;
|
ZipFile zipFile = null;
|
||||||
boolean isZipFile = false;
|
boolean isZipFile = false;
|
||||||
try {
|
try {
|
||||||
|
@ -51,6 +51,7 @@ public enum Format {
|
|||||||
Format22t(4),
|
Format22t(4),
|
||||||
Format22x(4),
|
Format22x(4),
|
||||||
Format23x(4),
|
Format23x(4),
|
||||||
|
Format25x(4),
|
||||||
Format30t(6),
|
Format30t(6),
|
||||||
Format31c(6),
|
Format31c(6),
|
||||||
Format31i(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),
|
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),
|
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_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_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", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_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", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
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", 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", 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", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
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", ReferenceType.NONE, Format.Format3rms, 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((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),
|
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),
|
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),
|
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
|
//if the instruction can throw an exception
|
||||||
public static final int CAN_THROW = 0x1;
|
public static final int CAN_THROW = 0x1;
|
||||||
@ -309,6 +319,8 @@ public enum Opcode
|
|||||||
public static final int JUMBO_OPCODE = 0x200;
|
public static final int JUMBO_OPCODE = 0x200;
|
||||||
//if the instruction can initialize an uninitialized object reference
|
//if the instruction can initialize an uninitialized object reference
|
||||||
public static final int CAN_INITIALIZE_REFERENCE = 0x400;
|
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;
|
private static final int ALL_APIS = 0xFFFF0000;
|
||||||
|
|
||||||
@ -417,4 +429,8 @@ public enum Opcode
|
|||||||
public final boolean canInitializeReference() {
|
public final boolean canInitializeReference() {
|
||||||
return (flags & CAN_INITIALIZE_REFERENCE) != 0;
|
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 Opcode[] opcodesByValue;
|
||||||
private final HashMap<String, Opcode> opcodesByName;
|
private final HashMap<String, Opcode> opcodesByName;
|
||||||
|
|
||||||
public Opcodes(int api) {
|
public Opcodes(int api, boolean experimental) {
|
||||||
opcodesByValue = new Opcode[256];
|
opcodesByValue = new Opcode[256];
|
||||||
opcodesByName = Maps.newHashMap();
|
opcodesByName = Maps.newHashMap();
|
||||||
|
|
||||||
for (Opcode opcode: Opcode.values()) {
|
for (Opcode opcode: Opcode.values()) {
|
||||||
if (!opcode.format.isPayloadFormat) {
|
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;
|
opcodesByValue[opcode.value] = opcode;
|
||||||
opcodesByName.put(opcode.name.toLowerCase(), opcode);
|
opcodesByName.put(opcode.name.toLowerCase(), opcode);
|
||||||
}
|
}
|
||||||
|
@ -170,18 +170,18 @@ public class ClassPath {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
||||||
int api) {
|
int api, boolean experimental) {
|
||||||
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17);
|
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17, experimental);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
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();
|
ArrayList<DexFile> dexFiles = Lists.newArrayList();
|
||||||
|
|
||||||
for (String classPathEntry: classPath) {
|
for (String classPathEntry: classPath) {
|
||||||
try {
|
try {
|
||||||
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api));
|
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
|
||||||
} catch (ExceptionWithContext e){}
|
} catch (ExceptionWithContext e){}
|
||||||
}
|
}
|
||||||
dexFiles.add(dexFile);
|
dexFiles.add(dexFile);
|
||||||
@ -192,7 +192,8 @@ public class ClassPath {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static DexFile loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
|
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);
|
File rawEntry = new File(bootClassPathEntry);
|
||||||
// strip off the path - we only care about the filename
|
// strip off the path - we only care about the filename
|
||||||
String entryName = rawEntry.getName();
|
String entryName = rawEntry.getName();
|
||||||
@ -227,7 +228,7 @@ public class ClassPath {
|
|||||||
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
|
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return DexFileFactory.loadDexFile(file, api);
|
return DexFileFactory.loadDexFile(file, api, experimental);
|
||||||
} catch (DexFileFactory.NoClassesDexException ex) {
|
} catch (DexFileFactory.NoClassesDexException ex) {
|
||||||
// ignore and continue
|
// ignore and continue
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
@ -73,6 +73,7 @@ public class DumpFields {
|
|||||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||||
String outFile = "fields.txt";
|
String outFile = "fields.txt";
|
||||||
int apiLevel = 15;
|
int apiLevel = 15;
|
||||||
|
boolean experimental = false;
|
||||||
|
|
||||||
for (int i=0; i<parsedOptions.length; i++) {
|
for (int i=0; i<parsedOptions.length; i++) {
|
||||||
Option option = parsedOptions[i];
|
Option option = parsedOptions[i];
|
||||||
@ -88,6 +89,9 @@ public class DumpFields {
|
|||||||
case 'a':
|
case 'a':
|
||||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||||
break;
|
break;
|
||||||
|
case 'X':
|
||||||
|
experimental = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
@ -107,9 +111,9 @@ public class DumpFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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");
|
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);
|
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
for (ClassDef classDef: dexFile.getClasses()) {
|
||||||
@ -163,8 +167,14 @@ public class DumpFields {
|
|||||||
.withArgName("API_LEVEL")
|
.withArgName("API_LEVEL")
|
||||||
.create("a");
|
.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(classPathDirOption);
|
||||||
options.addOption(outputFileOption);
|
options.addOption(outputFileOption);
|
||||||
options.addOption(apiLevelOption);
|
options.addOption(apiLevelOption);
|
||||||
|
options.addOption(experimentalOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ public class DumpVtables {
|
|||||||
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
|
||||||
String outFile = "vtables.txt";
|
String outFile = "vtables.txt";
|
||||||
int apiLevel = 15;
|
int apiLevel = 15;
|
||||||
|
boolean experimental = false;
|
||||||
|
|
||||||
for (int i=0; i<parsedOptions.length; i++) {
|
for (int i=0; i<parsedOptions.length; i++) {
|
||||||
Option option = parsedOptions[i];
|
Option option = parsedOptions[i];
|
||||||
@ -86,6 +87,9 @@ public class DumpVtables {
|
|||||||
case 'a':
|
case 'a':
|
||||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||||
break;
|
break;
|
||||||
|
case 'X':
|
||||||
|
experimental = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert false;
|
assert false;
|
||||||
}
|
}
|
||||||
@ -105,9 +109,9 @@ public class DumpVtables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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");
|
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);
|
FileOutputStream outStream = new FileOutputStream(outFile);
|
||||||
|
|
||||||
for (ClassDef classDef: dexFile.getClasses()) {
|
for (ClassDef classDef: dexFile.getClasses()) {
|
||||||
@ -167,8 +171,14 @@ public class DumpVtables {
|
|||||||
.withArgName("API_LEVEL")
|
.withArgName("API_LEVEL")
|
||||||
.create("a");
|
.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(classPathDirOption);
|
||||||
options.addOption(outputFileOption);
|
options.addOption(outputFileOption);
|
||||||
options.addOption(apiLevelOption);
|
options.addOption(apiLevelOption);
|
||||||
|
options.addOption(experimentalOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,7 +511,8 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
@Nonnull Instruction instruction) {
|
@Nonnull Instruction instruction) {
|
||||||
switch (instruction.getOpcode().format) {
|
switch (instruction.getOpcode().format) {
|
||||||
case Format10t:
|
case Format10t:
|
||||||
setInstruction(location, newBuilderInstruction10t(location.codeAddress, codeAddressToIndex,
|
setInstruction(location, newBuilderInstruction10t(location.codeAddress,
|
||||||
|
codeAddressToIndex,
|
||||||
(Instruction10t) instruction));
|
(Instruction10t) instruction));
|
||||||
return;
|
return;
|
||||||
case Format10x:
|
case Format10x:
|
||||||
@ -530,7 +531,8 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
setInstruction(location, newBuilderInstruction20bc((Instruction20bc) instruction));
|
setInstruction(location, newBuilderInstruction20bc((Instruction20bc) instruction));
|
||||||
return;
|
return;
|
||||||
case Format20t:
|
case Format20t:
|
||||||
setInstruction(location, newBuilderInstruction20t(location.codeAddress, codeAddressToIndex,
|
setInstruction(location, newBuilderInstruction20t(location.codeAddress,
|
||||||
|
codeAddressToIndex,
|
||||||
(Instruction20t) instruction));
|
(Instruction20t) instruction));
|
||||||
return;
|
return;
|
||||||
case Format21c:
|
case Format21c:
|
||||||
@ -546,7 +548,8 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
setInstruction(location, newBuilderInstruction21s((Instruction21s) instruction));
|
setInstruction(location, newBuilderInstruction21s((Instruction21s) instruction));
|
||||||
return;
|
return;
|
||||||
case Format21t:
|
case Format21t:
|
||||||
setInstruction(location, newBuilderInstruction21t(location.codeAddress, codeAddressToIndex,
|
setInstruction(location, newBuilderInstruction21t(location.codeAddress,
|
||||||
|
codeAddressToIndex,
|
||||||
(Instruction21t) instruction));
|
(Instruction21t) instruction));
|
||||||
return;
|
return;
|
||||||
case Format22b:
|
case Format22b:
|
||||||
@ -559,7 +562,8 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
setInstruction(location, newBuilderInstruction22s((Instruction22s) instruction));
|
setInstruction(location, newBuilderInstruction22s((Instruction22s) instruction));
|
||||||
return;
|
return;
|
||||||
case Format22t:
|
case Format22t:
|
||||||
setInstruction(location, newBuilderInstruction22t(location.codeAddress, codeAddressToIndex,
|
setInstruction(location, newBuilderInstruction22t(location.codeAddress,
|
||||||
|
codeAddressToIndex,
|
||||||
(Instruction22t) instruction));
|
(Instruction22t) instruction));
|
||||||
return;
|
return;
|
||||||
case Format22x:
|
case Format22x:
|
||||||
@ -568,8 +572,12 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
case Format23x:
|
case Format23x:
|
||||||
setInstruction(location, newBuilderInstruction23x((Instruction23x) instruction));
|
setInstruction(location, newBuilderInstruction23x((Instruction23x) instruction));
|
||||||
return;
|
return;
|
||||||
|
case Format25x:
|
||||||
|
setInstruction(location, newBuilderInstruction25x((Instruction25x) instruction));
|
||||||
|
return;
|
||||||
case Format30t:
|
case Format30t:
|
||||||
setInstruction(location, newBuilderInstruction30t(location.codeAddress, codeAddressToIndex,
|
setInstruction(location, newBuilderInstruction30t(location.codeAddress,
|
||||||
|
codeAddressToIndex,
|
||||||
(Instruction30t) instruction));
|
(Instruction30t) instruction));
|
||||||
return;
|
return;
|
||||||
case Format31c:
|
case Format31c:
|
||||||
@ -820,6 +828,18 @@ public class MutableMethodImplementation implements MethodImplementation {
|
|||||||
instruction.getReference());
|
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
|
@Nonnull
|
||||||
private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) {
|
private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) {
|
||||||
return new BuilderInstruction3rc(
|
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);
|
return new DexBackedInstruction22x(dexFile, opcode, instructionStartOffset);
|
||||||
case Format23x:
|
case Format23x:
|
||||||
return new DexBackedInstruction23x(dexFile, opcode, instructionStartOffset);
|
return new DexBackedInstruction23x(dexFile, opcode, instructionStartOffset);
|
||||||
|
case Format25x:
|
||||||
|
return new DexBackedInstruction25x(dexFile, opcode, instructionStartOffset);
|
||||||
case Format30t:
|
case Format30t:
|
||||||
return new DexBackedInstruction30t(dexFile, opcode, instructionStartOffset);
|
return new DexBackedInstruction30t(dexFile, opcode, instructionStartOffset);
|
||||||
case Format31c:
|
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:
|
case Format10x:
|
||||||
annotateInstruction10x(out, instruction);
|
annotateInstruction10x(out, instruction);
|
||||||
break;
|
break;
|
||||||
|
case Format25x:
|
||||||
|
annotateInstruction25x(out, (Instruction25x) instruction);
|
||||||
|
break;
|
||||||
case Format35c:
|
case Format35c:
|
||||||
annotateInstruction35c(out, (Instruction35c)instruction);
|
annotateInstruction35c(out, (Instruction35c)instruction);
|
||||||
break;
|
break;
|
||||||
@ -282,6 +285,30 @@ public class CodeItem {
|
|||||||
instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
|
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) {
|
private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
|
||||||
int startRegister = instruction.getStartRegister();
|
int startRegister = instruction.getStartRegister();
|
||||||
int endRegister = startRegister + instruction.getRegisterCount() - 1;
|
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);
|
return ImmutableInstruction22x.of((Instruction22x)instruction);
|
||||||
case Format23x:
|
case Format23x:
|
||||||
return ImmutableInstruction23x.of((Instruction23x)instruction);
|
return ImmutableInstruction23x.of((Instruction23x)instruction);
|
||||||
|
case Format25x:
|
||||||
|
return ImmutableInstruction25x.of((Instruction25x) instruction);
|
||||||
case Format30t:
|
case Format30t:
|
||||||
return ImmutableInstruction30t.of((Instruction30t)instruction);
|
return ImmutableInstruction30t.of((Instruction30t)instruction);
|
||||||
case Format31c:
|
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;
|
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) {
|
public static int checkRegisterRangeCount(int registerCount) {
|
||||||
if ((registerCount & 0xFFFFFF00) != 0) {
|
if ((registerCount & 0xFFFFFF00) != 0) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
@ -976,6 +976,9 @@ public abstract class DexWriter<
|
|||||||
case Format23x:
|
case Format23x:
|
||||||
instructionWriter.write((Instruction23x)instruction);
|
instructionWriter.write((Instruction23x)instruction);
|
||||||
break;
|
break;
|
||||||
|
case Format25x:
|
||||||
|
instructionWriter.write((Instruction25x)instruction);
|
||||||
|
break;
|
||||||
case Format30t:
|
case Format30t:
|
||||||
instructionWriter.write((Instruction30t)instruction);
|
instructionWriter.write((Instruction30t)instruction);
|
||||||
break;
|
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) {
|
public void write(@Nonnull Instruction3rc instruction) {
|
||||||
try {
|
try {
|
||||||
writer.write(instruction.getOpcode().value);
|
writer.write(instruction.getOpcode().value);
|
||||||
|
@ -69,7 +69,7 @@ public class CustomMethodInlineTableTest {
|
|||||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||||
15);
|
15, false);
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ public class CustomMethodInlineTableTest {
|
|||||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||||
15);
|
15, false);
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ public class CustomMethodInlineTableTest {
|
|||||||
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
|
||||||
|
|
||||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||||
15);
|
15, false);
|
||||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public class DexWriterTest {
|
|||||||
throw new RuntimeException(ex);
|
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);
|
ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||||
Assert.assertNotNull(dbClassDef);
|
Assert.assertNotNull(dbClassDef);
|
||||||
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
|
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
|
||||||
|
@ -92,7 +92,7 @@ public class JumboStringConversionTest {
|
|||||||
MemoryDataStore dexStore = new MemoryDataStore();
|
MemoryDataStore dexStore = new MemoryDataStore();
|
||||||
dexBuilder.writeTo(dexStore);
|
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);
|
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||||
Assert.assertNotNull(classDef);
|
Assert.assertNotNull(classDef);
|
||||||
@ -189,7 +189,7 @@ public class JumboStringConversionTest {
|
|||||||
MemoryDataStore dexStore = new MemoryDataStore();
|
MemoryDataStore dexStore = new MemoryDataStore();
|
||||||
dexBuilder.writeTo(dexStore);
|
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);
|
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||||
Assert.assertNotNull(classDef);
|
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_FIELD_ODEX;
|
||||||
INSTRUCTION_FORMAT21c_STRING;
|
INSTRUCTION_FORMAT21c_STRING;
|
||||||
INSTRUCTION_FORMAT21c_TYPE;
|
INSTRUCTION_FORMAT21c_TYPE;
|
||||||
|
INSTRUCTION_FORMAT21c_LAMBDA;
|
||||||
|
INSTRUCTION_FORMAT21c_METHOD;
|
||||||
INSTRUCTION_FORMAT21ih;
|
INSTRUCTION_FORMAT21ih;
|
||||||
INSTRUCTION_FORMAT21lh;
|
INSTRUCTION_FORMAT21lh;
|
||||||
INSTRUCTION_FORMAT21s;
|
INSTRUCTION_FORMAT21s;
|
||||||
@ -94,12 +96,14 @@ tokens {
|
|||||||
INSTRUCTION_FORMAT22c_FIELD;
|
INSTRUCTION_FORMAT22c_FIELD;
|
||||||
INSTRUCTION_FORMAT22c_FIELD_ODEX;
|
INSTRUCTION_FORMAT22c_FIELD_ODEX;
|
||||||
INSTRUCTION_FORMAT22c_TYPE;
|
INSTRUCTION_FORMAT22c_TYPE;
|
||||||
|
INSTRUCTION_FORMAT22c_STRING;
|
||||||
INSTRUCTION_FORMAT22cs_FIELD;
|
INSTRUCTION_FORMAT22cs_FIELD;
|
||||||
INSTRUCTION_FORMAT22s;
|
INSTRUCTION_FORMAT22s;
|
||||||
INSTRUCTION_FORMAT22s_OR_ID;
|
INSTRUCTION_FORMAT22s_OR_ID;
|
||||||
INSTRUCTION_FORMAT22t;
|
INSTRUCTION_FORMAT22t;
|
||||||
INSTRUCTION_FORMAT22x;
|
INSTRUCTION_FORMAT22x;
|
||||||
INSTRUCTION_FORMAT23x;
|
INSTRUCTION_FORMAT23x;
|
||||||
|
INSTRUCTION_FORMAT25x;
|
||||||
INSTRUCTION_FORMAT30t;
|
INSTRUCTION_FORMAT30t;
|
||||||
INSTRUCTION_FORMAT31c;
|
INSTRUCTION_FORMAT31c;
|
||||||
INSTRUCTION_FORMAT31i;
|
INSTRUCTION_FORMAT31i;
|
||||||
@ -209,6 +213,8 @@ tokens {
|
|||||||
I_STATEMENT_FORMAT21c_TYPE;
|
I_STATEMENT_FORMAT21c_TYPE;
|
||||||
I_STATEMENT_FORMAT21c_FIELD;
|
I_STATEMENT_FORMAT21c_FIELD;
|
||||||
I_STATEMENT_FORMAT21c_STRING;
|
I_STATEMENT_FORMAT21c_STRING;
|
||||||
|
I_STATEMENT_FORMAT21c_LAMBDA;
|
||||||
|
I_STATEMENT_FORMAT21c_METHOD;
|
||||||
I_STATEMENT_FORMAT21ih;
|
I_STATEMENT_FORMAT21ih;
|
||||||
I_STATEMENT_FORMAT21lh;
|
I_STATEMENT_FORMAT21lh;
|
||||||
I_STATEMENT_FORMAT21s;
|
I_STATEMENT_FORMAT21s;
|
||||||
@ -216,10 +222,12 @@ tokens {
|
|||||||
I_STATEMENT_FORMAT22b;
|
I_STATEMENT_FORMAT22b;
|
||||||
I_STATEMENT_FORMAT22c_FIELD;
|
I_STATEMENT_FORMAT22c_FIELD;
|
||||||
I_STATEMENT_FORMAT22c_TYPE;
|
I_STATEMENT_FORMAT22c_TYPE;
|
||||||
|
I_STATEMENT_FORMAT22c_STRING;
|
||||||
I_STATEMENT_FORMAT22s;
|
I_STATEMENT_FORMAT22s;
|
||||||
I_STATEMENT_FORMAT22t;
|
I_STATEMENT_FORMAT22t;
|
||||||
I_STATEMENT_FORMAT22x;
|
I_STATEMENT_FORMAT22x;
|
||||||
I_STATEMENT_FORMAT23x;
|
I_STATEMENT_FORMAT23x;
|
||||||
|
I_STATEMENT_FORMAT25x;
|
||||||
I_STATEMENT_FORMAT30t;
|
I_STATEMENT_FORMAT30t;
|
||||||
I_STATEMENT_FORMAT31c;
|
I_STATEMENT_FORMAT31c;
|
||||||
I_STATEMENT_FORMAT31i;
|
I_STATEMENT_FORMAT31i;
|
||||||
@ -252,7 +260,7 @@ import org.jf.dexlib2.Opcodes;
|
|||||||
private boolean verboseErrors = false;
|
private boolean verboseErrors = false;
|
||||||
private boolean allowOdex = false;
|
private boolean allowOdex = false;
|
||||||
private int apiLevel = 15;
|
private int apiLevel = 15;
|
||||||
private Opcodes opcodes = new Opcodes(apiLevel);
|
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||||
|
|
||||||
public void setVerboseErrors(boolean verboseErrors) {
|
public void setVerboseErrors(boolean verboseErrors) {
|
||||||
this.verboseErrors = verboseErrors;
|
this.verboseErrors = verboseErrors;
|
||||||
@ -262,8 +270,8 @@ import org.jf.dexlib2.Opcodes;
|
|||||||
this.allowOdex = allowOdex;
|
this.allowOdex = allowOdex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApiLevel(int apiLevel) {
|
public void setApiLevel(int apiLevel, boolean experimental) {
|
||||||
this.opcodes = new Opcodes(apiLevel);
|
this.opcodes = new Opcodes(apiLevel, experimental);
|
||||||
this.apiLevel = apiLevel;
|
this.apiLevel = apiLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,14 +569,18 @@ simple_name
|
|||||||
| INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX]
|
| INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX]
|
||||||
| INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING]
|
| INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING]
|
||||||
| INSTRUCTION_FORMAT21c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_TYPE]
|
| 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_FORMAT21t -> SIMPLE_NAME[$INSTRUCTION_FORMAT21t]
|
||||||
| INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD]
|
| INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD]
|
||||||
| INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX]
|
| INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX]
|
||||||
| INSTRUCTION_FORMAT22c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_TYPE]
|
| 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_FORMAT22cs_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22cs_FIELD]
|
||||||
| INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID]
|
| INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID]
|
||||||
| INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t]
|
| INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t]
|
||||||
| INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x]
|
| INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x]
|
||||||
|
| INSTRUCTION_FORMAT25x -> SIMPLE_NAME[$INSTRUCTION_FORMAT25x]
|
||||||
| INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID]
|
| INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID]
|
||||||
| INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t]
|
| INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t]
|
||||||
| INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD]
|
| INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD]
|
||||||
@ -806,6 +818,8 @@ instruction
|
|||||||
| insn_format21c_field_odex
|
| insn_format21c_field_odex
|
||||||
| insn_format21c_string
|
| insn_format21c_string
|
||||||
| insn_format21c_type
|
| insn_format21c_type
|
||||||
|
| insn_format21c_lambda
|
||||||
|
| insn_format21c_method
|
||||||
| insn_format21ih
|
| insn_format21ih
|
||||||
| insn_format21lh
|
| insn_format21lh
|
||||||
| insn_format21s
|
| insn_format21s
|
||||||
@ -814,11 +828,13 @@ instruction
|
|||||||
| insn_format22c_field
|
| insn_format22c_field
|
||||||
| insn_format22c_field_odex
|
| insn_format22c_field_odex
|
||||||
| insn_format22c_type
|
| insn_format22c_type
|
||||||
|
| insn_format22c_string
|
||||||
| insn_format22cs_field
|
| insn_format22cs_field
|
||||||
| insn_format22s
|
| insn_format22s
|
||||||
| insn_format22t
|
| insn_format22t
|
||||||
| insn_format22x
|
| insn_format22x
|
||||||
| insn_format23x
|
| insn_format23x
|
||||||
|
| insn_format25x
|
||||||
| insn_format30t
|
| insn_format30t
|
||||||
| insn_format31c
|
| insn_format31c
|
||||||
| insn_format31i
|
| insn_format31i
|
||||||
@ -912,6 +928,16 @@ insn_format21c_type
|
|||||||
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor
|
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor
|
||||||
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER 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
|
insn_format21ih
|
||||||
: //e.g. const/high16 v1, 1234
|
: //e.g. const/high16 v1, 1234
|
||||||
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal
|
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal
|
||||||
@ -957,6 +983,11 @@ insn_format22c_type
|
|||||||
INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor
|
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);
|
-> ^(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
|
insn_format22cs_field
|
||||||
: //e.g. iget-quick v0, v1, field@0xc
|
: //e.g. iget-quick v0, v1, field@0xc
|
||||||
INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET
|
INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET
|
||||||
@ -984,6 +1015,11 @@ insn_format23x
|
|||||||
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER
|
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER
|
||||||
-> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER 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
|
insn_format30t
|
||||||
: //e.g. goto/32 endloop:
|
: //e.g. goto/32 endloop:
|
||||||
INSTRUCTION_FORMAT30t label_ref
|
INSTRUCTION_FORMAT30t label_ref
|
||||||
|
@ -77,15 +77,15 @@ import java.util.*;
|
|||||||
public String classType;
|
public String classType;
|
||||||
private boolean verboseErrors = false;
|
private boolean verboseErrors = false;
|
||||||
private int apiLevel = 15;
|
private int apiLevel = 15;
|
||||||
private Opcodes opcodes = new Opcodes(apiLevel);
|
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||||
private DexBuilder dexBuilder;
|
private DexBuilder dexBuilder;
|
||||||
|
|
||||||
public void setDexBuilder(DexBuilder dexBuilder) {
|
public void setDexBuilder(DexBuilder dexBuilder) {
|
||||||
this.dexBuilder = dexBuilder;
|
this.dexBuilder = dexBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApiLevel(int apiLevel) {
|
public void setApiLevel(int apiLevel, boolean experimental) {
|
||||||
this.opcodes = new Opcodes(apiLevel);
|
this.opcodes = new Opcodes(apiLevel, experimental);
|
||||||
this.apiLevel = apiLevel;
|
this.apiLevel = apiLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,6 +674,22 @@ register_list returns[byte[\] registers, byte registerCount]
|
|||||||
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text);
|
$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]
|
register_range returns[int startRegister, int endRegister]
|
||||||
: ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?)
|
: ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?)
|
||||||
{
|
{
|
||||||
@ -726,6 +742,8 @@ instruction
|
|||||||
| insn_format21c_field
|
| insn_format21c_field
|
||||||
| insn_format21c_string
|
| insn_format21c_string
|
||||||
| insn_format21c_type
|
| insn_format21c_type
|
||||||
|
| insn_format21c_lambda
|
||||||
|
| insn_format21c_method
|
||||||
| insn_format21ih
|
| insn_format21ih
|
||||||
| insn_format21lh
|
| insn_format21lh
|
||||||
| insn_format21s
|
| insn_format21s
|
||||||
@ -733,10 +751,12 @@ instruction
|
|||||||
| insn_format22b
|
| insn_format22b
|
||||||
| insn_format22c_field
|
| insn_format22c_field
|
||||||
| insn_format22c_type
|
| insn_format22c_type
|
||||||
|
| insn_format22c_string
|
||||||
| insn_format22s
|
| insn_format22s
|
||||||
| insn_format22t
|
| insn_format22t
|
||||||
| insn_format22x
|
| insn_format22x
|
||||||
| insn_format23x
|
| insn_format23x
|
||||||
|
| insn_format25x
|
||||||
| insn_format30t
|
| insn_format30t
|
||||||
| insn_format31c
|
| insn_format31c
|
||||||
| insn_format31i
|
| insn_format31i
|
||||||
@ -861,6 +881,30 @@ insn_format21c_type
|
|||||||
dexBuilder.internTypeReference($nonvoid_type_descriptor.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
|
insn_format21ih
|
||||||
: //e.g. const/high16 v1, 1234
|
: //e.g. const/high16 v1, 1234
|
||||||
^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal)
|
^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal)
|
||||||
@ -947,6 +991,18 @@ insn_format22c_type
|
|||||||
dexBuilder.internTypeReference($nonvoid_type_descriptor.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
|
insn_format22s
|
||||||
: //e.g. add-int/lit16 v0, v1, 12345
|
: //e.g. add-int/lit16 v0, v1, 12345
|
||||||
^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal)
|
^(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));
|
$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
|
insn_format30t
|
||||||
: //e.g. goto/32 endloop:
|
: //e.g. goto/32 endloop:
|
||||||
^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref)
|
^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref)
|
||||||
|
@ -48,10 +48,16 @@ import java.io.Reader;
|
|||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
public class SmaliTestUtils {
|
public class SmaliTestUtils {
|
||||||
|
|
||||||
public static ClassDef compileSmali(String smaliText) throws RecognitionException, IOException {
|
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;
|
CommonTokenStream tokens;
|
||||||
LexerErrorInterface lexer;
|
LexerErrorInterface lexer;
|
||||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
|
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
|
||||||
|
|
||||||
Reader reader = new StringReader(smaliText);
|
Reader reader = new StringReader(smaliText);
|
||||||
|
|
||||||
@ -61,7 +67,7 @@ public class SmaliTestUtils {
|
|||||||
smaliParser parser = new smaliParser(tokens);
|
smaliParser parser = new smaliParser(tokens);
|
||||||
parser.setVerboseErrors(true);
|
parser.setVerboseErrors(true);
|
||||||
parser.setAllowOdex(false);
|
parser.setAllowOdex(false);
|
||||||
parser.setApiLevel(15);
|
parser.setApiLevel(apiLevel, experimental);
|
||||||
|
|
||||||
smaliParser.smali_file_return result = parser.smali_file();
|
smaliParser.smali_file_return result = parser.smali_file();
|
||||||
|
|
||||||
@ -75,6 +81,7 @@ public class SmaliTestUtils {
|
|||||||
treeStream.setTokenStream(tokens);
|
treeStream.setTokenStream(tokens);
|
||||||
|
|
||||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||||
|
dexGen.setApiLevel(apiLevel, experimental);
|
||||||
dexGen.setVerboseErrors(true);
|
dexGen.setVerboseErrors(true);
|
||||||
dexGen.setDexBuilder(dexBuilder);
|
dexGen.setDexBuilder(dexBuilder);
|
||||||
dexGen.smali_file();
|
dexGen.smali_file();
|
||||||
@ -87,7 +94,8 @@ public class SmaliTestUtils {
|
|||||||
|
|
||||||
dexBuilder.writeTo(dataStore);
|
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);
|
return Iterables.getFirst(dexFile.getClasses(), null);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import org.antlr.runtime.Token;
|
|||||||
import org.antlr.runtime.TokenSource;
|
import org.antlr.runtime.TokenSource;
|
||||||
import org.antlr.runtime.tree.CommonTree;
|
import org.antlr.runtime.tree.CommonTree;
|
||||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||||
|
import org.antlr.runtime.tree.TreeNodeStream;
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||||
@ -110,6 +111,7 @@ public class main {
|
|||||||
boolean allowOdex = false;
|
boolean allowOdex = false;
|
||||||
boolean verboseErrors = false;
|
boolean verboseErrors = false;
|
||||||
boolean printTokens = false;
|
boolean printTokens = false;
|
||||||
|
boolean experimental = false;
|
||||||
|
|
||||||
int apiLevel = 15;
|
int apiLevel = 15;
|
||||||
|
|
||||||
@ -142,6 +144,9 @@ public class main {
|
|||||||
case 'x':
|
case 'x':
|
||||||
allowOdex = true;
|
allowOdex = true;
|
||||||
break;
|
break;
|
||||||
|
case 'X':
|
||||||
|
experimental = true;
|
||||||
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
|
||||||
break;
|
break;
|
||||||
@ -198,11 +203,12 @@ public class main {
|
|||||||
final boolean finalPrintTokens = printTokens;
|
final boolean finalPrintTokens = printTokens;
|
||||||
final boolean finalAllowOdex = allowOdex;
|
final boolean finalAllowOdex = allowOdex;
|
||||||
final int finalApiLevel = apiLevel;
|
final int finalApiLevel = apiLevel;
|
||||||
|
final boolean finalExperimental = experimental;
|
||||||
for (final File file: filesToProcess) {
|
for (final File file: filesToProcess) {
|
||||||
tasks.add(executor.submit(new Callable<Boolean>() {
|
tasks.add(executor.submit(new Callable<Boolean>() {
|
||||||
@Override public Boolean call() throws Exception {
|
@Override public Boolean call() throws Exception {
|
||||||
return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens,
|
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,
|
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 {
|
throws Exception {
|
||||||
CommonTokenStream tokens;
|
CommonTokenStream tokens;
|
||||||
|
|
||||||
@ -276,12 +283,14 @@ public class main {
|
|||||||
|
|
||||||
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
smaliParser parser = new smaliParser(tokens);
|
smaliParser parser = new smaliParser(tokens);
|
||||||
parser.setVerboseErrors(verboseErrors);
|
parser.setVerboseErrors(verboseErrors);
|
||||||
parser.setAllowOdex(allowOdex);
|
parser.setAllowOdex(allowOdex);
|
||||||
parser.setApiLevel(apiLevel);
|
parser.setApiLevel(apiLevel, experimental);
|
||||||
|
|
||||||
smaliParser.smali_file_return result = parser.smali_file();
|
smaliParser.smali_file_return result = parser.smali_file();
|
||||||
|
|
||||||
@ -294,7 +303,13 @@ public class main {
|
|||||||
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
||||||
treeStream.setTokenStream(tokens);
|
treeStream.setTokenStream(tokens);
|
||||||
|
|
||||||
|
if (printTokens) {
|
||||||
|
System.out.println(t.toStringTree());
|
||||||
|
}
|
||||||
|
|
||||||
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
|
||||||
|
dexGen.setApiLevel(apiLevel, experimental);
|
||||||
|
|
||||||
dexGen.setVerboseErrors(verboseErrors);
|
dexGen.setVerboseErrors(verboseErrors);
|
||||||
dexGen.setDexBuilder(dexBuilder);
|
dexGen.setDexBuilder(dexBuilder);
|
||||||
dexGen.smali_file();
|
dexGen.smali_file();
|
||||||
@ -363,6 +378,11 @@ public class main {
|
|||||||
.withArgName("API_LEVEL")
|
.withArgName("API_LEVEL")
|
||||||
.create("a");
|
.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")
|
Option jobsOption = OptionBuilder.withLongOpt("jobs")
|
||||||
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
|
||||||
"maximum of 6")
|
"maximum of 6")
|
||||||
@ -383,6 +403,7 @@ public class main {
|
|||||||
basicOptions.addOption(outputOption);
|
basicOptions.addOption(outputOption);
|
||||||
basicOptions.addOption(allowOdexOption);
|
basicOptions.addOption(allowOdexOption);
|
||||||
basicOptions.addOption(apiLevelOption);
|
basicOptions.addOption(apiLevelOption);
|
||||||
|
basicOptions.addOption(experimentalOption);
|
||||||
basicOptions.addOption(jobsOption);
|
basicOptions.addOption(jobsOption);
|
||||||
|
|
||||||
debugOptions.addOption(verboseErrorsOption);
|
debugOptions.addOption(verboseErrorsOption);
|
||||||
|
@ -461,6 +461,14 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
|||||||
return newToken(INSTRUCTION_FORMAT21c_TYPE);
|
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" {
|
"const/high16" {
|
||||||
return newToken(INSTRUCTION_FORMAT21ih);
|
return newToken(INSTRUCTION_FORMAT21ih);
|
||||||
}
|
}
|
||||||
@ -492,10 +500,13 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
|||||||
return newToken(INSTRUCTION_FORMAT22c_FIELD_ODEX);
|
return newToken(INSTRUCTION_FORMAT22c_FIELD_ODEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
"instance-of" | "new-array" {
|
"instance-of" | "new-array" | "unbox-lambda" {
|
||||||
return newToken(INSTRUCTION_FORMAT22c_TYPE);
|
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" {
|
"iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" {
|
||||||
return newToken(INSTRUCTION_FORMAT22cs_FIELD);
|
return newToken(INSTRUCTION_FORMAT22cs_FIELD);
|
||||||
}
|
}
|
||||||
@ -513,7 +524,7 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
|||||||
return newToken(INSTRUCTION_FORMAT22t);
|
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);
|
return newToken(INSTRUCTION_FORMAT22x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,6 +538,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
|
|||||||
return newToken(INSTRUCTION_FORMAT23x);
|
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" {
|
"goto/32" {
|
||||||
return newToken(INSTRUCTION_FORMAT30t);
|
return newToken(INSTRUCTION_FORMAT30t);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ const-string
|
|||||||
check-cast
|
check-cast
|
||||||
new-instance
|
new-instance
|
||||||
const-class
|
const-class
|
||||||
|
capture-variable
|
||||||
|
create-lambda
|
||||||
const/high16
|
const/high16
|
||||||
const-wide/high16
|
const-wide/high16
|
||||||
const/16
|
const/16
|
||||||
@ -122,6 +124,8 @@ iput-wide-volatile
|
|||||||
iput-object-volatile
|
iput-object-volatile
|
||||||
instance-of
|
instance-of
|
||||||
new-array
|
new-array
|
||||||
|
unbox-lambda
|
||||||
|
liberate-variable
|
||||||
iget-quick
|
iget-quick
|
||||||
iget-wide-quick
|
iget-wide-quick
|
||||||
iget-object-quick
|
iget-object-quick
|
||||||
@ -144,6 +148,7 @@ if-le
|
|||||||
move/from16
|
move/from16
|
||||||
move-wide/from16
|
move-wide/from16
|
||||||
move-object/from16
|
move-object/from16
|
||||||
|
box-lambda
|
||||||
cmpl-float
|
cmpl-float
|
||||||
cmpg-float
|
cmpg-float
|
||||||
cmpl-double
|
cmpl-double
|
||||||
@ -194,6 +199,7 @@ add-double
|
|||||||
sub-double
|
sub-double
|
||||||
mul-double
|
mul-double
|
||||||
div-double
|
div-double
|
||||||
|
invoke-lambda
|
||||||
goto/32
|
goto/32
|
||||||
const-string/jumbo
|
const-string/jumbo
|
||||||
const
|
const
|
||||||
|
@ -84,6 +84,8 @@ INSTRUCTION_FORMAT21c_STRING("const-string")
|
|||||||
INSTRUCTION_FORMAT21c_TYPE("check-cast")
|
INSTRUCTION_FORMAT21c_TYPE("check-cast")
|
||||||
INSTRUCTION_FORMAT21c_TYPE("new-instance")
|
INSTRUCTION_FORMAT21c_TYPE("new-instance")
|
||||||
INSTRUCTION_FORMAT21c_TYPE("const-class")
|
INSTRUCTION_FORMAT21c_TYPE("const-class")
|
||||||
|
INSTRUCTION_FORMAT21c_LAMBDA("capture-variable")
|
||||||
|
INSTRUCTION_FORMAT21c_METHOD("create-lambda")
|
||||||
INSTRUCTION_FORMAT21ih("const/high16")
|
INSTRUCTION_FORMAT21ih("const/high16")
|
||||||
INSTRUCTION_FORMAT21lh("const-wide/high16")
|
INSTRUCTION_FORMAT21lh("const-wide/high16")
|
||||||
INSTRUCTION_FORMAT21s("const/16")
|
INSTRUCTION_FORMAT21s("const/16")
|
||||||
@ -122,6 +124,8 @@ INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-wide-volatile")
|
|||||||
INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-object-volatile")
|
INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-object-volatile")
|
||||||
INSTRUCTION_FORMAT22c_TYPE("instance-of")
|
INSTRUCTION_FORMAT22c_TYPE("instance-of")
|
||||||
INSTRUCTION_FORMAT22c_TYPE("new-array")
|
INSTRUCTION_FORMAT22c_TYPE("new-array")
|
||||||
|
INSTRUCTION_FORMAT22c_TYPE("unbox-lambda")
|
||||||
|
INSTRUCTION_FORMAT22c_STRING("liberate-variable")
|
||||||
INSTRUCTION_FORMAT22cs_FIELD("iget-quick")
|
INSTRUCTION_FORMAT22cs_FIELD("iget-quick")
|
||||||
INSTRUCTION_FORMAT22cs_FIELD("iget-wide-quick")
|
INSTRUCTION_FORMAT22cs_FIELD("iget-wide-quick")
|
||||||
INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick")
|
INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick")
|
||||||
@ -144,6 +148,7 @@ INSTRUCTION_FORMAT22t("if-le")
|
|||||||
INSTRUCTION_FORMAT22x("move/from16")
|
INSTRUCTION_FORMAT22x("move/from16")
|
||||||
INSTRUCTION_FORMAT22x("move-wide/from16")
|
INSTRUCTION_FORMAT22x("move-wide/from16")
|
||||||
INSTRUCTION_FORMAT22x("move-object/from16")
|
INSTRUCTION_FORMAT22x("move-object/from16")
|
||||||
|
INSTRUCTION_FORMAT22x("box-lambda")
|
||||||
INSTRUCTION_FORMAT23x("cmpl-float")
|
INSTRUCTION_FORMAT23x("cmpl-float")
|
||||||
INSTRUCTION_FORMAT23x("cmpg-float")
|
INSTRUCTION_FORMAT23x("cmpg-float")
|
||||||
INSTRUCTION_FORMAT23x("cmpl-double")
|
INSTRUCTION_FORMAT23x("cmpl-double")
|
||||||
@ -194,6 +199,7 @@ INSTRUCTION_FORMAT23x("add-double")
|
|||||||
INSTRUCTION_FORMAT23x("sub-double")
|
INSTRUCTION_FORMAT23x("sub-double")
|
||||||
INSTRUCTION_FORMAT23x("mul-double")
|
INSTRUCTION_FORMAT23x("mul-double")
|
||||||
INSTRUCTION_FORMAT23x("div-double")
|
INSTRUCTION_FORMAT23x("div-double")
|
||||||
|
INSTRUCTION_FORMAT25x("invoke-lambda")
|
||||||
INSTRUCTION_FORMAT30t("goto/32")
|
INSTRUCTION_FORMAT30t("goto/32")
|
||||||
INSTRUCTION_FORMAT31c("const-string/jumbo")
|
INSTRUCTION_FORMAT31c("const-string/jumbo")
|
||||||
INSTRUCTION_FORMAT31i_OR_ID("const")
|
INSTRUCTION_FORMAT31i_OR_ID("const")
|
||||||
|
@ -50,10 +50,28 @@ public class TextUtils {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static String normalizeWhitespace(@Nonnull String source) {
|
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);
|
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…
Reference in New Issue
Block a user