Skip to content

Commit

Permalink
Support generating locals that reference generated class types (#146)
Browse files Browse the repository at this point in the history
We have had support for the const-class operation, but it was only possible to
fill a local with the value of a Class object you could reference in the geneeration
context. This meant that it was not possible to generate a reference to a generated
class, as it did not exist in the class loader until after the generation was finished
and the new class loader could be used.

The real issue here is the restriction of the loadConstant() method, which requires an
actual Class object. This class object gets turned into a string internally for code
generation, and so we don't actually need a real class to fill the variable value
correctly. Using a TypeId will still get converted to a ConstantInsn under the hood,
and the value will be assigned correctly.
  • Loading branch information
kkoser committed Nov 27, 2019
1 parent dfad9e8 commit 53339f9
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ public void testInvokeStatic() throws Exception {
assertEquals(10, getMethod().invoke(null, 4));
}

@Test
public void testLoadDeferredClassConstant() throws Exception {
/*
* public static String call() {
* Class clazz = Generated.class;
* return clazz.getSimpleName();
* }
*/
MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call");
Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
Local<Class> clazz = code.newLocal(TypeId.get(Class.class));
Local<String> retValue = code.newLocal(TypeId.STRING);
code.loadDeferredClassConstant(clazz, GENERATED);
MethodId<Class, String> getSimpleName = TypeId.get(Class.class).getMethod(TypeId.STRING, "getSimpleName");
code.invokeVirtual(getSimpleName, retValue, clazz);
code.returnValue(retValue);

assertEquals("Generated", getMethod().invoke(null));
}

@Test
public void testCreateLocalMethodAsNull() throws Exception {
/*
Expand Down
20 changes: 16 additions & 4 deletions dexmaker/src/main/java/com/android/dx/Code.java
Original file line number Diff line number Diff line change
Expand Up @@ -476,15 +476,27 @@ private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels
* must be a primitive, String, Class, TypeId, or null.
*/
public <T> void loadConstant(Local<T> target, T value) {
loadConstantInternal(target, value);
}

/**
* Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)}
* is that the {@value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types.
*/
public void loadDeferredClassConstant(Local<Class> target, TypeId value) {
loadConstantInternal(target, value);
}

private void loadConstantInternal(Local target, Object value) {
Rop rop = value == null
? Rops.CONST_OBJECT_NOTHROW
: Rops.opConst(target.type.ropType);
? Rops.CONST_OBJECT_NOTHROW
: Rops.opConst(target.type.ropType);
if (rop.getBranchingness() == BRANCH_NONE) {
addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
RegisterSpecList.EMPTY, Constants.getConstant(value)));
RegisterSpecList.EMPTY, Constants.getConstant(value)));
} else {
addInstruction(new ThrowingCstInsn(rop, sourcePosition,
RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
moveResult(target, true);
}
}
Expand Down

0 comments on commit 53339f9

Please sign in to comment.