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:
Igor Murashkin 2015-02-18 17:38:30 -08:00 committed by Connor Tumbleson
parent d54c78d966
commit 58b7c27316
44 changed files with 1160 additions and 180 deletions

View File

@ -41,8 +41,6 @@ import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
@ -300,6 +298,11 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write(", ");
writeThirdRegister(writer);
break;
case Format25x:
writeOpcode(writer);
writer.write(' ');
writeInvoke25xRegisters(writer); // vC, {vD, ...}
break;
case Format35c:
writeOpcode(writer);
writer.write(' ');
@ -425,6 +428,43 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
writer.write('}');
}
protected void writeInvoke25xRegisters(IndentingWriter writer) throws IOException {
OneFixedFourParameterRegisterInstruction instruction =
(OneFixedFourParameterRegisterInstruction)this.instruction;
final int parameterRegCount = instruction.getParameterRegisterCount();
writeRegister(writer, instruction.getRegisterFixedC()); // fixed register always present
writer.write(", {");
switch (parameterRegCount) {
case 1:
writeRegister(writer, instruction.getRegisterParameterD());
break;
case 2:
writeRegister(writer, instruction.getRegisterParameterD());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterE());
break;
case 3:
writeRegister(writer, instruction.getRegisterParameterD());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterE());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterF());
break;
case 4:
writeRegister(writer, instruction.getRegisterParameterD());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterE());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterF());
writer.write(", ");
writeRegister(writer, instruction.getRegisterParameterG());
break;
}
writer.write('}');
}
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;

View File

@ -67,7 +67,7 @@ public class baksmali {
options.classPath = ClassPath.fromClassPath(options.bootClassPathDirs,
Iterables.concat(options.bootClassPathEntries, extraClassPathEntries), dexFile,
options.apiLevel, options.checkPackagePrivateAccess);
options.apiLevel, options.checkPackagePrivateAccess, options.experimental);
if (options.customInlineDefinitions != null) {
options.inlineResolver = new CustomInlineMethodResolver(options.classPath,

View File

@ -73,6 +73,7 @@ public class baksmaliOptions {
public boolean noAccessorComments = false;
public boolean allowOdex = false;
public boolean deodex = false;
public boolean experimental = false;
public boolean ignoreErrors = false;
public boolean checkPackagePrivateAccess = false;
public boolean useImplicitReferences = false;

View File

@ -40,7 +40,7 @@ import java.io.IOException;
import java.io.Writer;
public class dump {
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel, boolean experimental) throws IOException {
if (dumpFileName != null) {
Writer writer = null;
@ -52,7 +52,7 @@ public class dump {
consoleWidth = 120;
}
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel), dexFile);
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel, experimental), dexFile);
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
annotator.writeAnnotations(writer);
} catch (IOException ex) {

View File

@ -192,6 +192,9 @@ public class main {
case 'x':
options.deodex = true;
break;
case 'X':
options.experimental = true;
break;
case 'm':
options.noAccessorComments = true;
break;
@ -253,7 +256,8 @@ public class main {
}
//Read in and parse the dex file
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel);
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry,
options.apiLevel, options.experimental);
if (dexFile.isOdexFile()) {
if (!options.deodex) {
@ -270,13 +274,15 @@ public class main {
if (dexFile instanceof DexBackedOdexFile) {
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
} else {
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel);
options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
options.experimental);
}
}
if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
options.inlineResolver =
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
InlineMethodResolver.createInlineMethodResolver(
((DexBackedOdexFile)dexFile).getOdexVersion());
}
boolean errorOccurred = false;
@ -288,7 +294,7 @@ public class main {
if (dumpFileName == null) {
dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
}
dump.dump(dexFile, dumpFileName, options.apiLevel);
dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
}
if (errorOccurred) {
@ -352,6 +358,10 @@ public class main {
"odex file")
.create("x");
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
.withDescription("enable experimental opcodes to be disassembled, even if they aren't necessarily supported in the Android runtime yet")
.create("X");
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
.withDescription("output the .locals directive with the number of non-parameter registers, rather" +
" than the .register directive with the total number of register")
@ -469,6 +479,7 @@ public class main {
basicOptions.addOption(outputDirOption);
basicOptions.addOption(noParameterRegistersOption);
basicOptions.addOption(deodexerantOption);
basicOptions.addOption(experimentalOption);
basicOptions.addOption(useLocalsOption);
basicOptions.addOption(sequentialLabelsOption);
basicOptions.addOption(noDebugInfoOption);
@ -496,9 +507,9 @@ public class main {
options.addOption((Option)option);
}
}
@Nonnull
private static List<String> getDefaultBootClassPathForApi(int apiLevel) {
private static List<String> getDefaultBootClassPathForApi(int apiLevel, boolean experimental) {
if (apiLevel < 9) {
return Lists.newArrayList(
"/system/framework/core.jar",

View File

@ -85,7 +85,7 @@ public class AnalysisTest {
public void runTest(String test, boolean registerInfo) throws IOException, URISyntaxException {
String dexFilePath = String.format("%s%sclasses.dex", test, File.separatorChar);
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15);
DexFile dexFile = DexFileFactory.loadDexFile(findResource(dexFilePath), 15, false);
baksmaliOptions options = new baksmaliOptions();
if (registerInfo) {

View File

@ -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(); }
}

View File

@ -31,22 +31,15 @@
package org.jf.baksmali;
import junit.framework.Assert;
import org.antlr.runtime.RecognitionException;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.smali.SmaliTestUtils;
import org.jf.util.IndentingWriter;
import org.jf.util.TextUtils;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
public class ImplicitReferenceTest {
@Test
public void testImplicitMethodReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@ -55,7 +48,7 @@ public class ImplicitReferenceTest {
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
".end method");
".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@ -72,19 +65,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitMethodReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@ -93,7 +79,7 @@ public class ImplicitReferenceTest {
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
".end method");
".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@ -110,25 +96,18 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
".field public static field4:Ljava/lang/Class; = I");
".field public static field4:Ljava/lang/Class; = I";
String expected = "" +
".class public LHelloWorld;\n" +
@ -142,25 +121,18 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
".field public static field4:Ljava/lang/Class; = I");
".field public static field4:Ljava/lang/Class; = I";
String expected = "" +
".class public LHelloWorld;\n" +
@ -174,19 +146,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitFieldReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@ -195,7 +160,7 @@ public class ImplicitReferenceTest {
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
".end method");
".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@ -212,19 +177,12 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitFieldReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
@ -233,7 +191,7 @@ public class ImplicitReferenceTest {
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
".end method");
".end method";
String expected = "" +
".class public LHelloWorld;\n" +
@ -250,24 +208,17 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
String expected = "" +
".class public LHelloWorld;\n" +
@ -280,24 +231,17 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
@Test
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
String source = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I";
String expected = "" +
".class public LHelloWorld;\n" +
@ -310,13 +254,7 @@ public class ImplicitReferenceTest {
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
BaksmaliTestUtils.assertSmaliCompiledEquals(source, expected, options);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -79,7 +79,7 @@ public class AccessorTest {
public void testAccessors() throws IOException {
URL url = AccessorTest.class.getClassLoader().getResource("accessorTest.dex");
Assert.assertNotNull(url);
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15);
DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15, false);
SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses());

View File

@ -45,22 +45,26 @@ import java.util.zip.ZipFile;
public final class DexFileFactory {
@Nonnull
public static DexBackedDexFile loadDexFile(String path, int api) throws IOException {
return loadDexFile(new File(path), "classes.dex", new Opcodes(api));
public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental)
throws IOException {
return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental));
}
@Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException {
return loadDexFile(dexFile, "classes.dex", new Opcodes(api));
public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental)
throws IOException {
return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental));
}
@Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api) throws IOException {
return loadDexFile(dexFile, dexEntry, new Opcodes(api));
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api,
boolean experimental) throws IOException {
return loadDexFile(dexFile, dexEntry, new Opcodes(api, experimental));
}
@Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, @Nonnull Opcodes opcodes) throws IOException {
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry,
@Nonnull Opcodes opcodes) throws IOException {
ZipFile zipFile = null;
boolean isZipFile = false;
try {

View File

@ -51,6 +51,7 @@ public enum Format {
Format22t(4),
Format22x(4),
Format23x(4),
Format25x(4),
Format30t(6),
Format31c(6),
Format31i(6),

View File

@ -269,13 +269,13 @@ public enum Opcode
INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", minApi(14), ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", minApi(11), ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
IPUT_QUICK((short)0xf5, "iput-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
IPUT_QUICK((short)0xf5, "iput-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", maxApi(22), ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", maxApi(22), ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
@ -285,7 +285,17 @@ public enum Opcode
PACKED_SWITCH_PAYLOAD((short)0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
SPARSE_SWITCH_PAYLOAD((short)0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0);
ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
// Reuse the deprecated f3-ff opcodes in Art:
INVOKE_LAMBDA((short)0xf3, "invoke-lambda", minApi(23), ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
// TODO: What about JUMBO support if the string ID is too large?
CAPTURE_VARIABLE((short)0xf5, "capture-variable", minApi(23), ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
CREATE_LAMBDA((short)0xf6, "create-lambda", minApi(23), ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
// TODO: do we need a capture/liberate wide?
LIBERATE_VARIABLE((short)0xf7, "liberate-variable", minApi(23), ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
BOX_LAMBDA((short)0xf8, "box-lambda", minApi(23), ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
UNBOX_LAMBDA((short)0xf9, "unbox-lambda", minApi(23), ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
//if the instruction can throw an exception
public static final int CAN_THROW = 0x1;
@ -309,6 +319,8 @@ public enum Opcode
public static final int JUMBO_OPCODE = 0x200;
//if the instruction can initialize an uninitialized object reference
public static final int CAN_INITIALIZE_REFERENCE = 0x400;
//if the instruction is experimental (not potentially supported by Android runtime yet)
public static final int EXPERIMENTAL = 0x800;
private static final int ALL_APIS = 0xFFFF0000;
@ -417,4 +429,8 @@ public enum Opcode
public final boolean canInitializeReference() {
return (flags & CAN_INITIALIZE_REFERENCE) != 0;
}
public final boolean isExperimental() {
return (flags & EXPERIMENTAL) != 0;
}
}

View File

@ -40,13 +40,14 @@ public class Opcodes {
private final Opcode[] opcodesByValue;
private final HashMap<String, Opcode> opcodesByName;
public Opcodes(int api) {
public Opcodes(int api, boolean experimental) {
opcodesByValue = new Opcode[256];
opcodesByName = Maps.newHashMap();
for (Opcode opcode: Opcode.values()) {
if (!opcode.format.isPayloadFormat) {
if (api <= opcode.getMaxApi() && api >= opcode.getMinApi()) {
if (api <= opcode.getMaxApi() && api >= opcode.getMinApi() &&
(experimental || !opcode.isExperimental())) {
opcodesByValue[opcode.value] = opcode;
opcodesByName.put(opcode.name.toLowerCase(), opcode);
}

View File

@ -170,18 +170,18 @@ public class ClassPath {
@Nonnull
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
int api) {
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17);
int api, boolean experimental) {
return fromClassPath(classPathDirs, classPath, dexFile, api, api == 17, experimental);
}
@Nonnull
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
int api, boolean checkPackagePrivateAccess) {
int api, boolean checkPackagePrivateAccess, boolean experimental) {
ArrayList<DexFile> dexFiles = Lists.newArrayList();
for (String classPathEntry: classPath) {
try {
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api));
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
} catch (ExceptionWithContext e){}
}
dexFiles.add(dexFile);
@ -192,7 +192,8 @@ public class ClassPath {
@Nonnull
private static DexFile loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
@Nonnull String bootClassPathEntry, int api) {
@Nonnull String bootClassPathEntry, int api,
boolean experimental) {
File rawEntry = new File(bootClassPathEntry);
// strip off the path - we only care about the filename
String entryName = rawEntry.getName();
@ -227,7 +228,7 @@ public class ClassPath {
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
} else {
try {
return DexFileFactory.loadDexFile(file, api);
return DexFileFactory.loadDexFile(file, api, experimental);
} catch (DexFileFactory.NoClassesDexException ex) {
// ignore and continue
} catch (Exception ex) {

View File

@ -73,6 +73,7 @@ public class DumpFields {
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
String outFile = "fields.txt";
int apiLevel = 15;
boolean experimental = false;
for (int i=0; i<parsedOptions.length; i++) {
Option option = parsedOptions[i];
@ -88,6 +89,9 @@ public class DumpFields {
case 'a':
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
break;
case 'X':
experimental = true;
break;
default:
assert false;
}
@ -107,9 +111,9 @@ public class DumpFields {
}
try {
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel);
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
FileOutputStream outStream = new FileOutputStream(outFile);
for (ClassDef classDef: dexFile.getClasses()) {
@ -163,8 +167,14 @@ public class DumpFields {
.withArgName("API_LEVEL")
.create("a");
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
"supported by the android runtime yet.")
.create("X");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
options.addOption(apiLevelOption);
options.addOption(experimentalOption);
}
}

View File

@ -71,6 +71,7 @@ public class DumpVtables {
ArrayList<String> bootClassPathDirs = Lists.newArrayList();
String outFile = "vtables.txt";
int apiLevel = 15;
boolean experimental = false;
for (int i=0; i<parsedOptions.length; i++) {
Option option = parsedOptions[i];
@ -86,6 +87,9 @@ public class DumpVtables {
case 'a':
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
break;
case 'X':
experimental = true;
break;
default:
assert false;
}
@ -105,9 +109,9 @@ public class DumpVtables {
}
try {
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel);
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, apiLevel, experimental);
Iterable<String> bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar");
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel);
ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel, experimental);
FileOutputStream outStream = new FileOutputStream(outFile);
for (ClassDef classDef: dexFile.getClasses()) {
@ -167,8 +171,14 @@ public class DumpVtables {
.withArgName("API_LEVEL")
.create("a");
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
.withDescription("Enable dumping experimental opcodes, that aren't necessarily " +
"supported by the android runtime yet.")
.create("X");
options.addOption(classPathDirOption);
options.addOption(outputFileOption);
options.addOption(apiLevelOption);
options.addOption(experimentalOption);
}
}

View File

@ -511,82 +511,90 @@ public class MutableMethodImplementation implements MethodImplementation {
@Nonnull Instruction instruction) {
switch (instruction.getOpcode().format) {
case Format10t:
setInstruction(location, newBuilderInstruction10t(location.codeAddress, codeAddressToIndex,
(Instruction10t)instruction));
setInstruction(location, newBuilderInstruction10t(location.codeAddress,
codeAddressToIndex,
(Instruction10t) instruction));
return;
case Format10x:
setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction));
setInstruction(location, newBuilderInstruction10x((Instruction10x) instruction));
return;
case Format11n:
setInstruction(location, newBuilderInstruction11n((Instruction11n)instruction));
setInstruction(location, newBuilderInstruction11n((Instruction11n) instruction));
return;
case Format11x:
setInstruction(location, newBuilderInstruction11x((Instruction11x)instruction));
setInstruction(location, newBuilderInstruction11x((Instruction11x) instruction));
return;
case Format12x:
setInstruction(location, newBuilderInstruction12x((Instruction12x)instruction));
setInstruction(location, newBuilderInstruction12x((Instruction12x) instruction));
return;
case Format20bc:
setInstruction(location, newBuilderInstruction20bc((Instruction20bc)instruction));
setInstruction(location, newBuilderInstruction20bc((Instruction20bc) instruction));
return;
case Format20t:
setInstruction(location, newBuilderInstruction20t(location.codeAddress, codeAddressToIndex,
(Instruction20t)instruction));
setInstruction(location, newBuilderInstruction20t(location.codeAddress,
codeAddressToIndex,
(Instruction20t) instruction));
return;
case Format21c:
setInstruction(location, newBuilderInstruction21c((Instruction21c)instruction));
setInstruction(location, newBuilderInstruction21c((Instruction21c) instruction));
return;
case Format21ih:
setInstruction(location, newBuilderInstruction21ih((Instruction21ih)instruction));
setInstruction(location, newBuilderInstruction21ih((Instruction21ih) instruction));
return;
case Format21lh:
setInstruction(location, newBuilderInstruction21lh((Instruction21lh)instruction));
setInstruction(location, newBuilderInstruction21lh((Instruction21lh) instruction));
return;
case Format21s:
setInstruction(location, newBuilderInstruction21s((Instruction21s)instruction));
setInstruction(location, newBuilderInstruction21s((Instruction21s) instruction));
return;
case Format21t:
setInstruction(location, newBuilderInstruction21t(location.codeAddress, codeAddressToIndex,
(Instruction21t)instruction));
setInstruction(location, newBuilderInstruction21t(location.codeAddress,
codeAddressToIndex,
(Instruction21t) instruction));
return;
case Format22b:
setInstruction(location, newBuilderInstruction22b((Instruction22b)instruction));
setInstruction(location, newBuilderInstruction22b((Instruction22b) instruction));
return;
case Format22c:
setInstruction(location, newBuilderInstruction22c((Instruction22c)instruction));
setInstruction(location, newBuilderInstruction22c((Instruction22c) instruction));
return;
case Format22s:
setInstruction(location, newBuilderInstruction22s((Instruction22s)instruction));
setInstruction(location, newBuilderInstruction22s((Instruction22s) instruction));
return;
case Format22t:
setInstruction(location, newBuilderInstruction22t(location.codeAddress, codeAddressToIndex,
(Instruction22t)instruction));
setInstruction(location, newBuilderInstruction22t(location.codeAddress,
codeAddressToIndex,
(Instruction22t) instruction));
return;
case Format22x:
setInstruction(location, newBuilderInstruction22x((Instruction22x)instruction));
setInstruction(location, newBuilderInstruction22x((Instruction22x) instruction));
return;
case Format23x:
setInstruction(location, newBuilderInstruction23x((Instruction23x)instruction));
setInstruction(location, newBuilderInstruction23x((Instruction23x) instruction));
return;
case Format25x:
setInstruction(location, newBuilderInstruction25x((Instruction25x) instruction));
return;
case Format30t:
setInstruction(location, newBuilderInstruction30t(location.codeAddress, codeAddressToIndex,
(Instruction30t)instruction));
setInstruction(location, newBuilderInstruction30t(location.codeAddress,
codeAddressToIndex,
(Instruction30t) instruction));
return;
case Format31c:
setInstruction(location, newBuilderInstruction31c((Instruction31c)instruction));
setInstruction(location, newBuilderInstruction31c((Instruction31c) instruction));
return;
case Format31i:
setInstruction(location, newBuilderInstruction31i((Instruction31i)instruction));
setInstruction(location, newBuilderInstruction31i((Instruction31i) instruction));
return;
case Format31t:
setInstruction(location, newBuilderInstruction31t(location, codeAddressToIndex,
(Instruction31t)instruction));
(Instruction31t) instruction));
return;
case Format32x:
setInstruction(location, newBuilderInstruction32x((Instruction32x)instruction));
setInstruction(location, newBuilderInstruction32x((Instruction32x) instruction));
return;
case Format35c:
setInstruction(location, newBuilderInstruction35c((Instruction35c)instruction));
setInstruction(location, newBuilderInstruction35c((Instruction35c) instruction));
return;
case Format3rc:
setInstruction(location, newBuilderInstruction3rc((Instruction3rc)instruction));
@ -820,6 +828,18 @@ public class MutableMethodImplementation implements MethodImplementation {
instruction.getReference());
}
@Nonnull
private BuilderInstruction25x newBuilderInstruction25x(@Nonnull Instruction25x instruction) {
return new BuilderInstruction25x(
instruction.getOpcode(),
instruction.getParameterRegisterCount(),
instruction.getRegisterFixedC(),
instruction.getRegisterParameterD(),
instruction.getRegisterParameterE(),
instruction.getRegisterParameterF(),
instruction.getRegisterParameterG());
}
@Nonnull
private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) {
return new BuilderInstruction3rc(

View File

@ -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; }
}

View File

@ -115,6 +115,8 @@ public abstract class DexBackedInstruction implements Instruction {
return new DexBackedInstruction22x(dexFile, opcode, instructionStartOffset);
case Format23x:
return new DexBackedInstruction23x(dexFile, opcode, instructionStartOffset);
case Format25x:
return new DexBackedInstruction25x(dexFile, opcode, instructionStartOffset);
case Format30t:
return new DexBackedInstruction30t(dexFile, opcode, instructionStartOffset);
case Format31c:

View File

@ -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));
}
}

View File

@ -129,6 +129,9 @@ public class CodeItem {
case Format10x:
annotateInstruction10x(out, instruction);
break;
case Format25x:
annotateInstruction25x(out, (Instruction25x) instruction);
break;
case Format35c:
annotateInstruction35c(out, (Instruction35c)instruction);
break;
@ -282,6 +285,30 @@ public class CodeItem {
instruction.getOpcode().name, Joiner.on(", ").join(args), reference));
}
private void annotateInstruction25x(@Nonnull AnnotatedBytes out,
@Nonnull Instruction25x instruction) {
List<String> args = Lists.newArrayList();
int registerCount = instruction.getRegisterCount(); //at least 1.
if (registerCount == 2) {
args.add(formatRegister(instruction.getRegisterParameterD()));
} else if (registerCount == 3) {
args.add(formatRegister(instruction.getRegisterParameterD()));
args.add(formatRegister(instruction.getRegisterParameterE()));
} else if (registerCount == 4) {
args.add(formatRegister(instruction.getRegisterParameterD()));
args.add(formatRegister(instruction.getRegisterParameterE()));
args.add(formatRegister(instruction.getRegisterParameterF()));
} else if (registerCount == 5) {
args.add(formatRegister(instruction.getRegisterParameterD()));
args.add(formatRegister(instruction.getRegisterParameterE()));
args.add(formatRegister(instruction.getRegisterParameterF()));
args.add(formatRegister(instruction.getRegisterParameterG()));
}
out.annotate(6, String.format("%s %s, {%s}",
instruction.getOpcode().name, instruction.getRegisterFixedC(), Joiner.on(", ").join(args)));
}
private void annotateInstruction3rc(@Nonnull AnnotatedBytes out, @Nonnull Instruction3rc instruction) {
int startRegister = instruction.getStartRegister();
int endRegister = startRegister + instruction.getRegisterCount() - 1;

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -97,6 +97,8 @@ public abstract class ImmutableInstruction implements Instruction {
return ImmutableInstruction22x.of((Instruction22x)instruction);
case Format23x:
return ImmutableInstruction23x.of((Instruction23x)instruction);
case Format25x:
return ImmutableInstruction25x.of((Instruction25x) instruction);
case Format30t:
return ImmutableInstruction30t.of((Instruction30t)instruction);
case Format31c:

View File

@ -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; }
}

View File

@ -133,6 +133,15 @@ public class Preconditions {
return registerCount;
}
public static int check25xParameterRegisterCount(int registerCount) {
if (registerCount < 0 || registerCount > 4) {
throw new IllegalArgumentException(
String.format("Invalid parameter register count: %d. " +
"Must be between 0 and 4, inclusive.", registerCount));
}
return registerCount;
}
public static int checkRegisterRangeCount(int registerCount) {
if ((registerCount & 0xFFFFFF00) != 0) {
throw new IllegalArgumentException(

View File

@ -976,6 +976,9 @@ public abstract class DexWriter<
case Format23x:
instructionWriter.write((Instruction23x)instruction);
break;
case Format25x:
instructionWriter.write((Instruction25x)instruction);
break;
case Format30t:
instructionWriter.write((Instruction30t)instruction);
break;

View File

@ -317,6 +317,19 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
}
}
public void write(@Nonnull Instruction25x instruction) {
try {
writer.write(instruction.getOpcode().value);
writer.write(packNibbles(
instruction.getRegisterParameterG(), instruction.getParameterRegisterCount()));
writer.write(packNibbles(
instruction.getRegisterFixedC(), instruction.getRegisterParameterD()));
writer.write(packNibbles(
instruction.getRegisterParameterE(), instruction.getRegisterParameterF()));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public void write(@Nonnull Instruction3rc instruction) {
try {
writer.write(instruction.getOpcode().value);

View File

@ -69,7 +69,7 @@ public class CustomMethodInlineTableTest {
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15);
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
@ -96,7 +96,7 @@ public class CustomMethodInlineTableTest {
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15);
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
@ -123,7 +123,7 @@ public class CustomMethodInlineTableTest {
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15);
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);

View File

@ -75,7 +75,7 @@ public class DexWriterTest {
throw new RuntimeException(ex);
}
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dataStore.getData());
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
Assert.assertNotNull(dbClassDef);
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);

View File

@ -92,7 +92,7 @@ public class JumboStringConversionTest {
MemoryDataStore dexStore = new MemoryDataStore();
dexBuilder.writeTo(dexStore);
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
Assert.assertNotNull(classDef);
@ -189,7 +189,7 @@ public class JumboStringConversionTest {
MemoryDataStore dexStore = new MemoryDataStore();
dexBuilder.writeTo(dexStore);
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dexStore.getData());
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
Assert.assertNotNull(classDef);

View File

@ -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

View File

@ -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

View File

@ -86,6 +86,8 @@ tokens {
INSTRUCTION_FORMAT21c_FIELD_ODEX;
INSTRUCTION_FORMAT21c_STRING;
INSTRUCTION_FORMAT21c_TYPE;
INSTRUCTION_FORMAT21c_LAMBDA;
INSTRUCTION_FORMAT21c_METHOD;
INSTRUCTION_FORMAT21ih;
INSTRUCTION_FORMAT21lh;
INSTRUCTION_FORMAT21s;
@ -94,12 +96,14 @@ tokens {
INSTRUCTION_FORMAT22c_FIELD;
INSTRUCTION_FORMAT22c_FIELD_ODEX;
INSTRUCTION_FORMAT22c_TYPE;
INSTRUCTION_FORMAT22c_STRING;
INSTRUCTION_FORMAT22cs_FIELD;
INSTRUCTION_FORMAT22s;
INSTRUCTION_FORMAT22s_OR_ID;
INSTRUCTION_FORMAT22t;
INSTRUCTION_FORMAT22x;
INSTRUCTION_FORMAT23x;
INSTRUCTION_FORMAT25x;
INSTRUCTION_FORMAT30t;
INSTRUCTION_FORMAT31c;
INSTRUCTION_FORMAT31i;
@ -209,6 +213,8 @@ tokens {
I_STATEMENT_FORMAT21c_TYPE;
I_STATEMENT_FORMAT21c_FIELD;
I_STATEMENT_FORMAT21c_STRING;
I_STATEMENT_FORMAT21c_LAMBDA;
I_STATEMENT_FORMAT21c_METHOD;
I_STATEMENT_FORMAT21ih;
I_STATEMENT_FORMAT21lh;
I_STATEMENT_FORMAT21s;
@ -216,10 +222,12 @@ tokens {
I_STATEMENT_FORMAT22b;
I_STATEMENT_FORMAT22c_FIELD;
I_STATEMENT_FORMAT22c_TYPE;
I_STATEMENT_FORMAT22c_STRING;
I_STATEMENT_FORMAT22s;
I_STATEMENT_FORMAT22t;
I_STATEMENT_FORMAT22x;
I_STATEMENT_FORMAT23x;
I_STATEMENT_FORMAT25x;
I_STATEMENT_FORMAT30t;
I_STATEMENT_FORMAT31c;
I_STATEMENT_FORMAT31i;
@ -252,7 +260,7 @@ import org.jf.dexlib2.Opcodes;
private boolean verboseErrors = false;
private boolean allowOdex = false;
private int apiLevel = 15;
private Opcodes opcodes = new Opcodes(apiLevel);
private Opcodes opcodes = new Opcodes(apiLevel, false);
public void setVerboseErrors(boolean verboseErrors) {
this.verboseErrors = verboseErrors;
@ -262,8 +270,8 @@ import org.jf.dexlib2.Opcodes;
this.allowOdex = allowOdex;
}
public void setApiLevel(int apiLevel) {
this.opcodes = new Opcodes(apiLevel);
public void setApiLevel(int apiLevel, boolean experimental) {
this.opcodes = new Opcodes(apiLevel, experimental);
this.apiLevel = apiLevel;
}
@ -561,14 +569,18 @@ simple_name
| INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX]
| INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING]
| INSTRUCTION_FORMAT21c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_TYPE]
| INSTRUCTION_FORMAT21c_LAMBDA -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_LAMBDA]
| INSTRUCTION_FORMAT21c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_METHOD]
| INSTRUCTION_FORMAT21t -> SIMPLE_NAME[$INSTRUCTION_FORMAT21t]
| INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD]
| INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX]
| INSTRUCTION_FORMAT22c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_TYPE]
| INSTRUCTION_FORMAT22c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_STRING]
| INSTRUCTION_FORMAT22cs_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22cs_FIELD]
| INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID]
| INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t]
| INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x]
| INSTRUCTION_FORMAT25x -> SIMPLE_NAME[$INSTRUCTION_FORMAT25x]
| INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID]
| INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t]
| INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD]
@ -806,6 +818,8 @@ instruction
| insn_format21c_field_odex
| insn_format21c_string
| insn_format21c_type
| insn_format21c_lambda
| insn_format21c_method
| insn_format21ih
| insn_format21lh
| insn_format21s
@ -814,11 +828,13 @@ instruction
| insn_format22c_field
| insn_format22c_field_odex
| insn_format22c_type
| insn_format22c_string
| insn_format22cs_field
| insn_format22s
| insn_format22t
| insn_format22x
| insn_format23x
| insn_format25x
| insn_format30t
| insn_format31c
| insn_format31i
@ -912,6 +928,16 @@ insn_format21c_type
INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER nonvoid_type_descriptor);
insn_format21c_lambda
: //e.g. capture-variable v1, "foobar"
INSTRUCTION_FORMAT21c_LAMBDA REGISTER COMMA STRING_LITERAL
-> ^(I_STATEMENT_FORMAT21c_LAMBDA[$start, "I_STATEMENT_FORMAT21c_LAMBDA"] INSTRUCTION_FORMAT21c_LAMBDA REGISTER STRING_LITERAL);
insn_format21c_method
: //e.g. create-lambda v1, java/io/PrintStream/print(Ljava/lang/Stream;)V
INSTRUCTION_FORMAT21c_METHOD REGISTER COMMA method_reference
-> ^(I_STATEMENT_FORMAT21c_METHOD[$start, "I_STATEMENT_FORMAT21c_METHOD"] INSTRUCTION_FORMAT21c_METHOD REGISTER method_reference);
insn_format21ih
: //e.g. const/high16 v1, 1234
INSTRUCTION_FORMAT21ih REGISTER COMMA fixed_32bit_literal
@ -957,6 +983,11 @@ insn_format22c_type
INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor
-> ^(I_STATEMENT_FORMAT22c_TYPE[$start, "I_STATEMENT_FORMAT22c_TYPE"] INSTRUCTION_FORMAT22c_TYPE REGISTER REGISTER nonvoid_type_descriptor);
insn_format22c_string
: //e.g. liberate-variable v0, v1, "baz"
INSTRUCTION_FORMAT22c_STRING REGISTER COMMA REGISTER COMMA STRING_LITERAL
-> ^(I_STATEMENT_FORMAT22c_STRING[$start, "I_STATEMENT_FORMAT22c_STRING"] INSTRUCTION_FORMAT22c_STRING REGISTER REGISTER STRING_LITERAL);
insn_format22cs_field
: //e.g. iget-quick v0, v1, field@0xc
INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET
@ -984,6 +1015,11 @@ insn_format23x
INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER
-> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER REGISTER);
insn_format25x
: //e.g. invoke-lambda vClosure, {vA, vB, vC, vD} -- up to 4 parameters + the closure.
INSTRUCTION_FORMAT25x REGISTER COMMA OPEN_BRACE register_list CLOSE_BRACE
-> ^(I_STATEMENT_FORMAT25x[$start, "I_STATEMENT_FORMAT25x"] INSTRUCTION_FORMAT25x REGISTER register_list);
insn_format30t
: //e.g. goto/32 endloop:
INSTRUCTION_FORMAT30t label_ref

View File

@ -77,15 +77,15 @@ import java.util.*;
public String classType;
private boolean verboseErrors = false;
private int apiLevel = 15;
private Opcodes opcodes = new Opcodes(apiLevel);
private Opcodes opcodes = new Opcodes(apiLevel, false);
private DexBuilder dexBuilder;
public void setDexBuilder(DexBuilder dexBuilder) {
this.dexBuilder = dexBuilder;
}
public void setApiLevel(int apiLevel) {
this.opcodes = new Opcodes(apiLevel);
public void setApiLevel(int apiLevel, boolean experimental) {
this.opcodes = new Opcodes(apiLevel, experimental);
this.apiLevel = apiLevel;
}
@ -674,6 +674,22 @@ register_list returns[byte[\] registers, byte registerCount]
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text);
})*);
register_list4 returns[byte[\] registers, byte registerCount]
@init
{
$registers = new byte[4];
$registerCount = 0;
}
: ^(I_REGISTER_LIST
(REGISTER
{
if ($registerCount == 4) {
throw new SemanticException(input, $I_REGISTER_LIST, "A list4 of registers can only have a maximum of 4 " +
"registers. Use the <op>/range alternate opcode instead.");
}
$registers[$registerCount++] = parseRegister_nibble($REGISTER.text);
})*);
register_range returns[int startRegister, int endRegister]
: ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?)
{
@ -726,6 +742,8 @@ instruction
| insn_format21c_field
| insn_format21c_string
| insn_format21c_type
| insn_format21c_lambda
| insn_format21c_method
| insn_format21ih
| insn_format21lh
| insn_format21s
@ -733,10 +751,12 @@ instruction
| insn_format22b
| insn_format22c_field
| insn_format22c_type
| insn_format22c_string
| insn_format22s
| insn_format22t
| insn_format22x
| insn_format23x
| insn_format25x
| insn_format30t
| insn_format31c
| insn_format31i
@ -861,6 +881,30 @@ insn_format21c_type
dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
};
insn_format21c_lambda
: //e.g. capture-variable v1, "foobar"
^(I_STATEMENT_FORMAT21c_LAMBDA INSTRUCTION_FORMAT21c_LAMBDA REGISTER string_literal)
{
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_LAMBDA.text);
short regA = parseRegister_byte($REGISTER.text);
$method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
dexBuilder.internStringReference($string_literal.value)));
};
insn_format21c_method
: //e.g. create-lambda v1, java/io/PrintStream/print(Ljava/lang/Stream;)V
^(I_STATEMENT_FORMAT21c_METHOD INSTRUCTION_FORMAT21c_METHOD REGISTER method_reference)
{
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_METHOD.text);
short regA = parseRegister_byte($REGISTER.text);
ImmutableMethodReference methodReference = $method_reference.methodReference;
$method::methodBuilder.addInstruction(new BuilderInstruction21c(opcode, regA,
dexBuilder.internMethodReference(methodReference)));
};
insn_format21ih
: //e.g. const/high16 v1, 1234
^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal)
@ -947,6 +991,18 @@ insn_format22c_type
dexBuilder.internTypeReference($nonvoid_type_descriptor.type)));
};
insn_format22c_string
: //e.g. liberate-variable v0, v1, "baz"
^(I_STATEMENT_FORMAT22c_STRING INSTRUCTION_FORMAT22c_STRING registerA=REGISTER registerB=REGISTER string_literal)
{
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT22c_STRING.text);
byte regA = parseRegister_nibble($registerA.text);
byte regB = parseRegister_nibble($registerB.text);
$method::methodBuilder.addInstruction(new BuilderInstruction22c(opcode, regA, regB,
dexBuilder.internStringReference($string_literal.value)));
};
insn_format22s
: //e.g. add-int/lit16 v0, v1, 12345
^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal)
@ -994,6 +1050,23 @@ insn_format23x
$method::methodBuilder.addInstruction(new BuilderInstruction23x(opcode, regA, regB, regC));
};
insn_format25x
: //e.g. invoke-lambda vClosure, {vD, vE, vF, vG} -- up to 4 parameters + the closure.
^(I_STATEMENT_FORMAT25x INSTRUCTION_FORMAT25x REGISTER register_list4)
{
Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT25x.text);
byte closureRegister = parseRegister_nibble($REGISTER.text);
//this depends on the fact that register_list4 returns a byte[4]
byte[] registers = $register_list4.registers;
int parameterRegisterCount = $register_list4.registerCount; // don't count closure register
$method::methodBuilder.addInstruction(new BuilderInstruction25x(opcode,
parameterRegisterCount, closureRegister, registers[0], registers[1],
registers[2], registers[3]));
};
insn_format30t
: //e.g. goto/32 endloop:
^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t label_ref)

View File

@ -48,10 +48,16 @@ import java.io.Reader;
import java.io.StringReader;
public class SmaliTestUtils {
public static ClassDef compileSmali(String smaliText) throws RecognitionException, IOException {
return compileSmali(smaliText, 15, false);
}
public static ClassDef compileSmali(String smaliText, int apiLevel, boolean experimental)
throws RecognitionException, IOException {
CommonTokenStream tokens;
LexerErrorInterface lexer;
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
Reader reader = new StringReader(smaliText);
@ -61,7 +67,7 @@ public class SmaliTestUtils {
smaliParser parser = new smaliParser(tokens);
parser.setVerboseErrors(true);
parser.setAllowOdex(false);
parser.setApiLevel(15);
parser.setApiLevel(apiLevel, experimental);
smaliParser.smali_file_return result = parser.smali_file();
@ -75,6 +81,7 @@ public class SmaliTestUtils {
treeStream.setTokenStream(tokens);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.setApiLevel(apiLevel, experimental);
dexGen.setVerboseErrors(true);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();
@ -87,7 +94,8 @@ public class SmaliTestUtils {
dexBuilder.writeTo(dataStore);
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15), dataStore.getData());
DexBackedDexFile dexFile = new DexBackedDexFile(
new Opcodes(apiLevel, experimental), dataStore.getData());
return Iterables.getFirst(dexFile.getClasses(), null);
}

View File

@ -34,6 +34,7 @@ import org.antlr.runtime.Token;
import org.antlr.runtime.TokenSource;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.TreeNodeStream;
import org.apache.commons.cli.*;
import org.jf.dexlib2.writer.builder.DexBuilder;
import org.jf.dexlib2.writer.io.FileDataStore;
@ -110,6 +111,7 @@ public class main {
boolean allowOdex = false;
boolean verboseErrors = false;
boolean printTokens = false;
boolean experimental = false;
int apiLevel = 15;
@ -142,6 +144,9 @@ public class main {
case 'x':
allowOdex = true;
break;
case 'X':
experimental = true;
break;
case 'a':
apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
break;
@ -198,11 +203,12 @@ public class main {
final boolean finalPrintTokens = printTokens;
final boolean finalAllowOdex = allowOdex;
final int finalApiLevel = apiLevel;
final boolean finalExperimental = experimental;
for (final File file: filesToProcess) {
tasks.add(executor.submit(new Callable<Boolean>() {
@Override public Boolean call() throws Exception {
return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens,
finalAllowOdex, finalApiLevel);
finalAllowOdex, finalApiLevel, finalExperimental);
}
}));
}
@ -252,7 +258,8 @@ public class main {
}
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
boolean printTokens, boolean allowOdex, int apiLevel)
boolean printTokens, boolean allowOdex, int apiLevel,
boolean experimental)
throws Exception {
CommonTokenStream tokens;
@ -267,7 +274,7 @@ public class main {
if (printTokens) {
tokens.getTokens();
for (int i=0; i<tokens.size(); i++) {
Token token = tokens.get(i);
if (token.getChannel() == smaliParser.HIDDEN) {
@ -276,12 +283,14 @@ public class main {
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
}
System.out.flush();
}
smaliParser parser = new smaliParser(tokens);
parser.setVerboseErrors(verboseErrors);
parser.setAllowOdex(allowOdex);
parser.setApiLevel(apiLevel);
parser.setApiLevel(apiLevel, experimental);
smaliParser.smali_file_return result = parser.smali_file();
@ -294,7 +303,13 @@ public class main {
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
treeStream.setTokenStream(tokens);
if (printTokens) {
System.out.println(t.toStringTree());
}
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.setApiLevel(apiLevel, experimental);
dexGen.setVerboseErrors(verboseErrors);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();
@ -363,6 +378,11 @@ public class main {
.withArgName("API_LEVEL")
.create("a");
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
.withDescription("enable experimental opcodes to be assembled, even if they " +
" aren't necessarily supported by the Android runtime yet")
.create("X");
Option jobsOption = OptionBuilder.withLongOpt("jobs")
.withDescription("The number of threads to use. Defaults to the number of cores available, up to a " +
"maximum of 6")
@ -383,6 +403,7 @@ public class main {
basicOptions.addOption(outputOption);
basicOptions.addOption(allowOdexOption);
basicOptions.addOption(apiLevelOption);
basicOptions.addOption(experimentalOption);
basicOptions.addOption(jobsOption);
debugOptions.addOption(verboseErrorsOption);

View File

@ -461,6 +461,14 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(INSTRUCTION_FORMAT21c_TYPE);
}
"capture-variable" { // e.g. 'capture-variable vB, <string id>'
return newToken(INSTRUCTION_FORMAT21c_LAMBDA);
}
"create-lambda" { // e.g. 'create-lambda vClosure, <method id>'
return newToken(INSTRUCTION_FORMAT21c_METHOD);
}
"const/high16" {
return newToken(INSTRUCTION_FORMAT21ih);
}
@ -492,10 +500,13 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(INSTRUCTION_FORMAT22c_FIELD_ODEX);
}
"instance-of" | "new-array" {
"instance-of" | "new-array" | "unbox-lambda" {
return newToken(INSTRUCTION_FORMAT22c_TYPE);
}
"liberate-variable" {
return newToken(INSTRUCTION_FORMAT22c_STRING);
}
"iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" {
return newToken(INSTRUCTION_FORMAT22cs_FIELD);
}
@ -513,7 +524,7 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(INSTRUCTION_FORMAT22t);
}
"move/from16" | "move-wide/from16" | "move-object/from16" {
"move/from16" | "move-wide/from16" | "move-object/from16" | "box-lambda" {
return newToken(INSTRUCTION_FORMAT22x);
}
@ -527,6 +538,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
return newToken(INSTRUCTION_FORMAT23x);
}
"invoke-lambda" { // e.g. invoke-lambda vClosure, {vD, vE, vF, vG} -- at most 4 params
return newToken(INSTRUCTION_FORMAT25x);
}
"goto/32" {
return newToken(INSTRUCTION_FORMAT30t);
}

View File

@ -84,6 +84,8 @@ const-string
check-cast
new-instance
const-class
capture-variable
create-lambda
const/high16
const-wide/high16
const/16
@ -122,6 +124,8 @@ iput-wide-volatile
iput-object-volatile
instance-of
new-array
unbox-lambda
liberate-variable
iget-quick
iget-wide-quick
iget-object-quick
@ -144,6 +148,7 @@ if-le
move/from16
move-wide/from16
move-object/from16
box-lambda
cmpl-float
cmpg-float
cmpl-double
@ -194,6 +199,7 @@ add-double
sub-double
mul-double
div-double
invoke-lambda
goto/32
const-string/jumbo
const

View File

@ -84,6 +84,8 @@ INSTRUCTION_FORMAT21c_STRING("const-string")
INSTRUCTION_FORMAT21c_TYPE("check-cast")
INSTRUCTION_FORMAT21c_TYPE("new-instance")
INSTRUCTION_FORMAT21c_TYPE("const-class")
INSTRUCTION_FORMAT21c_LAMBDA("capture-variable")
INSTRUCTION_FORMAT21c_METHOD("create-lambda")
INSTRUCTION_FORMAT21ih("const/high16")
INSTRUCTION_FORMAT21lh("const-wide/high16")
INSTRUCTION_FORMAT21s("const/16")
@ -122,6 +124,8 @@ INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-wide-volatile")
INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-object-volatile")
INSTRUCTION_FORMAT22c_TYPE("instance-of")
INSTRUCTION_FORMAT22c_TYPE("new-array")
INSTRUCTION_FORMAT22c_TYPE("unbox-lambda")
INSTRUCTION_FORMAT22c_STRING("liberate-variable")
INSTRUCTION_FORMAT22cs_FIELD("iget-quick")
INSTRUCTION_FORMAT22cs_FIELD("iget-wide-quick")
INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick")
@ -144,6 +148,7 @@ INSTRUCTION_FORMAT22t("if-le")
INSTRUCTION_FORMAT22x("move/from16")
INSTRUCTION_FORMAT22x("move-wide/from16")
INSTRUCTION_FORMAT22x("move-object/from16")
INSTRUCTION_FORMAT22x("box-lambda")
INSTRUCTION_FORMAT23x("cmpl-float")
INSTRUCTION_FORMAT23x("cmpg-float")
INSTRUCTION_FORMAT23x("cmpl-double")
@ -194,6 +199,7 @@ INSTRUCTION_FORMAT23x("add-double")
INSTRUCTION_FORMAT23x("sub-double")
INSTRUCTION_FORMAT23x("mul-double")
INSTRUCTION_FORMAT23x("div-double")
INSTRUCTION_FORMAT25x("invoke-lambda")
INSTRUCTION_FORMAT30t("goto/32")
INSTRUCTION_FORMAT31c("const-string/jumbo")
INSTRUCTION_FORMAT31i_OR_ID("const")

View File

@ -50,10 +50,28 @@ public class TextUtils {
@Nonnull
public static String normalizeWhitespace(@Nonnull String source) {
source = normalizeNewlines(source, "\n");
// Go to native system new lines so that ^/$ work correctly
source = normalizeNewlines(source);
Pattern pattern = Pattern.compile("(\n[ \t]*)+");
// Remove all suffix/prefix whitespace
Pattern pattern = Pattern.compile("((^[ \t]+)|([ \t]+))");
Matcher matcher = pattern.matcher(source);
return matcher.replaceAll("\n");
source = matcher.replaceAll("");
// Remove all empty lines
Pattern pattern2 = Pattern.compile("^\r?\n?", Pattern.MULTILINE);
Matcher matcher2 = pattern2.matcher(source);
source = matcher2.replaceAll("");
// Go back to unix-style \n newlines
source = normalizeNewlines(source, "\n");
return source;
}
@Nonnull
public static String stripComments(@Nonnull String source) {
Pattern pattern = Pattern.compile("#(.*)");
Matcher matcher = pattern.matcher(source);
return matcher.replaceAll("");
}
}

View File

@ -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"));
}
}