Top Description Inners Fields Constructors Methods
org.droidparts.dexmaker

public final Class DexMaker

extends Object
Class Inheritance
Imports
org.droidparts.dexmaker.dx.dex.DexFormat, .DexOptions, org.droidparts.dexmaker.dx.dex.code.DalvCode, .PositionList, .RopTranslator, org.droidparts.dexmaker.dx.dex.file.ClassDefItem, .DexFile, .EncodedField, .EncodedMethod, org.droidparts.dexmaker.dx.rop.code.AccessFlags, .LocalVariableInfo, .RopMethod, org.droidparts.dexmaker.dx.rop.cst.CstString, .CstType, org.droidparts.dexmaker.dx.rop.type.StdTypeList, java.io.File, .FileOutputStream, .IOException, java.lang.reflect.InvocationTargetException, .Modifier, java.util.*, java.util.jar.JarEntry, .JarOutputStream

Generates a Dalvik EXecutable (dex) file for execution on Android. Dex files define classes and interfaces, including their member methods and fields, executable code, and debugging information. They also define annotations, though this API currently has no facility to create a dex file that contains annotations.

This library is intended to satisfy two use cases:

Example: Fibonacci

To illustrate how this API is used, we'll use DexMaker to generate a class equivalent to the following Java source:
 package com.publicobject.fib;

public class Fibonacci {
  public static int fib(int i) {
    if (i < 2) {
      return i;
    }
    return fib(i - 1) + fib(i - 2);
  }
}

We start by creating a TypeId to identify the generated Fibonacci class. DexMaker identifies types by their internal names like Ljava/lang/Object; rather than their Java identifiers like java.lang.Object.

   TypeId<?> fibonacci = TypeId.get("Lcom/google/dexmaker/examples/Fibonacci;");

Next we declare the class. It allows us to specify the type's source file for stack traces, its modifiers, its superclass, and the interfaces it implements. In this case, Fibonacci is a public class that extends from Object:

   String fileName = "Fibonacci.generated";
  DexMaker dexMaker = new DexMaker();
  dexMaker.declare(fibonacci, fileName, Modifier.PUBLIC, TypeId.OBJECT);
It is illegal to declare members of a class without also declaring the class itself.

To make it easier to go from our Java method to dex instructions, we'll manually translate it to pseudocode fit for an assembler. We need to replace control flow like if() blocks and for() loops with labels and branches. We'll also avoid performing multiple operations in one statement, using local variables to hold intermediate values as necessary:

   int constant1 = 1;
  int constant2 = 2;
  if (i < constant2) goto baseCase;
  int a = i - constant1;
  int b = i - constant2;
  int c = fib(a);
  int d = fib(b);
  int result = c + d;
  return result;
baseCase:
  return i;

We look up the MethodId for the method on the declaring type. This takes the method's return type (possibly TypeId#VOID), its name and its parameters types. Next we declare the method, specifying its modifiers by bitwise ORing constants from java.lang.reflect.Modifier. The declare call returns a Code object, which we'll use to define the method's instructions.

   MethodId<?, Integer> fib = fibonacci.getMethod(TypeId.INT, "fib", TypeId.INT);
  Code code = dexMaker.declare(fib, Modifier.PUBLIC | Modifier.STATIC);

One limitation of DexMaker's API is that it requires all local variables to be created before any instructions are emitted. Use newLocal() to create a new local variable. The method's parameters are exposed as locals using getParameter(). For non-static methods the this pointer is exposed using getThis(). Here we declare all of the local variables that we'll need for our fib() method:

   Local<Integer> i = code.getParameter(0, TypeId.INT);
  Local<Integer> constant1 = code.newLocal(TypeId.INT);
  Local<Integer> constant2 = code.newLocal(TypeId.INT);
  Local<Integer> a = code.newLocal(TypeId.INT);
  Local<Integer> b = code.newLocal(TypeId.INT);
  Local<Integer> c = code.newLocal(TypeId.INT);
  Local<Integer> d = code.newLocal(TypeId.INT);
  Local<Integer> result = code.newLocal(TypeId.INT);

Notice that Local has a type parameter of Integer. This is useful for generating code that works with existing types like String and Integer, but it can be a hindrance when generating code that involves new types. For this reason you may prefer to use raw types only and add @SuppressWarnings("unsafe") on your calling code. This will yield the same result but you won't get IDE support if you make a type error.

We're ready to start defining our method's instructions. The Code class catalogs the available instructions and their use.

   code.loadConstant(constant1, 1);
  code.loadConstant(constant2, 2);
  Label baseCase = new Label();
  code.compare(Comparison.LT, baseCase, i, constant2);
  code.op(BinaryOp.SUBTRACT, a, i, constant1);
  code.op(BinaryOp.SUBTRACT, b, i, constant2);
  code.invokeStatic(fib, c, a);
  code.invokeStatic(fib, d, b);
  code.op(BinaryOp.ADD, result, c, d);
  code.returnValue(result);
  code.mark(baseCase);
  code.returnValue(i);

We're done defining the dex file. We just need to write it to the filesystem or load it into the current process. For this example we'll load the generated code into the current process. This only works when the current process is running on Android. We use generateAndLoad() which takes the class loader that will be used as our generated code's parent class loader. It also requires a directory where temporary files can be written.

   ClassLoader loader = dexMaker.generateAndLoad(
      FibonacciMaker.class.getClassLoader(), getDataDirectory());
Finally we'll use reflection to lookup our generated class on its class loader and invoke its fib() method:
   Class<?> fibonacciClass = loader.loadClass("com.google.dexmaker.examples.Fibonacci");
  Method fibMethod = fibonacciClass.getMethod("fib", int.class);
  System.out.println(fibMethod.invoke(null, 8));

Nested and Inner Type Summary

Modifier and TypeClass and Description
pack-priv static class
pack-priv static class
private static class

Field Summary

Modifier and TypeField and Description
private final Map<TypeId<?>, DexMaker.TypeDeclaration>

Constructor Summary

AccessConstructor and Description
public
DexMaker()

Creates a new DexMaker instance, which can be used to create a single dex file.

Method Summary

Modifier and TypeMethod and Description
public void
declare(TypeId<?> type, String sourceFile, int
a bitwise combination of Modifier#PUBLIC, Modifier#FINAL and Modifier#ABSTRACT.
flags
,
TypeId<?> supertype, TypeId<?>... interfaces)

Declares type.

public Code
declare(MethodId<?, ?> method, int
a bitwise combination of Modifier#PUBLIC, Modifier#PRIVATE, Modifier#PROTECTED, Modifier#STATIC, Modifier#FINAL and Modifier#SYNCHRONIZED.

Warning

the Modifier#SYNCHRONIZED flag is insufficient to generate a synchronized method. You must also use Code#monitorEnter and Code#monitorExit to acquire a monitor.

flags
)

Declares a method or constructor.

public void
declare(FieldId<?, ?> fieldId, int flags, Object
a constant representing the initial value for the static field, possibly null. This must be null if this field is non-static.
staticValue
)

Declares a field.

public byte[]
generate()

Generates a dex file and returns its bytes.

public ClassLoader
generateAndLoad(ClassLoader
the parent ClassLoader to be used when loading our generated types
parent
,
File
the destination directory where generated and optimized dex files will be written. If null, this class will try to guess the application's private data dir.
dexCache
)

Generates a dex file and loads its types into the current process.

private ClassLoader
generateClassLoader(File result, File dexCache, ClassLoader parent)

private String
private DexMaker.TypeDeclaration
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

typesback to summary
private final Map<TypeId<?>, DexMaker.TypeDeclaration> types

Constructor Detail

DexMakerback to summary
public DexMaker()

Creates a new DexMaker instance, which can be used to create a single dex file.

Method Detail

declareback to summary
public void declare(TypeId<?> type, String sourceFile, int flags, TypeId<?> supertype, TypeId<?>... interfaces)

Declares type.

Parameters
flags:int

a bitwise combination of Modifier#PUBLIC, Modifier#FINAL and Modifier#ABSTRACT.

declareback to summary
public Code declare(MethodId<?, ?> method, int flags)

Declares a method or constructor.

Parameters
flags:int

a bitwise combination of Modifier#PUBLIC, Modifier#PRIVATE, Modifier#PROTECTED, Modifier#STATIC, Modifier#FINAL and Modifier#SYNCHRONIZED.

Warning

the Modifier#SYNCHRONIZED flag is insufficient to generate a synchronized method. You must also use Code#monitorEnter and Code#monitorExit to acquire a monitor.

declareback to summary
public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue)

Declares a field.

Parameters
flags:int

a bitwise combination of Modifier#PUBLIC, Modifier#PRIVATE, Modifier#PROTECTED, Modifier#STATIC, Modifier#FINAL, Modifier#VOLATILE, and Modifier#TRANSIENT.

staticValue:Object

a constant representing the initial value for the static field, possibly null. This must be null if this field is non-static.

generateback to summary
public byte[] generate()

Generates a dex file and returns its bytes.

generateAndLoadback to summary
public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException

Generates a dex file and loads its types into the current process.

Picking a dex cache directory

The dexCache should be an application-private directory. If you pass a world-writable directory like /sdcard a malicious app could inject code into your process. Most applications should use this:
   File dexCache = getApplicationContext().getDir("dx", Context.MODE_PRIVATE);
If the dexCache is null, this method will consult the dexmaker.dexcache system property. If that exists, it will be used for the dex cache. If it doesn't exist, this method will attempt to guess the application's private data directory as a last resort. If that fails, this method will fail with an unchecked exception. You can avoid the exception by either providing a non-null value or setting the system property.
Parameters
parent:ClassLoader

the parent ClassLoader to be used when loading our generated types

dexCache:File

the destination directory where generated and optimized dex files will be written. If null, this class will try to guess the application's private data dir.

generateClassLoaderback to summary
private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader parent)
generateFileNameback to summary
private String generateFileName()
getTypeDeclarationback to summary
private DexMaker.TypeDeclaration getTypeDeclaration(TypeId<?> type)
org.droidparts.dexmaker back to summary

pack-priv Class DexMaker.FieldDeclaration

extends Object
Class Inheritance

Field Summary

Modifier and TypeField and Description
private final int
pack-priv final FieldId<?, ?>
private final Object

Constructor Summary

AccessConstructor and Description
pack-priv
FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue)

Method Summary

Modifier and TypeMethod and Description
public boolean
pack-priv EncodedField
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

accessFlagsback to summary
private final int accessFlags
fieldIdback to summary
pack-priv final FieldId<?, ?> fieldId
staticValueback to summary
private final Object staticValue

Constructor Detail

FieldDeclarationback to summary
pack-priv FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue)

Method Detail

isStaticback to summary
public boolean isStatic()
toEncodedFieldback to summary
pack-priv EncodedField toEncodedField()
org.droidparts.dexmaker back to summary

pack-priv Class DexMaker.MethodDeclaration

extends Object
Class Inheritance

Field Summary

Modifier and TypeField and Description
private final Code
private final int
pack-priv final MethodId<?, ?>

Constructor Summary

AccessConstructor and Description
public
MethodDeclaration(MethodId<?, ?> method, int flags)

Method Summary

Modifier and TypeMethod and Description
pack-priv boolean
pack-priv boolean
pack-priv EncodedMethod
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

codeback to summary
private final Code code
flagsback to summary
private final int flags
methodback to summary
pack-priv final MethodId<?, ?> method

Constructor Detail

MethodDeclarationback to summary
public MethodDeclaration(MethodId<?, ?> method, int flags)

Method Detail

isDirectback to summary
pack-priv boolean isDirect()
isStaticback to summary
pack-priv boolean isStatic()
toEncodedMethodback to summary
pack-priv EncodedMethod toEncodedMethod(DexOptions dexOptions)
org.droidparts.dexmaker back to summary

private Class DexMaker.TypeDeclaration

extends Object
Class Inheritance

Field Summary

Modifier and TypeField and Description
private boolean
declared

declared state

private final Map<FieldId<D, V>, DexMaker.FieldDeclaration>
private int
private TypeList
private final Map<MethodId<D, R>, DexMaker.MethodDeclaration>
private String
private TypeId<?>
private final TypeId<?>

Constructor Summary

AccessConstructor and Description
pack-priv

Method Summary

Modifier and TypeMethod and Description
pack-priv ClassDefItem
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

declaredback to summary
private boolean declared

declared state

fieldsback to summary
private final Map<FieldId<D, V>, DexMaker.FieldDeclaration> fields
flagsback to summary
private int flags
interfacesback to summary
private TypeList interfaces
methodsback to summary
private final Map<MethodId<D, R>, DexMaker.MethodDeclaration> methods
sourceFileback to summary
private String sourceFile
supertypeback to summary
private TypeId<?> supertype
typeback to summary
private final TypeId<?> type

Constructor Detail

TypeDeclarationback to summary
pack-priv TypeDeclaration(TypeId<?> type)

Method Detail

toClassDefItemback to summary
pack-priv ClassDefItem toClassDefItem()