1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Proxy;
23 import java.util.regex.Pattern;
24 
25 import dalvik.annotation.optimization.CriticalNative;
26 import dalvik.annotation.optimization.FastNative;
27 
28 public class Main {
main(String[] args)29     public static void main(String[] args) {
30         System.loadLibrary(args[0]);
31 
32         if (!isSlowDebug()) {
33           throw new RuntimeException("Slow-debug flags unexpectedly off.");
34         }
35 
36         testFieldSubclass();
37         testFindClassOnAttachedNativeThread();
38         testFindFieldOnAttachedNativeThread();
39         testReflectFieldGetFromAttachedNativeThreadNative();
40         testCallStaticVoidMethodOnSubClass();
41         testGetMirandaMethod();
42         testZeroLengthByteBuffers();
43         testByteMethod();
44         testShortMethod();
45         testBooleanMethod();
46         testCharMethod();
47         testIsAssignableFromOnPrimitiveTypes();
48         testShallowGetCallingClassLoader();
49         testShallowGetStackClass2();
50         testCallNonvirtual();
51         testNewStringObject();
52         testRemoveLocalObject();
53         testProxyGetMethodID();
54         testJniCriticalSectionAndGc();
55         testCallDefaultMethods();
56         String lambda = "λ";
57         testInvokeLambdaMethod(() -> { System.out.println("hi-lambda: " + lambda); });
58         String def = "δ";
59         testInvokeLambdaDefaultMethod(() -> { System.out.println("hi-default " + def + lambda); });
60 
61         registerNativesJniTest();
62         testFastNativeMethods();
63         testCriticalNativeMethods();
64 
65         testClinitMethodLookup();
66 
67         testDoubleLoad(args[0]);
68     }
69 
70     static class ABC { public static int XYZ = 12; }
71     static class DEF extends ABC {}
testFieldSubclass()72     public static void testFieldSubclass() {
73       try {
74         System.out.println("ABC.XYZ = " + ABC.XYZ + ", GetStaticIntField(DEF.class, 'XYZ') = " +
75             getFieldSubclass(ABC.class.getDeclaredField("XYZ"), DEF.class));
76       } catch (Exception e) {
77         throw new RuntimeException("Failed to test get static field on a subclass", e);
78       }
79     }
80 
getFieldSubclass(Field f, Class sub)81     public static native int getFieldSubclass(Field f, Class sub);
82 
registerNativesJniTest()83     private static native boolean registerNativesJniTest();
84 
testCallDefaultMethods()85     private static native void testCallDefaultMethods();
86 
testFindClassOnAttachedNativeThread()87     private static native void testFindClassOnAttachedNativeThread();
88 
89     private static boolean testFindFieldOnAttachedNativeThreadField;
90 
testReflectFieldGetFromAttachedNativeThreadNative()91     private static native void testReflectFieldGetFromAttachedNativeThreadNative();
92 
93     public static boolean testReflectFieldGetFromAttachedNativeThreadField;
94 
testFindFieldOnAttachedNativeThread()95     private static void testFindFieldOnAttachedNativeThread() {
96       testFindFieldOnAttachedNativeThreadNative();
97       if (!testFindFieldOnAttachedNativeThreadField) {
98             throw new AssertionError();
99         }
100     }
101 
testFindFieldOnAttachedNativeThreadNative()102     private static native void testFindFieldOnAttachedNativeThreadNative();
103 
testCallStaticVoidMethodOnSubClass()104     private static void testCallStaticVoidMethodOnSubClass() {
105         testCallStaticVoidMethodOnSubClassNative();
106         if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) {
107             throw new AssertionError();
108         }
109     }
110 
testCallStaticVoidMethodOnSubClassNative()111     private static native void testCallStaticVoidMethodOnSubClassNative();
112 
113     private static class testCallStaticVoidMethodOnSubClass_SuperClass {
114         private static boolean executed = false;
execute()115         private static void execute() {
116             executed = true;
117         }
118     }
119 
120     private static class testCallStaticVoidMethodOnSubClass_SubClass
121         extends testCallStaticVoidMethodOnSubClass_SuperClass {
122     }
123 
testGetMirandaMethodNative()124     private static native Method testGetMirandaMethodNative();
125 
testGetMirandaMethod()126     private static void testGetMirandaMethod() {
127         Method m = testGetMirandaMethodNative();
128         if (m.getDeclaringClass() != testGetMirandaMethod_MirandaInterface.class) {
129             throw new AssertionError();
130         }
131     }
132 
testZeroLengthByteBuffers()133     private static native void testZeroLengthByteBuffers();
134 
135     private static abstract class testGetMirandaMethod_MirandaAbstract implements testGetMirandaMethod_MirandaInterface {
inAbstract()136         public boolean inAbstract() {
137             return true;
138         }
139     }
140 
141     private static interface testGetMirandaMethod_MirandaInterface {
inInterface()142         public boolean inInterface();
143     }
144 
145     // Test sign-extension for values < 32b
146 
byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8, byte b9, byte b10)147     static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
148         byte b8, byte b9, byte b10);
149 
testByteMethod()150     private static void testByteMethod() {
151       byte returns[] = { 0, 1, 2, 127, -1, -2, -128 };
152       for (int i = 0; i < returns.length; i++) {
153         byte result = byteMethod((byte)i, (byte)2, (byte)(-3), (byte)4, (byte)(-5), (byte)6,
154             (byte)(-7), (byte)8, (byte)(-9), (byte)10);
155         if (returns[i] != result) {
156           System.out.println("Run " + i + " with " + returns[i] + " vs " + result);
157           throw new AssertionError();
158         }
159       }
160     }
161 
removeLocalObject(Object o)162     private static native void removeLocalObject(Object o);
163 
testRemoveLocalObject()164     private static void testRemoveLocalObject() {
165         removeLocalObject(new Object());
166     }
167 
shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7, short s8, short s9, short s10)168     private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
169         short s8, short s9, short s10);
170 
testShortMethod()171     private static void testShortMethod() {
172       short returns[] = { 0, 1, 2, 127, 32767, -1, -2, -128, -32768 };
173       for (int i = 0; i < returns.length; i++) {
174         short result = shortMethod((short)i, (short)2, (short)(-3), (short)4, (short)(-5), (short)6,
175             (short)(-7), (short)8, (short)(-9), (short)10);
176         if (returns[i] != result) {
177           System.out.println("Run " + i + " with " + returns[i] + " vs " + result);
178           throw new AssertionError();
179         }
180       }
181     }
182 
183     // Test zero-extension for values < 32b
184 
booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7, boolean b8, boolean b9, boolean b10)185     private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
186         boolean b8, boolean b9, boolean b10);
187 
testBooleanMethod()188     private static void testBooleanMethod() {
189       if (booleanMethod(false, true, false, true, false, true, false, true, false, true)) {
190         throw new AssertionError();
191       }
192 
193       if (!booleanMethod(true, true, false, true, false, true, false, true, false, true)) {
194         throw new AssertionError();
195       }
196     }
197 
charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7, char c8, char c9, char c10)198     private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
199         char c8, char c9, char c10);
200 
testCharMethod()201     private static void testCharMethod() {
202       char returns[] = { (char)0, (char)1, (char)2, (char)127, (char)255, (char)256, (char)15000,
203           (char)34000 };
204       for (int i = 0; i < returns.length; i++) {
205         char result = charMethod((char)i, 'a', 'b', 'c', '0', '1', '2', (char)1234, (char)2345,
206             (char)3456);
207         if (returns[i] != result) {
208           System.out.println("Run " + i + " with " + (int)returns[i] + " vs " + (int)result);
209           throw new AssertionError();
210         }
211       }
212     }
213 
214     // http://b/16531674
testIsAssignableFromOnPrimitiveTypes()215     private static void testIsAssignableFromOnPrimitiveTypes() {
216       if (!nativeIsAssignableFrom(int.class, Integer.TYPE)) {
217         System.out.println("IsAssignableFrom(int.class, Integer.TYPE) returned false, expected true");
218         throw new AssertionError();
219       }
220 
221       if (!nativeIsAssignableFrom(Integer.TYPE, int.class)) {
222         System.out.println("IsAssignableFrom(Integer.TYPE, int.class) returned false, expected true");
223         throw new AssertionError();
224       }
225     }
226 
nativeIsAssignableFrom(Class<?> from, Class<?> to)227     private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
228 
testShallowGetCallingClassLoader()229     private static void testShallowGetCallingClassLoader() {
230         nativeTestShallowGetCallingClassLoader();
231     }
232 
nativeTestShallowGetCallingClassLoader()233     private native static void nativeTestShallowGetCallingClassLoader();
234 
testShallowGetStackClass2()235     private static void testShallowGetStackClass2() {
236         nativeTestShallowGetStackClass2();
237     }
238 
nativeTestShallowGetStackClass2()239     private static native void nativeTestShallowGetStackClass2();
240 
testCallNonvirtual()241     private static native void testCallNonvirtual();
242 
testNewStringObject()243     private static native void testNewStringObject();
244 
245     private interface SimpleInterface {
a()246         void a();
247     }
248 
249     private static class DummyInvocationHandler implements InvocationHandler {
invoke(Object proxy, Method method, Object[] args)250         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
251             return null;
252         }
253     }
254 
testProxyGetMethodID()255     private static void testProxyGetMethodID() {
256         InvocationHandler handler = new DummyInvocationHandler();
257         SimpleInterface proxy =
258                 (SimpleInterface) Proxy.newProxyInstance(SimpleInterface.class.getClassLoader(),
259                         new Class<?>[] {SimpleInterface.class}, handler);
260         if (testGetMethodID(SimpleInterface.class) == 0) {
261             throw new AssertionError();
262         }
263         if (testGetMethodID(proxy.getClass()) == 0) {
264             throw new AssertionError();
265         }
266     }
267 
testGetMethodID(Class<?> c)268     private static native long testGetMethodID(Class<?> c);
269 
270     // Exercise GC and JNI critical sections in parallel.
testJniCriticalSectionAndGc()271     private static void testJniCriticalSectionAndGc() {
272         Thread runGcThread = new Thread(new Runnable() {
273             @Override
274             public void run() {
275                 for (int i = 0; i < 10; ++i) {
276                     Runtime.getRuntime().gc();
277                 }
278             }
279         });
280         Thread jniCriticalThread = new Thread(new Runnable() {
281             @Override
282             public void run() {
283                 final int arraySize = 32;
284                 byte[] array0 = new byte[arraySize];
285                 byte[] array1 = new byte[arraySize];
286                 enterJniCriticalSection(arraySize, array0, array1);
287             }
288         });
289         jniCriticalThread.start();
290         runGcThread.start();
291         try {
292             jniCriticalThread.join();
293             runGcThread.join();
294         } catch (InterruptedException ignored) {}
295     }
296 
enterJniCriticalSection(int arraySize, byte[] array0, byte[] array)297     private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array);
298 
testInvokeLambdaMethod(LambdaInterface iface)299     private static native void testInvokeLambdaMethod(LambdaInterface iface);
300 
testInvokeLambdaDefaultMethod(LambdaInterface iface)301     private static native void testInvokeLambdaDefaultMethod(LambdaInterface iface);
302 
303     // Test invoking @FastNative methods works correctly.
304 
305     // Return sum of a+b+c.
306     @FastNative
intFastNativeMethod(int a, int b, int c)307     static native int intFastNativeMethod(int a, int b, int c);
308 
testFastNativeMethods()309     private static void testFastNativeMethods() {
310       int returns[] = { 0, 3, 6, 9, 12 };
311       for (int i = 0; i < returns.length; i++) {
312         int result = intFastNativeMethod(i, i, i);
313         if (returns[i] != result) {
314           System.out.println("FastNative Int Run " + i + " with " + returns[i] + " vs " + result);
315           throw new AssertionError();
316         }
317       }
318     }
319 
320     // Smoke test for @CriticalNative
321     // TODO: Way more thorough tests since it involved quite a bit of changes.
322 
323     // Return sum of a+b+c.
324     @CriticalNative
intCriticalNativeMethod(int a, int b, int c)325     static native int intCriticalNativeMethod(int a, int b, int c);
326 
testCriticalNativeMethods()327     private static void testCriticalNativeMethods() {
328       int returns[] = { 3, 6, 9, 12, 15 };
329       for (int i = 0; i < returns.length; i++) {
330         int result = intCriticalNativeMethod(i, i+1, i+2);
331         if (returns[i] != result) {
332           System.out.println("CriticalNative Int Run " + i + " with " + returns[i] + " vs " + result);
333           throw new AssertionError();
334         }
335       }
336     }
337 
isSlowDebug()338     private static native boolean isSlowDebug();
339 
testClinitMethodLookup()340     private static void testClinitMethodLookup() {
341       // Expect this to print <NSME Exception>
342       try {
343         System.out.println("Clinit Lookup: ClassWithoutClinit: " + methodString(lookupClinit(ClassWithoutClinit.class)));
344       } catch (NoSuchMethodError e) {
345         System.out.println("Clinit Lookup: ClassWithoutClinit: <NSME Exception>");
346       }
347       // Expect this to print <clinit>
348       try {
349         System.out.println("Clinit Lookup: ClassWithClinit: " + methodString(lookupClinit(ClassWithClinit.class)));
350       } catch (NoSuchMethodError e) {
351         System.out.println("Clinit Lookup: ClassWithClinit: <NSME Exception>");
352       }
353    }
354 
methodString(java.lang.reflect.Executable method)355     private static String methodString(java.lang.reflect.Executable method) {
356       if (method == null) {
357         return "<<null>>";
358       } else {
359         return method.toString() + "(Class: " + method.getClass().toString() + ")";
360       }
361     }
lookupClinit(Class kls)362     private static native java.lang.reflect.Executable lookupClinit(Class kls);
363 
364     private static class ClassWithoutClinit {
365     }
366     private static class ClassWithClinit {
367       static {}
368     }
369 
testDoubleLoad(String library)370   private static void testDoubleLoad(String library) {
371     // Test that nothing observably happens on loading "library" again.
372     System.loadLibrary(library);
373 
374     // Now load code in a separate classloader and try to let it load.
375     ClassLoader loader = createClassLoader();
376     try {
377       Class<?> aClass = loader.loadClass("A");
378       Method runMethod = aClass.getDeclaredMethod("run", String.class);
379       runMethod.invoke(null, library);
380     } catch (InvocationTargetException ite) {
381       if (ite.getCause() instanceof UnsatisfiedLinkError) {
382         if (!(loader instanceof java.net.URLClassLoader)) {
383           String msg = ite.getCause().getMessage();
384           String pattern = "^Shared library .*libarttest.* already opened by ClassLoader.*" +
385                            "004-JniTest.jar.*; can't open in ClassLoader.*004-JniTest-ex.jar.*";
386           if (!Pattern.matches(pattern, msg)) {
387             throw new RuntimeException("Could not find pattern in message", ite.getCause());
388           }
389         }
390         System.out.println("Got UnsatisfiedLinkError for duplicate loadLibrary");
391       } else {
392         throw new RuntimeException(ite);
393       }
394     } catch (Throwable t) {
395       // Anything else just let die.
396       throw new RuntimeException(t);
397     }
398   }
399 
createClassLoader()400   private static ClassLoader createClassLoader() {
401     String location = System.getenv("DEX_LOCATION");
402     try {
403       Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
404       Constructor<?> ctor = class_loader_class.getConstructor(String.class, ClassLoader.class);
405 
406       return (ClassLoader)ctor.newInstance(location + "/004-JniTest-ex.jar",
407                                            Main.class.getClassLoader());
408     } catch (ClassNotFoundException e) {
409       // Running on RI. Use URLClassLoader.
410       try {
411         return new java.net.URLClassLoader(
412             new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
413       } catch (Throwable t) {
414         throw new RuntimeException(t);
415       }
416     } catch (Throwable t) {
417       throw new RuntimeException(t);
418     }
419   }
420 }
421 
422 @FunctionalInterface
423 interface LambdaInterface {
sayHi()424   public void sayHi();
sayHiTwice()425   public default void sayHiTwice() {
426     sayHi();
427     sayHi();
428   }
429 }
430 
431 class JniCallNonvirtualTest {
432     public boolean nonstaticMethodSuperCalled = false;
433     public boolean nonstaticMethodSubCalled = false;
434 
testCallNonvirtual()435     private static native void testCallNonvirtual();
436 
JniCallNonvirtualTest()437     public JniCallNonvirtualTest() {
438         System.out.println("Super.<init>");
439     }
440 
staticMethod()441     public static void staticMethod() {
442         System.out.println("Super.staticMethod");
443     }
444 
nonstaticMethod()445     public void nonstaticMethod() {
446         System.out.println("Super.nonstaticMethod");
447         nonstaticMethodSuperCalled = true;
448     }
449 }
450 
451 class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
452 
JniCallNonvirtualTestSubclass()453     public JniCallNonvirtualTestSubclass() {
454         System.out.println("Subclass.<init>");
455     }
456 
staticMethod()457     public static void staticMethod() {
458         System.out.println("Subclass.staticMethod");
459     }
460 
nonstaticMethod()461     public void nonstaticMethod() {
462         System.out.println("Subclass.nonstaticMethod");
463         nonstaticMethodSubCalled = true;
464     }
465 }
466