Skip to content

Commit 53339f9

Browse files
committed
Support generating locals that reference generated class types (#146)
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.
1 parent dfad9e8 commit 53339f9

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,26 @@ public void testInvokeStatic() throws Exception {
171171
assertEquals(10, getMethod().invoke(null, 4));
172172
}
173173

174+
@Test
175+
public void testLoadDeferredClassConstant() throws Exception {
176+
/*
177+
* public static String call() {
178+
* Class clazz = Generated.class;
179+
* return clazz.getSimpleName();
180+
* }
181+
*/
182+
MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call");
183+
Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
184+
Local<Class> clazz = code.newLocal(TypeId.get(Class.class));
185+
Local<String> retValue = code.newLocal(TypeId.STRING);
186+
code.loadDeferredClassConstant(clazz, GENERATED);
187+
MethodId<Class, String> getSimpleName = TypeId.get(Class.class).getMethod(TypeId.STRING, "getSimpleName");
188+
code.invokeVirtual(getSimpleName, retValue, clazz);
189+
code.returnValue(retValue);
190+
191+
assertEquals("Generated", getMethod().invoke(null));
192+
}
193+
174194
@Test
175195
public void testCreateLocalMethodAsNull() throws Exception {
176196
/*

dexmaker/src/main/java/com/android/dx/Code.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,15 +476,27 @@ private void splitCurrentLabel(Label alternateSuccessor, List<Label> catchLabels
476476
* must be a primitive, String, Class, TypeId, or null.
477477
*/
478478
public <T> void loadConstant(Local<T> target, T value) {
479+
loadConstantInternal(target, value);
480+
}
481+
482+
/**
483+
* Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)}
484+
* is that the {@value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types.
485+
*/
486+
public void loadDeferredClassConstant(Local<Class> target, TypeId value) {
487+
loadConstantInternal(target, value);
488+
}
489+
490+
private void loadConstantInternal(Local target, Object value) {
479491
Rop rop = value == null
480-
? Rops.CONST_OBJECT_NOTHROW
481-
: Rops.opConst(target.type.ropType);
492+
? Rops.CONST_OBJECT_NOTHROW
493+
: Rops.opConst(target.type.ropType);
482494
if (rop.getBranchingness() == BRANCH_NONE) {
483495
addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
484-
RegisterSpecList.EMPTY, Constants.getConstant(value)));
496+
RegisterSpecList.EMPTY, Constants.getConstant(value)));
485497
} else {
486498
addInstruction(new ThrowingCstInsn(rop, sourcePosition,
487-
RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
499+
RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
488500
moveResult(target, true);
489501
}
490502
}

0 commit comments

Comments
 (0)