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