1/* 2 * Copyright (C) 2016 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 17package libcore.java.lang; 18 19import junit.framework.TestCase; 20 21import java.io.ByteArrayInputStream; 22import java.io.ByteArrayOutputStream; 23import java.io.IOException; 24import java.io.NotSerializableException; 25import java.io.ObjectInputStream; 26import java.io.ObjectOutputStream; 27import java.io.Serializable; 28import java.lang.reflect.Method; 29import java.lang.reflect.Modifier; 30import java.util.ArrayList; 31import java.util.Arrays; 32import java.util.HashSet; 33import java.util.List; 34import java.util.Set; 35import java.util.concurrent.Callable; 36 37public class LambdaImplementationTest extends TestCase { 38 39 private static final String STATIC_METHOD_RESPONSE = "StaticMethodResponse"; 40 41 public void testNonCapturingLambda() throws Exception { 42 Callable<String> r1 = () -> "Hello World"; 43 assertGeneralLambdaClassCharacteristics(r1); 44 assertLambdaImplementsInterfaces(r1, Callable.class); 45 assertLambdaMethodCharacteristics(r1, Callable.class); 46 assertNonSerializableLambdaCharacteristics(r1); 47 assertCallableBehavior(r1, "Hello World"); 48 49 List<Callable<String>> callables = new ArrayList<>(); 50 for (int i = 0; i < 2; i++) { 51 callables.add(() -> "Hello World"); 52 } 53 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 54 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 55 } 56 57 interface Condition<T> { 58 boolean check(T arg); 59 } 60 61 public void testInstanceMethodReferenceLambda() throws Exception { 62 Condition<String> c = String::isEmpty; 63 Class<?> lambdaClass = c.getClass(); 64 assertGeneralLambdaClassCharacteristics(c); 65 assertLambdaImplementsInterfaces(c, Condition.class); 66 assertLambdaMethodCharacteristics(c, Condition.class); 67 assertNonSerializableLambdaCharacteristics(c); 68 69 // Check the behavior of the lambda's method. 70 assertTrue(c.check("")); 71 assertFalse(c.check("notEmpty")); 72 73 Method implCallMethod = lambdaClass.getMethod( 74 "check", Object.class /* type erasure => not String.class */); 75 assertTrue((Boolean) implCallMethod.invoke(c, "")); 76 assertFalse((Boolean) implCallMethod.invoke(c, "notEmpty")); 77 78 Method interfaceCallMethod = Condition.class.getDeclaredMethod( 79 "check", Object.class /* type erasure => not String.class */); 80 assertTrue((Boolean) interfaceCallMethod.invoke(c, "")); 81 assertFalse((Boolean) interfaceCallMethod.invoke(c, "notEmpty")); 82 } 83 84 public void testStaticMethodReferenceLambda() throws Exception { 85 Callable<String> r1 = LambdaImplementationTest::staticMethod; 86 assertGeneralLambdaClassCharacteristics(r1); 87 assertLambdaImplementsInterfaces(r1, Callable.class); 88 assertLambdaMethodCharacteristics(r1, Callable.class); 89 assertNonSerializableLambdaCharacteristics(r1); 90 91 assertCallableBehavior(r1, STATIC_METHOD_RESPONSE); 92 93 List<Callable<String>> callables = new ArrayList<>(); 94 for (int i = 0; i < 2; i++) { 95 callables.add(LambdaImplementationTest::staticMethod); 96 } 97 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 98 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 99 } 100 101 public void testObjectMethodReferenceLambda() throws Exception { 102 String msg = "Hello"; 103 StringBuilder o = new StringBuilder(msg); 104 Callable<String> r1 = o::toString; 105 assertGeneralLambdaClassCharacteristics(r1); 106 assertLambdaImplementsInterfaces(r1, Callable.class); 107 assertLambdaMethodCharacteristics(r1, Callable.class); 108 assertNonSerializableLambdaCharacteristics(r1); 109 110 assertCallableBehavior(r1, msg); 111 112 List<Callable<String>> callables = new ArrayList<>(); 113 for (int i = 0; i < 2; i++) { 114 callables.add(o::toString); 115 } 116 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 117 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 118 } 119 120 public void testArgumentCapturingLambda() throws Exception { 121 checkArgumentCapturingLambda("Argument"); 122 } 123 124 private void checkArgumentCapturingLambda(String msg) throws Exception { 125 Callable<String> r1 = () -> msg; 126 assertGeneralLambdaClassCharacteristics(r1); 127 assertLambdaImplementsInterfaces(r1, Callable.class); 128 assertLambdaMethodCharacteristics(r1, Callable.class); 129 assertNonSerializableLambdaCharacteristics(r1); 130 131 assertCallableBehavior(r1, msg); 132 133 List<Callable<String>> callables = new ArrayList<>(); 134 for (int i = 0; i < 2; i++) { 135 callables.add(() -> msg); 136 } 137 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 138 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 139 } 140 141 public void testSerializableLambda_withoutState() throws Exception { 142 Callable<String> r1 = (Callable<String> & Serializable) () -> "No State"; 143 assertGeneralLambdaClassCharacteristics(r1); 144 assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); 145 assertLambdaMethodCharacteristics(r1, Callable.class); 146 assertSerializableLambdaCharacteristics(r1); 147 148 assertCallableBehavior(r1, "No State"); 149 150 List<Callable<String>> callables = new ArrayList<>(); 151 for (int i = 0; i < 2; i++) { 152 Callable<String> callable = (Callable<String> & Serializable) () -> "No State"; 153 assertLambdaImplementsInterfaces(callable, Callable.class, Serializable.class); 154 callables.add(callable); 155 } 156 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 157 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 158 } 159 160 public void testSerializableLambda_withState() throws Exception { 161 final long state = System.currentTimeMillis(); 162 Callable<String> r1 = (Callable<String> & Serializable) () -> "State:" + state; 163 assertGeneralLambdaClassCharacteristics(r1); 164 assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); 165 assertLambdaMethodCharacteristics(r1, Callable.class); 166 assertSerializableLambdaCharacteristics(r1); 167 168 assertCallableBehavior(r1, "State:" + state); 169 170 Callable<String> deserializedR1 = roundtripSerialization(r1); 171 assertEquals(r1.call(), deserializedR1.call()); 172 173 List<Callable<String>> callables = new ArrayList<>(); 174 for (int i = 0; i < 2; i++) { 175 Callable<String> callable = (Callable<String> & Serializable) () -> "State:" + state; 176 assertLambdaImplementsInterfaces(callable, Callable.class, Serializable.class); 177 callables.add(callable); 178 } 179 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 180 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 181 } 182 183 public void testBadSerializableLambda() throws Exception { 184 final Object state = new Object(); // Not Serializable 185 Callable<String> r1 = (Callable<String> & Serializable) () -> "Hello world: " + state; 186 assertGeneralLambdaClassCharacteristics(r1); 187 assertLambdaMethodCharacteristics(r1, Callable.class); 188 assertLambdaImplementsInterfaces(r1, Callable.class, Serializable.class); 189 190 try { 191 serializeObject(r1); 192 fail(); 193 } catch (NotSerializableException expected) { 194 } 195 } 196 197 public void testMultipleInterfaceLambda() throws Exception { 198 Callable<String> r1 = (Callable<String> & MarkerInterface) () -> "MultipleInterfaces"; 199 assertTrue(r1 instanceof MarkerInterface); 200 assertGeneralLambdaClassCharacteristics(r1); 201 assertLambdaMethodCharacteristics(r1, Callable.class); 202 assertLambdaImplementsInterfaces(r1, Callable.class, MarkerInterface.class); 203 assertNonSerializableLambdaCharacteristics(r1); 204 205 assertCallableBehavior(r1, "MultipleInterfaces"); 206 207 List<Callable<String>> callables = new ArrayList<>(); 208 for (int i = 0; i < 2; i++) { 209 Callable<String> callable = 210 (Callable<String> & MarkerInterface) () -> "MultipleInterfaces"; 211 assertLambdaImplementsInterfaces(callable, Callable.class, MarkerInterface.class); 212 callables.add(callable); 213 } 214 assertLambdaImplementsInterfaces(r1, Callable.class, MarkerInterface.class); 215 assertMultipleDefinitionCharacteristics(r1, callables.get(0)); 216 assertMultipleInstanceCharacteristics(callables.get(0), callables.get(1)); 217 } 218 219 private static void assertSerializableLambdaCharacteristics(Object r1) throws Exception { 220 assertTrue(r1 instanceof Serializable); 221 222 Object deserializedR1 = roundtripSerialization(r1); 223 assertFalse(deserializedR1.equals(r1)); 224 assertNotSame(deserializedR1, r1); 225 } 226 227 @SuppressWarnings("unchecked") 228 private static <T> T roundtripSerialization(T r1) throws Exception { 229 byte[] bytes = serializeObject(r1); 230 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 231 try (ObjectInputStream is = new ObjectInputStream(bais)) { 232 return (T) is.readObject(); 233 } 234 } 235 236 private static <T> byte[] serializeObject(T r1) throws IOException { 237 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 238 try (ObjectOutputStream os = new ObjectOutputStream(baos)) { 239 os.writeObject(r1); 240 os.flush(); 241 } 242 return baos.toByteArray(); 243 } 244 245 private static <T> void assertLambdaImplementsInterfaces(T r1, Class<?>... expectedInterfaces) 246 throws Exception { 247 Class<?> lambdaClass = r1.getClass(); 248 249 // Check directly implemented interfaces. Ordering is well-defined. 250 Class<?>[] actualInterfaces = lambdaClass.getInterfaces(); 251 assertEquals(expectedInterfaces.length, actualInterfaces.length); 252 List<Class<?>> actual = Arrays.asList(actualInterfaces); 253 List<Class<?>> expected = Arrays.asList(expectedInterfaces); 254 assertEquals(expected, actual); 255 256 // Confirm that the only method declared on the lambda's class are those defined by 257 // interfaces it implements. i.e. there's no additional public contract. 258 Set<Method> declaredMethods = new HashSet<>(); 259 addNonStaticPublicMethods(lambdaClass, declaredMethods); 260 Set<Method> expectedMethods = new HashSet<>(); 261 for (Class<?> interfaceClass : expectedInterfaces) { 262 // Obtain methods declared by super-interfaces too. 263 while (interfaceClass != null) { 264 addNonStaticPublicMethods(interfaceClass, expectedMethods); 265 interfaceClass = interfaceClass.getSuperclass(); 266 } 267 } 268 assertEquals(expectedMethods.size(), declaredMethods.size()); 269 270 // Check the method signatures are compatible. 271 for (Method expectedMethod : expectedMethods) { 272 Method actualMethod = 273 lambdaClass.getMethod(expectedMethod.getName(), 274 expectedMethod.getParameterTypes()); 275 assertEquals(expectedMethod.getReturnType(), actualMethod.getReturnType()); 276 } 277 } 278 279 private static void addNonStaticPublicMethods(Class<?> clazz, Set<Method> methodSet) { 280 for (Method interfaceMethod : clazz.getDeclaredMethods()) { 281 int modifiers = interfaceMethod.getModifiers(); 282 if ((!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { 283 methodSet.add(interfaceMethod); 284 } 285 } 286 } 287 288 private static void assertNonSerializableLambdaCharacteristics(Object r1) throws Exception { 289 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 290 try (ObjectOutputStream os = new ObjectOutputStream(baos)) { 291 os.writeObject(r1); 292 os.flush(); 293 fail(); 294 } catch (NotSerializableException expected) { 295 } 296 } 297 298 /** 299 * Asserts that necessary conditions hold when there are two lambdas with separate but identical 300 * definitions. 301 */ 302 private static void assertMultipleDefinitionCharacteristics( 303 Callable<String> r1, Callable<String> r2) throws Exception { 304 305 // Sanity check that the lambdas do the same thing. 306 assertEquals(r1.call(), r2.call()); 307 308 // Unclear if any of this is *guaranteed* to be true. 309 310 // Check the objects are not the same and do not equal. This could influence collection 311 // behavior. 312 assertNotSame(r1, r2); 313 assertTrue(!r1.equals(r2)); 314 315 // Two lambdas from different definitions can share the same class or may not. 316 // See JLS 15.27.4. 317 } 318 319 /** 320 * Asserts that necessary conditions hold when there are two lambdas created from the same 321 * definition. 322 */ 323 private static void assertMultipleInstanceCharacteristics( 324 Callable<String> r1, Callable<String> r2) throws Exception { 325 326 // Sanity check that the lambdas do the same thing. 327 assertEquals(r1.call(), r2.call()); 328 329 // There doesn't appear to be anything else that is safe to assert here. Two lambdas 330 // created from the same definition can be the same, as can their class, but they can also 331 // be different. See JLS 15.27.4. 332 } 333 334 private static void assertGeneralLambdaClassCharacteristics(Object r1) throws Exception { 335 Class<?> lambdaClass = r1.getClass(); 336 337 // Lambda objects have classes that have names. 338 assertNotNull(lambdaClass.getName()); 339 assertNotNull(lambdaClass.getSimpleName()); 340 assertNotNull(lambdaClass.getCanonicalName()); 341 342 // Lambda classes are "synthetic classes" that are not arrays. 343 assertFalse(lambdaClass.isAnnotation()); 344 assertFalse(lambdaClass.isInterface()); 345 assertFalse(lambdaClass.isArray()); 346 assertFalse(lambdaClass.isEnum()); 347 assertFalse(lambdaClass.isPrimitive()); 348 assertTrue(lambdaClass.isSynthetic()); 349 assertNull(lambdaClass.getComponentType()); 350 351 // Expected modifiers 352 int classModifiers = lambdaClass.getModifiers(); 353 assertTrue(Modifier.isFinal(classModifiers)); 354 355 // Unexpected modifiers 356 assertFalse(Modifier.isPrivate(classModifiers)); 357 assertFalse(Modifier.isPublic(classModifiers)); 358 assertFalse(Modifier.isProtected(classModifiers)); 359 assertFalse(Modifier.isStatic(classModifiers)); 360 assertFalse(Modifier.isSynchronized(classModifiers)); 361 assertFalse(Modifier.isVolatile(classModifiers)); 362 assertFalse(Modifier.isTransient(classModifiers)); 363 assertFalse(Modifier.isNative(classModifiers)); 364 assertFalse(Modifier.isInterface(classModifiers)); 365 assertFalse(Modifier.isAbstract(classModifiers)); 366 assertFalse(Modifier.isStrict(classModifiers)); 367 368 // Check the classloader, inheritance hierarchy and package. 369 assertSame(LambdaImplementationTest.class.getClassLoader(), lambdaClass.getClassLoader()); 370 assertSame(Object.class, lambdaClass.getSuperclass()); 371 assertSame(Object.class, lambdaClass.getGenericSuperclass()); 372 assertEquals(LambdaImplementationTest.class.getPackage(), lambdaClass.getPackage()); 373 374 // Check the implementation of the non-final public methods that all Objects possess. 375 assertNotNull(r1.toString()); 376 assertTrue(r1.equals(r1)); 377 assertEquals(System.identityHashCode(r1), r1.hashCode()); 378 } 379 380 private static <T> void assertLambdaMethodCharacteristics(T r1, Class<?> samInterfaceClass) 381 throws Exception { 382 // Find the single abstract method on the interface. 383 Method singleAbstractMethod = null; 384 for (Method method : samInterfaceClass.getDeclaredMethods()) { 385 if (Modifier.isAbstract(method.getModifiers())) { 386 singleAbstractMethod = method; 387 break; 388 } 389 } 390 assertNotNull(singleAbstractMethod); 391 392 // Confirm the lambda implements the method as expected. 393 Method implementationMethod = r1.getClass().getMethod( 394 singleAbstractMethod.getName(), singleAbstractMethod.getParameterTypes()); 395 assertSame(singleAbstractMethod.getReturnType(), implementationMethod.getReturnType()); 396 assertSame(r1.getClass(), implementationMethod.getDeclaringClass()); 397 assertFalse(implementationMethod.isSynthetic()); 398 assertFalse(implementationMethod.isBridge()); 399 assertFalse(implementationMethod.isDefault()); 400 } 401 402 private static String staticMethod() { 403 return STATIC_METHOD_RESPONSE; 404 } 405 406 private interface MarkerInterface { 407 } 408 409 private static <T> void assertCallableBehavior(Callable<T> r1, T expectedResult) 410 throws Exception { 411 assertEquals(expectedResult, r1.call()); 412 413 Method implCallMethod = r1.getClass().getDeclaredMethod("call"); 414 assertEquals(expectedResult, implCallMethod.invoke(r1)); 415 416 Method interfaceCallMethod = Callable.class.getDeclaredMethod("call"); 417 assertEquals(expectedResult, interfaceCallMethod.invoke(r1)); 418 } 419} 420