2009-10-22 06:19:48 +00:00
/ *
2012-11-02 15:50:11 +00:00
* Copyright ( c ) 2009 , 2012 , Oracle and / or its affiliates . All rights reserved .
2009-10-22 06:19:48 +00:00
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER .
*
* This code is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 only , as
2010-05-25 22:58:33 +00:00
* published by the Free Software Foundation . Oracle designates this
2009-10-22 06:19:48 +00:00
* particular file as subject to the " Classpath " exception as provided
2010-05-25 22:58:33 +00:00
* by Oracle in the LICENSE file that accompanied this code .
2009-10-22 06:19:48 +00:00
*
* This code is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* version 2 for more details ( a copy is included in the LICENSE file that
* accompanied this code ) .
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
2010-05-25 22:58:33 +00:00
* Please contact Oracle , 500 Oracle Parkway , Redwood Shores , CA 94065 USA
* or visit www . oracle . com if you need additional information or have any
* questions .
2009-10-22 06:19:48 +00:00
* /
/ * @test
2011-03-24 06:02:31 +00:00
* @summary unit tests for java . lang . invoke . MethodHandles
2012-07-24 17:47:44 +00:00
* @compile MethodHandlesTest . java remote / RemoteExample . java
2012-11-05 17:51:14 +00:00
* @run junit / othervm / timeout = 2500 - XX : + IgnoreUnrecognizedVMOptions - XX : - VerifyDependencies - esa test . java . lang . invoke . MethodHandlesTest
2009-10-22 06:19:48 +00:00
* /
2011-03-24 06:02:31 +00:00
package test.java.lang.invoke ;
2009-10-22 06:19:48 +00:00
2012-07-24 17:47:44 +00:00
import test.java.lang.invoke.remote.RemoteExample ;
2011-03-24 06:02:31 +00:00
import java.lang.invoke.* ;
import java.lang.invoke.MethodHandles.Lookup ;
2009-10-22 06:19:48 +00:00
import java.lang.reflect.* ;
import java.util.* ;
import org.junit.* ;
import static org.junit.Assert.* ;
/ * *
*
* @author jrose
* /
public class MethodHandlesTest {
2011-07-16 22:44:33 +00:00
static final Class < ? > THIS_CLASS = MethodHandlesTest . class ;
2009-10-22 06:19:48 +00:00
// How much output?
2010-05-01 06:48:23 +00:00
static int verbosity = 0 ;
2010-06-09 06:08:56 +00:00
static {
2011-07-16 22:44:33 +00:00
String vstr = System . getProperty ( THIS_CLASS . getSimpleName ( ) + " .verbosity " ) ;
if ( vstr = = null )
vstr = System . getProperty ( THIS_CLASS . getName ( ) + " .verbosity " ) ;
2010-06-09 06:08:56 +00:00
if ( vstr ! = null ) verbosity = Integer . parseInt ( vstr ) ;
}
2009-10-22 06:19:48 +00:00
// Set this true during development if you want to fast-forward to
// a particular new, non-working test. Tests which are known to
// work (or have recently worked) test this flag and return on true.
2012-07-24 17:47:44 +00:00
static final boolean CAN_SKIP_WORKING ;
static {
String vstr = System . getProperty ( THIS_CLASS . getSimpleName ( ) + " .CAN_SKIP_WORKING " ) ;
if ( vstr = = null )
vstr = System . getProperty ( THIS_CLASS . getName ( ) + " .CAN_SKIP_WORKING " ) ;
CAN_SKIP_WORKING = Boolean . parseBoolean ( vstr ) ;
}
2009-10-22 06:19:48 +00:00
2011-07-16 22:44:33 +00:00
// Set 'true' to do about 15x fewer tests, especially those redundant with RicochetTest.
// This might be useful with -Xcomp stress tests that compile all method handles.
static boolean CAN_TEST_LIGHTLY = Boolean . getBoolean ( THIS_CLASS . getName ( ) + " .CAN_TEST_LIGHTLY " ) ;
2009-10-22 06:19:48 +00:00
@Test
public void testFirst ( ) throws Throwable {
verbosity + = 9 ; try {
// left blank for debugging
2010-05-01 06:48:23 +00:00
} finally { printCounts ( ) ; verbosity - = 9 ; }
2009-10-22 06:19:48 +00:00
}
static final int MAX_ARG_INCREASE = 3 ;
public MethodHandlesTest ( ) {
}
String testName ;
2010-06-09 06:08:56 +00:00
static int allPosTests , allNegTests ;
2009-10-22 06:19:48 +00:00
int posTests , negTests ;
@After
public void printCounts ( ) {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 2 & & ( posTests | negTests ) ! = 0 ) {
2009-10-22 06:19:48 +00:00
System . out . println ( ) ;
if ( posTests ! = 0 ) System . out . println ( " === " + testName + " : " + posTests + " positive test cases run " ) ;
if ( negTests ! = 0 ) System . out . println ( " === " + testName + " : " + negTests + " negative test cases run " ) ;
2010-06-09 06:08:56 +00:00
allPosTests + = posTests ;
allNegTests + = negTests ;
2010-05-01 06:48:23 +00:00
posTests = negTests = 0 ;
2009-10-22 06:19:48 +00:00
}
}
void countTest ( boolean positive ) {
if ( positive ) + + posTests ;
else + + negTests ;
}
void countTest ( ) { countTest ( true ) ; }
void startTest ( String name ) {
if ( testName ! = null ) printCounts ( ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
2009-10-22 06:19:48 +00:00
System . out . println ( name ) ;
posTests = negTests = 0 ;
testName = name ;
}
@BeforeClass
public static void setUpClass ( ) throws Exception {
calledLog . clear ( ) ;
calledLog . add ( null ) ;
2010-05-01 06:48:23 +00:00
nextArgVal = INITIAL_ARG_VAL ;
2009-10-22 06:19:48 +00:00
}
@AfterClass
public static void tearDownClass ( ) throws Exception {
2010-06-09 06:08:56 +00:00
int posTests = allPosTests , negTests = allNegTests ;
2011-07-16 22:44:33 +00:00
if ( verbosity > = 0 & & ( posTests | negTests ) ! = 0 ) {
2010-06-09 06:08:56 +00:00
System . out . println ( ) ;
if ( posTests ! = 0 ) System . out . println ( " === " + posTests + " total positive test cases " ) ;
if ( negTests ! = 0 ) System . out . println ( " === " + negTests + " total negative test cases " ) ;
}
2009-10-22 06:19:48 +00:00
}
2012-01-19 01:34:29 +00:00
static List < Object > calledLog = new ArrayList < > ( ) ;
2009-10-22 06:19:48 +00:00
static Object logEntry ( String name , Object . . . args ) {
return Arrays . asList ( name , Arrays . asList ( args ) ) ;
}
2012-07-24 17:47:44 +00:00
public static Object called ( String name , Object . . . args ) {
2009-10-22 06:19:48 +00:00
Object entry = logEntry ( name , args ) ;
calledLog . add ( entry ) ;
return entry ;
}
static void assertCalled ( String name , Object . . . args ) {
Object expected = logEntry ( name , args ) ;
Object actual = calledLog . get ( calledLog . size ( ) - 1 ) ;
2010-05-01 06:48:23 +00:00
if ( expected . equals ( actual ) & & verbosity < 9 ) return ;
2009-10-22 06:19:48 +00:00
System . out . println ( " assertCalled " + name + " : " ) ;
System . out . println ( " expected: " + expected ) ;
System . out . println ( " actual: " + actual ) ;
System . out . println ( " ex. types: " + getClasses ( expected ) ) ;
System . out . println ( " act. types: " + getClasses ( actual ) ) ;
assertEquals ( " previous method call " , expected , actual ) ;
}
static void printCalled ( MethodHandle target , String name , Object . . . args ) {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
System . out . println ( " calling MH= " + target + " to " + name + Arrays . toString ( args ) ) ;
2009-10-22 06:19:48 +00:00
}
static Object castToWrapper ( Object value , Class < ? > dst ) {
Object wrap = null ;
if ( value instanceof Number )
wrap = castToWrapperOrNull ( ( ( Number ) value ) . longValue ( ) , dst ) ;
if ( value instanceof Character )
wrap = castToWrapperOrNull ( ( char ) ( Character ) value , dst ) ;
if ( wrap ! = null ) return wrap ;
return dst . cast ( value ) ;
}
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " cast " ) // primitive cast to (long) is part of the pattern
2009-10-22 06:19:48 +00:00
static Object castToWrapperOrNull ( long value , Class < ? > dst ) {
if ( dst = = int . class | | dst = = Integer . class )
return ( int ) ( value ) ;
if ( dst = = long . class | | dst = = Long . class )
return ( long ) ( value ) ;
if ( dst = = char . class | | dst = = Character . class )
return ( char ) ( value ) ;
if ( dst = = short . class | | dst = = Short . class )
return ( short ) ( value ) ;
if ( dst = = float . class | | dst = = Float . class )
return ( float ) ( value ) ;
if ( dst = = double . class | | dst = = Double . class )
return ( double ) ( value ) ;
2010-01-08 00:16:45 +00:00
if ( dst = = byte . class | | dst = = Byte . class )
return ( byte ) ( value ) ;
if ( dst = = boolean . class | | dst = = boolean . class )
return ( ( value % 29 ) & 1 ) = = 0 ;
2009-10-22 06:19:48 +00:00
return null ;
}
2010-05-01 06:48:23 +00:00
static final int ONE_MILLION = ( 1000 * 1000 ) , // first int value
TEN_BILLION = ( 10 * 1000 * 1000 * 1000 ) , // scale factor to reach upper 32 bits
INITIAL_ARG_VAL = ONE_MILLION < < 1 ; // <<1 makes space for sign bit;
static long nextArgVal ;
static long nextArg ( boolean moreBits ) {
long val = nextArgVal + + ;
long sign = - ( val & 1 ) ; // alternate signs
val > > = 1 ;
if ( moreBits )
// Guarantee some bits in the high word.
// In any case keep the decimal representation simple-looking,
// with lots of zeroes, so as not to make the printed decimal
// strings unnecessarily noisy.
val + = ( val % ONE_MILLION ) * TEN_BILLION ;
return val ^ sign ;
}
static int nextArg ( ) {
// Produce a 32-bit result something like ONE_MILLION+(smallint).
// Example: 1_000_042.
return ( int ) nextArg ( false ) ;
}
static long nextArg ( Class < ? > kind ) {
if ( kind = = long . class | | kind = = Long . class | |
kind = = double . class | | kind = = Double . class )
// produce a 64-bit result something like
// ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
// Example: 10_000_420_001_000_042.
return nextArg ( true ) ;
return ( long ) nextArg ( ) ;
}
2009-10-22 06:19:48 +00:00
static Object randomArg ( Class < ? > param ) {
2010-05-01 06:48:23 +00:00
Object wrap = castToWrapperOrNull ( nextArg ( param ) , param ) ;
2009-10-22 06:19:48 +00:00
if ( wrap ! = null ) {
return wrap ;
}
2011-03-24 06:02:31 +00:00
// import sun.invoke.util.Wrapper;
2009-10-22 06:19:48 +00:00
// Wrapper wrap = Wrapper.forBasicType(dst);
// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
// wrap = Wrapper.forWrapperType(dst);
// if (wrap != Wrapper.OBJECT)
// return wrap.wrap(nextArg++);
2010-09-09 01:40:34 +00:00
if ( param . isInterface ( ) ) {
for ( Class < ? > c : param . getClasses ( ) ) {
if ( param . isAssignableFrom ( c ) & & ! c . isInterface ( ) )
{ param = c ; break ; }
}
}
2012-07-12 07:11:35 +00:00
if ( param . isInterface ( ) & & param . isAssignableFrom ( List . class ) )
return Arrays . asList ( " # " + nextArg ( ) ) ;
2009-10-22 06:19:48 +00:00
if ( param . isInterface ( ) | | param . isAssignableFrom ( String . class ) )
2010-05-01 06:48:23 +00:00
return " # " + nextArg ( ) ;
2009-10-22 06:19:48 +00:00
else
try {
return param . newInstance ( ) ;
2012-01-19 01:34:29 +00:00
} catch ( InstantiationException | IllegalAccessException ex ) {
2009-10-22 06:19:48 +00:00
}
return null ; // random class not Object, String, Integer, etc.
}
static Object [ ] randomArgs ( Class < ? > . . . params ) {
Object [ ] args = new Object [ params . length ] ;
for ( int i = 0 ; i < args . length ; i + + )
args [ i ] = randomArg ( params [ i ] ) ;
return args ;
}
static Object [ ] randomArgs ( int nargs , Class < ? > param ) {
Object [ ] args = new Object [ nargs ] ;
for ( int i = 0 ; i < args . length ; i + + )
args [ i ] = randomArg ( param ) ;
return args ;
}
2012-01-19 01:34:29 +00:00
@SafeVarargs @SuppressWarnings ( " varargs " )
2009-10-22 06:19:48 +00:00
static < T , E extends T > T [ ] array ( Class < T [ ] > atype , E . . . a ) {
return Arrays . copyOf ( a , a . length , atype ) ;
}
2012-01-19 01:34:29 +00:00
@SafeVarargs @SuppressWarnings ( " varargs " )
2009-10-22 06:19:48 +00:00
static < T > T [ ] cat ( T [ ] a , T . . . b ) {
int alen = a . length , blen = b . length ;
if ( blen = = 0 ) return a ;
T [ ] c = Arrays . copyOf ( a , alen + blen ) ;
System . arraycopy ( b , 0 , c , alen , blen ) ;
return c ;
}
static Integer [ ] boxAll ( int . . . vx ) {
Integer [ ] res = new Integer [ vx . length ] ;
for ( int i = 0 ; i < res . length ; i + + ) {
res [ i ] = vx [ i ] ;
}
return res ;
}
static Object getClasses ( Object x ) {
if ( x = = null ) return x ;
if ( x instanceof String ) return x ; // keep the name
if ( x instanceof List ) {
// recursively report classes of the list elements
Object [ ] xa = ( ( List ) x ) . toArray ( ) ;
for ( int i = 0 ; i < xa . length ; i + + )
xa [ i ] = getClasses ( xa [ i ] ) ;
return Arrays . asList ( xa ) ;
}
return x . getClass ( ) . getSimpleName ( ) ;
}
2011-02-11 09:26:28 +00:00
/** Return lambda(arg...[arity]) { new Object[]{ arg... } } */
static MethodHandle varargsList ( int arity ) {
return ValueConversions . varargsList ( arity ) ;
}
/** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */
static MethodHandle varargsArray ( int arity ) {
return ValueConversions . varargsArray ( arity ) ;
}
2011-05-13 02:27:49 +00:00
static MethodHandle varargsArray ( Class < ? > arrayType , int arity ) {
return ValueConversions . varargsArray ( arrayType , arity ) ;
}
2011-02-11 09:26:28 +00:00
/** Variation of varargsList, but with the given rtype. */
static MethodHandle varargsList ( int arity , Class < ? > rtype ) {
MethodHandle list = varargsList ( arity ) ;
MethodType listType = list . type ( ) . changeReturnType ( rtype ) ;
if ( List . class . isAssignableFrom ( rtype ) | | rtype = = void . class | | rtype = = Object . class ) {
// OK
} else if ( rtype . isAssignableFrom ( String . class ) ) {
if ( LIST_TO_STRING = = null )
try {
LIST_TO_STRING = PRIVATE . findStatic ( PRIVATE . lookupClass ( ) , " listToString " ,
MethodType . methodType ( String . class , List . class ) ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchMethodException | IllegalAccessException ex ) { throw new RuntimeException ( ex ) ; }
2011-02-11 09:26:28 +00:00
list = MethodHandles . filterReturnValue ( list , LIST_TO_STRING ) ;
} else if ( rtype . isPrimitive ( ) ) {
if ( LIST_TO_INT = = null )
try {
LIST_TO_INT = PRIVATE . findStatic ( PRIVATE . lookupClass ( ) , " listToInt " ,
MethodType . methodType ( int . class , List . class ) ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchMethodException | IllegalAccessException ex ) { throw new RuntimeException ( ex ) ; }
2011-02-11 09:26:28 +00:00
list = MethodHandles . filterReturnValue ( list , LIST_TO_INT ) ;
list = MethodHandles . explicitCastArguments ( list , listType ) ;
} else {
throw new RuntimeException ( " varargsList: " + rtype ) ;
}
return list . asType ( listType ) ;
}
private static MethodHandle LIST_TO_STRING , LIST_TO_INT ;
2012-01-19 01:34:29 +00:00
private static String listToString ( List < ? > x ) { return x . toString ( ) ; }
private static int listToInt ( List < ? > x ) { return x . toString ( ) . hashCode ( ) ; }
2011-02-11 09:26:28 +00:00
2009-10-22 06:19:48 +00:00
static MethodHandle changeArgTypes ( MethodHandle target , Class < ? > argType ) {
return changeArgTypes ( target , 0 , 999 , argType ) ;
}
static MethodHandle changeArgTypes ( MethodHandle target ,
int beg , int end , Class < ? > argType ) {
MethodType targetType = target . type ( ) ;
end = Math . min ( end , targetType . parameterCount ( ) ) ;
2012-01-19 01:34:29 +00:00
ArrayList < Class < ? > > argTypes = new ArrayList < > ( targetType . parameterList ( ) ) ;
2009-10-22 06:19:48 +00:00
Collections . fill ( argTypes . subList ( beg , end ) , argType ) ;
2010-01-08 00:16:45 +00:00
MethodType ttype2 = MethodType . methodType ( targetType . returnType ( ) , argTypes ) ;
2011-05-27 00:37:36 +00:00
return target . asType ( ttype2 ) ;
2009-10-22 06:19:48 +00:00
}
2011-07-16 22:44:33 +00:00
static MethodHandle addTrailingArgs ( MethodHandle target , int nargs , Class < ? > argClass ) {
int targetLen = target . type ( ) . parameterCount ( ) ;
int extra = ( nargs - targetLen ) ;
if ( extra < = 0 ) return target ;
List < Class < ? > > fakeArgs = Collections . < Class < ? > > nCopies ( extra , argClass ) ;
return MethodHandles . dropArguments ( target , targetLen , fakeArgs ) ;
}
2009-10-22 06:19:48 +00:00
// This lookup is good for all members in and under MethodHandlesTest.
static final Lookup PRIVATE = MethodHandles . lookup ( ) ;
// This lookup is good for package-private members but not private ones.
static final Lookup PACKAGE = PackageSibling . lookup ( ) ;
2012-07-24 17:47:44 +00:00
// This lookup is good for public members and protected members of PubExample
static final Lookup SUBCLASS = RemoteExample . lookup ( ) ;
2009-10-22 06:19:48 +00:00
// This lookup is good only for public members.
2010-01-08 00:16:45 +00:00
static final Lookup PUBLIC = MethodHandles . publicLookup ( ) ;
2009-10-22 06:19:48 +00:00
// Subject methods...
static class Example implements IntExample {
final String name ;
2010-05-01 06:48:23 +00:00
public Example ( ) { name = " Example# " + nextArg ( ) ; }
2009-10-22 06:19:48 +00:00
protected Example ( String name ) { this . name = name ; }
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " LeakingThisInConstructor " )
2009-10-22 06:19:48 +00:00
protected Example ( int x ) { this ( ) ; called ( " protected <init> " , this , x ) ; }
@Override public String toString ( ) { return name ; }
public void v0 ( ) { called ( " v0 " , this ) ; }
2012-07-24 17:47:44 +00:00
protected void pro_v0 ( ) { called ( " pro_v0 " , this ) ; }
2009-10-22 06:19:48 +00:00
void pkg_v0 ( ) { called ( " pkg_v0 " , this ) ; }
private void pri_v0 ( ) { called ( " pri_v0 " , this ) ; }
public static void s0 ( ) { called ( " s0 " ) ; }
2012-07-24 17:47:44 +00:00
protected static void pro_s0 ( ) { called ( " pro_s0 " ) ; }
2009-10-22 06:19:48 +00:00
static void pkg_s0 ( ) { called ( " pkg_s0 " ) ; }
private static void pri_s0 ( ) { called ( " pri_s0 " ) ; }
public Object v1 ( Object x ) { return called ( " v1 " , this , x ) ; }
public Object v2 ( Object x , Object y ) { return called ( " v2 " , this , x , y ) ; }
public Object v2 ( Object x , int y ) { return called ( " v2 " , this , x , y ) ; }
public Object v2 ( int x , Object y ) { return called ( " v2 " , this , x , y ) ; }
public Object v2 ( int x , int y ) { return called ( " v2 " , this , x , y ) ; }
public static Object s1 ( Object x ) { return called ( " s1 " , x ) ; }
public static Object s2 ( int x ) { return called ( " s2 " , x ) ; }
public static Object s3 ( long x ) { return called ( " s3 " , x ) ; }
public static Object s4 ( int x , int y ) { return called ( " s4 " , x , y ) ; }
public static Object s5 ( long x , int y ) { return called ( " s5 " , x , y ) ; }
public static Object s6 ( int x , long y ) { return called ( " s6 " , x , y ) ; }
public static Object s7 ( float x , double y ) { return called ( " s7 " , x , y ) ; }
2010-05-01 06:48:23 +00:00
2011-07-16 22:44:33 +00:00
// for testing findConstructor:
public Example ( String x , int y ) { this . name = x + y ; called ( " Example.<init> " , x , y ) ; }
public Example ( int x , String y ) { this . name = x + y ; called ( " Example.<init> " , x , y ) ; }
2012-07-24 17:47:44 +00:00
public Example ( int x , int y ) { this . name = x + " " + y ; called ( " Example.<init> " , x , y ) ; }
public Example ( int x , long y ) { this . name = x + " " + y ; called ( " Example.<init> " , x , y ) ; }
public Example ( int x , float y ) { this . name = x + " " + y ; called ( " Example.<init> " , x , y ) ; }
public Example ( int x , double y ) { this . name = x + " " + y ; called ( " Example.<init> " , x , y ) ; }
public Example ( int x , int y , int z ) { this . name = x + " " + y + " " + z ; called ( " Example.<init> " , x , y , z ) ; }
public Example ( int x , int y , int z , int a ) { this . name = x + " " + y + " " + z + " " + a ; called ( " Example.<init> " , x , y , z , a ) ; }
2011-07-16 22:44:33 +00:00
2010-05-01 06:48:23 +00:00
static final Lookup EXAMPLE = MethodHandles . lookup ( ) ; // for testing findSpecial
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
static final Lookup EXAMPLE = Example . EXAMPLE ;
2009-10-22 06:19:48 +00:00
public static class PubExample extends Example {
2012-07-24 17:47:44 +00:00
public PubExample ( ) { this ( " PubExample " ) ; }
protected PubExample ( String prefix ) { super ( prefix + " # " + nextArg ( ) ) ; }
protected void pro_v0 ( ) { called ( " Pub/pro_v0 " , this ) ; }
protected static void pro_s0 ( ) { called ( " Pub/pro_s0 " ) ; }
2009-10-22 06:19:48 +00:00
}
static class SubExample extends Example {
@Override public void v0 ( ) { called ( " Sub/v0 " , this ) ; }
@Override void pkg_v0 ( ) { called ( " Sub/pkg_v0 " , this ) ; }
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " LeakingThisInConstructor " )
2009-10-22 06:19:48 +00:00
private SubExample ( int x ) { called ( " <init> " , this , x ) ; }
2010-05-01 06:48:23 +00:00
public SubExample ( ) { super ( " SubExample# " + nextArg ( ) ) ; }
2009-10-22 06:19:48 +00:00
}
public static interface IntExample {
public void v0 ( ) ;
2010-09-09 01:40:34 +00:00
public static class Impl implements IntExample {
2009-10-22 06:19:48 +00:00
public void v0 ( ) { called ( " Int/v0 " , this ) ; }
final String name ;
2010-05-01 06:48:23 +00:00
public Impl ( ) { name = " Impl# " + nextArg ( ) ; }
@Override public String toString ( ) { return name ; }
2009-10-22 06:19:48 +00:00
}
}
2012-07-12 07:11:35 +00:00
static interface SubIntExample extends IntExample { }
2009-10-22 06:19:48 +00:00
static final Object [ ] [ ] [ ] ACCESS_CASES = {
2012-07-24 17:47:44 +00:00
{ { false , PUBLIC } , { false , SUBCLASS } , { false , PACKAGE } , { false , PRIVATE } , { false , EXAMPLE } } , //[0]: all false
{ { false , PUBLIC } , { false , SUBCLASS } , { false , PACKAGE } , { true , PRIVATE } , { true , EXAMPLE } } , //[1]: only PRIVATE
{ { false , PUBLIC } , { false , SUBCLASS } , { true , PACKAGE } , { true , PRIVATE } , { true , EXAMPLE } } , //[2]: PUBLIC false
{ { false , PUBLIC } , { true , SUBCLASS } , { true , PACKAGE } , { true , PRIVATE } , { true , EXAMPLE } } , //[3]: subclass OK
{ { true , PUBLIC } , { true , SUBCLASS } , { true , PACKAGE } , { true , PRIVATE } , { true , EXAMPLE } } , //[4]: all true
2009-10-22 06:19:48 +00:00
} ;
2010-05-01 06:48:23 +00:00
static Object [ ] [ ] accessCases ( Class < ? > defc , String name , boolean isSpecial ) {
Object [ ] [ ] cases ;
if ( name . contains ( " pri_ " ) | | isSpecial ) {
cases = ACCESS_CASES [ 1 ] ; // PRIVATE only
} else if ( name . contains ( " pkg_ " ) | | ! Modifier . isPublic ( defc . getModifiers ( ) ) ) {
cases = ACCESS_CASES [ 2 ] ; // not PUBLIC
2012-07-24 17:47:44 +00:00
} else if ( name . contains ( " pro_ " ) ) {
cases = ACCESS_CASES [ 3 ] ; // PUBLIC class, protected member
2010-01-08 00:16:45 +00:00
} else {
2012-07-24 17:47:44 +00:00
assertTrue ( name . indexOf ( '_' ) < 0 | | name . contains ( " fin_ " ) ) ;
2010-01-08 00:16:45 +00:00
boolean pubc = Modifier . isPublic ( defc . getModifiers ( ) ) ;
if ( pubc )
2012-07-24 17:47:44 +00:00
cases = ACCESS_CASES [ 4 ] ; // all access levels
2010-05-01 06:48:23 +00:00
else
cases = ACCESS_CASES [ 2 ] ; // PACKAGE but not PUBLIC
2010-01-08 00:16:45 +00:00
}
2010-05-01 06:48:23 +00:00
if ( defc ! = Example . class & & cases [ cases . length - 1 ] [ 1 ] = = EXAMPLE )
cases = Arrays . copyOfRange ( cases , 0 , cases . length - 1 ) ;
return cases ;
}
static Object [ ] [ ] accessCases ( Class < ? > defc , String name ) {
return accessCases ( defc , name , false ) ;
2009-10-22 06:19:48 +00:00
}
2012-07-24 17:47:44 +00:00
static Lookup maybeMoveIn ( Lookup lookup , Class < ? > defc ) {
if ( lookup = = PUBLIC | | lookup = = SUBCLASS | | lookup = = PACKAGE )
// external views stay external
return lookup ;
return lookup . in ( defc ) ;
}
2009-10-22 06:19:48 +00:00
@Test
public void testFindStatic ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " findStatic " ) ;
testFindStatic ( PubExample . class , void . class , " s0 " ) ;
testFindStatic ( Example . class , void . class , " s0 " ) ;
testFindStatic ( Example . class , void . class , " pkg_s0 " ) ;
testFindStatic ( Example . class , void . class , " pri_s0 " ) ;
2012-07-24 17:47:44 +00:00
testFindStatic ( Example . class , void . class , " pro_s0 " ) ;
testFindStatic ( PubExample . class , void . class , " Pub/pro_s0 " ) ;
2009-10-22 06:19:48 +00:00
testFindStatic ( Example . class , Object . class , " s1 " , Object . class ) ;
testFindStatic ( Example . class , Object . class , " s2 " , int . class ) ;
testFindStatic ( Example . class , Object . class , " s3 " , long . class ) ;
testFindStatic ( Example . class , Object . class , " s4 " , int . class , int . class ) ;
testFindStatic ( Example . class , Object . class , " s5 " , long . class , int . class ) ;
testFindStatic ( Example . class , Object . class , " s6 " , int . class , long . class ) ;
testFindStatic ( Example . class , Object . class , " s7 " , float . class , double . class ) ;
testFindStatic ( false , PRIVATE , Example . class , void . class , " bogus " ) ;
2012-07-24 17:47:44 +00:00
testFindStatic ( false , PRIVATE , Example . class , void . class , " v0 " ) ;
2009-10-22 06:19:48 +00:00
}
void testFindStatic ( Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name ) ) {
testFindStatic ( ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , defc , ret , name , params ) ;
}
}
void testFindStatic ( Lookup lookup , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
testFindStatic ( true , lookup , defc , ret , name , params ) ;
}
void testFindStatic ( boolean positive , Lookup lookup , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
countTest ( positive ) ;
2012-07-24 17:47:44 +00:00
String methodName = name . substring ( 1 + name . indexOf ( '/' ) ) ; // foo/bar => foo
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . methodType ( ret , params ) ;
2009-10-22 06:19:48 +00:00
MethodHandle target = null ;
2010-09-09 01:40:23 +00:00
Exception noAccess = null ;
2009-10-22 06:19:48 +00:00
try {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " " + name + type ) ;
2012-07-24 17:47:44 +00:00
target = maybeMoveIn ( lookup , defc ) . findStatic ( defc , methodName , type ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
2009-10-22 06:19:48 +00:00
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( name . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchMethodException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
System . out . println ( " findStatic " + lookup + " : " + defc . getName ( ) + " . " + name + " / " + type + " => " + target
2009-10-22 06:19:48 +00:00
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
assertEquals ( type , target . type ( ) ) ;
2012-07-24 17:47:44 +00:00
assertNameStringContains ( target , methodName ) ;
2009-10-22 06:19:48 +00:00
Object [ ] args = randomArgs ( params ) ;
printCalled ( target , name , args ) ;
2010-10-31 04:08:23 +00:00
target . invokeWithArguments ( args ) ;
2009-10-22 06:19:48 +00:00
assertCalled ( name , args ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
System . out . print ( ':' ) ;
2009-10-22 06:19:48 +00:00
}
2011-05-18 02:48:19 +00:00
static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean . getBoolean ( " java.lang.invoke.MethodHandle.DEBUG_NAMES " ) ;
2010-10-31 04:08:23 +00:00
// rough check of name string
2011-05-18 02:48:19 +00:00
static void assertNameStringContains ( MethodHandle x , String s ) {
if ( ! DEBUG_METHOD_HANDLE_NAMES ) {
// ignore s
assertEquals ( " MethodHandle " + x . type ( ) , x . toString ( ) ) ;
return ;
}
2010-10-31 04:08:23 +00:00
if ( x . toString ( ) . contains ( s ) ) return ;
assertEquals ( s , x ) ;
}
2009-10-22 06:19:48 +00:00
@Test
public void testFindVirtual ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " findVirtual " ) ;
testFindVirtual ( Example . class , void . class , " v0 " ) ;
testFindVirtual ( Example . class , void . class , " pkg_v0 " ) ;
testFindVirtual ( Example . class , void . class , " pri_v0 " ) ;
testFindVirtual ( Example . class , Object . class , " v1 " , Object . class ) ;
testFindVirtual ( Example . class , Object . class , " v2 " , Object . class , Object . class ) ;
testFindVirtual ( Example . class , Object . class , " v2 " , Object . class , int . class ) ;
testFindVirtual ( Example . class , Object . class , " v2 " , int . class , Object . class ) ;
testFindVirtual ( Example . class , Object . class , " v2 " , int . class , int . class ) ;
2012-07-24 17:47:44 +00:00
testFindVirtual ( Example . class , void . class , " pro_v0 " ) ;
testFindVirtual ( PubExample . class , void . class , " Pub/pro_v0 " ) ;
2009-10-22 06:19:48 +00:00
testFindVirtual ( false , PRIVATE , Example . class , Example . class , void . class , " bogus " ) ;
2012-07-24 17:47:44 +00:00
testFindVirtual ( false , PRIVATE , Example . class , Example . class , void . class , " s0 " ) ;
2009-10-22 06:19:48 +00:00
// test dispatch
testFindVirtual ( SubExample . class , SubExample . class , void . class , " Sub/v0 " ) ;
testFindVirtual ( SubExample . class , Example . class , void . class , " Sub/v0 " ) ;
testFindVirtual ( SubExample . class , IntExample . class , void . class , " Sub/v0 " ) ;
testFindVirtual ( SubExample . class , SubExample . class , void . class , " Sub/pkg_v0 " ) ;
testFindVirtual ( SubExample . class , Example . class , void . class , " Sub/pkg_v0 " ) ;
testFindVirtual ( Example . class , IntExample . class , void . class , " v0 " ) ;
testFindVirtual ( IntExample . Impl . class , IntExample . class , void . class , " Int/v0 " ) ;
}
void testFindVirtual ( Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
Class < ? > rcvc = defc ;
testFindVirtual ( rcvc , defc , ret , name , params ) ;
}
void testFindVirtual ( Class < ? > rcvc , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name ) ) {
testFindVirtual ( ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , rcvc , defc , ret , name , params ) ;
}
}
void testFindVirtual ( Lookup lookup , Class < ? > rcvc , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
testFindVirtual ( true , lookup , rcvc , defc , ret , name , params ) ;
}
void testFindVirtual ( boolean positive , Lookup lookup , Class < ? > rcvc , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
countTest ( positive ) ;
String methodName = name . substring ( 1 + name . indexOf ( '/' ) ) ; // foo/bar => foo
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . methodType ( ret , params ) ;
2009-10-22 06:19:48 +00:00
MethodHandle target = null ;
2010-09-09 01:40:23 +00:00
Exception noAccess = null ;
2009-10-22 06:19:48 +00:00
try {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " " + name + type ) ;
2012-07-24 17:47:44 +00:00
target = maybeMoveIn ( lookup , defc ) . findVirtual ( defc , methodName , type ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
2009-10-22 06:19:48 +00:00
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( name . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchMethodException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
System . out . println ( " findVirtual " + lookup + " : " + defc . getName ( ) + " . " + name + " / " + type + " => " + target
2009-10-22 06:19:48 +00:00
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
2012-07-24 17:47:44 +00:00
Class < ? > selfc = defc ;
// predict receiver type narrowing:
if ( lookup = = SUBCLASS & &
name . contains ( " pro_ " ) & &
selfc . isAssignableFrom ( lookup . lookupClass ( ) ) ) {
selfc = lookup . lookupClass ( ) ;
if ( name . startsWith ( " Pub/ " ) ) name = " Rem/ " + name . substring ( 4 ) ;
}
Class < ? > [ ] paramsWithSelf = cat ( array ( Class [ ] . class , ( Class ) selfc ) , params ) ;
2010-01-08 00:16:45 +00:00
MethodType typeWithSelf = MethodType . methodType ( ret , paramsWithSelf ) ;
2010-05-01 06:48:23 +00:00
assertEquals ( typeWithSelf , target . type ( ) ) ;
2010-10-31 04:08:23 +00:00
assertNameStringContains ( target , methodName ) ;
2009-10-22 06:19:48 +00:00
Object [ ] argsWithSelf = randomArgs ( paramsWithSelf ) ;
2012-07-24 17:47:44 +00:00
if ( selfc . isAssignableFrom ( rcvc ) & & rcvc ! = selfc ) argsWithSelf [ 0 ] = randomArg ( rcvc ) ;
2009-10-22 06:19:48 +00:00
printCalled ( target , name , argsWithSelf ) ;
2010-10-31 04:08:23 +00:00
target . invokeWithArguments ( argsWithSelf ) ;
2009-10-22 06:19:48 +00:00
assertCalled ( name , argsWithSelf ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
System . out . print ( ':' ) ;
2009-10-22 06:19:48 +00:00
}
@Test
public void testFindSpecial ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " findSpecial " ) ;
2010-05-01 06:48:23 +00:00
testFindSpecial ( SubExample . class , Example . class , void . class , " v0 " ) ;
testFindSpecial ( SubExample . class , Example . class , void . class , " pkg_v0 " ) ;
2012-07-24 17:47:44 +00:00
testFindSpecial ( RemoteExample . class , PubExample . class , void . class , " Pub/pro_v0 " ) ;
2010-05-01 06:48:23 +00:00
// Do some negative testing:
2011-02-11 09:26:32 +00:00
testFindSpecial ( false , EXAMPLE , SubExample . class , Example . class , void . class , " bogus " ) ;
testFindSpecial ( false , PRIVATE , SubExample . class , Example . class , void . class , " bogus " ) ;
2010-05-01 06:48:23 +00:00
for ( Lookup lookup : new Lookup [ ] { PRIVATE , EXAMPLE , PACKAGE , PUBLIC } ) {
testFindSpecial ( false , lookup , Object . class , Example . class , void . class , " v0 " ) ;
testFindSpecial ( false , lookup , SubExample . class , Example . class , void . class , " <init> " , int . class ) ;
testFindSpecial ( false , lookup , SubExample . class , Example . class , void . class , " s0 " ) ;
}
}
void testFindSpecial ( Class < ? > specialCaller ,
Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( specialCaller = = RemoteExample . class ) {
testFindSpecial ( false , EXAMPLE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , PRIVATE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , PACKAGE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( true , SUBCLASS , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , PUBLIC , specialCaller , defc , ret , name , params ) ;
return ;
}
testFindSpecial ( true , EXAMPLE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( true , PRIVATE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , PACKAGE , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , SUBCLASS , specialCaller , defc , ret , name , params ) ;
testFindSpecial ( false , PUBLIC , specialCaller , defc , ret , name , params ) ;
2010-05-01 06:48:23 +00:00
}
void testFindSpecial ( boolean positive , Lookup lookup , Class < ? > specialCaller ,
Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
2009-10-22 06:19:48 +00:00
countTest ( positive ) ;
2012-07-24 17:47:44 +00:00
String methodName = name . substring ( 1 + name . indexOf ( '/' ) ) ; // foo/bar => foo
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . methodType ( ret , params ) ;
2009-10-22 06:19:48 +00:00
MethodHandle target = null ;
2010-09-09 01:40:23 +00:00
Exception noAccess = null ;
2009-10-22 06:19:48 +00:00
try {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " " + name + type ) ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) System . out . println ( " lookup => " + maybeMoveIn ( lookup , specialCaller ) ) ;
target = maybeMoveIn ( lookup , specialCaller ) . findSpecial ( defc , methodName , type , specialCaller ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
2009-10-22 06:19:48 +00:00
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( name . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchMethodException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
System . out . println ( " findSpecial from " + specialCaller . getName ( ) + " to " + defc . getName ( ) + " . " + name + " / " + type + " => " + target
+ ( target = = null ? " " : target . type ( ) )
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
2009-10-22 06:19:48 +00:00
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
2010-05-01 06:48:23 +00:00
assertEquals ( specialCaller , target . type ( ) . parameterType ( 0 ) ) ;
assertEquals ( type , target . type ( ) . dropParameterTypes ( 0 , 1 ) ) ;
Class < ? > [ ] paramsWithSelf = cat ( array ( Class [ ] . class , ( Class ) specialCaller ) , params ) ;
2010-01-08 00:16:45 +00:00
MethodType typeWithSelf = MethodType . methodType ( ret , paramsWithSelf ) ;
2012-07-24 17:47:44 +00:00
assertNameStringContains ( target , methodName ) ;
2009-10-22 06:19:48 +00:00
Object [ ] args = randomArgs ( paramsWithSelf ) ;
printCalled ( target , name , args ) ;
2010-10-31 04:08:23 +00:00
target . invokeWithArguments ( args ) ;
2009-10-22 06:19:48 +00:00
assertCalled ( name , args ) ;
}
2011-07-16 22:44:33 +00:00
@Test
public void testFindConstructor ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " findConstructor " ) ;
testFindConstructor ( true , EXAMPLE , Example . class ) ;
testFindConstructor ( true , EXAMPLE , Example . class , int . class ) ;
2012-07-24 17:47:44 +00:00
testFindConstructor ( true , EXAMPLE , Example . class , int . class , int . class ) ;
testFindConstructor ( true , EXAMPLE , Example . class , int . class , long . class ) ;
testFindConstructor ( true , EXAMPLE , Example . class , int . class , float . class ) ;
testFindConstructor ( true , EXAMPLE , Example . class , int . class , double . class ) ;
2011-07-16 22:44:33 +00:00
testFindConstructor ( true , EXAMPLE , Example . class , String . class ) ;
2012-07-24 17:47:44 +00:00
testFindConstructor ( true , EXAMPLE , Example . class , int . class , int . class , int . class ) ;
testFindConstructor ( true , EXAMPLE , Example . class , int . class , int . class , int . class , int . class ) ;
2011-07-16 22:44:33 +00:00
}
void testFindConstructor ( boolean positive , Lookup lookup ,
Class < ? > defc , Class < ? > . . . params ) throws Throwable {
countTest ( positive ) ;
MethodType type = MethodType . methodType ( void . class , params ) ;
MethodHandle target = null ;
Exception noAccess = null ;
try {
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " <init> " + type ) ;
target = lookup . findConstructor ( defc , type ) ;
} catch ( ReflectiveOperationException ex ) {
noAccess = ex ;
assertTrue ( noAccess instanceof IllegalAccessException ) ;
}
if ( verbosity > = 3 )
System . out . println ( " findConstructor " + defc . getName ( ) + " .<init>/ " + type + " => " + target
+ ( target = = null ? " " : target . type ( ) )
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
assertEquals ( type . changeReturnType ( defc ) , target . type ( ) ) ;
Object [ ] args = randomArgs ( params ) ;
printCalled ( target , defc . getSimpleName ( ) , args ) ;
Object obj = target . invokeWithArguments ( args ) ;
if ( ! ( defc = = Example . class & & params . length < 2 ) )
assertCalled ( defc . getSimpleName ( ) + " .<init> " , args ) ;
assertTrue ( " instance of " + defc . getName ( ) , defc . isInstance ( obj ) ) ;
}
2009-10-22 06:19:48 +00:00
@Test
public void testBind ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " bind " ) ;
testBind ( Example . class , void . class , " v0 " ) ;
testBind ( Example . class , void . class , " pkg_v0 " ) ;
testBind ( Example . class , void . class , " pri_v0 " ) ;
testBind ( Example . class , Object . class , " v1 " , Object . class ) ;
testBind ( Example . class , Object . class , " v2 " , Object . class , Object . class ) ;
testBind ( Example . class , Object . class , " v2 " , Object . class , int . class ) ;
testBind ( Example . class , Object . class , " v2 " , int . class , Object . class ) ;
testBind ( Example . class , Object . class , " v2 " , int . class , int . class ) ;
testBind ( false , PRIVATE , Example . class , void . class , " bogus " ) ;
testBind ( SubExample . class , void . class , " Sub/v0 " ) ;
testBind ( SubExample . class , void . class , " Sub/pkg_v0 " ) ;
testBind ( IntExample . Impl . class , void . class , " Int/v0 " ) ;
}
void testBind ( Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name ) ) {
testBind ( ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , defc , ret , name , params ) ;
}
}
void testBind ( boolean positive , Lookup lookup , Class < ? > defc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
countTest ( positive ) ;
String methodName = name . substring ( 1 + name . indexOf ( '/' ) ) ; // foo/bar => foo
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . methodType ( ret , params ) ;
2009-10-22 06:19:48 +00:00
Object receiver = randomArg ( defc ) ;
MethodHandle target = null ;
2010-09-09 01:40:23 +00:00
Exception noAccess = null ;
2009-10-22 06:19:48 +00:00
try {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " " + name + type ) ;
2012-07-24 17:47:44 +00:00
target = maybeMoveIn ( lookup , defc ) . bind ( receiver , methodName , type ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
2009-10-22 06:19:48 +00:00
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( name . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchMethodException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2009-10-22 06:19:48 +00:00
System . out . println ( " bind " + receiver + " . " + name + " / " + type + " => " + target
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
assertEquals ( type , target . type ( ) ) ;
Object [ ] args = randomArgs ( params ) ;
printCalled ( target , name , args ) ;
2010-10-31 04:08:23 +00:00
target . invokeWithArguments ( args ) ;
2009-10-22 06:19:48 +00:00
Object [ ] argsWithReceiver = cat ( array ( Object [ ] . class , receiver ) , args ) ;
assertCalled ( name , argsWithReceiver ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
System . out . print ( ':' ) ;
2009-10-22 06:19:48 +00:00
}
@Test
public void testUnreflect ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " unreflect " ) ;
testUnreflect ( Example . class , true , void . class , " s0 " ) ;
2012-07-24 17:47:44 +00:00
testUnreflect ( Example . class , true , void . class , " pro_s0 " ) ;
2009-10-22 06:19:48 +00:00
testUnreflect ( Example . class , true , void . class , " pkg_s0 " ) ;
testUnreflect ( Example . class , true , void . class , " pri_s0 " ) ;
testUnreflect ( Example . class , true , Object . class , " s1 " , Object . class ) ;
testUnreflect ( Example . class , true , Object . class , " s2 " , int . class ) ;
2010-05-01 06:48:23 +00:00
testUnreflect ( Example . class , true , Object . class , " s3 " , long . class ) ;
testUnreflect ( Example . class , true , Object . class , " s4 " , int . class , int . class ) ;
testUnreflect ( Example . class , true , Object . class , " s5 " , long . class , int . class ) ;
testUnreflect ( Example . class , true , Object . class , " s6 " , int . class , long . class ) ;
2009-10-22 06:19:48 +00:00
testUnreflect ( Example . class , false , void . class , " v0 " ) ;
testUnreflect ( Example . class , false , void . class , " pkg_v0 " ) ;
testUnreflect ( Example . class , false , void . class , " pri_v0 " ) ;
testUnreflect ( Example . class , false , Object . class , " v1 " , Object . class ) ;
testUnreflect ( Example . class , false , Object . class , " v2 " , Object . class , Object . class ) ;
testUnreflect ( Example . class , false , Object . class , " v2 " , Object . class , int . class ) ;
testUnreflect ( Example . class , false , Object . class , " v2 " , int . class , Object . class ) ;
testUnreflect ( Example . class , false , Object . class , " v2 " , int . class , int . class ) ;
2012-07-24 17:47:44 +00:00
// Test a public final member in another package:
testUnreflect ( RemoteExample . class , false , void . class , " Rem/fin_v0 " ) ;
2009-10-22 06:19:48 +00:00
}
void testUnreflect ( Class < ? > defc , boolean isStatic , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name ) ) {
2010-05-01 06:48:23 +00:00
testUnreflectMaybeSpecial ( null , ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , defc , ( isStatic ? null : defc ) , ret , name , params ) ;
2009-10-22 06:19:48 +00:00
}
}
2010-05-01 06:48:23 +00:00
void testUnreflect ( Class < ? > defc , Class < ? > rcvc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name ) ) {
testUnreflectMaybeSpecial ( null , ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , defc , rcvc , ret , name , params ) ;
2009-10-22 06:19:48 +00:00
}
}
2010-05-01 06:48:23 +00:00
void testUnreflectMaybeSpecial ( Class < ? > specialCaller ,
boolean positive , Lookup lookup ,
Class < ? > defc , Class < ? > rcvc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
2009-10-22 06:19:48 +00:00
countTest ( positive ) ;
2012-07-24 17:47:44 +00:00
String methodName = name . substring ( 1 + name . indexOf ( '/' ) ) ; // foo/bar => foo
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . methodType ( ret , params ) ;
2012-07-24 17:47:44 +00:00
Method rmethod = defc . getDeclaredMethod ( methodName , params ) ;
2009-10-22 06:19:48 +00:00
MethodHandle target = null ;
2010-09-09 01:40:23 +00:00
Exception noAccess = null ;
2010-05-01 06:48:23 +00:00
boolean isStatic = ( rcvc = = null ) ;
boolean isSpecial = ( specialCaller ! = null ) ;
2009-10-22 06:19:48 +00:00
try {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) System . out . println ( " lookup via " + lookup + " of " + defc + " " + name + type ) ;
if ( isSpecial )
2012-07-24 17:47:44 +00:00
target = maybeMoveIn ( lookup , specialCaller ) . unreflectSpecial ( rmethod , specialCaller ) ;
2010-05-01 06:48:23 +00:00
else
2012-07-24 17:47:44 +00:00
target = maybeMoveIn ( lookup , defc ) . unreflect ( rmethod ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
2009-10-22 06:19:48 +00:00
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( name . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchMethodException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
System . out . println ( " unreflect " + ( isSpecial ? " Special " : " " ) + " " + defc . getName ( ) + " . " + name + " / " + type
+ ( ! isSpecial ? " " : " specialCaller= " + specialCaller )
+ ( isStatic ? " " : " receiver= " + rcvc )
+ " => " + target
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
2009-10-22 06:19:48 +00:00
if ( positive & & noAccess ! = null ) throw noAccess ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
2010-05-01 06:48:23 +00:00
assertEquals ( isStatic , Modifier . isStatic ( rmethod . getModifiers ( ) ) ) ;
2009-10-22 06:19:48 +00:00
Class < ? > [ ] paramsMaybeWithSelf = params ;
if ( ! isStatic ) {
2010-05-01 06:48:23 +00:00
paramsMaybeWithSelf = cat ( array ( Class [ ] . class , ( Class ) rcvc ) , params ) ;
2009-10-22 06:19:48 +00:00
}
2010-01-08 00:16:45 +00:00
MethodType typeMaybeWithSelf = MethodType . methodType ( ret , paramsMaybeWithSelf ) ;
2010-05-01 06:48:23 +00:00
if ( isStatic ) {
assertEquals ( typeMaybeWithSelf , target . type ( ) ) ;
} else {
if ( isSpecial )
assertEquals ( specialCaller , target . type ( ) . parameterType ( 0 ) ) ;
else
assertEquals ( defc , target . type ( ) . parameterType ( 0 ) ) ;
assertEquals ( typeMaybeWithSelf , target . type ( ) . changeParameterType ( 0 , rcvc ) ) ;
}
2009-10-22 06:19:48 +00:00
Object [ ] argsMaybeWithSelf = randomArgs ( paramsMaybeWithSelf ) ;
printCalled ( target , name , argsMaybeWithSelf ) ;
2010-10-31 04:08:23 +00:00
target . invokeWithArguments ( argsMaybeWithSelf ) ;
2009-10-22 06:19:48 +00:00
assertCalled ( name , argsMaybeWithSelf ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
System . out . print ( ':' ) ;
}
void testUnreflectSpecial ( Class < ? > defc , Class < ? > rcvc , Class < ? > ret , String name , Class < ? > . . . params ) throws Throwable {
for ( Object [ ] ac : accessCases ( defc , name , true ) ) {
Class < ? > specialCaller = rcvc ;
testUnreflectMaybeSpecial ( specialCaller , ( Boolean ) ac [ 0 ] , ( Lookup ) ac [ 1 ] , defc , rcvc , ret , name , params ) ;
}
2009-10-22 06:19:48 +00:00
}
2010-05-01 06:48:23 +00:00
@Test
2009-10-22 06:19:48 +00:00
public void testUnreflectSpecial ( ) throws Throwable {
2010-05-01 06:48:23 +00:00
if ( CAN_SKIP_WORKING ) return ;
2009-10-22 06:19:48 +00:00
startTest ( " unreflectSpecial " ) ;
2010-05-01 06:48:23 +00:00
testUnreflectSpecial ( Example . class , Example . class , void . class , " v0 " ) ;
testUnreflectSpecial ( Example . class , SubExample . class , void . class , " v0 " ) ;
testUnreflectSpecial ( Example . class , Example . class , void . class , " pkg_v0 " ) ;
testUnreflectSpecial ( Example . class , SubExample . class , void . class , " pkg_v0 " ) ;
testUnreflectSpecial ( Example . class , Example . class , Object . class , " v2 " , int . class , int . class ) ;
testUnreflectSpecial ( Example . class , SubExample . class , Object . class , " v2 " , int . class , int . class ) ;
testUnreflectMaybeSpecial ( Example . class , false , PRIVATE , Example . class , Example . class , void . class , " s0 " ) ;
2009-10-22 06:19:48 +00:00
}
2010-01-08 00:16:45 +00:00
public static class HasFields {
boolean fZ = false ;
byte fB = ( byte ) 'B' ;
short fS = ( short ) 'S' ;
char fC = 'C' ;
int fI = 'I' ;
long fJ = 'J' ;
float fF = 'F' ;
double fD = 'D' ;
static boolean sZ = true ;
static byte sB = 1 + ( byte ) 'B' ;
static short sS = 1 + ( short ) 'S' ;
static char sC = 1 + 'C' ;
static int sI = 1 + 'I' ;
static long sJ = 1 + 'J' ;
static float sF = 1 + 'F' ;
static double sD = 1 + 'D' ;
Object fL = 'L' ;
String fR = " R " ;
static Object sL = 'M' ;
static String sR = " S " ;
static final Object [ ] [ ] CASES ;
static {
2012-01-19 01:34:29 +00:00
ArrayList < Object [ ] > cases = new ArrayList < > ( ) ;
2010-01-08 00:16:45 +00:00
Object types [ ] [ ] = {
{ 'L' , Object . class } , { 'R' , String . class } ,
{ 'I' , int . class } , { 'J' , long . class } ,
{ 'F' , float . class } , { 'D' , double . class } ,
{ 'Z' , boolean . class } , { 'B' , byte . class } ,
{ 'S' , short . class } , { 'C' , char . class } ,
} ;
HasFields fields = new HasFields ( ) ;
for ( Object [ ] t : types ) {
for ( int kind = 0 ; kind < = 1 ; kind + + ) {
boolean isStatic = ( kind ! = 0 ) ;
char btc = ( Character ) t [ 0 ] ;
String name = ( isStatic ? " s " : " f " ) + btc ;
Class < ? > type = ( Class < ? > ) t [ 1 ] ;
Object value ;
Field field ;
2011-05-13 02:27:49 +00:00
try {
2010-01-08 00:16:45 +00:00
field = HasFields . class . getDeclaredField ( name ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchFieldException | SecurityException ex ) {
2010-01-08 00:16:45 +00:00
throw new InternalError ( " no field HasFields. " + name ) ;
}
try {
value = field . get ( fields ) ;
2012-01-19 01:34:29 +00:00
} catch ( IllegalArgumentException | IllegalAccessException ex ) {
2010-01-08 00:16:45 +00:00
throw new InternalError ( " cannot fetch field HasFields. " + name ) ;
}
if ( type = = float . class ) {
float v = 'F' ;
if ( isStatic ) v + + ;
2011-02-11 09:26:32 +00:00
assertTrue ( value . equals ( v ) ) ;
2010-01-08 00:16:45 +00:00
}
2011-02-11 09:26:32 +00:00
assertTrue ( name . equals ( field . getName ( ) ) ) ;
assertTrue ( type . equals ( field . getType ( ) ) ) ;
assertTrue ( isStatic = = ( Modifier . isStatic ( field . getModifiers ( ) ) ) ) ;
2010-01-08 00:16:45 +00:00
cases . add ( new Object [ ] { field , value } ) ;
}
}
2011-02-11 09:26:32 +00:00
cases . add ( new Object [ ] { new Object [ ] { false , HasFields . class , " bogus_fD " , double . class } , Error . class } ) ;
cases . add ( new Object [ ] { new Object [ ] { true , HasFields . class , " bogus_sL " , Object . class } , Error . class } ) ;
2010-01-08 00:16:45 +00:00
CASES = cases . toArray ( new Object [ 0 ] [ ] ) ;
}
}
2012-07-24 17:47:44 +00:00
static final int TEST_UNREFLECT = 1 , TEST_FIND_FIELD = 2 , TEST_FIND_STATIC = 3 , TEST_SETTER = 0x10 , TEST_BOUND = 0x20 , TEST_NPE = 0x40 ;
2010-06-09 06:08:56 +00:00
static boolean testModeMatches ( int testMode , boolean isStatic ) {
switch ( testMode ) {
2011-02-11 09:26:32 +00:00
case TEST_FIND_STATIC : return isStatic ;
2010-06-09 06:08:56 +00:00
case TEST_FIND_FIELD : return ! isStatic ;
2011-02-11 09:26:32 +00:00
case TEST_UNREFLECT : return true ; // unreflect matches both
2010-06-09 06:08:56 +00:00
}
2011-02-11 09:26:32 +00:00
throw new InternalError ( " testMode= " + testMode ) ;
2010-06-09 06:08:56 +00:00
}
2010-01-08 00:16:45 +00:00
@Test
2009-10-22 06:19:48 +00:00
public void testUnreflectGetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2009-10-22 06:19:48 +00:00
startTest ( " unreflectGetter " ) ;
2010-06-09 06:08:56 +00:00
testGetter ( TEST_UNREFLECT ) ;
}
@Test
public void testFindGetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2010-06-09 06:08:56 +00:00
startTest ( " findGetter " ) ;
testGetter ( TEST_FIND_FIELD ) ;
2012-07-24 17:47:44 +00:00
testGetter ( TEST_FIND_FIELD | TEST_BOUND ) ;
2010-06-09 06:08:56 +00:00
}
@Test
public void testFindStaticGetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2010-06-09 06:08:56 +00:00
startTest ( " findStaticGetter " ) ;
2011-02-11 09:26:32 +00:00
testGetter ( TEST_FIND_STATIC ) ;
2010-06-09 06:08:56 +00:00
}
public void testGetter ( int testMode ) throws Throwable {
Lookup lookup = PRIVATE ; // FIXME: test more lookups than this one
2010-01-08 00:16:45 +00:00
for ( Object [ ] c : HasFields . CASES ) {
2011-02-11 09:26:32 +00:00
boolean positive = ( c [ 1 ] ! = Error . class ) ;
testGetter ( positive , lookup , c [ 0 ] , c [ 1 ] , testMode ) ;
2012-07-12 07:11:35 +00:00
if ( positive )
testGetter ( positive , lookup , c [ 0 ] , c [ 1 ] , testMode | TEST_NPE ) ;
2011-02-11 09:26:32 +00:00
}
testGetter ( true , lookup ,
new Object [ ] { true , System . class , " out " , java . io . PrintStream . class } ,
System . out , testMode ) ;
for ( int isStaticN = 0 ; isStaticN < = 1 ; isStaticN + + ) {
testGetter ( false , lookup ,
new Object [ ] { ( isStaticN ! = 0 ) , System . class , " bogus " , char . class } ,
null , testMode ) ;
}
}
public void testGetter ( boolean positive , MethodHandles . Lookup lookup ,
Object fieldRef , Object value , int testMode ) throws Throwable {
testAccessor ( positive , lookup , fieldRef , value , testMode ) ;
}
2012-07-12 07:11:35 +00:00
public void testAccessor ( boolean positive0 , MethodHandles . Lookup lookup ,
2011-02-11 09:26:32 +00:00
Object fieldRef , Object value , int testMode0 ) throws Throwable {
2011-07-16 22:44:33 +00:00
if ( verbosity > = 4 )
2012-07-12 07:11:35 +00:00
System . out . println ( " testAccessor " + Arrays . deepToString ( new Object [ ] { positive0 , lookup , fieldRef , value , testMode0 } ) ) ;
2011-02-11 09:26:32 +00:00
boolean isGetter = ( ( testMode0 & TEST_SETTER ) = = 0 ) ;
2012-07-24 17:47:44 +00:00
boolean doBound = ( ( testMode0 & TEST_BOUND ) ! = 0 ) ;
2012-07-12 07:11:35 +00:00
boolean testNPE = ( ( testMode0 & TEST_NPE ) ! = 0 ) ;
2012-07-24 17:47:44 +00:00
int testMode = testMode0 & ~ ( TEST_SETTER | TEST_BOUND | TEST_NPE ) ;
2012-07-12 07:11:35 +00:00
boolean positive = positive0 & & ! testNPE ;
2011-02-11 09:26:32 +00:00
boolean isStatic ;
Class < ? > fclass ;
String fname ;
Class < ? > ftype ;
Field f = ( fieldRef instanceof Field ? ( Field ) fieldRef : null ) ;
if ( f ! = null ) {
isStatic = Modifier . isStatic ( f . getModifiers ( ) ) ;
fclass = f . getDeclaringClass ( ) ;
fname = f . getName ( ) ;
ftype = f . getType ( ) ;
} else {
Object [ ] scnt = ( Object [ ] ) fieldRef ;
isStatic = ( Boolean ) scnt [ 0 ] ;
fclass = ( Class < ? > ) scnt [ 1 ] ;
fname = ( String ) scnt [ 2 ] ;
ftype = ( Class < ? > ) scnt [ 3 ] ;
try {
f = fclass . getDeclaredField ( fname ) ;
} catch ( ReflectiveOperationException ex ) {
f = null ;
}
}
2010-06-09 06:08:56 +00:00
if ( ! testModeMatches ( testMode , isStatic ) ) return ;
2011-02-11 09:26:32 +00:00
if ( f = = null & & testMode = = TEST_UNREFLECT ) return ;
2012-07-12 07:11:35 +00:00
if ( testNPE & & isStatic ) return ;
2011-02-11 09:26:32 +00:00
countTest ( positive ) ;
MethodType expType ;
if ( isGetter )
expType = MethodType . methodType ( ftype , HasFields . class ) ;
else
expType = MethodType . methodType ( void . class , HasFields . class , ftype ) ;
2010-01-08 00:16:45 +00:00
if ( isStatic ) expType = expType . dropParameterTypes ( 0 , 1 ) ;
2011-02-11 09:26:32 +00:00
Exception noAccess = null ;
MethodHandle mh ;
try {
2012-07-24 17:47:44 +00:00
switch ( testMode0 & ~ ( TEST_BOUND | TEST_NPE ) ) {
2011-02-11 09:26:32 +00:00
case TEST_UNREFLECT : mh = lookup . unreflectGetter ( f ) ; break ;
case TEST_FIND_FIELD : mh = lookup . findGetter ( fclass , fname , ftype ) ; break ;
case TEST_FIND_STATIC : mh = lookup . findStaticGetter ( fclass , fname , ftype ) ; break ;
case TEST_SETTER |
TEST_UNREFLECT : mh = lookup . unreflectSetter ( f ) ; break ;
case TEST_SETTER |
TEST_FIND_FIELD : mh = lookup . findSetter ( fclass , fname , ftype ) ; break ;
case TEST_SETTER |
TEST_FIND_STATIC : mh = lookup . findStaticSetter ( fclass , fname , ftype ) ; break ;
default :
throw new InternalError ( " testMode= " + testMode ) ;
}
} catch ( ReflectiveOperationException ex ) {
mh = null ;
noAccess = ex ;
2012-07-24 17:47:44 +00:00
if ( verbosity > = 5 ) ex . printStackTrace ( System . out ) ;
2011-02-11 09:26:32 +00:00
if ( fname . contains ( " bogus " ) )
assertTrue ( noAccess instanceof NoSuchFieldException ) ;
else
assertTrue ( noAccess instanceof IllegalAccessException ) ;
}
if ( verbosity > = 3 )
System . out . println ( " find " + ( isStatic ? " Static " : " " ) + ( isGetter ? " Getter " : " Setter " ) + " " + fclass . getName ( ) + " . " + fname + " / " + ftype
+ " => " + mh
+ ( noAccess = = null ? " " : " !! " + noAccess ) ) ;
2012-07-12 07:11:35 +00:00
if ( positive & & ! testNPE & & noAccess ! = null ) throw new RuntimeException ( noAccess ) ;
assertEquals ( positive0 ? " positive test " : " negative test erroneously passed " , positive0 , mh ! = null ) ;
if ( ! positive & & ! testNPE ) return ; // negative access test failed as expected
2011-02-11 09:26:32 +00:00
assertEquals ( ( isStatic ? 0 : 1 ) + ( isGetter ? 0 : 1 ) , mh . type ( ) . parameterCount ( ) ) ;
2010-01-08 00:16:45 +00:00
assertSame ( mh . type ( ) , expType ) ;
2012-07-24 17:47:44 +00:00
//assertNameStringContains(mh, fname); // This does not hold anymore with LFs
2010-01-08 00:16:45 +00:00
HasFields fields = new HasFields ( ) ;
2012-07-12 07:11:35 +00:00
HasFields fieldsForMH = fields ;
if ( testNPE ) fieldsForMH = null ; // perturb MH argument to elicit expected error
2012-07-24 17:47:44 +00:00
if ( doBound )
mh = mh . bindTo ( fieldsForMH ) ;
2010-01-08 00:16:45 +00:00
Object sawValue ;
2011-02-11 09:26:32 +00:00
Class < ? > vtype = ftype ;
if ( ftype ! = int . class ) vtype = Object . class ;
if ( isGetter ) {
2011-05-27 00:37:36 +00:00
mh = mh . asType ( mh . type ( ) . generic ( )
. changeReturnType ( vtype ) ) ;
2011-02-11 09:26:32 +00:00
} else {
int last = mh . type ( ) . parameterCount ( ) - 1 ;
2011-05-27 00:37:36 +00:00
mh = mh . asType ( mh . type ( ) . generic ( )
. changeReturnType ( void . class )
. changeParameterType ( last , vtype ) ) ;
2011-02-11 09:26:32 +00:00
}
if ( f ! = null & & f . getDeclaringClass ( ) = = HasFields . class ) {
assertEquals ( f . get ( fields ) , value ) ; // clean to start with
}
2012-07-12 07:11:35 +00:00
Throwable caughtEx = null ;
2011-02-11 09:26:32 +00:00
if ( isGetter ) {
Object expValue = value ;
for ( int i = 0 ; i < = 1 ; i + + ) {
2012-07-24 17:47:44 +00:00
sawValue = null ; // make DA rules happy under try/catch
try {
if ( isStatic | | doBound ) {
if ( ftype = = int . class )
sawValue = ( int ) mh . invokeExact ( ) ; // do these exactly
else
sawValue = mh . invokeExact ( ) ;
} else {
2012-07-12 07:11:35 +00:00
if ( ftype = = int . class )
sawValue = ( int ) mh . invokeExact ( ( Object ) fieldsForMH ) ;
else
sawValue = mh . invokeExact ( ( Object ) fieldsForMH ) ;
2012-07-24 17:47:44 +00:00
}
} catch ( RuntimeException ex ) {
if ( ex instanceof NullPointerException & & testNPE ) {
caughtEx = ex ;
break ;
2012-07-12 07:11:35 +00:00
}
2011-02-11 09:26:32 +00:00
}
assertEquals ( sawValue , expValue ) ;
if ( f ! = null & & f . getDeclaringClass ( ) = = HasFields . class
& & ! Modifier . isFinal ( f . getModifiers ( ) ) ) {
Object random = randomArg ( ftype ) ;
f . set ( fields , random ) ;
expValue = random ;
} else {
break ;
}
}
} else {
for ( int i = 0 ; i < = 1 ; i + + ) {
Object putValue = randomArg ( ftype ) ;
2012-07-24 17:47:44 +00:00
try {
if ( isStatic | | doBound ) {
if ( ftype = = int . class )
mh . invokeExact ( ( int ) putValue ) ; // do these exactly
else
mh . invokeExact ( putValue ) ;
} else {
2012-07-12 07:11:35 +00:00
if ( ftype = = int . class )
mh . invokeExact ( ( Object ) fieldsForMH , ( int ) putValue ) ;
else
mh . invokeExact ( ( Object ) fieldsForMH , putValue ) ;
2012-07-24 17:47:44 +00:00
}
} catch ( RuntimeException ex ) {
if ( ex instanceof NullPointerException & & testNPE ) {
caughtEx = ex ;
break ;
2012-07-12 07:11:35 +00:00
}
2011-02-11 09:26:32 +00:00
}
if ( f ! = null & & f . getDeclaringClass ( ) = = HasFields . class ) {
assertEquals ( f . get ( fields ) , putValue ) ;
}
2010-01-08 00:16:45 +00:00
}
}
2011-02-11 09:26:32 +00:00
if ( f ! = null & & f . getDeclaringClass ( ) = = HasFields . class ) {
f . set ( fields , value ) ; // put it back
}
2012-07-12 07:11:35 +00:00
if ( testNPE ) {
if ( caughtEx = = null | | ! ( caughtEx instanceof NullPointerException ) )
throw new RuntimeException ( " failed to catch NPE exception " + ( caughtEx = = null ? " (caughtEx=null) " : " " ) , caughtEx ) ;
caughtEx = null ; // nullify expected exception
}
if ( caughtEx ! = null ) {
throw new RuntimeException ( " unexpected exception " , caughtEx ) ;
}
2009-10-22 06:19:48 +00:00
}
2010-01-08 00:16:45 +00:00
@Test
2009-10-22 06:19:48 +00:00
public void testUnreflectSetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2010-06-09 06:08:56 +00:00
startTest ( " unreflectSetter " ) ;
testSetter ( TEST_UNREFLECT ) ;
}
@Test
public void testFindSetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2010-06-09 06:08:56 +00:00
startTest ( " findSetter " ) ;
testSetter ( TEST_FIND_FIELD ) ;
2012-07-24 17:47:44 +00:00
testSetter ( TEST_FIND_FIELD | TEST_BOUND ) ;
2010-06-09 06:08:56 +00:00
}
@Test
public void testFindStaticSetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2010-06-09 06:08:56 +00:00
startTest ( " findStaticSetter " ) ;
2011-02-11 09:26:32 +00:00
testSetter ( TEST_FIND_STATIC ) ;
2010-06-09 06:08:56 +00:00
}
public void testSetter ( int testMode ) throws Throwable {
2009-10-22 06:19:48 +00:00
Lookup lookup = PRIVATE ; // FIXME: test more lookups than this one
startTest ( " unreflectSetter " ) ;
2010-01-08 00:16:45 +00:00
for ( Object [ ] c : HasFields . CASES ) {
2011-02-11 09:26:32 +00:00
boolean positive = ( c [ 1 ] ! = Error . class ) ;
testSetter ( positive , lookup , c [ 0 ] , c [ 1 ] , testMode ) ;
2012-07-12 07:11:35 +00:00
if ( positive )
testSetter ( positive , lookup , c [ 0 ] , c [ 1 ] , testMode | TEST_NPE ) ;
2011-02-11 09:26:32 +00:00
}
for ( int isStaticN = 0 ; isStaticN < = 1 ; isStaticN + + ) {
testSetter ( false , lookup ,
new Object [ ] { ( isStaticN ! = 0 ) , System . class , " bogus " , char . class } ,
null , testMode ) ;
2010-01-08 00:16:45 +00:00
}
2011-02-11 09:26:32 +00:00
}
public void testSetter ( boolean positive , MethodHandles . Lookup lookup ,
Object fieldRef , Object value , int testMode ) throws Throwable {
testAccessor ( positive , lookup , fieldRef , value , testMode | TEST_SETTER ) ;
2009-10-22 06:19:48 +00:00
}
2010-01-08 00:16:45 +00:00
@Test
2009-10-22 06:19:48 +00:00
public void testArrayElementGetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2009-10-22 06:19:48 +00:00
startTest ( " arrayElementGetter " ) ;
2010-05-01 06:48:23 +00:00
testArrayElementGetterSetter ( false ) ;
2009-10-22 06:19:48 +00:00
}
2010-01-08 00:16:45 +00:00
@Test
2009-10-22 06:19:48 +00:00
public void testArrayElementSetter ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2009-10-22 06:19:48 +00:00
startTest ( " arrayElementSetter " ) ;
2010-05-01 06:48:23 +00:00
testArrayElementGetterSetter ( true ) ;
}
2012-07-12 07:11:35 +00:00
private static final int TEST_ARRAY_NONE = 0 , TEST_ARRAY_NPE = 1 , TEST_ARRAY_OOB = 2 , TEST_ARRAY_ASE = 3 ;
2010-05-01 06:48:23 +00:00
public void testArrayElementGetterSetter ( boolean testSetter ) throws Throwable {
2012-07-12 07:11:35 +00:00
testArrayElementGetterSetter ( testSetter , TEST_ARRAY_NONE ) ;
}
@Test
public void testArrayElementErrors ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
2012-07-12 07:11:35 +00:00
startTest ( " arrayElementErrors " ) ;
testArrayElementGetterSetter ( false , TEST_ARRAY_NPE ) ;
testArrayElementGetterSetter ( true , TEST_ARRAY_NPE ) ;
testArrayElementGetterSetter ( false , TEST_ARRAY_OOB ) ;
testArrayElementGetterSetter ( true , TEST_ARRAY_OOB ) ;
testArrayElementGetterSetter ( new Object [ 10 ] , true , TEST_ARRAY_ASE ) ;
testArrayElementGetterSetter ( new Example [ 10 ] , true , TEST_ARRAY_ASE ) ;
testArrayElementGetterSetter ( new IntExample [ 10 ] , true , TEST_ARRAY_ASE ) ;
}
public void testArrayElementGetterSetter ( boolean testSetter , int negTest ) throws Throwable {
testArrayElementGetterSetter ( new String [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new Iterable < ? > [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new Example [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new IntExample [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new Object [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new boolean [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new byte [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new char [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new short [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new int [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new float [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new long [ 10 ] , testSetter , negTest ) ;
testArrayElementGetterSetter ( new double [ 10 ] , testSetter , negTest ) ;
}
public void testArrayElementGetterSetter ( Object array , boolean testSetter , int negTest ) throws Throwable {
boolean positive = ( negTest = = TEST_ARRAY_NONE ) ;
int length = java . lang . reflect . Array . getLength ( array ) ;
2010-01-08 00:16:45 +00:00
Class < ? > arrayType = array . getClass ( ) ;
Class < ? > elemType = arrayType . getComponentType ( ) ;
2012-07-12 07:11:35 +00:00
Object arrayToMH = array ;
// this stanza allows negative tests to make argument perturbations:
switch ( negTest ) {
case TEST_ARRAY_NPE :
arrayToMH = null ;
break ;
case TEST_ARRAY_OOB :
assert ( length > 0 ) ;
arrayToMH = java . lang . reflect . Array . newInstance ( elemType , 0 ) ;
break ;
case TEST_ARRAY_ASE :
assert ( testSetter & & ! elemType . isPrimitive ( ) ) ;
if ( elemType = = Object . class )
arrayToMH = new StringBuffer [ length ] ; // very random subclass of Object!
else if ( elemType = = Example . class )
arrayToMH = new SubExample [ length ] ;
else if ( elemType = = IntExample . class )
arrayToMH = new SubIntExample [ length ] ;
else
return ; // can't make an ArrayStoreException test
assert ( arrayType . isInstance ( arrayToMH ) )
: Arrays . asList ( arrayType , arrayToMH . getClass ( ) , testSetter , negTest ) ;
break ;
}
countTest ( positive ) ;
if ( verbosity > 2 ) System . out . println ( " array type = " + array . getClass ( ) . getComponentType ( ) . getName ( ) + " [ " + length + " ] " + ( positive ? " " : " negative test # " + negTest + " using " + Arrays . deepToString ( new Object [ ] { arrayToMH } ) ) ) ;
2010-01-08 00:16:45 +00:00
MethodType expType = ! testSetter
? MethodType . methodType ( elemType , arrayType , int . class )
: MethodType . methodType ( void . class , arrayType , int . class , elemType ) ;
MethodHandle mh = ! testSetter
? MethodHandles . arrayElementGetter ( arrayType )
: MethodHandles . arrayElementSetter ( arrayType ) ;
assertSame ( mh . type ( ) , expType ) ;
2010-05-01 06:48:23 +00:00
if ( elemType ! = int . class & & elemType ! = boolean . class ) {
2012-07-12 07:11:35 +00:00
MethodType gtype = mh . type ( ) . generic ( ) . changeParameterType ( 1 , int . class ) ;
2011-05-13 02:27:49 +00:00
if ( testSetter ) gtype = gtype . changeReturnType ( void . class ) ;
2011-05-27 00:37:36 +00:00
mh = mh . asType ( gtype ) ;
2010-05-01 06:48:23 +00:00
}
2010-01-08 00:16:45 +00:00
Object sawValue , expValue ;
List < Object > model = array2list ( array ) ;
2012-07-12 07:11:35 +00:00
Throwable caughtEx = null ;
2010-01-08 00:16:45 +00:00
for ( int i = 0 ; i < length ; i + + ) {
// update array element
Object random = randomArg ( elemType ) ;
model . set ( i , random ) ;
if ( testSetter ) {
2012-07-12 07:11:35 +00:00
try {
if ( elemType = = int . class )
mh . invokeExact ( ( int [ ] ) arrayToMH , i , ( int ) random ) ;
else if ( elemType = = boolean . class )
mh . invokeExact ( ( boolean [ ] ) arrayToMH , i , ( boolean ) random ) ;
else
mh . invokeExact ( arrayToMH , i , random ) ;
} catch ( RuntimeException ex ) {
caughtEx = ex ;
break ;
}
2010-01-08 00:16:45 +00:00
assertEquals ( model , array2list ( array ) ) ;
} else {
Array . set ( array , i , random ) ;
2010-05-01 06:48:23 +00:00
}
if ( verbosity > = 5 ) {
List < Object > array2list = array2list ( array ) ;
System . out . println ( " a[ " + i + " ]= " + random + " => " + array2list ) ;
if ( ! array2list . equals ( model ) )
System . out . println ( " *** != " + model ) ;
2010-01-08 00:16:45 +00:00
}
// observe array element
sawValue = Array . get ( array , i ) ;
if ( ! testSetter ) {
expValue = sawValue ;
2012-07-12 07:11:35 +00:00
try {
if ( elemType = = int . class )
sawValue = ( int ) mh . invokeExact ( ( int [ ] ) arrayToMH , i ) ;
else if ( elemType = = boolean . class )
sawValue = ( boolean ) mh . invokeExact ( ( boolean [ ] ) arrayToMH , i ) ;
else
sawValue = mh . invokeExact ( arrayToMH , i ) ;
} catch ( RuntimeException ex ) {
caughtEx = ex ;
break ;
}
2010-01-08 00:16:45 +00:00
assertEquals ( sawValue , expValue ) ;
assertEquals ( model , array2list ( array ) ) ;
}
}
2012-07-12 07:11:35 +00:00
if ( ! positive ) {
if ( caughtEx = = null )
throw new RuntimeException ( " failed to catch exception for negTest= " + negTest ) ;
// test the kind of exception
Class < ? > reqType = null ;
switch ( negTest ) {
case TEST_ARRAY_ASE : reqType = ArrayStoreException . class ; break ;
case TEST_ARRAY_OOB : reqType = ArrayIndexOutOfBoundsException . class ; break ;
case TEST_ARRAY_NPE : reqType = NullPointerException . class ; break ;
default : assert ( false ) ;
}
if ( reqType . isInstance ( caughtEx ) ) {
caughtEx = null ; // nullify expected exception
}
}
if ( caughtEx ! = null ) {
throw new RuntimeException ( " unexpected exception " , caughtEx ) ;
}
2010-01-08 00:16:45 +00:00
}
List < Object > array2list ( Object array ) {
int length = Array . getLength ( array ) ;
2012-01-19 01:34:29 +00:00
ArrayList < Object > model = new ArrayList < > ( length ) ;
2010-01-08 00:16:45 +00:00
for ( int i = 0 ; i < length ; i + + )
model . add ( Array . get ( array , i ) ) ;
return model ;
2009-10-22 06:19:48 +00:00
}
static class Callee {
static Object id ( ) { return called ( " id " ) ; }
static Object id ( Object x ) { return called ( " id " , x ) ; }
static Object id ( Object x , Object y ) { return called ( " id " , x , y ) ; }
static Object id ( Object x , Object y , Object z ) { return called ( " id " , x , y , z ) ; }
static Object id ( Object . . . vx ) { return called ( " id " , vx ) ; }
static MethodHandle ofType ( int n ) {
return ofType ( Object . class , n ) ;
}
static MethodHandle ofType ( Class < ? > rtype , int n ) {
if ( n = = - 1 )
2010-01-08 00:16:45 +00:00
return ofType ( MethodType . methodType ( rtype , Object [ ] . class ) ) ;
return ofType ( MethodType . genericMethodType ( n ) . changeReturnType ( rtype ) ) ;
2009-10-22 06:19:48 +00:00
}
static MethodHandle ofType ( Class < ? > rtype , Class < ? > . . . ptypes ) {
2010-01-08 00:16:45 +00:00
return ofType ( MethodType . methodType ( rtype , ptypes ) ) ;
2009-10-22 06:19:48 +00:00
}
static MethodHandle ofType ( MethodType type ) {
Class < ? > rtype = type . returnType ( ) ;
String pfx = " " ;
if ( rtype ! = Object . class )
pfx = rtype . getSimpleName ( ) . substring ( 0 , 1 ) . toLowerCase ( ) ;
String name = pfx + " id " ;
2010-09-09 01:40:23 +00:00
try {
return PRIVATE . findStatic ( Callee . class , name , type ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchMethodException | IllegalAccessException ex ) {
2010-09-09 01:40:23 +00:00
throw new RuntimeException ( ex ) ;
}
2009-10-22 06:19:48 +00:00
}
}
@Test
public void testConvertArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " convertArguments " ) ;
testConvert ( Callee . ofType ( 1 ) , null , " id " , int . class ) ;
testConvert ( Callee . ofType ( 1 ) , null , " id " , String . class ) ;
testConvert ( Callee . ofType ( 1 ) , null , " id " , Integer . class ) ;
testConvert ( Callee . ofType ( 1 ) , null , " id " , short . class ) ;
2010-05-01 06:48:23 +00:00
testConvert ( Callee . ofType ( 1 ) , null , " id " , char . class ) ;
testConvert ( Callee . ofType ( 1 ) , null , " id " , byte . class ) ;
2009-10-22 06:19:48 +00:00
}
void testConvert ( MethodHandle id , Class < ? > rtype , String name , Class < ? > . . . params ) throws Throwable {
2011-05-27 00:37:36 +00:00
testConvert ( true , id , rtype , name , params ) ;
2009-10-22 06:19:48 +00:00
}
2011-05-27 00:37:36 +00:00
void testConvert ( boolean positive ,
2010-10-31 04:08:23 +00:00
MethodHandle id , Class < ? > rtype , String name , Class < ? > . . . params ) throws Throwable {
2009-10-22 06:19:48 +00:00
countTest ( positive ) ;
MethodType idType = id . type ( ) ;
if ( rtype = = null ) rtype = idType . returnType ( ) ;
for ( int i = 0 ; i < params . length ; i + + ) {
if ( params [ i ] = = null ) params [ i ] = idType . parameterType ( i ) ;
}
// simulate the pairwise conversion
2010-01-08 00:16:45 +00:00
MethodType newType = MethodType . methodType ( rtype , params ) ;
2009-10-22 06:19:48 +00:00
Object [ ] args = randomArgs ( newType . parameterArray ( ) ) ;
Object [ ] convArgs = args . clone ( ) ;
for ( int i = 0 ; i < args . length ; i + + ) {
Class < ? > src = newType . parameterType ( i ) ;
Class < ? > dst = idType . parameterType ( i ) ;
if ( src ! = dst )
convArgs [ i ] = castToWrapper ( convArgs [ i ] , dst ) ;
}
2010-10-31 04:08:23 +00:00
Object convResult = id . invokeWithArguments ( convArgs ) ;
2009-10-22 06:19:48 +00:00
{
Class < ? > dst = newType . returnType ( ) ;
Class < ? > src = idType . returnType ( ) ;
if ( src ! = dst )
convResult = castToWrapper ( convResult , dst ) ;
}
MethodHandle target = null ;
RuntimeException error = null ;
try {
2011-05-27 00:37:36 +00:00
target = id . asType ( newType ) ;
2012-08-17 20:42:25 +00:00
} catch ( WrongMethodTypeException ex ) {
2009-10-22 06:19:48 +00:00
error = ex ;
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2009-10-22 06:19:48 +00:00
System . out . println ( " convert " + id + " to " + newType + " => " + target
+ ( error = = null ? " " : " !! " + error ) ) ;
if ( positive & & error ! = null ) throw error ;
assertEquals ( positive ? " positive test " : " negative test erroneously passed " , positive , target ! = null ) ;
if ( ! positive ) return ; // negative test failed as expected
assertEquals ( newType , target . type ( ) ) ;
printCalled ( target , id . toString ( ) , args ) ;
2010-10-31 04:08:23 +00:00
Object result = target . invokeWithArguments ( args ) ;
2009-10-22 06:19:48 +00:00
assertCalled ( name , convArgs ) ;
assertEquals ( convResult , result ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 1 )
System . out . print ( ':' ) ;
2009-10-22 06:19:48 +00:00
}
2011-02-11 09:26:24 +00:00
@Test
public void testVarargsCollector ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
if ( CAN_SKIP_WORKING ) return ;
startTest ( " varargsCollector " ) ;
2011-02-11 09:26:24 +00:00
MethodHandle vac0 = PRIVATE . findStatic ( MethodHandlesTest . class , " called " ,
MethodType . methodType ( Object . class , String . class , Object [ ] . class ) ) ;
vac0 = vac0 . bindTo ( " vac " ) ;
MethodHandle vac = vac0 . asVarargsCollector ( Object [ ] . class ) ;
2011-05-27 00:37:36 +00:00
testConvert ( true , vac . asType ( MethodType . genericMethodType ( 0 ) ) , null , " vac " ) ;
testConvert ( true , vac . asType ( MethodType . genericMethodType ( 0 ) ) , null , " vac " ) ;
2012-01-19 01:34:29 +00:00
for ( Class < ? > at : new Class < ? > [ ] { Object . class , String . class , Integer . class } ) {
2011-05-27 00:37:36 +00:00
testConvert ( true , vac . asType ( MethodType . genericMethodType ( 1 ) ) , null , " vac " , at ) ;
testConvert ( true , vac . asType ( MethodType . genericMethodType ( 2 ) ) , null , " vac " , at , at ) ;
2011-02-11 09:26:24 +00:00
}
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2010-01-08 00:16:45 +00:00
public void testPermuteArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " permuteArguments " ) ;
2011-07-16 22:44:33 +00:00
testPermuteArguments ( 4 , Integer . class , 2 , long . class , 6 ) ;
if ( CAN_TEST_LIGHTLY ) return ;
2010-01-08 00:16:45 +00:00
testPermuteArguments ( 4 , Integer . class , 2 , String . class , 0 ) ;
2011-05-27 00:37:36 +00:00
testPermuteArguments ( 6 , Integer . class , 0 , null , 30 ) ;
2010-01-08 00:16:45 +00:00
}
public void testPermuteArguments ( int max , Class < ? > type1 , int t2c , Class < ? > type2 , int dilution ) throws Throwable {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 2 )
2010-01-08 00:16:45 +00:00
System . out . println ( " permuteArguments " + max + " * " + type1 . getName ( )
+ ( t2c = = 0 ? " " : " / " + t2c + " * " + type2 . getName ( ) )
+ ( dilution > 0 ? " with dilution " + dilution : " " ) ) ;
int t2pos = t2c = = 0 ? 0 : 1 ;
for ( int inargs = t2pos + 1 ; inargs < = max ; inargs + + ) {
Class < ? > [ ] types = new Class < ? > [ inargs ] ;
Arrays . fill ( types , type1 ) ;
if ( t2c ! = 0 ) {
// Fill in a middle range with type2:
Arrays . fill ( types , t2pos , Math . min ( t2pos + t2c , inargs ) , type2 ) ;
}
Object [ ] args = randomArgs ( types ) ;
int numcases = 1 ;
for ( int outargs = 0 ; outargs < = max ; outargs + + ) {
if ( outargs - inargs > = MAX_ARG_INCREASE ) continue ;
int casStep = dilution + 1 ;
// Avoid some common factors:
while ( ( casStep > 2 & & casStep % 2 = = 0 & & inargs % 2 = = 0 ) | |
( casStep > 3 & & casStep % 3 = = 0 & & inargs % 3 = = 0 ) )
casStep + + ;
2011-05-13 02:27:49 +00:00
testPermuteArguments ( args , types , outargs , numcases , casStep ) ;
2010-01-08 00:16:45 +00:00
numcases * = inargs ;
2011-07-16 22:44:33 +00:00
if ( CAN_TEST_LIGHTLY & & outargs < max - 2 ) continue ;
2010-01-08 00:16:45 +00:00
if ( dilution > 10 & & outargs > = 4 ) {
2011-07-16 22:44:33 +00:00
if ( CAN_TEST_LIGHTLY ) continue ;
2011-05-13 02:27:49 +00:00
int [ ] reorder = new int [ outargs ] ;
2010-01-08 00:16:45 +00:00
// Do some special patterns, which we probably missed.
// Replication of a single argument or argument pair.
for ( int i = 0 ; i < inargs ; i + + ) {
Arrays . fill ( reorder , i ) ;
testPermuteArguments ( args , types , reorder ) ;
for ( int d = 1 ; d < = 2 ; d + + ) {
if ( i + d > = inargs ) continue ;
for ( int j = 1 ; j < outargs ; j + = 2 )
reorder [ j ] + = 1 ;
testPermuteArguments ( args , types , reorder ) ;
testPermuteArguments ( args , types , reverse ( reorder ) ) ;
}
}
// Repetition of a sequence of 3 or more arguments.
for ( int i = 1 ; i < inargs ; i + + ) {
for ( int len = 3 ; len < = inargs ; len + + ) {
for ( int j = 0 ; j < outargs ; j + + )
reorder [ j ] = ( i + ( j % len ) ) % inargs ;
testPermuteArguments ( args , types , reorder ) ;
testPermuteArguments ( args , types , reverse ( reorder ) ) ;
}
}
}
}
}
}
2011-05-13 02:27:49 +00:00
public void testPermuteArguments ( Object [ ] args , Class < ? > [ ] types ,
int outargs , int numcases , int casStep ) throws Throwable {
int inargs = args . length ;
int [ ] reorder = new int [ outargs ] ;
for ( int cas = 0 ; cas < numcases ; cas + = casStep ) {
for ( int i = 0 , c = cas ; i < outargs ; i + + ) {
reorder [ i ] = c % inargs ;
c / = inargs ;
}
2011-07-16 22:44:33 +00:00
if ( CAN_TEST_LIGHTLY & & outargs > = 3 & & ( reorder [ 0 ] = = reorder [ 1 ] | | reorder [ 1 ] = = reorder [ 2 ] ) ) continue ;
2011-05-13 02:27:49 +00:00
testPermuteArguments ( args , types , reorder ) ;
}
}
2010-01-08 00:16:45 +00:00
static int [ ] reverse ( int [ ] reorder ) {
reorder = reorder . clone ( ) ;
for ( int i = 0 , imax = reorder . length / 2 ; i < imax ; i + + ) {
int j = reorder . length - 1 - i ;
int tem = reorder [ i ] ;
reorder [ i ] = reorder [ j ] ;
reorder [ j ] = tem ;
}
return reorder ;
}
void testPermuteArguments ( Object [ ] args , Class < ? > [ ] types , int [ ] reorder ) throws Throwable {
countTest ( ) ;
if ( args = = null & & types = = null ) {
int max = 0 ;
for ( int j : reorder ) {
if ( max < j ) max = j ;
}
args = randomArgs ( max + 1 , Integer . class ) ;
}
if ( args = = null ) {
args = randomArgs ( types ) ;
}
if ( types = = null ) {
types = new Class < ? > [ args . length ] ;
for ( int i = 0 ; i < args . length ; i + + )
types [ i ] = args [ i ] . getClass ( ) ;
}
int inargs = args . length , outargs = reorder . length ;
2011-02-11 09:26:32 +00:00
assertTrue ( inargs = = types . length ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " permuteArguments " + Arrays . toString ( reorder ) ) ;
Object [ ] permArgs = new Object [ outargs ] ;
Class < ? > [ ] permTypes = new Class < ? > [ outargs ] ;
for ( int i = 0 ; i < outargs ; i + + ) {
permArgs [ i ] = args [ reorder [ i ] ] ;
permTypes [ i ] = types [ reorder [ i ] ] ;
}
2010-05-01 06:48:23 +00:00
if ( verbosity > = 4 ) {
2010-01-08 00:16:45 +00:00
System . out . println ( " in args: " + Arrays . asList ( args ) ) ;
System . out . println ( " out args: " + Arrays . asList ( permArgs ) ) ;
System . out . println ( " in types: " + Arrays . asList ( types ) ) ;
System . out . println ( " out types: " + Arrays . asList ( permTypes ) ) ;
}
MethodType inType = MethodType . methodType ( Object . class , types ) ;
MethodType outType = MethodType . methodType ( Object . class , permTypes ) ;
2011-05-27 00:37:36 +00:00
MethodHandle target = varargsList ( outargs ) . asType ( outType ) ;
2010-01-08 00:16:45 +00:00
MethodHandle newTarget = MethodHandles . permuteArguments ( target , inType , reorder ) ;
2011-05-27 00:37:36 +00:00
if ( verbosity > = 5 ) System . out . println ( " newTarget = " + newTarget ) ;
2010-10-31 04:08:23 +00:00
Object result = newTarget . invokeWithArguments ( args ) ;
2010-01-08 00:16:45 +00:00
Object expected = Arrays . asList ( permArgs ) ;
2011-05-13 02:27:49 +00:00
if ( ! expected . equals ( result ) ) {
System . out . println ( " *** failed permuteArguments " + Arrays . toString ( reorder ) + " types= " + Arrays . asList ( types ) ) ;
System . out . println ( " in args: " + Arrays . asList ( args ) ) ;
System . out . println ( " out args: " + expected ) ;
System . out . println ( " bad args: " + result ) ;
}
2010-01-08 00:16:45 +00:00
assertEquals ( expected , result ) ;
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2010-01-08 00:16:45 +00:00
public void testSpreadArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " spreadArguments " ) ;
2012-01-19 01:34:29 +00:00
for ( Class < ? > argType : new Class < ? > [ ] { Object . class , Integer . class , int . class } ) {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " spreadArguments " + argType ) ;
2011-07-16 22:44:33 +00:00
for ( int nargs = 0 ; nargs < 50 ; nargs + + ) {
2012-07-24 17:47:44 +00:00
if ( CAN_TEST_LIGHTLY & & nargs > 11 ) break ;
2011-07-16 22:44:33 +00:00
for ( int pos = 0 ; pos < = nargs ; pos + + ) {
if ( CAN_TEST_LIGHTLY & & pos > 2 & & pos < nargs - 2 ) continue ;
if ( nargs > 10 & & pos > 4 & & pos < nargs - 4 & & pos % 10 ! = 3 )
continue ;
testSpreadArguments ( argType , pos , nargs ) ;
2010-01-08 00:16:45 +00:00
}
}
}
}
public void testSpreadArguments ( Class < ? > argType , int pos , int nargs ) throws Throwable {
countTest ( ) ;
2011-05-13 02:27:49 +00:00
Class < ? > arrayType = java . lang . reflect . Array . newInstance ( argType , 0 ) . getClass ( ) ;
MethodHandle target2 = varargsArray ( arrayType , nargs ) ;
MethodHandle target = target2 . asType ( target2 . type ( ) . generic ( ) ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " spread into " + target2 + " [ " + pos + " .. " + nargs + " ] " ) ;
Object [ ] args = randomArgs ( target2 . type ( ) . parameterArray ( ) ) ;
// make sure the target does what we think it does:
2011-05-13 02:27:49 +00:00
if ( pos = = 0 & & nargs < 5 & & ! argType . isPrimitive ( ) ) {
2012-01-19 01:34:29 +00:00
Object [ ] check = ( Object [ ] ) target . invokeWithArguments ( args ) ;
2010-01-08 00:16:45 +00:00
assertArrayEquals ( args , check ) ;
switch ( nargs ) {
case 0 :
2011-05-13 02:27:49 +00:00
check = ( Object [ ] ) ( Object ) target . invokeExact ( ) ;
2010-01-08 00:16:45 +00:00
assertArrayEquals ( args , check ) ;
break ;
case 1 :
2011-05-13 02:27:49 +00:00
check = ( Object [ ] ) ( Object ) target . invokeExact ( args [ 0 ] ) ;
2010-01-08 00:16:45 +00:00
assertArrayEquals ( args , check ) ;
break ;
case 2 :
2011-05-13 02:27:49 +00:00
check = ( Object [ ] ) ( Object ) target . invokeExact ( args [ 0 ] , args [ 1 ] ) ;
2010-01-08 00:16:45 +00:00
assertArrayEquals ( args , check ) ;
break ;
}
}
2012-01-19 01:34:29 +00:00
List < Class < ? > > newParams = new ArrayList < > ( target2 . type ( ) . parameterList ( ) ) ;
2010-01-08 00:16:45 +00:00
{ // modify newParams in place
List < Class < ? > > spreadParams = newParams . subList ( pos , nargs ) ;
2011-05-13 02:27:49 +00:00
spreadParams . clear ( ) ; spreadParams . add ( arrayType ) ;
2010-01-08 00:16:45 +00:00
}
2011-05-13 02:27:49 +00:00
MethodType newType = MethodType . methodType ( arrayType , newParams ) ;
MethodHandle result = target2 . asSpreader ( arrayType , nargs - pos ) ;
assert ( result . type ( ) = = newType ) : Arrays . asList ( result , newType ) ;
result = result . asType ( newType . generic ( ) ) ;
Object returnValue ;
2010-01-08 00:16:45 +00:00
if ( pos = = 0 ) {
2011-05-13 02:27:49 +00:00
Object args2 = ValueConversions . changeArrayType ( arrayType , Arrays . copyOfRange ( args , pos , args . length ) ) ;
returnValue = result . invokeExact ( args2 ) ;
2010-01-08 00:16:45 +00:00
} else {
Object [ ] args1 = Arrays . copyOfRange ( args , 0 , pos + 1 ) ;
2011-05-13 02:27:49 +00:00
args1 [ pos ] = ValueConversions . changeArrayType ( arrayType , Arrays . copyOfRange ( args , pos , args . length ) ) ;
returnValue = result . invokeWithArguments ( args1 ) ;
}
String argstr = Arrays . toString ( args ) ;
if ( ! argType . isPrimitive ( ) ) {
Object [ ] rv = ( Object [ ] ) returnValue ;
String rvs = Arrays . toString ( rv ) ;
if ( ! Arrays . equals ( args , rv ) ) {
System . out . println ( " method: " + result ) ;
System . out . println ( " expected: " + argstr ) ;
System . out . println ( " returned: " + rvs ) ;
assertArrayEquals ( args , rv ) ;
}
} else if ( argType = = int . class ) {
String rvs = Arrays . toString ( ( int [ ] ) returnValue ) ;
if ( ! argstr . equals ( rvs ) ) {
System . out . println ( " method: " + result ) ;
System . out . println ( " expected: " + argstr ) ;
System . out . println ( " returned: " + rvs ) ;
assertEquals ( argstr , rvs ) ;
}
} else if ( argType = = long . class ) {
String rvs = Arrays . toString ( ( long [ ] ) returnValue ) ;
if ( ! argstr . equals ( rvs ) ) {
System . out . println ( " method: " + result ) ;
System . out . println ( " expected: " + argstr ) ;
System . out . println ( " returned: " + rvs ) ;
assertEquals ( argstr , rvs ) ;
}
} else {
// cannot test...
2010-01-08 00:16:45 +00:00
}
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2010-01-08 00:16:45 +00:00
public void testCollectArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " collectArguments " ) ;
2012-01-19 01:34:29 +00:00
for ( Class < ? > argType : new Class < ? > [ ] { Object . class , Integer . class , int . class } ) {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " collectArguments " + argType ) ;
2011-07-16 22:44:33 +00:00
for ( int nargs = 0 ; nargs < 50 ; nargs + + ) {
2012-07-24 17:47:44 +00:00
if ( CAN_TEST_LIGHTLY & & nargs > 11 ) break ;
2011-07-16 22:44:33 +00:00
for ( int pos = 0 ; pos < = nargs ; pos + + ) {
if ( CAN_TEST_LIGHTLY & & pos > 2 & & pos < nargs - 2 ) continue ;
if ( nargs > 10 & & pos > 4 & & pos < nargs - 4 & & pos % 10 ! = 3 )
continue ;
2010-01-08 00:16:45 +00:00
testCollectArguments ( argType , pos , nargs ) ;
}
}
}
}
public void testCollectArguments ( Class < ? > argType , int pos , int nargs ) throws Throwable {
countTest ( ) ;
// fake up a MH with the same type as the desired adapter:
2011-02-11 09:26:28 +00:00
MethodHandle fake = varargsArray ( nargs ) ;
2010-01-08 00:16:45 +00:00
fake = changeArgTypes ( fake , argType ) ;
MethodType newType = fake . type ( ) ;
Object [ ] args = randomArgs ( newType . parameterArray ( ) ) ;
// here is what should happen:
Object [ ] collectedArgs = Arrays . copyOfRange ( args , 0 , pos + 1 ) ;
collectedArgs [ pos ] = Arrays . copyOfRange ( args , pos , args . length ) ;
// here is the MH which will witness the collected argument tail:
2011-02-11 09:26:28 +00:00
MethodHandle target = varargsArray ( pos + 1 ) ;
2010-01-08 00:16:45 +00:00
target = changeArgTypes ( target , 0 , pos , argType ) ;
target = changeArgTypes ( target , pos , pos + 1 , Object [ ] . class ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " collect from " + Arrays . asList ( args ) + " [ " + pos + " .. " + nargs + " ] " ) ;
2011-02-11 09:26:28 +00:00
MethodHandle result = target . asCollector ( Object [ ] . class , nargs - pos ) . asType ( newType ) ;
2010-10-31 04:08:23 +00:00
Object [ ] returnValue = ( Object [ ] ) result . invokeWithArguments ( args ) ;
2010-01-08 00:16:45 +00:00
// assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]);
// returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]);
// collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]);
assertArrayEquals ( collectedArgs , returnValue ) ;
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2009-10-22 06:19:48 +00:00
public void testInsertArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " insertArguments " ) ;
2011-07-16 22:44:33 +00:00
for ( int nargs = 0 ; nargs < 50 ; nargs + + ) {
2012-07-24 17:47:44 +00:00
if ( CAN_TEST_LIGHTLY & & nargs > 11 ) break ;
2011-07-16 22:44:33 +00:00
for ( int ins = 0 ; ins < = nargs ; ins + + ) {
if ( nargs > 10 & & ins > 4 & & ins < nargs - 4 & & ins % 10 ! = 3 )
continue ;
2009-10-22 06:19:48 +00:00
for ( int pos = 0 ; pos < = nargs ; pos + + ) {
2011-07-16 22:44:33 +00:00
if ( nargs > 10 & & pos > 4 & & pos < nargs - 4 & & pos % 10 ! = 3 )
continue ;
if ( CAN_TEST_LIGHTLY & & pos > 2 & & pos < nargs - 2 ) continue ;
2009-10-22 06:19:48 +00:00
testInsertArguments ( nargs , pos , ins ) ;
}
}
}
}
void testInsertArguments ( int nargs , int pos , int ins ) throws Throwable {
countTest ( ) ;
2011-02-11 09:26:28 +00:00
MethodHandle target = varargsArray ( nargs + ins ) ;
2009-10-22 06:19:48 +00:00
Object [ ] args = randomArgs ( target . type ( ) . parameterArray ( ) ) ;
List < Object > resList = Arrays . asList ( args ) ;
2012-01-19 01:34:29 +00:00
List < Object > argsToPass = new ArrayList < > ( resList ) ;
2009-10-22 06:19:48 +00:00
List < Object > argsToInsert = argsToPass . subList ( pos , pos + ins ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2012-07-24 17:47:44 +00:00
System . out . println ( " insert: " + argsToInsert + " @ " + pos + " into " + target ) ;
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " cast " ) // cast to spread Object... is helpful
2010-01-08 00:16:45 +00:00
MethodHandle target2 = MethodHandles . insertArguments ( target , pos ,
2012-01-19 01:34:29 +00:00
( Object [ ] /*...*/ ) argsToInsert . toArray ( ) ) ;
2009-10-22 06:19:48 +00:00
argsToInsert . clear ( ) ; // remove from argsToInsert
2010-10-31 04:08:23 +00:00
Object res2 = target2 . invokeWithArguments ( argsToPass ) ;
2009-10-22 06:19:48 +00:00
Object res2List = Arrays . asList ( ( Object [ ] ) res2 ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2009-10-22 06:19:48 +00:00
System . out . println ( " result: " + res2List ) ;
//if (!resList.equals(res2List))
// System.out.println("*** fail at n/p/i = "+nargs+"/"+pos+"/"+ins+": "+resList+" => "+res2List);
assertEquals ( resList , res2List ) ;
}
2011-02-11 09:26:28 +00:00
@Test
public void testFilterReturnValue ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " filterReturnValue " ) ;
Class < ? > classOfVCList = varargsList ( 1 ) . invokeWithArguments ( 0 ) . getClass ( ) ;
assertTrue ( List . class . isAssignableFrom ( classOfVCList ) ) ;
for ( int nargs = 0 ; nargs < = 3 ; nargs + + ) {
2012-01-19 01:34:29 +00:00
for ( Class < ? > rtype : new Class < ? > [ ] { Object . class ,
2011-02-11 09:26:28 +00:00
List . class ,
int . class ,
2011-07-16 22:44:33 +00:00
byte . class ,
long . class ,
2011-02-11 09:26:28 +00:00
CharSequence . class ,
String . class } ) {
testFilterReturnValue ( nargs , rtype ) ;
}
}
}
void testFilterReturnValue ( int nargs , Class < ? > rtype ) throws Throwable {
countTest ( ) ;
MethodHandle target = varargsList ( nargs , rtype ) ;
MethodHandle filter ;
if ( List . class . isAssignableFrom ( rtype ) | | rtype . isAssignableFrom ( List . class ) )
filter = varargsList ( 1 ) ; // add another layer of list-ness
else
filter = MethodHandles . identity ( rtype ) ;
filter = filter . asType ( MethodType . methodType ( target . type ( ) . returnType ( ) , rtype ) ) ;
Object [ ] argsToPass = randomArgs ( nargs , Object . class ) ;
if ( verbosity > = 3 )
System . out . println ( " filter " + target + " to " + rtype . getSimpleName ( ) + " with " + filter ) ;
MethodHandle target2 = MethodHandles . filterReturnValue ( target , filter ) ;
if ( verbosity > = 4 )
System . out . println ( " filtered target: " + target2 ) ;
// Simulate expected effect of filter on return value:
Object unfiltered = target . invokeWithArguments ( argsToPass ) ;
Object expected = filter . invokeWithArguments ( unfiltered ) ;
if ( verbosity > = 4 )
System . out . println ( " unfiltered: " + unfiltered + " : " + unfiltered . getClass ( ) . getSimpleName ( ) ) ;
if ( verbosity > = 4 )
System . out . println ( " expected: " + expected + " : " + expected . getClass ( ) . getSimpleName ( ) ) ;
Object result = target2 . invokeWithArguments ( argsToPass ) ;
if ( verbosity > = 3 )
System . out . println ( " result: " + result + " : " + result . getClass ( ) . getSimpleName ( ) ) ;
if ( ! expected . equals ( result ) )
System . out . println ( " *** fail at n/rt = " + nargs + " / " + rtype . getSimpleName ( ) + " : " + Arrays . asList ( argsToPass ) + " => " + result + " != " + expected ) ;
assertEquals ( expected , result ) ;
}
2010-01-08 00:16:45 +00:00
@Test
public void testFilterArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " filterArguments " ) ;
for ( int nargs = 1 ; nargs < = 6 ; nargs + + ) {
for ( int pos = 0 ; pos < nargs ; pos + + ) {
testFilterArguments ( nargs , pos ) ;
}
}
}
void testFilterArguments ( int nargs , int pos ) throws Throwable {
countTest ( ) ;
2011-02-11 09:26:28 +00:00
MethodHandle target = varargsList ( nargs ) ;
MethodHandle filter = varargsList ( 1 ) ;
2011-05-27 00:37:36 +00:00
filter = filter . asType ( filter . type ( ) . generic ( ) ) ;
2010-01-08 00:16:45 +00:00
Object [ ] argsToPass = randomArgs ( nargs , Object . class ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " filter " + target + " at " + pos + " with " + filter ) ;
2010-10-31 04:08:23 +00:00
MethodHandle target2 = MethodHandles . filterArguments ( target , pos , filter ) ;
2010-01-08 00:16:45 +00:00
// Simulate expected effect of filter on arglist:
Object [ ] filteredArgs = argsToPass . clone ( ) ;
2010-05-01 06:48:23 +00:00
filteredArgs [ pos ] = filter . invokeExact ( filteredArgs [ pos ] ) ;
2010-01-08 00:16:45 +00:00
List < Object > expected = Arrays . asList ( filteredArgs ) ;
2010-10-31 04:08:23 +00:00
Object result = target2 . invokeWithArguments ( argsToPass ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " result: " + result ) ;
if ( ! expected . equals ( result ) )
2011-02-11 09:26:28 +00:00
System . out . println ( " *** fail at n/p = " + nargs + " / " + pos + " : " + Arrays . asList ( argsToPass ) + " => " + result + " != " + expected ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( expected , result ) ;
}
@Test
public void testFoldArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " foldArguments " ) ;
for ( int nargs = 0 ; nargs < = 4 ; nargs + + ) {
for ( int fold = 0 ; fold < = nargs ; fold + + ) {
for ( int pos = 0 ; pos < = nargs ; pos + + ) {
testFoldArguments ( nargs , pos , fold ) ;
}
}
}
}
void testFoldArguments ( int nargs , int pos , int fold ) throws Throwable {
if ( pos ! = 0 ) return ; // can fold only at pos=0 for now
countTest ( ) ;
2011-02-11 09:26:28 +00:00
MethodHandle target = varargsList ( 1 + nargs ) ;
MethodHandle combine = varargsList ( fold ) . asType ( MethodType . genericMethodType ( fold ) ) ;
2010-01-08 00:16:45 +00:00
List < Object > argsToPass = Arrays . asList ( randomArgs ( nargs , Object . class ) ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " fold " + target + " with " + combine ) ;
MethodHandle target2 = MethodHandles . foldArguments ( target , combine ) ;
// Simulate expected effect of combiner on arglist:
2012-01-19 01:34:29 +00:00
List < Object > expected = new ArrayList < > ( argsToPass ) ;
2010-01-08 00:16:45 +00:00
List < Object > argsToFold = expected . subList ( pos , pos + fold ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " fold: " + argsToFold + " into " + target2 ) ;
2010-10-31 04:08:23 +00:00
Object foldedArgs = combine . invokeWithArguments ( argsToFold ) ;
2010-01-08 00:16:45 +00:00
argsToFold . add ( 0 , foldedArgs ) ;
2010-10-31 04:08:23 +00:00
Object result = target2 . invokeWithArguments ( argsToPass ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " result: " + result ) ;
if ( ! expected . equals ( result ) )
2011-02-11 09:26:28 +00:00
System . out . println ( " *** fail at n/p/f = " + nargs + " / " + pos + " / " + fold + " : " + argsToPass + " => " + result + " != " + expected ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( expected , result ) ;
}
@Test
public void testDropArguments ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " dropArguments " ) ;
for ( int nargs = 0 ; nargs < = 4 ; nargs + + ) {
for ( int drop = 1 ; drop < = 4 ; drop + + ) {
for ( int pos = 0 ; pos < = nargs ; pos + + ) {
testDropArguments ( nargs , pos , drop ) ;
}
}
}
}
void testDropArguments ( int nargs , int pos , int drop ) throws Throwable {
countTest ( ) ;
2011-02-11 09:26:28 +00:00
MethodHandle target = varargsArray ( nargs ) ;
2010-01-08 00:16:45 +00:00
Object [ ] args = randomArgs ( target . type ( ) . parameterArray ( ) ) ;
MethodHandle target2 = MethodHandles . dropArguments ( target , pos ,
2012-01-19 01:34:29 +00:00
Collections . nCopies ( drop , Object . class ) . toArray ( new Class < ? > [ 0 ] ) ) ;
2010-01-08 00:16:45 +00:00
List < Object > resList = Arrays . asList ( args ) ;
2012-01-19 01:34:29 +00:00
List < Object > argsToDrop = new ArrayList < > ( resList ) ;
2010-01-08 00:16:45 +00:00
for ( int i = drop ; i > 0 ; i - - ) {
argsToDrop . add ( pos , " blort# " + i ) ;
}
2010-10-31 04:08:23 +00:00
Object res2 = target2 . invokeWithArguments ( argsToDrop ) ;
2010-01-08 00:16:45 +00:00
Object res2List = Arrays . asList ( ( Object [ ] ) res2 ) ;
//if (!resList.equals(res2List))
// System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List);
assertEquals ( resList , res2List ) ;
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2010-01-08 00:16:45 +00:00
public void testInvokers ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker " ) ;
// exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker
2012-01-19 01:34:29 +00:00
Set < MethodType > done = new HashSet < > ( ) ;
2010-01-08 00:16:45 +00:00
for ( int i = 0 ; i < = 6 ; i + + ) {
2011-07-16 22:44:33 +00:00
if ( CAN_TEST_LIGHTLY & & i > 3 ) break ;
2010-01-08 00:16:45 +00:00
MethodType gtype = MethodType . genericMethodType ( i ) ;
2012-01-19 01:34:29 +00:00
for ( Class < ? > argType : new Class < ? > [ ] { Object . class , Integer . class , int . class } ) {
2010-01-08 00:16:45 +00:00
for ( int j = - 1 ; j < i ; j + + ) {
MethodType type = gtype ;
if ( j < 0 )
type = type . changeReturnType ( argType ) ;
else if ( argType = = void . class )
continue ;
else
type = type . changeParameterType ( j , argType ) ;
if ( done . add ( type ) )
testInvokers ( type ) ;
MethodType vtype = type . changeReturnType ( void . class ) ;
if ( done . add ( vtype ) )
testInvokers ( vtype ) ;
}
}
}
}
public void testInvokers ( MethodType type ) throws Throwable {
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( " test invokers for " + type ) ;
int nargs = type . parameterCount ( ) ;
boolean testRetCode = type . returnType ( ) ! = void . class ;
MethodHandle target = PRIVATE . findStatic ( MethodHandlesTest . class , " invokee " ,
MethodType . genericMethodType ( 0 , true ) ) ;
2011-02-11 09:26:28 +00:00
assertTrue ( target . isVarargsCollector ( ) ) ;
target = target . asType ( type ) ;
2010-01-08 00:16:45 +00:00
Object [ ] args = randomArgs ( type . parameterArray ( ) ) ;
2012-01-19 01:34:29 +00:00
List < Object > targetPlusArgs = new ArrayList < > ( Arrays . asList ( args ) ) ;
2010-01-08 00:16:45 +00:00
targetPlusArgs . add ( 0 , target ) ;
int code = ( Integer ) invokee ( args ) ;
Object log = logEntry ( " invokee " , args ) ;
assertEquals ( log . hashCode ( ) , code ) ;
assertCalled ( " invokee " , args ) ;
MethodHandle inv ;
Object result ;
// exact invoker
countTest ( ) ;
calledLog . clear ( ) ;
inv = MethodHandles . exactInvoker ( type ) ;
2010-10-31 04:08:23 +00:00
result = inv . invokeWithArguments ( targetPlusArgs ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
// generic invoker
countTest ( ) ;
2011-05-13 02:27:33 +00:00
inv = MethodHandles . invoker ( type ) ;
2011-05-27 00:37:36 +00:00
if ( nargs < = 3 & & type = = type . generic ( ) ) {
2010-01-08 00:16:45 +00:00
calledLog . clear ( ) ;
switch ( nargs ) {
case 0 :
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target ) ;
2010-01-08 00:16:45 +00:00
break ;
case 1 :
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] ) ;
2010-01-08 00:16:45 +00:00
break ;
case 2 :
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] , args [ 1 ] ) ;
2010-01-08 00:16:45 +00:00
break ;
case 3 :
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
2010-01-08 00:16:45 +00:00
break ;
}
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
calledLog . clear ( ) ;
2010-10-31 04:08:23 +00:00
result = inv . invokeWithArguments ( targetPlusArgs ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
// varargs invoker #0
calledLog . clear ( ) ;
2011-02-11 09:26:24 +00:00
inv = MethodHandles . spreadInvoker ( type , 0 ) ;
2011-05-27 00:37:36 +00:00
if ( type . returnType ( ) = = Object . class ) {
result = inv . invokeExact ( target , args ) ;
} else if ( type . returnType ( ) = = void . class ) {
result = null ; inv . invokeExact ( target , args ) ;
} else {
result = inv . invokeWithArguments ( target , ( Object ) args ) ;
}
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
2011-05-27 00:37:36 +00:00
if ( nargs > = 1 & & type = = type . generic ( ) ) {
2010-01-08 00:16:45 +00:00
// varargs invoker #1
calledLog . clear ( ) ;
2011-02-11 09:26:24 +00:00
inv = MethodHandles . spreadInvoker ( type , 1 ) ;
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] , Arrays . copyOfRange ( args , 1 , nargs ) ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
2011-05-27 00:37:36 +00:00
if ( nargs > = 2 & & type = = type . generic ( ) ) {
2010-01-08 00:16:45 +00:00
// varargs invoker #2
calledLog . clear ( ) ;
2011-02-11 09:26:24 +00:00
inv = MethodHandles . spreadInvoker ( type , 2 ) ;
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] , args [ 1 ] , Arrays . copyOfRange ( args , 2 , nargs ) ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
2011-05-27 00:37:36 +00:00
if ( nargs > = 3 & & type = = type . generic ( ) ) {
2010-01-08 00:16:45 +00:00
// varargs invoker #3
calledLog . clear ( ) ;
2011-02-11 09:26:24 +00:00
inv = MethodHandles . spreadInvoker ( type , 3 ) ;
2010-05-01 06:48:23 +00:00
result = inv . invokeExact ( target , args [ 0 ] , args [ 1 ] , args [ 2 ] , Arrays . copyOfRange ( args , 3 , nargs ) ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
for ( int k = 0 ; k < = nargs ; k + + ) {
// varargs invoker #0..N
2011-07-16 22:44:33 +00:00
if ( CAN_TEST_LIGHTLY & & ( k > 1 | | k < nargs - 1 ) ) continue ;
2010-01-08 00:16:45 +00:00
countTest ( ) ;
calledLog . clear ( ) ;
2011-02-11 09:26:24 +00:00
inv = MethodHandles . spreadInvoker ( type , k ) ;
2011-05-27 00:37:36 +00:00
MethodType expType = ( type . dropParameterTypes ( k , nargs )
. appendParameterTypes ( Object [ ] . class )
. insertParameterTypes ( 0 , MethodHandle . class ) ) ;
assertEquals ( expType , inv . type ( ) ) ;
2012-01-19 01:34:29 +00:00
List < Object > targetPlusVarArgs = new ArrayList < > ( targetPlusArgs ) ;
2010-01-08 00:16:45 +00:00
List < Object > tailList = targetPlusVarArgs . subList ( 1 + k , 1 + nargs ) ;
Object [ ] tail = tailList . toArray ( ) ;
tailList . clear ( ) ; tailList . add ( tail ) ;
2010-10-31 04:08:23 +00:00
result = inv . invokeWithArguments ( targetPlusVarArgs ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
2010-10-31 04:08:23 +00:00
2010-01-08 00:16:45 +00:00
// dynamic invoker
countTest ( ) ;
2010-12-16 23:59:27 +00:00
CallSite site = new MutableCallSite ( type ) ;
inv = site . dynamicInvoker ( ) ;
2010-10-31 04:08:23 +00:00
// see if we get the result of the original target:
try {
result = inv . invokeWithArguments ( args ) ;
assertTrue ( " should not reach here " , false ) ;
} catch ( IllegalStateException ex ) {
String msg = ex . getMessage ( ) ;
assertTrue ( msg , msg . contains ( " site " ) ) ;
}
// set new target after invoker is created, to make sure we track target
2010-01-08 00:16:45 +00:00
site . setTarget ( target ) ;
calledLog . clear ( ) ;
2010-10-31 04:08:23 +00:00
result = inv . invokeWithArguments ( args ) ;
2010-01-08 00:16:45 +00:00
if ( testRetCode ) assertEquals ( code , result ) ;
assertCalled ( " invokee " , args ) ;
}
static Object invokee ( Object . . . args ) {
return called ( " invokee " , args ) . hashCode ( ) ;
}
2009-10-22 06:19:48 +00:00
private static final String MISSING_ARG = " missingArg " ;
2011-07-16 22:44:33 +00:00
private static final String MISSING_ARG_2 = " missingArg#2 " ;
2009-10-22 06:19:48 +00:00
static Object targetIfEquals ( ) {
return called ( " targetIfEquals " ) ;
}
static Object fallbackIfNotEquals ( ) {
return called ( " fallbackIfNotEquals " ) ;
}
static Object targetIfEquals ( Object x ) {
assertEquals ( x , MISSING_ARG ) ;
return called ( " targetIfEquals " , x ) ;
}
static Object fallbackIfNotEquals ( Object x ) {
assertFalse ( x . toString ( ) , x . equals ( MISSING_ARG ) ) ;
return called ( " fallbackIfNotEquals " , x ) ;
}
static Object targetIfEquals ( Object x , Object y ) {
assertEquals ( x , y ) ;
return called ( " targetIfEquals " , x , y ) ;
}
static Object fallbackIfNotEquals ( Object x , Object y ) {
assertFalse ( x . toString ( ) , x . equals ( y ) ) ;
return called ( " fallbackIfNotEquals " , x , y ) ;
}
static Object targetIfEquals ( Object x , Object y , Object z ) {
assertEquals ( x , y ) ;
return called ( " targetIfEquals " , x , y , z ) ;
}
static Object fallbackIfNotEquals ( Object x , Object y , Object z ) {
assertFalse ( x . toString ( ) , x . equals ( y ) ) ;
return called ( " fallbackIfNotEquals " , x , y , z ) ;
}
2010-01-08 00:16:45 +00:00
@Test
public void testGuardWithTest ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " guardWithTest " ) ;
2011-07-16 22:44:33 +00:00
for ( int nargs = 0 ; nargs < = 50 ; nargs + + ) {
if ( CAN_TEST_LIGHTLY & & nargs > 7 ) break ;
2010-01-08 00:16:45 +00:00
testGuardWithTest ( nargs , Object . class ) ;
testGuardWithTest ( nargs , String . class ) ;
}
}
void testGuardWithTest ( int nargs , Class < ? > argClass ) throws Throwable {
2011-07-16 22:44:33 +00:00
testGuardWithTest ( nargs , 0 , argClass ) ;
if ( nargs < = 5 | | nargs % 10 = = 3 ) {
for ( int testDrops = 1 ; testDrops < = nargs ; testDrops + + )
testGuardWithTest ( nargs , testDrops , argClass ) ;
}
}
void testGuardWithTest ( int nargs , int testDrops , Class < ? > argClass ) throws Throwable {
2010-01-08 00:16:45 +00:00
countTest ( ) ;
2011-07-16 22:44:33 +00:00
int nargs1 = Math . min ( 3 , nargs ) ;
2010-01-08 00:16:45 +00:00
MethodHandle test = PRIVATE . findVirtual ( Object . class , " equals " , MethodType . methodType ( boolean . class , Object . class ) ) ;
2011-07-16 22:44:33 +00:00
MethodHandle target = PRIVATE . findStatic ( MethodHandlesTest . class , " targetIfEquals " , MethodType . genericMethodType ( nargs1 ) ) ;
MethodHandle fallback = PRIVATE . findStatic ( MethodHandlesTest . class , " fallbackIfNotEquals " , MethodType . genericMethodType ( nargs1 ) ) ;
2010-01-08 00:16:45 +00:00
while ( test . type ( ) . parameterCount ( ) > nargs )
2011-07-16 22:44:33 +00:00
// 0: test = constant(MISSING_ARG.equals(MISSING_ARG))
// 1: test = lambda (_) MISSING_ARG.equals(_)
2010-01-08 00:16:45 +00:00
test = MethodHandles . insertArguments ( test , 0 , MISSING_ARG ) ;
if ( argClass ! = Object . class ) {
test = changeArgTypes ( test , argClass ) ;
target = changeArgTypes ( target , argClass ) ;
fallback = changeArgTypes ( fallback , argClass ) ;
}
2011-07-16 22:44:33 +00:00
int testArgs = nargs - testDrops ;
assert ( testArgs > = 0 ) ;
test = addTrailingArgs ( test , Math . min ( testArgs , nargs ) , argClass ) ;
target = addTrailingArgs ( target , nargs , argClass ) ;
fallback = addTrailingArgs ( fallback , nargs , argClass ) ;
2010-01-08 00:16:45 +00:00
Object [ ] [ ] argLists = {
{ } ,
{ " foo " } , { MISSING_ARG } ,
{ " foo " , " foo " } , { " foo " , " bar " } ,
{ " foo " , " foo " , " baz " } , { " foo " , " bar " , " baz " }
} ;
for ( Object [ ] argList : argLists ) {
2011-07-16 22:44:33 +00:00
Object [ ] argList1 = argList ;
if ( argList . length ! = nargs ) {
if ( argList . length ! = nargs1 ) continue ;
argList1 = Arrays . copyOf ( argList , nargs ) ;
Arrays . fill ( argList1 , nargs1 , nargs , MISSING_ARG_2 ) ;
}
MethodHandle test1 = test ;
if ( test1 . type ( ) . parameterCount ( ) > testArgs ) {
int pc = test1 . type ( ) . parameterCount ( ) ;
test1 = MethodHandles . insertArguments ( test , testArgs , Arrays . copyOfRange ( argList1 , testArgs , pc ) ) ;
}
MethodHandle mh = MethodHandles . guardWithTest ( test1 , target , fallback ) ;
assertEquals ( target . type ( ) , mh . type ( ) ) ;
2010-01-08 00:16:45 +00:00
boolean equals ;
switch ( nargs ) {
case 0 : equals = true ; break ;
case 1 : equals = MISSING_ARG . equals ( argList [ 0 ] ) ; break ;
default : equals = argList [ 0 ] . equals ( argList [ 1 ] ) ; break ;
}
String willCall = ( equals ? " targetIfEquals " : " fallbackIfNotEquals " ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2010-01-08 00:16:45 +00:00
System . out . println ( logEntry ( willCall , argList ) ) ;
2011-07-16 22:44:33 +00:00
Object result = mh . invokeWithArguments ( argList1 ) ;
2010-01-08 00:16:45 +00:00
assertCalled ( willCall , argList ) ;
}
}
@Test
public void testCatchException ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " catchException " ) ;
2011-07-16 22:44:33 +00:00
for ( int nargs = 0 ; nargs < 40 ; nargs + + ) {
2012-07-24 17:47:44 +00:00
if ( CAN_TEST_LIGHTLY & & nargs > 11 ) break ;
2011-07-16 22:44:33 +00:00
for ( int throwMode = 0 ; throwMode < THROW_MODE_LIMIT ; throwMode + + ) {
testCatchException ( int . class , new ClassCastException ( " testing " ) , throwMode , nargs ) ;
if ( CAN_TEST_LIGHTLY & & nargs > 3 ) continue ;
testCatchException ( void . class , new java . io . IOException ( " testing " ) , throwMode , nargs ) ;
testCatchException ( String . class , new LinkageError ( " testing " ) , throwMode , nargs ) ;
2010-01-08 00:16:45 +00:00
}
}
}
2011-07-16 22:44:33 +00:00
static final int THROW_NOTHING = 0 , THROW_CAUGHT = 1 , THROW_UNCAUGHT = 2 , THROW_THROUGH_ADAPTER = 3 , THROW_MODE_LIMIT = 4 ;
void testCatchException ( Class < ? > returnType , Throwable thrown , int throwMode , int nargs ) throws Throwable {
testCatchException ( returnType , thrown , throwMode , nargs , 0 ) ;
if ( nargs < = 5 | | nargs % 10 = = 3 ) {
for ( int catchDrops = 1 ; catchDrops < = nargs ; catchDrops + + )
testCatchException ( returnType , thrown , throwMode , nargs , catchDrops ) ;
}
}
2010-01-08 00:16:45 +00:00
private static < T extends Throwable >
Object throwOrReturn ( Object normal , T exception ) throws T {
2011-07-16 22:44:33 +00:00
if ( exception ! = null ) {
called ( " throwOrReturn/throw " , normal , exception ) ;
throw exception ;
}
called ( " throwOrReturn/normal " , normal , exception ) ;
2010-01-08 00:16:45 +00:00
return normal ;
}
2011-07-16 22:44:33 +00:00
private int fakeIdentityCount ;
private Object fakeIdentity ( Object x ) {
System . out . println ( " should throw through this! " ) ;
fakeIdentityCount + + ;
return x ;
}
2010-01-08 00:16:45 +00:00
2011-07-16 22:44:33 +00:00
void testCatchException ( Class < ? > returnType , Throwable thrown , int throwMode , int nargs , int catchDrops ) throws Throwable {
2010-01-08 00:16:45 +00:00
countTest ( ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > = 3 )
2011-07-16 22:44:33 +00:00
System . out . println ( " catchException rt= " + returnType + " throw= " + throwMode + " nargs= " + nargs + " drops= " + catchDrops ) ;
2010-01-08 00:16:45 +00:00
Class < ? extends Throwable > exType = thrown . getClass ( ) ;
2011-07-16 22:44:33 +00:00
if ( throwMode > THROW_CAUGHT ) thrown = new UnsupportedOperationException ( " do not catch this " ) ;
2010-01-08 00:16:45 +00:00
MethodHandle throwOrReturn
= PRIVATE . findStatic ( MethodHandlesTest . class , " throwOrReturn " ,
MethodType . methodType ( Object . class , Object . class , Throwable . class ) ) ;
2011-07-16 22:44:33 +00:00
if ( throwMode = = THROW_THROUGH_ADAPTER ) {
MethodHandle fakeIdentity
= PRIVATE . findVirtual ( MethodHandlesTest . class , " fakeIdentity " ,
MethodType . methodType ( Object . class , Object . class ) ) . bindTo ( this ) ;
for ( int i = 0 ; i < 10 ; i + + )
throwOrReturn = MethodHandles . filterReturnValue ( throwOrReturn , fakeIdentity ) ;
}
int nargs1 = Math . max ( 2 , nargs ) ;
2010-12-16 23:59:27 +00:00
MethodHandle thrower = throwOrReturn . asType ( MethodType . genericMethodType ( 2 ) ) ;
2011-07-16 22:44:33 +00:00
thrower = addTrailingArgs ( thrower , nargs , Object . class ) ;
int catchArgc = 1 + nargs - catchDrops ;
MethodHandle catcher = varargsList ( catchArgc ) . asType ( MethodType . genericMethodType ( catchArgc ) ) ;
Object [ ] args = randomArgs ( nargs , Object . class ) ;
Object arg0 = MISSING_ARG ;
Object arg1 = ( throwMode = = THROW_NOTHING ) ? ( Throwable ) null : thrown ;
if ( nargs > 0 ) arg0 = args [ 0 ] ;
if ( nargs > 1 ) args [ 1 ] = arg1 ;
assertEquals ( nargs1 , thrower . type ( ) . parameterCount ( ) ) ;
if ( nargs < nargs1 ) {
Object [ ] appendArgs = { arg0 , arg1 } ;
appendArgs = Arrays . copyOfRange ( appendArgs , nargs , nargs1 ) ;
thrower = MethodHandles . insertArguments ( thrower , nargs , appendArgs ) ;
}
assertEquals ( nargs , thrower . type ( ) . parameterCount ( ) ) ;
MethodHandle target = MethodHandles . catchException ( thrower , exType , catcher ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( thrower . type ( ) , target . type ( ) ) ;
2011-07-16 22:44:33 +00:00
assertEquals ( nargs , target . type ( ) . parameterCount ( ) ) ;
2010-01-08 00:16:45 +00:00
//System.out.println("catching with "+target+" : "+throwOrReturn);
2011-07-16 22:44:33 +00:00
Object returned ;
try {
returned = target . invokeWithArguments ( args ) ;
} catch ( Throwable ex ) {
assertSame ( " must get the out-of-band exception " , thrown , ex ) ;
if ( throwMode < = THROW_CAUGHT )
assertEquals ( THROW_UNCAUGHT , throwMode ) ;
returned = ex ;
}
assertCalled ( " throwOrReturn/ " + ( throwMode = = THROW_NOTHING ? " normal " : " throw " ) , arg0 , arg1 ) ;
2010-01-08 00:16:45 +00:00
//System.out.println("return from "+target+" : "+returned);
2011-07-16 22:44:33 +00:00
if ( throwMode = = THROW_NOTHING ) {
assertSame ( arg0 , returned ) ;
} else if ( throwMode = = THROW_CAUGHT ) {
2012-01-19 01:34:29 +00:00
List < Object > catchArgs = new ArrayList < > ( Arrays . asList ( args ) ) ;
2011-07-16 22:44:33 +00:00
// catcher receives an initial subsequence of target arguments:
catchArgs . subList ( nargs - catchDrops , nargs ) . clear ( ) ;
// catcher also receives the exception, prepended:
2010-01-08 00:16:45 +00:00
catchArgs . add ( 0 , thrown ) ;
assertEquals ( catchArgs , returned ) ;
}
2011-07-16 22:44:33 +00:00
assertEquals ( 0 , fakeIdentityCount ) ;
2010-01-08 00:16:45 +00:00
}
@Test
public void testThrowException ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " throwException " ) ;
testThrowException ( int . class , new ClassCastException ( " testing " ) ) ;
testThrowException ( void . class , new java . io . IOException ( " testing " ) ) ;
testThrowException ( String . class , new LinkageError ( " testing " ) ) ;
}
void testThrowException ( Class < ? > returnType , Throwable thrown ) throws Throwable {
countTest ( ) ;
Class < ? extends Throwable > exType = thrown . getClass ( ) ;
MethodHandle target = MethodHandles . throwException ( returnType , exType ) ;
//System.out.println("throwing with "+target+" : "+thrown);
MethodType expectedType = MethodType . methodType ( returnType , exType ) ;
assertEquals ( expectedType , target . type ( ) ) ;
2011-05-27 00:37:36 +00:00
target = target . asType ( target . type ( ) . generic ( ) ) ;
2010-01-08 00:16:45 +00:00
Throwable caught = null ;
try {
2010-05-01 06:48:23 +00:00
Object res = target . invokeExact ( ( Object ) thrown ) ;
2010-01-08 00:16:45 +00:00
fail ( " got " + res + " instead of throwing " + thrown ) ;
} catch ( Throwable ex ) {
if ( ex ! = thrown ) {
if ( ex instanceof Error ) throw ( Error ) ex ;
if ( ex instanceof RuntimeException ) throw ( RuntimeException ) ex ;
}
caught = ex ;
}
assertSame ( thrown , caught ) ;
}
2012-08-17 20:42:25 +00:00
@Test
2011-07-16 22:44:33 +00:00
public void testInterfaceCast ( ) throws Throwable {
2012-07-24 17:47:44 +00:00
//if (CAN_SKIP_WORKING) return;
startTest ( " interfaceCast " ) ;
2012-08-17 20:42:25 +00:00
assert ( ( ( ( Object ) " foo " ) instanceof CharSequence ) ) ;
assert ( ! ( ( ( Object ) " foo " ) instanceof Iterable ) ) ;
for ( MethodHandle mh : new MethodHandle [ ] {
MethodHandles . identity ( String . class ) ,
MethodHandles . identity ( CharSequence . class ) ,
MethodHandles . identity ( Iterable . class )
} ) {
if ( verbosity > 0 ) System . out . println ( " -- mh = " + mh ) ;
for ( Class < ? > ctype : new Class < ? > [ ] {
Object . class , String . class , CharSequence . class ,
Number . class , Iterable . class
} ) {
if ( verbosity > 0 ) System . out . println ( " ---- ctype = " + ctype . getName ( ) ) ;
// doret docast
testInterfaceCast ( mh , ctype , false , false ) ;
testInterfaceCast ( mh , ctype , true , false ) ;
testInterfaceCast ( mh , ctype , false , true ) ;
testInterfaceCast ( mh , ctype , true , true ) ;
}
2011-07-16 22:44:33 +00:00
}
}
2012-08-17 20:42:25 +00:00
private static Class < ? > i2o ( Class < ? > c ) {
return ( c . isInterface ( ) ? Object . class : c ) ;
}
public void testInterfaceCast ( MethodHandle mh , Class < ? > ctype ,
boolean doret , boolean docast ) throws Throwable {
MethodHandle mh0 = mh ;
if ( verbosity > 1 )
System . out . println ( " mh= " + mh + " , ctype= " + ctype . getName ( ) + " , doret= " + doret + " , docast= " + docast ) ;
String normalRetVal = " normal return value " ;
2011-07-16 22:44:33 +00:00
MethodType mt = mh . type ( ) ;
2012-08-17 20:42:25 +00:00
MethodType mt0 = mt ;
2011-07-16 22:44:33 +00:00
if ( doret ) mt = mt . changeReturnType ( ctype ) ;
else mt = mt . changeParameterType ( 0 , ctype ) ;
if ( docast ) mh = MethodHandles . explicitCastArguments ( mh , mt ) ;
else mh = mh . asType ( mt ) ;
2012-08-17 20:42:25 +00:00
assertEquals ( mt , mh . type ( ) ) ;
MethodType mt1 = mt ;
2011-07-16 22:44:33 +00:00
// this bit is needed to make the interface types disappear for invokeWithArguments:
mh = MethodHandles . explicitCastArguments ( mh , mt . generic ( ) ) ;
2012-08-17 20:42:25 +00:00
Class < ? > [ ] step = {
mt1 . parameterType ( 0 ) , // param as passed to mh at first
mt0 . parameterType ( 0 ) , // param after incoming cast
mt0 . returnType ( ) , // return value before cast
mt1 . returnType ( ) , // return value after outgoing cast
} ;
// where might a checkCast occur?
boolean [ ] checkCast = new boolean [ step . length ] ;
// the string value must pass each step without causing an exception
if ( ! docast ) {
if ( ! doret ) {
if ( step [ 0 ] ! = step [ 1 ] )
checkCast [ 1 ] = true ; // incoming value is cast
} else {
if ( step [ 2 ] ! = step [ 3 ] )
checkCast [ 3 ] = true ; // outgoing value is cast
}
2011-07-16 22:44:33 +00:00
}
2012-08-17 20:42:25 +00:00
boolean expectFail = false ;
for ( int i = 0 ; i < step . length ; i + + ) {
Class < ? > c = step [ i ] ;
if ( ! checkCast [ i ] ) c = i2o ( c ) ;
if ( ! c . isInstance ( normalRetVal ) ) {
if ( verbosity > 3 )
System . out . println ( " expect failure at step " + i + " in " + Arrays . toString ( step ) + Arrays . toString ( checkCast ) ) ;
expectFail = true ;
break ;
}
}
countTest ( ! expectFail ) ;
if ( verbosity > 2 )
System . out . println ( " expectFail= " + expectFail + " , mt= " + mt ) ;
2011-07-16 22:44:33 +00:00
Object res ;
try {
2012-08-17 20:42:25 +00:00
res = mh . invokeWithArguments ( normalRetVal ) ;
2011-07-16 22:44:33 +00:00
} catch ( Exception ex ) {
res = ex ;
}
boolean sawFail = ! ( res instanceof String ) ;
if ( sawFail ! = expectFail ) {
2012-08-17 20:42:25 +00:00
System . out . println ( " *** testInterfaceCast: mh0 = " + mh0 ) ;
System . out . println ( " retype using " + ( docast ? " explicitCastArguments " : " asType " ) + " to " + mt + " => " + mh ) ;
System . out . println ( " call returned " + res ) ;
System . out . println ( " expected " + ( expectFail ? " an exception " : normalRetVal ) ) ;
}
if ( ! expectFail ) {
assertFalse ( res . toString ( ) , sawFail ) ;
assertEquals ( normalRetVal , res ) ;
2011-07-16 22:44:33 +00:00
} else {
2012-08-17 20:42:25 +00:00
assertTrue ( res . toString ( ) , sawFail ) ;
2011-07-16 22:44:33 +00:00
}
}
2012-07-24 17:47:44 +00:00
@Test // SLOW
2010-01-08 00:16:45 +00:00
public void testCastFailure ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " testCastFailure " ) ;
testCastFailure ( " cast/argument " , 11000 ) ;
2012-07-24 17:47:44 +00:00
if ( CAN_TEST_LIGHTLY ) return ;
2010-01-08 00:16:45 +00:00
testCastFailure ( " unbox/argument " , 11000 ) ;
testCastFailure ( " cast/return " , 11000 ) ;
testCastFailure ( " unbox/return " , 11000 ) ;
}
2010-10-31 04:08:23 +00:00
static class Surprise {
2010-09-09 01:40:23 +00:00
public MethodHandle asMethodHandle ( ) {
return VALUE . bindTo ( this ) ;
}
2010-01-08 00:16:45 +00:00
Object value ( Object x ) {
trace ( " value " , x ) ;
if ( boo ! = null ) return boo ;
return x ;
}
Object boo ;
void boo ( Object x ) { boo = x ; }
static void trace ( String x , Object y ) {
if ( verbosity > 8 ) System . out . println ( x + " = " + y ) ;
}
static Object refIdentity ( Object x ) { trace ( " ref.x " , x ) ; return x ; }
static Integer boxIdentity ( Integer x ) { trace ( " box.x " , x ) ; return x ; }
static int intIdentity ( int x ) { trace ( " int.x " , x ) ; return x ; }
2010-09-09 01:40:23 +00:00
static MethodHandle VALUE , REF_IDENTITY , BOX_IDENTITY , INT_IDENTITY ;
static {
try {
VALUE = PRIVATE . findVirtual (
Surprise . class , " value " ,
MethodType . methodType ( Object . class , Object . class ) ) ;
REF_IDENTITY = PRIVATE . findStatic (
Surprise . class , " refIdentity " ,
MethodType . methodType ( Object . class , Object . class ) ) ;
BOX_IDENTITY = PRIVATE . findStatic (
Surprise . class , " boxIdentity " ,
MethodType . methodType ( Integer . class , Integer . class ) ) ;
INT_IDENTITY = PRIVATE . findStatic (
Surprise . class , " intIdentity " ,
MethodType . methodType ( int . class , int . class ) ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchMethodException | IllegalAccessException ex ) {
2010-09-09 01:40:23 +00:00
throw new RuntimeException ( ex ) ;
}
}
2010-01-08 00:16:45 +00:00
}
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " ConvertToStringSwitch " )
2010-01-08 00:16:45 +00:00
void testCastFailure ( String mode , int okCount ) throws Throwable {
countTest ( false ) ;
2010-05-01 06:48:23 +00:00
if ( verbosity > 2 ) System . out . println ( " mode= " + mode ) ;
2010-01-08 00:16:45 +00:00
Surprise boo = new Surprise ( ) ;
2010-09-09 01:40:23 +00:00
MethodHandle identity = Surprise . REF_IDENTITY , surprise0 = boo . asMethodHandle ( ) , surprise = surprise0 ;
2010-01-08 00:16:45 +00:00
if ( mode . endsWith ( " /return " ) ) {
if ( mode . equals ( " unbox/return " ) ) {
// fail on return to ((Integer)surprise).intValue
2011-05-27 00:37:36 +00:00
surprise = surprise . asType ( MethodType . methodType ( int . class , Object . class ) ) ;
identity = identity . asType ( MethodType . methodType ( int . class , Object . class ) ) ;
2010-01-08 00:16:45 +00:00
} else if ( mode . equals ( " cast/return " ) ) {
// fail on return to (Integer)surprise
2011-05-27 00:37:36 +00:00
surprise = surprise . asType ( MethodType . methodType ( Integer . class , Object . class ) ) ;
identity = identity . asType ( MethodType . methodType ( Integer . class , Object . class ) ) ;
2010-01-08 00:16:45 +00:00
}
} else if ( mode . endsWith ( " /argument " ) ) {
MethodHandle callee = null ;
if ( mode . equals ( " unbox/argument " ) ) {
// fail on handing surprise to int argument
callee = Surprise . INT_IDENTITY ;
} else if ( mode . equals ( " cast/argument " ) ) {
// fail on handing surprise to Integer argument
callee = Surprise . BOX_IDENTITY ;
}
if ( callee ! = null ) {
2011-05-27 00:37:36 +00:00
callee = callee . asType ( MethodType . genericMethodType ( 1 ) ) ;
2010-10-31 04:08:23 +00:00
surprise = MethodHandles . filterArguments ( callee , 0 , surprise ) ;
identity = MethodHandles . filterArguments ( callee , 0 , identity ) ;
2010-01-08 00:16:45 +00:00
}
}
2010-09-09 01:40:23 +00:00
assertNotSame ( mode , surprise , surprise0 ) ;
2011-05-27 00:37:36 +00:00
identity = identity . asType ( MethodType . genericMethodType ( 1 ) ) ;
surprise = surprise . asType ( MethodType . genericMethodType ( 1 ) ) ;
2010-01-08 00:16:45 +00:00
Object x = 42 ;
for ( int i = 0 ; i < okCount ; i + + ) {
2010-05-01 06:48:23 +00:00
Object y = identity . invokeExact ( x ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( x , y ) ;
2010-05-01 06:48:23 +00:00
Object z = surprise . invokeExact ( x ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( x , z ) ;
}
boo . boo ( " Boo! " ) ;
2010-05-01 06:48:23 +00:00
Object y = identity . invokeExact ( x ) ;
2010-01-08 00:16:45 +00:00
assertEquals ( x , y ) ;
try {
2010-05-01 06:48:23 +00:00
Object z = surprise . invokeExact ( x ) ;
2010-01-08 00:16:45 +00:00
System . out . println ( " Failed to throw; got z= " + z ) ;
assertTrue ( false ) ;
2011-05-13 02:27:49 +00:00
} catch ( ClassCastException ex ) {
2010-01-08 00:16:45 +00:00
if ( verbosity > 2 )
2010-05-01 06:48:23 +00:00
System . out . println ( " caught " + ex ) ;
if ( verbosity > 3 )
2012-07-24 17:47:44 +00:00
ex . printStackTrace ( System . out ) ;
2011-05-13 02:27:49 +00:00
assertTrue ( true ) ; // all is well
2010-01-08 00:16:45 +00:00
}
}
2010-05-04 06:32:47 +00:00
static Example userMethod ( Object o , String s , int i ) {
called ( " userMethod " , o , s , i ) ;
return null ;
}
@Test
public void testUserClassInSignature ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " testUserClassInSignature " ) ;
Lookup lookup = MethodHandles . lookup ( ) ;
String name ; MethodType mt ; MethodHandle mh ;
Object [ ] args ;
// Try a static method.
name = " userMethod " ;
mt = MethodType . methodType ( Example . class , Object . class , String . class , int . class ) ;
mh = lookup . findStatic ( lookup . lookupClass ( ) , name , mt ) ;
assertEquals ( mt , mh . type ( ) ) ;
assertEquals ( Example . class , mh . type ( ) . returnType ( ) ) ;
args = randomArgs ( mh . type ( ) . parameterArray ( ) ) ;
2010-10-31 04:08:23 +00:00
mh . invokeWithArguments ( args ) ;
2010-05-04 06:32:47 +00:00
assertCalled ( name , args ) ;
// Try a virtual method.
name = " v2 " ;
mt = MethodType . methodType ( Object . class , Object . class , int . class ) ;
mh = lookup . findVirtual ( Example . class , name , mt ) ;
assertEquals ( mt , mh . type ( ) . dropParameterTypes ( 0 , 1 ) ) ;
assertTrue ( mh . type ( ) . parameterList ( ) . contains ( Example . class ) ) ;
args = randomArgs ( mh . type ( ) . parameterArray ( ) ) ;
2010-10-31 04:08:23 +00:00
mh . invokeWithArguments ( args ) ;
2010-05-04 06:32:47 +00:00
assertCalled ( name , args ) ;
}
2010-09-09 01:40:34 +00:00
static void runForRunnable ( ) {
called ( " runForRunnable " ) ;
}
2011-06-15 05:47:12 +00:00
public interface Fooable {
2011-07-16 22:44:33 +00:00
// overloads:
2012-01-19 01:34:29 +00:00
Object foo ( Object x , String y ) ;
List < ? > foo ( String x , int y ) ;
Object foo ( String x ) ;
2010-09-09 01:40:34 +00:00
}
2011-07-16 22:44:33 +00:00
static Object fooForFooable ( String x , Object . . . y ) {
return called ( " fooForFooable/ " + x , y ) ;
2010-09-09 01:40:34 +00:00
}
2012-01-19 01:34:29 +00:00
@SuppressWarnings ( " serial " ) // not really a public API, just a test case
2011-06-15 05:47:12 +00:00
public static class MyCheckedException extends Exception {
2010-09-09 01:40:34 +00:00
}
2011-06-15 05:47:12 +00:00
public interface WillThrow {
2010-09-09 01:40:34 +00:00
void willThrow ( ) throws MyCheckedException ;
}
2011-07-16 22:44:33 +00:00
/*non-public*/ interface PrivateRunnable {
public void run ( ) ;
}
2010-09-09 01:40:34 +00:00
@Test
2011-07-16 22:44:33 +00:00
public void testAsInterfaceInstance ( ) throws Throwable {
2010-09-09 01:40:34 +00:00
if ( CAN_SKIP_WORKING ) return ;
2012-07-24 17:47:44 +00:00
startTest ( " asInterfaceInstance " ) ;
2010-09-09 01:40:34 +00:00
Lookup lookup = MethodHandles . lookup ( ) ;
2011-07-16 22:44:33 +00:00
// test typical case: Runnable.run
2010-09-09 01:40:34 +00:00
{
2011-07-16 22:44:33 +00:00
countTest ( ) ;
if ( verbosity > = 2 ) System . out . println ( " Runnable " ) ;
2010-09-09 01:40:34 +00:00
MethodType mt = MethodType . methodType ( void . class ) ;
MethodHandle mh = lookup . findStatic ( MethodHandlesTest . class , " runForRunnable " , mt ) ;
2011-05-27 00:37:36 +00:00
Runnable proxy = MethodHandleProxies . asInterfaceInstance ( Runnable . class , mh ) ;
2010-09-09 01:40:34 +00:00
proxy . run ( ) ;
assertCalled ( " runForRunnable " ) ;
}
2011-07-16 22:44:33 +00:00
// well known single-name overloaded interface: Appendable.append
{
countTest ( ) ;
if ( verbosity > = 2 ) System . out . println ( " Appendable " ) ;
2012-01-19 01:34:29 +00:00
ArrayList < List < ? > > appendResults = new ArrayList < > ( ) ;
2011-07-16 22:44:33 +00:00
MethodHandle append = lookup . bind ( appendResults , " add " , MethodType . methodType ( boolean . class , Object . class ) ) ;
append = append . asType ( MethodType . methodType ( void . class , List . class ) ) ; // specialize the type
MethodHandle asList = lookup . findStatic ( Arrays . class , " asList " , MethodType . methodType ( List . class , Object [ ] . class ) ) ;
MethodHandle mh = MethodHandles . filterReturnValue ( asList , append ) . asVarargsCollector ( Object [ ] . class ) ;
Appendable proxy = MethodHandleProxies . asInterfaceInstance ( Appendable . class , mh ) ;
proxy . append ( " one " ) ;
proxy . append ( " two " , 3 , 4 ) ;
proxy . append ( '5' ) ;
assertEquals ( Arrays . asList ( Arrays . asList ( " one " ) ,
Arrays . asList ( " two " , 3 , 4 ) ,
Arrays . asList ( '5' ) ) ,
appendResults ) ;
if ( verbosity > = 3 ) System . out . println ( " appendResults= " + appendResults ) ;
appendResults . clear ( ) ;
Formatter formatter = new Formatter ( proxy ) ;
String fmt = " foo str=%s char='%c' num=%d " ;
Object [ ] fmtArgs = { " str! " , 'C' , 42 } ;
String expect = String . format ( fmt , fmtArgs ) ;
formatter . format ( fmt , fmtArgs ) ;
String actual = " " ;
if ( verbosity > = 3 ) System . out . println ( " appendResults= " + appendResults ) ;
2012-01-19 01:34:29 +00:00
for ( List < ? > l : appendResults ) {
2011-07-16 22:44:33 +00:00
Object x = l . get ( 0 ) ;
switch ( l . size ( ) ) {
case 1 : actual + = x ; continue ;
2012-01-19 01:34:29 +00:00
case 3 : actual + = ( ( String ) x ) . substring ( ( int ) ( Object ) l . get ( 1 ) , ( int ) ( Object ) l . get ( 2 ) ) ; continue ;
2011-07-16 22:44:33 +00:00
}
actual + = l ;
}
if ( verbosity > = 3 ) System . out . println ( " expect= " + expect ) ;
if ( verbosity > = 3 ) System . out . println ( " actual= " + actual ) ;
assertEquals ( expect , actual ) ;
}
// test case of an single name which is overloaded: Fooable.foo(...)
2010-09-09 01:40:34 +00:00
{
2011-07-16 22:44:33 +00:00
if ( verbosity > = 2 ) System . out . println ( " Fooable " ) ;
MethodHandle mh = lookup . findStatic ( MethodHandlesTest . class , " fooForFooable " ,
MethodType . methodType ( Object . class , String . class , Object [ ] . class ) ) ;
2011-05-27 00:37:36 +00:00
Fooable proxy = MethodHandleProxies . asInterfaceInstance ( Fooable . class , mh ) ;
2011-07-16 22:44:33 +00:00
for ( Method m : Fooable . class . getDeclaredMethods ( ) ) {
countTest ( ) ;
assertSame ( " foo " , m . getName ( ) ) ;
if ( verbosity > 3 )
System . out . println ( " calling " + m ) ;
MethodHandle invoker = lookup . unreflect ( m ) ;
MethodType mt = invoker . type ( ) ;
Class < ? > [ ] types = mt . parameterArray ( ) ;
types [ 0 ] = int . class ; // placeholder
Object [ ] args = randomArgs ( types ) ;
args [ 0 ] = proxy ;
if ( verbosity > 3 )
System . out . println ( " calling " + m + " on " + Arrays . asList ( args ) ) ;
Object result = invoker . invokeWithArguments ( args ) ;
if ( verbosity > 4 )
System . out . println ( " result = " + result ) ;
String name = " fooForFooable/ " + args [ 1 ] ;
Object [ ] argTail = Arrays . copyOfRange ( args , 2 , args . length ) ;
assertCalled ( name , argTail ) ;
assertEquals ( result , logEntry ( name , argTail ) ) ;
}
2010-09-09 01:40:34 +00:00
}
2011-07-16 22:44:33 +00:00
// test processing of thrown exceptions:
2010-09-09 01:40:34 +00:00
for ( Throwable ex : new Throwable [ ] { new NullPointerException ( " ok " ) ,
new InternalError ( " ok " ) ,
new Throwable ( " fail " ) ,
new Exception ( " fail " ) ,
new MyCheckedException ( )
} ) {
MethodHandle mh = MethodHandles . throwException ( void . class , Throwable . class ) ;
mh = MethodHandles . insertArguments ( mh , 0 , ex ) ;
2011-05-27 00:37:36 +00:00
WillThrow proxy = MethodHandleProxies . asInterfaceInstance ( WillThrow . class , mh ) ;
2010-09-09 01:40:34 +00:00
try {
2011-07-16 22:44:33 +00:00
countTest ( ) ;
2010-09-09 01:40:34 +00:00
proxy . willThrow ( ) ;
System . out . println ( " Failed to throw: " + ex ) ;
assertTrue ( false ) ;
} catch ( Throwable ex1 ) {
2011-07-16 22:44:33 +00:00
if ( verbosity > 3 ) {
2010-09-09 01:40:34 +00:00
System . out . println ( " throw " + ex ) ;
System . out . println ( " catch " + ( ex = = ex1 ? " UNWRAPPED " : ex1 ) ) ;
}
if ( ex instanceof RuntimeException | |
ex instanceof Error ) {
assertSame ( " must pass unchecked exception out without wrapping " , ex , ex1 ) ;
} else if ( ex instanceof MyCheckedException ) {
assertSame ( " must pass declared exception out without wrapping " , ex , ex1 ) ;
} else {
assertNotSame ( " must pass undeclared checked exception with wrapping " , ex , ex1 ) ;
2011-06-15 05:47:12 +00:00
if ( ! ( ex1 instanceof UndeclaredThrowableException ) | | ex1 . getCause ( ) ! = ex ) {
2012-07-24 17:47:44 +00:00
ex1 . printStackTrace ( System . out ) ;
2011-06-15 05:47:12 +00:00
}
assertSame ( ex , ex1 . getCause ( ) ) ;
2010-09-09 01:40:34 +00:00
UndeclaredThrowableException utex = ( UndeclaredThrowableException ) ex1 ;
}
}
}
2011-07-16 22:44:33 +00:00
// Test error checking on bad interfaces:
2012-01-19 01:34:29 +00:00
for ( Class < ? > nonSMI : new Class < ? > [ ] { Object . class ,
2010-09-09 01:40:34 +00:00
String . class ,
CharSequence . class ,
2011-07-16 22:44:33 +00:00
java . io . Serializable . class ,
PrivateRunnable . class ,
2010-09-09 01:40:34 +00:00
Example . class } ) {
2011-07-16 22:44:33 +00:00
if ( verbosity > 2 ) System . out . println ( nonSMI . getName ( ) ) ;
2010-09-09 01:40:34 +00:00
try {
2011-07-16 22:44:33 +00:00
countTest ( false ) ;
MethodHandleProxies . asInterfaceInstance ( nonSMI , varargsArray ( 0 ) ) ;
assertTrue ( " Failed to throw on " + nonSMI . getName ( ) , false ) ;
2010-09-09 01:40:34 +00:00
} catch ( IllegalArgumentException ex ) {
2011-07-16 22:44:33 +00:00
if ( verbosity > 2 ) System . out . println ( nonSMI . getSimpleName ( ) + " : " + ex ) ;
// Object: java.lang.IllegalArgumentException:
// not a public interface: java.lang.Object
// String: java.lang.IllegalArgumentException:
// not a public interface: java.lang.String
// CharSequence: java.lang.IllegalArgumentException:
// not a single-method interface: java.lang.CharSequence
// Serializable: java.lang.IllegalArgumentException:
// not a single-method interface: java.io.Serializable
// PrivateRunnable: java.lang.IllegalArgumentException:
// not a public interface: test.java.lang.invoke.MethodHandlesTest$PrivateRunnable
// Example: java.lang.IllegalArgumentException:
// not a public interface: test.java.lang.invoke.MethodHandlesTest$Example
}
}
// Test error checking on interfaces with the wrong method type:
2012-01-19 01:34:29 +00:00
for ( Class < ? > intfc : new Class < ? > [ ] { Runnable . class /*arity 0*/ ,
2011-07-16 22:44:33 +00:00
Fooable . class /*arity 1 & 2*/ } ) {
int badArity = 1 ; // known to be incompatible
if ( verbosity > 2 ) System . out . println ( intfc . getName ( ) ) ;
try {
countTest ( false ) ;
MethodHandleProxies . asInterfaceInstance ( intfc , varargsArray ( badArity ) ) ;
assertTrue ( " Failed to throw on " + intfc . getName ( ) , false ) ;
} catch ( WrongMethodTypeException ex ) {
if ( verbosity > 2 ) System . out . println ( intfc . getSimpleName ( ) + " : " + ex ) ;
// Runnable: java.lang.invoke.WrongMethodTypeException:
// cannot convert MethodHandle(Object)Object[] to ()void
// Fooable: java.lang.invoke.WrongMethodTypeException:
// cannot convert MethodHandle(Object)Object[] to (Object,String)Object
2010-09-09 01:40:34 +00:00
}
}
}
2011-07-16 22:40:13 +00:00
@Test
public void testRunnableProxy ( ) throws Throwable {
if ( CAN_SKIP_WORKING ) return ;
startTest ( " testRunnableProxy " ) ;
MethodHandles . Lookup lookup = MethodHandles . lookup ( ) ;
MethodHandle run = lookup . findStatic ( lookup . lookupClass ( ) , " runForRunnable " , MethodType . methodType ( void . class ) ) ;
Runnable r = MethodHandleProxies . asInterfaceInstance ( Runnable . class , run ) ;
testRunnableProxy ( r ) ;
assertCalled ( " runForRunnable " ) ;
}
private static void testRunnableProxy ( Runnable r ) {
//7058630: JSR 292 method handle proxy violates contract for Object methods
r . run ( ) ;
Object o = r ;
r = null ;
boolean eq = ( o = = o ) ;
int hc = System . identityHashCode ( o ) ;
String st = o . getClass ( ) . getName ( ) + " @ " + Integer . toHexString ( hc ) ;
Object expect = Arrays . asList ( st , eq , hc ) ;
if ( verbosity > = 2 ) System . out . println ( " expect st/eq/hc = " + expect ) ;
Object actual = Arrays . asList ( o . toString ( ) , o . equals ( o ) , o . hashCode ( ) ) ;
if ( verbosity > = 2 ) System . out . println ( " actual st/eq/hc = " + actual ) ;
assertEquals ( expect , actual ) ;
}
2009-10-22 06:19:48 +00:00
}
2011-03-24 06:02:31 +00:00
// Local abbreviated copy of sun.invoke.util.ValueConversions
2012-07-24 17:47:44 +00:00
// This guy tests access from outside the same package member, but inside
// the package itself.
2009-10-22 06:19:48 +00:00
class ValueConversions {
private static final Lookup IMPL_LOOKUP = MethodHandles . lookup ( ) ;
private static final Object [ ] NO_ARGS_ARRAY = { } ;
private static Object [ ] makeArray ( Object . . . args ) { return args ; }
private static Object [ ] array ( ) { return NO_ARGS_ARRAY ; }
private static Object [ ] array ( Object a0 )
{ return makeArray ( a0 ) ; }
private static Object [ ] array ( Object a0 , Object a1 )
{ return makeArray ( a0 , a1 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 )
{ return makeArray ( a0 , a1 , a2 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 )
{ return makeArray ( a0 , a1 , a2 , a3 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 , a5 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 , a5 , a6 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 ,
Object a8 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 ) ; }
private static Object [ ] array ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 ,
Object a8 , Object a9 )
{ return makeArray ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 , a9 ) ; }
static MethodHandle [ ] makeArrays ( ) {
2012-01-19 01:34:29 +00:00
ArrayList < MethodHandle > arrays = new ArrayList < > ( ) ;
2009-10-22 06:19:48 +00:00
MethodHandles . Lookup lookup = IMPL_LOOKUP ;
for ( ; ; ) {
int nargs = arrays . size ( ) ;
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . genericMethodType ( nargs ) . changeReturnType ( Object [ ] . class ) ;
2009-10-22 06:19:48 +00:00
String name = " array " ;
MethodHandle array = null ;
try {
array = lookup . findStatic ( ValueConversions . class , name , type ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
// break from loop!
2009-10-22 06:19:48 +00:00
}
if ( array = = null ) break ;
arrays . add ( array ) ;
}
2011-02-11 09:26:32 +00:00
assertTrue ( arrays . size ( ) = = 11 ) ; // current number of methods
2009-10-22 06:19:48 +00:00
return arrays . toArray ( new MethodHandle [ 0 ] ) ;
}
static final MethodHandle [ ] ARRAYS = makeArrays ( ) ;
/ * * Return a method handle that takes the indicated number of Object
* arguments and returns an Object array of them , as if for varargs .
* /
public static MethodHandle varargsArray ( int nargs ) {
if ( nargs < ARRAYS . length )
return ARRAYS [ nargs ] ;
2011-06-15 05:47:12 +00:00
return MethodHandles . identity ( Object [ ] . class ) . asCollector ( Object [ ] . class , nargs ) ;
2009-10-22 06:19:48 +00:00
}
2011-05-13 02:27:49 +00:00
public static MethodHandle varargsArray ( Class < ? > arrayType , int nargs ) {
Class < ? > elemType = arrayType . getComponentType ( ) ;
MethodType vaType = MethodType . methodType ( arrayType , Collections . < Class < ? > > nCopies ( nargs , elemType ) ) ;
MethodHandle mh = varargsArray ( nargs ) ;
if ( arrayType ! = Object [ ] . class )
mh = MethodHandles . filterReturnValue ( mh , CHANGE_ARRAY_TYPE . bindTo ( arrayType ) ) ;
return mh . asType ( vaType ) ;
}
static Object changeArrayType ( Class < ? > arrayType , Object [ ] a ) {
Class < ? > elemType = arrayType . getComponentType ( ) ;
if ( ! elemType . isPrimitive ( ) )
return Arrays . copyOf ( a , a . length , arrayType . asSubclass ( Object [ ] . class ) ) ;
Object b = java . lang . reflect . Array . newInstance ( elemType , a . length ) ;
for ( int i = 0 ; i < a . length ; i + + )
java . lang . reflect . Array . set ( b , i , a [ i ] ) ;
return b ;
}
private static final MethodHandle CHANGE_ARRAY_TYPE ;
static {
try {
CHANGE_ARRAY_TYPE = IMPL_LOOKUP . findStatic ( ValueConversions . class , " changeArrayType " ,
MethodType . methodType ( Object . class , Class . class , Object [ ] . class ) ) ;
} catch ( NoSuchMethodException | IllegalAccessException ex ) {
Error err = new InternalError ( " uncaught exception " ) ;
err . initCause ( ex ) ;
throw err ;
}
}
2010-01-08 00:16:45 +00:00
private static final List < Object > NO_ARGS_LIST = Arrays . asList ( NO_ARGS_ARRAY ) ;
private static List < Object > makeList ( Object . . . args ) { return Arrays . asList ( args ) ; }
private static List < Object > list ( ) { return NO_ARGS_LIST ; }
private static List < Object > list ( Object a0 )
{ return makeList ( a0 ) ; }
private static List < Object > list ( Object a0 , Object a1 )
{ return makeList ( a0 , a1 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 )
{ return makeList ( a0 , a1 , a2 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 )
{ return makeList ( a0 , a1 , a2 , a3 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 , a5 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 , a5 , a6 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 ,
Object a8 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 ) ; }
private static List < Object > list ( Object a0 , Object a1 , Object a2 , Object a3 ,
Object a4 , Object a5 , Object a6 , Object a7 ,
Object a8 , Object a9 )
{ return makeList ( a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 , a9 ) ; }
static MethodHandle [ ] makeLists ( ) {
2012-01-19 01:34:29 +00:00
ArrayList < MethodHandle > lists = new ArrayList < > ( ) ;
2010-01-08 00:16:45 +00:00
MethodHandles . Lookup lookup = IMPL_LOOKUP ;
for ( ; ; ) {
2011-02-11 09:26:28 +00:00
int nargs = lists . size ( ) ;
2010-01-08 00:16:45 +00:00
MethodType type = MethodType . genericMethodType ( nargs ) . changeReturnType ( List . class ) ;
String name = " list " ;
2011-02-11 09:26:28 +00:00
MethodHandle list = null ;
2010-01-08 00:16:45 +00:00
try {
2011-02-11 09:26:28 +00:00
list = lookup . findStatic ( ValueConversions . class , name , type ) ;
2011-02-11 09:26:32 +00:00
} catch ( ReflectiveOperationException ex ) {
// break from loop!
2010-01-08 00:16:45 +00:00
}
2011-02-11 09:26:28 +00:00
if ( list = = null ) break ;
lists . add ( list ) ;
2010-01-08 00:16:45 +00:00
}
2011-02-11 09:26:32 +00:00
assertTrue ( lists . size ( ) = = 11 ) ; // current number of methods
2011-02-11 09:26:28 +00:00
return lists . toArray ( new MethodHandle [ 0 ] ) ;
2010-01-08 00:16:45 +00:00
}
static final MethodHandle [ ] LISTS = makeLists ( ) ;
2011-06-15 05:47:12 +00:00
static final MethodHandle AS_LIST ;
static {
try {
AS_LIST = IMPL_LOOKUP . findStatic ( Arrays . class , " asList " , MethodType . methodType ( List . class , Object [ ] . class ) ) ;
2012-01-19 01:34:29 +00:00
} catch ( NoSuchMethodException | IllegalAccessException ex ) { throw new RuntimeException ( ex ) ; }
2011-06-15 05:47:12 +00:00
}
2010-01-08 00:16:45 +00:00
/ * * Return a method handle that takes the indicated number of Object
* arguments and returns List .
* /
public static MethodHandle varargsList ( int nargs ) {
if ( nargs < LISTS . length )
return LISTS [ nargs ] ;
2011-06-15 05:47:12 +00:00
return AS_LIST . asCollector ( Object [ ] . class , nargs ) ;
2010-01-08 00:16:45 +00:00
}
2009-10-22 06:19:48 +00:00
}
// This guy tests access from outside the same package member, but inside
// the package itself.
class PackageSibling {
static Lookup lookup ( ) {
return MethodHandles . lookup ( ) ;
}
}