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.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;

View File

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

View File

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

View File

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

View File

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

View File

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

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

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 { 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());

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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); 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:

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: 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;

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); 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:

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; 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(

View File

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

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) { public void write(@Nonnull Instruction3rc instruction) {
try { try {
writer.write(instruction.getOpcode().value); writer.write(instruction.getOpcode().value);

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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