/** * Copyright (C) 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ninja.utils; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.invoke.SerializedLambda; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Arrays; import ninja.Context; import ninja.Result; import ninja.Results; import ninja.params.Param; import ninja.ControllerMethods.ControllerMethod1; import ninja.ControllerMethods.ControllerMethod2; import ninja.utils.Lambdas.Kind; import ninja.utils.Lambdas.LambdaInfo; import static org.hamcrest.CoreMatchers.startsWith; import org.junit.Ignore; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class LambdasTest { @FunctionalInterface static public interface Function1<T,R> extends Serializable { R apply(T t); } @FunctionalInterface static public interface Function2<C,T,R> extends Serializable { R apply(C c, T t); } static private String longToString(Long value) { return value.toString(); } @Test public void staticMethodReference() throws Exception { Function1<Long,String> lambda = LambdasTest::longToString; LambdaInfo lambdaInfo = Lambdas.reflect(lambda); assertThat(lambdaInfo.getKind(), is(Kind.STATIC_METHOD_REFERENCE)); SerializedLambda serializedLambda = lambdaInfo.getSerializedLambda(); assertThat(serializedLambda.getFunctionalInterfaceMethodName(), is("apply")); assertThat(serializedLambda.getImplClass().replace('/', '.'), is(LambdasTest.class.getCanonicalName())); assertThat(serializedLambda.getImplMethodName(), is("longToString")); assertThat(serializedLambda.getImplMethodKind(), is(6)); // 6 = static method assertThat(serializedLambda.getCapturedArgCount(), is(0)); // verify it can be dynamically invoked String value = (String)lambdaInfo.getImplementationMethod().invoke(null, 1L); assertThat(value, is("1")); } static public class Calculator { private final Long initial; public Calculator(Long initial) { this.initial = initial; } public String l2s(Long value) { long calculated = initial + value; return Long.toString(calculated); } } @Test public void specificInstanceMethodReference() throws Exception { Calculator calc = new Calculator(1L); Function1<Long,String> lambda = calc::l2s; LambdaInfo lambdaInfo = Lambdas.reflect(lambda); assertThat(lambdaInfo.getKind(), is(Kind.SPECIFIC_INSTANCE_METHOD_REFERENCE)); SerializedLambda serializedLambda = lambdaInfo.getSerializedLambda(); assertThat(serializedLambda.getFunctionalInterfaceMethodName(), is("apply")); assertThat(serializedLambda.getImplClass().replace('/', '.').replace('$', '.'), is(Calculator.class.getCanonicalName())); assertThat(serializedLambda.getImplMethodName(), is("l2s")); assertThat(serializedLambda.getImplMethodSignature(), is("(Ljava/lang/Long;)Ljava/lang/String;")); assertThat(serializedLambda.getCapturedArgCount(), is(1)); // captured "this" assertThat(serializedLambda.getCapturedArg(0), is(calc)); // captured "this" // verify it can be dynamically invoked String value = (String)lambdaInfo.getFunctionalMethod().invoke(lambda, 1L); //String value = (String)lambda.getClass().getMethod("apply", Long.class).invoke(calc, 1L); assertThat(value, is("2")); } private Result home() { return Results.html().renderRaw("Hi".getBytes(StandardCharsets.UTF_8)); } @Test public void anyInstanceMethodReference() throws Exception { ControllerMethod1<LambdasTest> lambda = LambdasTest::home; LambdaInfo lambdaInfo = Lambdas.reflect(lambda); assertThat(lambdaInfo.getKind(), is(Kind.ANY_INSTANCE_METHOD_REFERENCE)); SerializedLambda serializedLambda = lambdaInfo.getSerializedLambda(); assertThat(serializedLambda.getFunctionalInterfaceMethodName(), is("apply")); assertThat(serializedLambda.getImplClass().replace('/', '.'), is(LambdasTest.class.getCanonicalName())); assertThat(serializedLambda.getImplMethodName(), is("home")); assertThat(serializedLambda.getImplMethodSignature(), is("()Lninja/Result;")); assertThat(serializedLambda.getCapturedArgCount(), is(0)); } @Test public void anonymousClassReference() throws Exception { @SuppressWarnings("Convert2Lambda") ControllerMethod1<Context> lambda = new ControllerMethod1<Context>() { @Override public Result apply(Context a) { return Results.html().renderRaw("".getBytes(StandardCharsets.UTF_8)); } }; try { LambdaInfo lambdaInfo = Lambdas.reflect(lambda); fail(); } catch (IllegalArgumentException e) { // expected } } @Test public void anonymousMethodReference() throws Exception { ControllerMethod1<Context> lambda = (Context context) -> Results.html().renderRaw("".getBytes(StandardCharsets.UTF_8)); LambdaInfo lambdaInfo = Lambdas.reflect(lambda); assertThat(lambdaInfo.getKind(), is(Kind.ANONYMOUS_METHOD_REFERENCE)); SerializedLambda serializedLambda = lambdaInfo.getSerializedLambda(); assertThat(serializedLambda.getFunctionalInterfaceMethodName(), is("apply")); assertThat(serializedLambda.getImplClass().replace('/', '.'), is(LambdasTest.class.getCanonicalName())); assertThat(serializedLambda.getImplMethodName(), startsWith("lambda$")); assertThat(serializedLambda.getInstantiatedMethodType(), is("(Lninja/Context;)Lninja/Result;")); // includes captured args btw... assertThat(serializedLambda.getImplMethodSignature(), is("(Lninja/Context;)Lninja/Result;")); assertThat(serializedLambda.getImplMethodKind(), is(6)); // 6 = REF_invokeStatic assertThat(serializedLambda.getCapturedArgCount(), is(0)); } }