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 MinimalInvocationHandler 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 MinimalInvocationHandler(); 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