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
17import java.lang.reflect.Constructor;
18import java.lang.reflect.InvocationHandler;
19import java.lang.reflect.InvocationTargetException;
20import java.lang.reflect.Method;
21import java.lang.reflect.Field;
22import java.lang.reflect.Proxy;
23import java.util.regex.Pattern;
24
25import dalvik.annotation.optimization.CriticalNative;
26import dalvik.annotation.optimization.FastNative;
27
28public class Main {
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 {}
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
81    public static native int getFieldSubclass(Field f, Class sub);
82
83    private static native boolean registerNativesJniTest();
84
85    private static native void testCallDefaultMethods();
86
87    private static native void testFindClassOnAttachedNativeThread();
88
89    private static boolean testFindFieldOnAttachedNativeThreadField;
90
91    private static native void testReflectFieldGetFromAttachedNativeThreadNative();
92
93    public static boolean testReflectFieldGetFromAttachedNativeThreadField;
94
95    private static void testFindFieldOnAttachedNativeThread() {
96      testFindFieldOnAttachedNativeThreadNative();
97      if (!testFindFieldOnAttachedNativeThreadField) {
98            throw new AssertionError();
99        }
100    }
101
102    private static native void testFindFieldOnAttachedNativeThreadNative();
103
104    private static void testCallStaticVoidMethodOnSubClass() {
105        testCallStaticVoidMethodOnSubClassNative();
106        if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) {
107            throw new AssertionError();
108        }
109    }
110
111    private static native void testCallStaticVoidMethodOnSubClassNative();
112
113    private static class testCallStaticVoidMethodOnSubClass_SuperClass {
114        private static boolean executed = false;
115        private static void execute() {
116            executed = true;
117        }
118    }
119
120    private static class testCallStaticVoidMethodOnSubClass_SubClass
121        extends testCallStaticVoidMethodOnSubClass_SuperClass {
122    }
123
124    private static native Method testGetMirandaMethodNative();
125
126    private static void testGetMirandaMethod() {
127        Method m = testGetMirandaMethodNative();
128        if (m.getDeclaringClass() != testGetMirandaMethod_MirandaInterface.class) {
129            throw new AssertionError();
130        }
131    }
132
133    private static native void testZeroLengthByteBuffers();
134
135    private static abstract class testGetMirandaMethod_MirandaAbstract implements testGetMirandaMethod_MirandaInterface {
136        public boolean inAbstract() {
137            return true;
138        }
139    }
140
141    private static interface testGetMirandaMethod_MirandaInterface {
142        public boolean inInterface();
143    }
144
145    // Test sign-extension for values < 32b
146
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
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
162    private static native void removeLocalObject(Object o);
163
164    private static void testRemoveLocalObject() {
165        removeLocalObject(new Object());
166    }
167
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
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
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
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
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
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
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
227    private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
228
229    private static void testShallowGetCallingClassLoader() {
230        nativeTestShallowGetCallingClassLoader();
231    }
232
233    private native static void nativeTestShallowGetCallingClassLoader();
234
235    private static void testShallowGetStackClass2() {
236        nativeTestShallowGetStackClass2();
237    }
238
239    private static native void nativeTestShallowGetStackClass2();
240
241    private static native void testCallNonvirtual();
242
243    private static native void testNewStringObject();
244
245    private interface SimpleInterface {
246        void a();
247    }
248
249    private static class DummyInvocationHandler implements InvocationHandler {
250        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
251            return null;
252        }
253    }
254
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
268    private static native long testGetMethodID(Class<?> c);
269
270    // Exercise GC and JNI critical sections in parallel.
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
297    private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array);
298
299    private static native void testInvokeLambdaMethod(LambdaInterface iface);
300
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
307    static native int intFastNativeMethod(int a, int b, int c);
308
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
325    static native int intCriticalNativeMethod(int a, int b, int c);
326
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
338    private static native boolean isSlowDebug();
339
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
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    }
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
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
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
423interface LambdaInterface {
424  public void sayHi();
425  public default void sayHiTwice() {
426    sayHi();
427    sayHi();
428  }
429}
430
431class JniCallNonvirtualTest {
432    public boolean nonstaticMethodSuperCalled = false;
433    public boolean nonstaticMethodSubCalled = false;
434
435    private static native void testCallNonvirtual();
436
437    public JniCallNonvirtualTest() {
438        System.out.println("Super.<init>");
439    }
440
441    public static void staticMethod() {
442        System.out.println("Super.staticMethod");
443    }
444
445    public void nonstaticMethod() {
446        System.out.println("Super.nonstaticMethod");
447        nonstaticMethodSuperCalled = true;
448    }
449}
450
451class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
452
453    public JniCallNonvirtualTestSubclass() {
454        System.out.println("Subclass.<init>");
455    }
456
457    public static void staticMethod() {
458        System.out.println("Subclass.staticMethod");
459    }
460
461    public void nonstaticMethod() {
462        System.out.println("Subclass.nonstaticMethod");
463        nonstaticMethodSubCalled = true;
464    }
465}
466