Skip to content

Commit 7929d11

Browse files
vmutafovdrewhannay
authored andcommitted
Add support for abstract and native methods generation
1 parent 7a776d8 commit 7929d11

File tree

2 files changed

+62
-28
lines changed

2 files changed

+62
-28
lines changed

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

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,57 @@ public <G> void testDeclareConstructor() throws Exception {
480480
assertEquals(0xabcd, a.get(instance));
481481
}
482482

483+
@Test
484+
public void testDeclareNativeMethod() throws Exception {
485+
/*
486+
* class Generated {
487+
* public Generated() {
488+
* }
489+
* public native void nativeMethod();
490+
* }
491+
*/
492+
493+
addDefaultConstructor();
494+
String nativeMethodName = "nativeMethod";
495+
MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, nativeMethodName);
496+
dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.NATIVE);
497+
498+
Class<?> generatedClass = generateAndLoad();
499+
Object instance = generatedClass.getConstructor().newInstance();
500+
Method nativeMethod = instance.getClass().getMethod(nativeMethodName);
501+
502+
assertTrue((nativeMethod.getModifiers() & NATIVE) != 0);
503+
assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0);
504+
assertEquals(void.class, nativeMethod.getReturnType());
505+
assertEquals(nativeMethodName, nativeMethod.getName());
506+
assertEquals(nativeMethod.getParameterTypes().length, 0);
507+
}
508+
509+
@Test
510+
public void testDeclareAbstractClassWithAbstractMethod() throws Exception {
511+
/*
512+
* public abstract class AbstractClass {
513+
* public abstract void abstractMethod();
514+
* }
515+
*/
516+
517+
dexMaker.declare(GENERATED, "AbstractClass.java", PUBLIC, TypeId.OBJECT);
518+
519+
String abstractMethodName = "abstractMethod";
520+
MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, abstractMethodName);
521+
dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | ABSTRACT);
522+
523+
Class<?> generatedClass = generateAndLoad();
524+
Method nativeMethod = generatedClass.getMethod(abstractMethodName);
525+
526+
assertTrue((nativeMethod.getModifiers() & ABSTRACT) != 0);
527+
assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0);
528+
assertEquals(void.class, nativeMethod.getReturnType());
529+
assertEquals(abstractMethodName, nativeMethod.getName());
530+
assertEquals(nativeMethod.getParameterTypes().length, 0);
531+
532+
}
533+
483534
@Test
484535
public void testReturnType() throws Exception {
485536
testReturnType(boolean.class, true);
@@ -1955,26 +2006,6 @@ public void testPrivateClassesAreUnsupported() {
19552006
}
19562007
}
19572008

1958-
@Test
1959-
public void testAbstractMethodsAreUnsupported() {
1960-
MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
1961-
try {
1962-
dexMaker.declare(methodId, ABSTRACT);
1963-
fail();
1964-
} catch (IllegalArgumentException expected) {
1965-
}
1966-
}
1967-
1968-
@Test
1969-
public void testNativeMethodsAreUnsupported() {
1970-
MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
1971-
try {
1972-
dexMaker.declare(methodId, NATIVE);
1973-
fail();
1974-
} catch (IllegalArgumentException expected) {
1975-
}
1976-
}
1977-
19782009
@Test
19792010
public void testSynchronizedFieldsAreUnsupported() {
19802011
try {
@@ -2005,7 +2036,6 @@ public void testInitialValueWithNonStaticField() {
20052036
// TODO: don't generate multiple times (?)
20062037
// TODO: test array types
20072038
// TODO: test generating an interface
2008-
// TODO: declare native method or abstract method
20092039
// TODO: get a thrown exception 'e' into a local
20102040
// TODO: move a primitive or reference
20112041

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
import java.util.jar.JarOutputStream;
4848

4949
import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR;
50-
import static java.lang.reflect.Modifier.PRIVATE;
51-
import static java.lang.reflect.Modifier.STATIC;
50+
import static java.lang.reflect.Modifier.*;
51+
;
5252

5353
/**
5454
* Generates a <strong>D</strong>alvik <strong>EX</strong>ecutable (dex)
@@ -231,7 +231,7 @@ TypeDeclaration getTypeDeclaration(TypeId<?> type) {
231231
* Modifier#FINAL} and {@link Modifier#ABSTRACT}.
232232
*/
233233
public void declare(TypeId<?> type, String sourceFile, int flags,
234-
TypeId<?> supertype, TypeId<?>... interfaces) {
234+
TypeId<?> supertype, TypeId<?>... interfaces) {
235235
TypeDeclaration declaration = getTypeDeclaration(type);
236236
int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT
237237
| AccessFlags.ACC_SYNTHETIC;
@@ -266,7 +266,7 @@ public Code declare(MethodId<?, ?> method, int flags) {
266266
throw new IllegalStateException("already declared: " + method);
267267
}
268268

269-
int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
269+
int supportedFlags = Modifier.ABSTRACT | Modifier.NATIVE | Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
270270
| Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED
271271
| AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE;
272272
if ((flags & ~supportedFlags) != 0) {
@@ -529,9 +529,9 @@ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOE
529529
* TODO: load the dex from memory where supported.
530530
*/
531531
result.createNewFile();
532-
532+
533533
JarOutputStream jarOut =
534-
new JarOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
534+
new JarOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
535535
try {
536536
JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME);
537537
entry.setSize(dex.length);
@@ -544,7 +544,7 @@ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOE
544544
} finally {
545545
jarOut.close();
546546
}
547-
547+
548548
return generateClassLoader(result, dexCache, parent);
549549
}
550550

@@ -655,6 +655,10 @@ boolean isDirect() {
655655
}
656656

657657
EncodedMethod toEncodedMethod(DexOptions dexOptions) {
658+
if((flags & ABSTRACT) != 0 || (flags & NATIVE) != 0){
659+
return new EncodedMethod(method.constant, flags, null, StdTypeList.EMPTY);
660+
}
661+
658662
RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0);
659663
LocalVariableInfo locals = null;
660664
DalvCode dalvCode = RopTranslator.translate(

0 commit comments

Comments
 (0)