--- /dev/null 2009-01-20 02:47:09.000000000 -0800 +++ new/src/share/projects/meth/README.txt 2009-01-20 02:47:08.000000000 -0800 @@ -0,0 +1,200 @@ +Standalone NetBeans project of JSR 292 RI + +(Not yet merged with the JDK; use -Xbootclasspath.) + +To open: + open -a netbeans . # Open Project => select this directory + +To build: + cd .; ant clean compile run + +To run a unit test (requires modified JVM): + #NetBeansResources=$(find /Applications/NetBeans* -type d -name Resources | tail -1) + #NetBeansResources="/Applications/NetBeans/NetBeans 6.1.app/Contents/Resources" + #junit="$NetBeansResources/NetBeans/java2/modules/ext/junit-3.8.2.jar" + export junit=$HOME/env/jars/junit-4.1.jar #(or wherever you have it stashed) + export mhproj="$HOME/Projects/MethodHandle" #(pwd -P) + export acproj="$HOME/Projects/AnonymousClass" #(cd ../AnonymousClass; pwd -P) + export cpath="$acproj/dist/AnonymousClass.jar:$mhproj/build/classes:$mhproj/build/test/classes:$junit" + gamma -XX:+MethodHandleSupport -Xbootclasspath/p:"$cpath" jdk.java.dyn.MethodHandleBytecodeTest + +Simpler (currently broken) demo: + gamma -XX:+MethodHandleSupport -Xbootclasspath/p:"$cpath" -cp "$mhproj"/dist/MethodHandle.jar jdk.java.dyn.MethodHandleDemo + +Using JUnit 4: + gamma -XX:+MethodHandleSupport -Xbootclasspath/p:"$cpath" org.junit.runner.JUnitCore jdk.java.dyn.MethodHandlesTest + +Current output: + +-------- -------- -------- -------- +VM option '+MethodHandleSupport' +JUnit version 4.1 +.findStatic +findStatic class jdk.java.dyn.MethodHandlesTest$Example.s0/()void => jdk.java.dyn.MethodHandlesTest$Example.s0()void +calling [s0, []] on jdk.java.dyn.MethodHandlesTest$Example.s0()void +new invoker: impl.java.dyn.util.MethodHandleInvoker$L0/11502424@d80be3 +:findStatic class jdk.java.dyn.MethodHandlesTest$Example.pkg_s0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_s0()void +calling [pkg_s0, []] on jdk.java.dyn.MethodHandlesTest$Example.pkg_s0()void +:findStatic class jdk.java.dyn.MethodHandlesTest$Example.pri_s0/()void => jdk.java.dyn.MethodHandlesTest$Example.pri_s0()void +calling [pri_s0, []] on jdk.java.dyn.MethodHandlesTest$Example.pri_s0()void +:findStatic class jdk.java.dyn.MethodHandlesTest$Example.s1/(java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.s1(java.lang.Object)java.lang.Object +calling [s1, [#1000000]] on jdk.java.dyn.MethodHandlesTest$Example.s1(java.lang.Object)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L1/26083064@15e83f9 +:findStatic class jdk.java.dyn.MethodHandlesTest$Example.s2/(int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.s2(int)java.lang.Object +calling [s2, [1000001]] on jdk.java.dyn.MethodHandlesTest$Example.s2(int)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L1/31149935@13c1b02 +:findStatic class jdk.java.dyn.MethodHandlesTest$Example.bogus/()void => null !! java.dyn.NoAccessException: cannot access: jdk.java.dyn.MethodHandlesTest$Example.bogus()void, from jdk.java.dyn.MethodHandlesTest$Example +.findVirtual +findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v0/()void => jdk.java.dyn.MethodHandlesTest$Example.v0()void +calling [v0, [Example#1000002]] on jdk.java.dyn.MethodHandlesTest$Example.v0()void +new invoker: impl.java.dyn.util.MethodHandleInvoker$L1/14867177@19209ea +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.pkg_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +calling [pkg_v0, [Example#1000003]] on jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.pri_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void +calling [pri_v0, [Example#1000004]] on jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v1/(java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object +calling [v1, [Example#1000005, #1000006]] on jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/18820833@3ecfff +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v2/(java.lang.Object,java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object +calling [v2, [Example#1000007, #1000008, #1000009]] on jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L3/10883428@bfc8e0 +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v2/(java.lang.Object,int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object +calling [v2, [Example#1000010, #1000011, 1000012]] on jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L3/597230@4a63d8 +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v2/(int,java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object +calling [v2, [Example#1000013, 1000014, #1000015]] on jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L3/22201561@f99ff5 +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.v2/(int,int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object +calling [v2, [Example#1000016, 1000017, 1000018]] on jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object +new invoker: impl.java.dyn.util.MethodHandleInvoker$L3/13783459@1edc073 +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.bogus/()void => null !! java.dyn.NoAccessException: cannot access: jdk.java.dyn.MethodHandlesTest$Example.bogus()void, from jdk.java.dyn.MethodHandlesTest +findVirtual class jdk.java.dyn.MethodHandlesTest$SubExample.Sub/v0/()void => jdk.java.dyn.MethodHandlesTest$SubExample.v0()void +calling [Sub/v0, [SubExample#1000019]] on jdk.java.dyn.MethodHandlesTest$SubExample.v0()void +new invoker: impl.java.dyn.util.MethodHandleInvoker$L1/9502784@1a786c3 +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.Sub/v0/()void => jdk.java.dyn.MethodHandlesTest$Example.v0()void +calling [Sub/v0, [SubExample#1000021]] on jdk.java.dyn.MethodHandlesTest$Example.v0()void +:findVirtual interface jdk.java.dyn.MethodHandlesTest$IntExample.Sub/v0/()void => jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +calling [Sub/v0, [SubExample#1000023]] on jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +new invoker: impl.java.dyn.util.MethodHandleInvoker$L1/20111677@1617189 +:findVirtual class jdk.java.dyn.MethodHandlesTest$SubExample.Sub/pkg_v0/()void => jdk.java.dyn.MethodHandlesTest$SubExample.pkg_v0()void +calling [Sub/pkg_v0, [SubExample#1000024]] on jdk.java.dyn.MethodHandlesTest$SubExample.pkg_v0()void +:findVirtual class jdk.java.dyn.MethodHandlesTest$Example.Sub/pkg_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +calling [Sub/pkg_v0, [SubExample#1000026]] on jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +:findVirtual interface jdk.java.dyn.MethodHandlesTest$IntExample.v0/()void => jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +calling [v0, [Example#1000028]] on jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +:findVirtual interface jdk.java.dyn.MethodHandlesTest$IntExample.Int/v0/()void => jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +calling [Int/v0, [jdk.java.dyn.MethodHandlesTest$IntExample$Impl@2bb514]] on jdk.java.dyn.MethodHandlesTest$IntExample.v0()void +:.findSpecial +findSpecial class jdk.java.dyn.MethodHandlesTest$Example.v0/()void => jdk.java.dyn.MethodHandlesTest$Example.v0()void +calling [v0, [Example#1000031]] on jdk.java.dyn.MethodHandlesTest$Example.v0()void +:findSpecial class jdk.java.dyn.MethodHandlesTest$Example.pkg_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +calling [pkg_v0, [Example#1000032]] on jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +:findSpecial class jdk.java.dyn.MethodHandlesTest$SubExample./(int)void => jdk.java.dyn.MethodHandlesTest$SubExample.(int)void +calling [, [SubExample#1000033, 1000034]] on jdk.java.dyn.MethodHandlesTest$SubExample.(int)void +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/26335425@2d9c06 +:findSpecial class jdk.java.dyn.MethodHandlesTest$Example./(int)void => null !! java.dyn.NoAccessException: constructor must be local to caller: jdk.java.dyn.MethodHandlesTest$Example.(int)void, from jdk.java.dyn.MethodHandlesTest$SubExample +findSpecial class jdk.java.dyn.MethodHandlesTest$Example.bogus/()void => null !! java.dyn.NoAccessException: cannot access: jdk.java.dyn.MethodHandlesTest$Example.bogus()void, from jdk.java.dyn.MethodHandlesTest$SubExample +.bind +init BMH type=()void argnum=0 vmargslot=0 +bind Example#1000036.v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$Example.v0()void] +calling [v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v0()void] +:init BMH type=()void argnum=0 vmargslot=0 +bind Example#1000037.pkg_v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void] +calling [pkg_v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void] +:init BMH type=()void argnum=0 vmargslot=0 +bind Example#1000038.pri_v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void] +calling [pri_v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void] +:init BMH type=(java.lang.Object)java.lang.Object argnum=0 vmargslot=1 +bind Example#1000039.v1/(java.lang.Object)java.lang.Object => Bound[jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object] +calling [v1, [#1000040]] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object] +:init BMH type=(java.lang.Object,java.lang.Object)java.lang.Object argnum=0 vmargslot=2 +bind Example#1000041.v2/(java.lang.Object,java.lang.Object)java.lang.Object => Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object] +calling [v2, [#1000042, #1000043]] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object] +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/32392776@482923 +:init BMH type=(java.lang.Object,int)java.lang.Object argnum=0 vmargslot=2 +bind Example#1000044.v2/(java.lang.Object,int)java.lang.Object => Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object] +calling [v2, [#1000045, 1000046]] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object] +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/10053659@181edf4 +:init BMH type=(int,java.lang.Object)java.lang.Object argnum=0 vmargslot=2 +bind Example#1000047.v2/(int,java.lang.Object)java.lang.Object => Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object] +calling [v2, [1000048, #1000049]] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object] +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/1760304@16df84b +:init BMH type=(int,int)java.lang.Object argnum=0 vmargslot=2 +bind Example#1000050.v2/(int,int)java.lang.Object => Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object] +calling [v2, [1000051, 1000052]] on Bound[jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object] +new invoker: impl.java.dyn.util.MethodHandleInvoker$L2/27940859@763f5d +:bind Example#1000053.bogus/()void => null !! java.dyn.NoAccessException: cannot access: jdk.java.dyn.MethodHandlesTest$Example.bogus()void, from jdk.java.dyn.MethodHandlesTest +init BMH type=()void argnum=0 vmargslot=0 +bind SubExample#1000054.Sub/v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$SubExample.v0()void] +calling [Sub/v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$SubExample.v0()void] +:init BMH type=()void argnum=0 vmargslot=0 +bind SubExample#1000055.Sub/pkg_v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$SubExample.pkg_v0()void] +calling [Sub/pkg_v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$SubExample.pkg_v0()void] +:init BMH type=()void argnum=0 vmargslot=0 +bind jdk.java.dyn.MethodHandlesTest$IntExample$Impl@13a317a.Int/v0/()void => Bound[jdk.java.dyn.MethodHandlesTest$IntExample$Impl.v0()void] +calling [Int/v0, []] on Bound[jdk.java.dyn.MethodHandlesTest$IntExample$Impl.v0()void] +:.unreflect +unreflect class jdk.java.dyn.MethodHandlesTest$Example.s0/()void => jdk.java.dyn.MethodHandlesTest$Example.s0()void +calling [s0, []] on jdk.java.dyn.MethodHandlesTest$Example.s0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.pkg_s0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_s0()void +calling [pkg_s0, []] on jdk.java.dyn.MethodHandlesTest$Example.pkg_s0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.pri_s0/()void => jdk.java.dyn.MethodHandlesTest$Example.pri_s0()void +calling [pri_s0, []] on jdk.java.dyn.MethodHandlesTest$Example.pri_s0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.s1/(java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.s1(java.lang.Object)java.lang.Object +calling [s1, [#1000057]] on jdk.java.dyn.MethodHandlesTest$Example.s1(java.lang.Object)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.s2/(int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.s2(int)java.lang.Object +calling [s2, [1000058]] on jdk.java.dyn.MethodHandlesTest$Example.s2(int)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v0/()void => jdk.java.dyn.MethodHandlesTest$Example.v0()void +calling [v0, [Example#1000059]] on jdk.java.dyn.MethodHandlesTest$Example.v0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.pkg_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +calling [pkg_v0, [Example#1000060]] on jdk.java.dyn.MethodHandlesTest$Example.pkg_v0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.pri_v0/()void => jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void +calling [pri_v0, [Example#1000061]] on jdk.java.dyn.MethodHandlesTest$Example.pri_v0()void +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v1/(java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object +calling [v1, [Example#1000062, #1000063]] on jdk.java.dyn.MethodHandlesTest$Example.v1(java.lang.Object)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v2/(java.lang.Object,java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object +calling [v2, [Example#1000064, #1000065, #1000066]] on jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,java.lang.Object)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v2/(java.lang.Object,int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object +calling [v2, [Example#1000067, #1000068, 1000069]] on jdk.java.dyn.MethodHandlesTest$Example.v2(java.lang.Object,int)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v2/(int,java.lang.Object)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object +calling [v2, [Example#1000070, 1000071, #1000072]] on jdk.java.dyn.MethodHandlesTest$Example.v2(int,java.lang.Object)java.lang.Object +:unreflect class jdk.java.dyn.MethodHandlesTest$Example.v2/(int,int)java.lang.Object => jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object +calling [v2, [Example#1000073, 1000074, 1000075]] on jdk.java.dyn.MethodHandlesTest$Example.v2(int,int)java.lang.Object +:IIIII.convertArguments/pairwise +convert jdk.java.dyn.MethodHandlesTest$Callee.id(java.lang.Object)java.lang.Object to (java.lang.String)java.lang.Object => null !! java.lang.UnsupportedOperationException: Not yet implemented +EIIIIIIII +Time: 2.544 +There was 1 failure: +1) testConvertArguments_pairwise(jdk.java.dyn.MethodHandlesTest) +java.lang.UnsupportedOperationException: Not yet implemented + at impl.java.dyn.MethodHandleImpl.convertArguments(MethodHandleImpl.java:185) + at java.dyn.MethodHandles.convertArguments(MethodHandles.java:565) + at jdk.java.dyn.MethodHandlesTest.testConvert(MethodHandlesTest.java:521) + at jdk.java.dyn.MethodHandlesTest.testConvert(MethodHandlesTest.java:508) + at jdk.java.dyn.MethodHandlesTest.testConvertArguments_pairwise(MethodHandlesTest.java:501) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99) + at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81) + at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) + at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75) + at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45) + at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71) + at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35) + at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42) + at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34) + at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52) + at org.junit.internal.runners.CompositeRunner.run(CompositeRunner.java:29) + at org.junit.runner.JUnitCore.run(JUnitCore.java:121) + at org.junit.runner.JUnitCore.run(JUnitCore.java:100) + at org.junit.runner.JUnitCore.run(JUnitCore.java:91) + at org.junit.runner.JUnitCore.runMain(JUnitCore.java:75) + at org.junit.runner.JUnitCore.main(JUnitCore.java:42) + +FAILURES!!! +Tests run: 6, Failures: 1 + + +-------- -------- -------- -------- --- /dev/null 2009-01-20 02:47:10.000000000 -0800 +++ new/src/share/projects/meth/TEST.sh 2009-01-20 02:47:10.000000000 -0800 @@ -0,0 +1,11 @@ +#! /bin/ksh +#NetBeansResources=$(find /Applications/NetBeans* -type d -name Resources | tail -1) +#NetBeansResources="/Applications/NetBeans/NetBeans 6.1.app/Contents/Resources" +#junit="$NetBeansResources/NetBeans/java2/modules/ext/junit-3.8.2.jar" +#export junit=$HOME/env/jars/junit-3.8.2.jar +export junit=$HOME/env/jars/junit-4.1.jar +export akproj="$HOME/Projects/AnonymousClass" +export mhproj="$HOME/Projects/MethodHandle" +export cpath="$mhproj/build/classes:$mhproj/build/test/classes:$junit:$akproj/build/classes:" +java -XX:+MethodHandles -Xbootclasspath/p:"$cpath" jdk.java.dyn.MethodHandleDemo +java -XX:+MethodHandles -Xbootclasspath/p:"$cpath" org.junit.runner.JUnitCore jdk.java.dyn.MethodHandlesTest \ No newline at end of file --- /dev/null 2009-01-20 02:47:11.000000000 -0800 +++ new/src/share/projects/meth/build.xml 2009-01-20 02:47:11.000000000 -0800 @@ -0,0 +1,141 @@ + + + + + + Builds, tests, and runs the project MethodHandle. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2009-01-20 02:47:12.000000000 -0800 +++ new/src/share/projects/meth/nbproject/project.properties 2009-01-20 02:47:12.000000000 -0800 @@ -0,0 +1,74 @@ +application.args= +application.title=MethodHandle +application.vendor=jrose +auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=default +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MethodHandle.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes=java/dyn/Anonymous*.java,java/dyn/*ConstantPool*.java,jdk/java/dyn/Anonymous*.java,links/** +includes=java/dyn/**,jdk/java/dyn/**,impl/java/dyn/** +#includes=** +jar.compress=true +javac.classpath=\ + ${reference.AnonymousClass.jar} +# Space-separated list of extra javac options +javac.compilerargs=-Xlint:unchecked +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit_4.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding= +javadoc.noindex=true +javadoc.nonavbar=true +javadoc.notree=true +javadoc.private=false +javadoc.splitindex=false +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=local +jnlp.descriptor=application +jnlp.enabled=false +jnlp.offline-allowed=false +jnlp.signed=false +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=JDK_1.6 +project.AnonymousClass=../AnonymousClass +reference.AnonymousClass.jar=${project.AnonymousClass}/dist/AnonymousClass.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +run.jvmargs=-Xbootclasspath/a:"${build.classes.dir}:${reference.AnonymousClass.jar}:${libs.junit.classpath}" +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +# One or both refs probably need fixing: +file.reference.projects=${env.HOME}/Projects +#file.reference.davinci.sources.jdk=${file.reference.projects}/davinci/sources/jdk +src.src.dir=src +test.src.dir=test +project.license=openjdk --- /dev/null 2009-01-20 02:47:13.000000000 -0800 +++ new/src/share/projects/meth/nbproject/project.xml 2009-01-20 02:47:13.000000000 -0800 @@ -0,0 +1,27 @@ + + + org.netbeans.modules.java.j2seproject + + + MethodHandle + 1.6.5 + + + + + + + + + + + AnonymousClass + jar + + jar + clean + jar + + + + --- /dev/null 2009-01-20 02:47:15.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/Access.java 2009-01-20 02:47:14.000000000 -0800 @@ -0,0 +1,93 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.MethodHandles; +import sun.reflect.Reflection; + +/** + * Access control to this package. + * Classes in other packages can attempt to acquire the access token, + * but will fail if they are not recognized as friends. + * Certain methods in this package, although public, require a non-null + * access token in order to proceed; they act like package-private methods. + * @author jrose + */ + +public class Access { + private Access() { } + + /** + * The heart of this pattern: The list of classes which are + * permitted to acquire the access token, and become honorary + * members of this package. + */ + static private final String[] FRIENDS = { + "java.dyn.", "impl.java.dyn." + }; + + /** + * The following object is NOT public. That's the point of the pattern. + * It is package-private, so that any member of this package + * can acquire the access token, and give it away to trusted friends. + */ + static final Access TOKEN = new Access(); + + /** + * @return Access.TOKEN, if the caller is a friend of this package + */ + public static Access getToken() { + Class callc = Reflection.getCallerClass(2); + if (callc.getClassLoader() == Access.class.getClassLoader()) { + String callcName = callc.getName(); + for (String friend : FRIENDS) { + if (callcName.startsWith(friend)) + return TOKEN; + } + } + throw new IllegalAccessError("bad caller: " + callc); + } + + /** + * Throw an IllegalAccessError if the caller does not possess + * the Access.TOKEN. + * @param must be Access.TOKEN + */ + public static void check(Access token) { + if (token == null) + fail(); + // else it must be the unique Access.TOKEN + assert(token == Access.TOKEN); + } + private static void fail() { + Class callc = Reflection.getCallerClass(3); + throw new IllegalAccessError("bad caller: " + callc); + } + + static { + //sun.reflect.Reflection.registerMethodsToFilter(MH.class, "getToken"); + } +} --- /dev/null 2009-01-20 02:47:16.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/AdapterMethodHandle.java 2009-01-20 02:47:16.000000000 -0800 @@ -0,0 +1,541 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import impl.java.dyn.util.VerifyType; +import impl.java.dyn.util.Wrappers; +import java.dyn.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import static impl.java.dyn.MethodHandleNatives.Constants.*; +import static impl.java.dyn.MethodHandleImpl.newIllegalArgumentException; + +/** + * This method handle performs simple conversion or checking of a single argument. + * @author jrose + */ +public class AdapterMethodHandle extends BoundMethodHandle { + //MethodHandle vmtarget; // next AMH or BMH in chain or final DMH + //Object argument; // parameter to the conversion if needed + //int vmargslot; // which argument slot is affected + private final int conversion; // the type of conversion: RETYPE_ONLY, etc. + + // Constructors in this class *must* be package scoped or private. + private AdapterMethodHandle(MethodHandle target, MethodType newType, + long conv, Object convArg) { + super(newType, convArg, newType.parameterSlot(convArgPos(conv))); + this.conversion = convCode(conv); + if (MethodHandleNatives.JVM_SUPPORT) { + // JVM might update VM-specific bits of conversion (ignore) + MethodHandleNatives.init(this, target, convArgPos(conv)); + } + } + private AdapterMethodHandle(MethodHandle target, MethodType newType, + long conv) { + this(target, newType, conv, null); + } + + private static final Access IMPL_TOKEN = Access.getToken(); + + // TO DO: When adapting another MH with a null conversion, clone + // the target and change its type, instead of adding another layer. + + /** + * Create a JVM-level adapter method handle to conform the given method + * handle to the similar newType, using only pairwise argument conversions. + * For each argument, convert incoming argument to the exact type needed. + * Only null conversions are allowed on the return value (until + * the JVM supports ricochet adapters). + * The argument conversions allowed are casting, unboxing, + * integral widening or narrowing, and floating point widening or narrowing. + * @param token access check + * @param newType required call type + * @param target original method handle + * @return an adapter to the original handle with the desired new type, + * or the original target if the types are already identical + * @throws IllegalArgumentException if the adaptation cannot be made + * directly by a JVM-level adapter, without help from Java code + */ + public static MethodHandle makePairwiseConversion(Access token, + MethodType newType, MethodHandle target) { + Access.check(token); + int len = newType.parameterCount(); + MethodType oldType = target.type(); + if (newType == oldType) return target; + if (len != oldType.parameterCount()) + throw newIllegalArgumentException("wrong number of arguments in "+newType); + + // Check return type. (Not much can be done with it.) + Class exp = newType.returnType(); + Class ret = oldType.returnType(); + if (!VerifyType.isNullConversion(ret, exp)) + throw newIllegalArgumentException("bad return conversion for "+newType); + + // Find last non-trivial conversion. + int lastConv = len-1; + while (lastConv >= 0) { + Class src = newType.parameterType(lastConv); // source type + Class dst = oldType.parameterType(lastConv); // destination type + if (src == dst || VerifyType.isNullConversion(src, dst)) { + --lastConv; + } else { + break; + } + } + // Now build a chain of one or more adapters. + MethodHandle adapter = target; + MethodType midType = oldType.changeReturnType(exp); + for (int i = 0; i <= lastConv; i++) { + Class src = newType.parameterType(i); // source type + Class dst = midType.parameterType(i); // destination type + if (src == dst || VerifyType.isNullConversion(src, dst)) { + // do nothing: difference is trivial + continue; + } + // Work the current type backward toward the desired caller type: + if (i != lastConv) { + midType = midType.changeParameterType(i, src); + } else { + // When doing the last (or only) real conversion, + // force all remaining null conversions to happen also. + assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src))); + midType = newType; + } + // Tricky case analysis follows. + boolean srcPrim = src.isPrimitive(); + boolean dstPrim = dst.isPrimitive(); + if (!srcPrim && !dstPrim && canCheckCast(src, dst)) { + // Simple reference conversion. + // Note: Do not check for a class hierarchy relation + // between src and dst. In all cases a 'null' argument + // will pass the cast conversion. + adapter = makeCheckCast(token, midType, adapter, i, dst); + } else if (srcPrim && dstPrim && canPrimCast(src, dst)) { + // Convert a primitive to a primitive, if the JVM supports it. + adapter = makePrimCast(token, midType, adapter, i, dst); + } else if (!srcPrim && dstPrim && canUnboxArgument(src, dst)) { + // Caller has boxed a primitive. Unbox it for the target. + // The box type must correspond exactly to the primitive type. + // This is simpler than the powerful set of widening + // conversions supported by reflect.Method.invoke. + // Those conversions require a big nest of if/then/else logic, + // which we prefer to make a user responsibility. + adapter = makeUnboxArgument(token, midType, adapter, i, dst); + } else { + throw newIllegalArgumentException("bad argument #"+i+" conversion in "+newType); + } + assert(adapter.type() == midType); + } + if (adapter.type() != newType) { + // Only trivial conversions remain. + adapter = makeRetypeOnly(IMPL_TOKEN, newType, adapter); + // Actually, that's because there were no non-trivial ones: + assert(lastConv == -1); + } + assert(adapter.type() == newType); + return adapter; + } + + /** + * Create a JVM-level adapter method handle to permute the arguments + * of the given method. + * @param token access check + * @param newType required call type + * @param target original method handle + * @param argumentMap for each target argument, position of its source in newType + * @return an adapter to the original handle with the desired new type, + * or the original target if the types are already identical + * and the permutation is null + * @throws IllegalArgumentException if the adaptation cannot be made + * directly by a JVM-level adapter, without help from Java code + */ + public static MethodHandle makePermutation(Access token, + MethodType newType, MethodHandle target, + int[] argumentMap) { + MethodType oldType = target.type(); + boolean nullPermutation = true; + for (int i = 0; i < argumentMap.length; i++) { + int pos = argumentMap[i]; + if (pos != i) + nullPermutation = false; + if (pos < 0 || pos >= newType.parameterCount()) { + argumentMap = new int[0]; break; + } + } + if (argumentMap.length != oldType.parameterCount()) + throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap)); + if (nullPermutation) + return makePairwiseConversion(token, newType, target); // well, that was easy + + // Check return type. (Not much can be done with it.) + Class exp = newType.returnType(); + Class ret = oldType.returnType(); + if (!VerifyType.isNullConversion(ret, exp)) + throw newIllegalArgumentException("bad return conversion for "+newType); + + // See if the argument types match up. + for (int i = 0; i < argumentMap.length; i++) { + int j = argumentMap[i]; + Class src = newType.parameterType(j); + Class dst = oldType.parameterType(i); + if (!VerifyType.isNullConversion(src, dst)) + throw newIllegalArgumentException("bad argument #"+j+" conversion for "+newType); + } + + // Now figure out a nice mix of SWAP, ROT, DUP, and DROP adapters. + // A workable greedy algorithm is as follows: + // Drop unused outgoing arguments (right to left: shallowest first). + // Duplicate doubly-used outgoing arguments (left to right: deepest first). + // Then the remaining problem is a true argument permutation. + // Marshal the outgoing arguments as required from left to right. + // That is, find the deepest outgoing stack position that does not yet + // have the correct argument value, and correct at least that position + // by swapping or rotating in the misplaced value (from a shallower place). + // If the misplaced value is followed by one or more consecutive values + // (also misplaced) issue a rotation which brings as many as possible + // into position. Otherwise make progress with either a swap or a + // rotation. Prefer the swap as cheaper, but do not use it if it + // breaks a slot pair. Prefer the rotation over the swap if it would + // preserve more consecutive values shallower than the target position. + // When more than one rotation will work (because the required value + // is already adjacent to the target position), then use a rotation + // which moves the old value in the target position adjacent to + // one of its consecutive values. Also, prefer shorter rotation + // spans, since they use fewer memory cycles for shuffling. + + throw new UnsupportedOperationException("NYI"); + } + + private static byte basicType(Class type) { + if (type == null) return T_VOID; + char c = Wrappers.basicTypeChar(type); + switch (c) { + case 'Z': return T_BOOLEAN; + case 'C': return T_CHAR; + case 'F': return T_FLOAT; + case 'D': return T_DOUBLE; + case 'B': return T_BYTE; + case 'S': return T_SHORT; + case 'I': return T_INT; + case 'J': return T_LONG; + case 'L': return T_OBJECT; + } + return 99; // T_ILLEGAL or some such + } + + /** Number of stack slots for the given type. + * Two for T_DOUBLE and T_FLOAT, one for the rest. + */ + private static int type2size(int type) { + assert(type >= T_BOOLEAN && type <= T_OBJECT); + return (type == T_FLOAT || type == T_DOUBLE) ? 2 : 1; + } + + /** Construct an adapter conversion descriptor for a single-argument conversion. */ + private static long makeConv(int convOp, int argnum, int src, int dest) { + assert(src == (src & 0xF)); + assert(dest == (dest & 0xF)); + assert(convOp == (convOp & CONV_OP_MASK)); + assert(convOp >= CHECK_CAST && convOp <= PRIM_TO_REF); + long stackMove = type2size(dest) - type2size(src); + return ((long) argnum << 32 | + (long) convOp | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + stackMove << CONV_STACK_MOVE_SHIFT + ); + } + private static long makeConv(int convOp, int argnum, int stackMove) { + assert(convOp == (convOp & CONV_OP_MASK)); + assert(convOp >= SWAP_ARGS && convOp <= SPREAD_ARGS); + byte src = 0, dest = 0; + if (convOp >= COLLECT_ARGS && convOp <= SPREAD_ARGS) + src = dest = T_OBJECT; + return ((long) argnum << 32 | + (long) convOp | + (int) src << CONV_SRC_TYPE_SHIFT | + (int) dest << CONV_DEST_TYPE_SHIFT | + stackMove << CONV_STACK_MOVE_SHIFT + ); + } + private static long makeConv(int convOp) { + assert(convOp == (convOp & CONV_OP_MASK)); + assert(convOp == RETYPE_ONLY); + return (long) convOp; // stackMove, src, dst, argnum all zero + } + private static int convCode(long conv) { + return (int)conv; + } + private static int convArgPos(long conv) { + return (int)(conv >>> 32); + } + + @Override + public String toString() { + MethodType adaptedType = ((MethodHandle)this).type(); + Object namh = nonAdapter((MethodHandle)vmtarget); + if (namh == null) namh = "unknown"; + return "Adapted[" + adaptedType + "," + namh + "]"; + } + + private static MethodHandle nonAdapter(MethodHandle mh) { + return (MethodHandle) + MethodHandleNatives.getTarget(mh, ETF_DIRECT_HANDLE); + } + + /* Return one plus the position of the first non-trivial difference + * between the given types. This is not a symmetric operation; + * we are considering adapting the targetType to adapterType. + * Trivial differences are those which could be ignored by the JVM + * without subverting the verifier. Otherwise, adaptable differences + * are ones for which we could create an adapter to make the type change. + * Return zero if there are no differences (other than trivial ones). + * Return 1+N if N is the only adaptable argument difference. + * Return the -2-N where N is the first of several adaptable + * argument differences. + * Return -1 if there there are differences which are not adaptable. + */ + private static int diffTypes(MethodType adapterType, + MethodType targetType) { + Class src = targetType.returnType(); + Class dst = adapterType.returnType(); + if (VerifyType.canPassUnchecked(src, dst) <= 0) + return -1; + int nargs = adapterType.parameterCount(); + if (nargs != targetType.parameterCount()) + return -1; + int diff = diffTypes(adapterType, 0, targetType, 0, nargs); + System.out.println("diff "+adapterType); + System.out.println(" "+diff+" "+targetType); + return diff; + } + private static int diffTypes(MethodType adapterType, int tstart, + MethodType targetType, int astart, + int nargs) { + int res = 0; + for (int i = 0; i < nargs; i++) { + Class src = adapterType.parameterType(tstart+i); + Class dest = targetType.parameterType(astart+i); + if (VerifyType.canPassUnchecked(src, dest) <= 0) { + // found a difference; is it the only one so far? + if (res != 0) + return -1-res; // return -2-i for prev. i + res = 1+i; + } + } + return res; + } + + /** Can a retyping adapter (alone) validly convert the target to newType? */ + public static boolean canRetypeOnly(MethodType newType, MethodType targetType) { + int diff = diffTypes(newType, targetType); + // %%% This assert is too strong. Factor diff into VerifyType and reconcile. + assert((diff == 0) == VerifyType.isNullConversion(newType, targetType)); + return diff == 0; + } + + /** Factory method: Performs no conversions; simply retypes the adapter. */ + public static MethodHandle makeRetypeOnly(Access token, + MethodType newType, MethodHandle target) { + Access.check(token); + assert(canRetypeOnly(newType, target.type())); + // %%% TO DO: If adapter is already an adapter, fold in the retyping. + return new AdapterMethodHandle(target, newType, makeConv(RETYPE_ONLY)); + } + + /** Can a checkcast adapter validly convert the target to newType? + * The JVM supports all kind of reference casts, even silly ones. + */ + public static boolean canCheckCast(MethodType newType, MethodType targetType, + int arg, Class castType) { + Class src = newType.parameterType(arg); + Class dst = targetType.parameterType(arg); + if (!canCheckCast(src, castType) + || !VerifyType.isNullConversion(castType, dst)) + return false; + int diff = diffTypes(newType, targetType); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive conversion adapter validly convert src to dst? */ + public static boolean canCheckCast(Class src, Class dst) { + return (!src.isPrimitive() && !dst.isPrimitive()); + } + + /** Factory method: Forces a cast at the given argument. + * The castType is the target of the cast, and can be any type + * with a null conversion to the corresponding target parameter. + */ + public static MethodHandle makeCheckCast(Access token, + MethodType newType, MethodHandle target, + int arg, Class castType) { + Access.check(token); + assert(canCheckCast(newType, target.type(), arg, castType)); + long conv = makeConv(CHECK_CAST, arg, 0); + return new AdapterMethodHandle(target, newType, conv, castType); + } + + /** Can an primitive conversion adapter validly convert the target to newType? + * The JVM currently supports all conversions except those between + * floating and integral types. + */ + public static boolean canPrimCast(MethodType newType, MethodType targetType, + int arg, Class convType) { + Class src = newType.parameterType(arg); + Class dst = targetType.parameterType(arg); + if (!canPrimCast(src, convType) + || !VerifyType.isNullConversion(convType, dst)) + return false; + int diff = diffTypes(newType, targetType); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive conversion adapter validly convert src to dst? */ + public static boolean canPrimCast(Class src, Class dst) { + if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) { + return false; + } else if (Wrappers.isFloating(dst)) { + // both must be floating types + return Wrappers.isFloating(src); + } else { + // both are integral, and all combinations work fine + assert(Wrappers.isIntegral(src) && Wrappers.isIntegral(dst)); + return true; + } + } + + /** Factory method: Truncate the given argument with zero or sign extension, + * and/or convert between single and doubleword versions of integer or float. + * The convType is the target of the conversion, and can be any type + * with a null conversion to the corresponding target parameter. + */ + public static MethodHandle makePrimCast(Access token, + MethodType newType, MethodHandle target, + int arg, Class convType) { + Access.check(token); + MethodType oldType = target.type(); + Class src = newType.parameterType(arg); + Class dst = oldType.parameterType(arg); + assert(canPrimCast(newType, oldType, arg, convType)); + long conv = makeConv(PRIM_TO_PRIM, arg, basicType(src), basicType(convType)); + return new AdapterMethodHandle(target, newType, conv); + } + + /** Can an unboxing conversion validly convert src to dst? + * The JVM currently supports all kinds of casting and unboxing. + * The convType is the unboxed type; it can be either a primitive or wrapper. + */ + public static boolean canUnboxArgument(MethodType newType, MethodType targetType, + int arg, Class convType) { + Class src = newType.parameterType(arg); + Class dst = targetType.parameterType(arg); + Class boxType = Wrappers.asWrapperType(convType); + convType = Wrappers.asPrimitiveType(convType); + if (!canCheckCast(src, boxType) + || boxType == convType + || !VerifyType.isNullConversion(convType, dst)) + return false; + int diff = diffTypes(newType, targetType); + return (diff == arg+1); // arg is sole non-trivial diff + } + /** Can an primitive unboxing adapter validly convert src to dst? */ + public static boolean canUnboxArgument(Class src, Class dst) { + return (!src.isPrimitive() && Wrappers.asPrimitiveType(dst).isPrimitive()); + } + + /** Factory method: Unbox the given argument. */ + public static MethodHandle makeUnboxArgument(Access token, + MethodType newType, MethodHandle target, + int arg, Class convType) { + MethodType oldType = target.type(); + Class src = newType.parameterType(arg); + Class dst = oldType.parameterType(arg); + Class boxType = Wrappers.asWrapperType(convType); + Class primType = Wrappers.asPrimitiveType(convType); + assert(canUnboxArgument(newType, oldType, arg, convType)); + MethodType castDone = newType; + if (!VerifyType.isNullConversion(src, boxType)) + castDone = newType.changeParameterType(arg, boxType); + long conv = makeConv(REF_TO_PRIM, arg, T_OBJECT, basicType(primType)); + MethodHandle adapter = new AdapterMethodHandle(target, castDone, conv, boxType); + if (castDone == newType) + return adapter; + return makeCheckCast(token, newType, adapter, arg, boxType); + } + + // TO DO: makeBoxArgument, makeSwapArguments, makeRotateArguments, makeDuplicateArguments + + /** Can an adapter simply drop arguments to convert the target to newType? */ + public static boolean canDropArguments(MethodType newType, MethodType targetType, + int dropArgPos, int dropArgCount) { + List> ptypes = targetType.parameterList(); + int nptypes = ptypes.size(); + if ((dropArgPos | dropArgCount) < 0) + return false; + if (dropArgPos == 0) + ptypes = ptypes.subList(dropArgCount, nptypes); + else if (dropArgPos + dropArgCount == nptypes) + ptypes = ptypes.subList(0, nptypes - dropArgCount); + else { + if (dropArgPos > nptypes || + dropArgPos + dropArgCount > nptypes) + return false; + ptypes = new ArrayList>(ptypes); + ptypes.subList(dropArgPos, dropArgPos + dropArgCount).clear(); + } + MethodType midType = MethodType.make(targetType.returnType(), ptypes); + return diffTypes(newType, midType) == 0; + } + + /** Factory method: Drop selected initial or final arguments. */ + public static MethodHandle makeDropArguments(Access token, + MethodType newType, MethodHandle target, + int dropArgPos, int dropArgCount) { + Access.check(token); + assert(canDropArguments(newType, target.type(), dropArgPos, dropArgCount)); + MethodType mt = target.type(); + int argCount = mt.parameterCount(); + int dropSlotCount, dropSlotPos; + if (dropArgCount <= 0) { + return makeRetypeOnly(IMPL_TOKEN, newType, target); + } else if (dropArgCount >= argCount) { + assert(dropArgPos == argCount-1); + dropSlotPos = 0; + dropSlotCount = mt.parameterSlotCount(); + } else { + // arglist: [0: keep... | dpos: drop... | dpos+dcount: keep... ] + int lastDroppedArg = dropArgPos + dropArgCount - 1; + int lastKeptArg = dropArgPos - 1; // might be -1, which is OK + dropSlotPos = mt.parameterSlot(lastDroppedArg); + int lastKeptSlot = mt.parameterSlot(lastKeptArg); + dropSlotCount = lastKeptSlot - dropSlotPos; + assert(dropSlotCount >= dropArgCount); + } + long conv = makeConv(DROP_ARGS, dropArgPos, +dropSlotCount); + return new AdapterMethodHandle(target, newType, dropSlotCount, conv); + } + + // TO DO: makeCollectArguments, makeSpreadArguments, makeFlyby, makeRicochet +} --- /dev/null 2009-01-20 02:47:17.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/BoundMethodHandle.java 2009-01-20 02:47:17.000000000 -0800 @@ -0,0 +1,88 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.*; + +/** + * The flavor of method handle which emulates an invoke instruction + * on a predetermined argument. The JVM dispatches to the correct method + * when the handle is created, not when it is invoked. + * @author jrose + */ +public class BoundMethodHandle extends MethodHandle { + //MethodHandle vmtarget; // next BMH or final DMH or methodOop + private final Object argument; // argument to insert + private final int vmargslot; // position at which it is inserted + + // Constructors in this class *must* be package scoped or private. + + /** Bind a direct MH to its receiver (or first ref. argument). + * The JVM will pre-dispatch the MH if it is not already static. + */ + BoundMethodHandle(DirectMethodHandle mh, Object argument) { + super(Access.TOKEN, mh.type().deleteParameterType(0)); + // check the type now, once for all: + this.argument = mh.type().parameterType(0).cast(argument); + this.vmargslot = this.type().parameterSlotCount(); + if (MethodHandleNatives.JVM_SUPPORT) { + this.vmtarget = null; // maybe updated by JVM + MethodHandleNatives.init(this, mh, 0); + } else { + this.vmtarget = mh; + } + } + + /** Insert an argument into an arbitrary method handle. + * If argnum is zero, inserts the first argument, etc. + */ + BoundMethodHandle(MethodHandle mh, Object argument, int argnum) { + super(Access.TOKEN, mh.type().deleteParameterType(argnum)); + this.argument = mh.type().parameterType(argnum).cast(argument); + this.vmargslot = this.type().parameterSlot(argnum-1); + System.out.println("init BMH type="+type()+" argnum="+argnum+" vmargslot="+vmargslot); + if (MethodHandleNatives.JVM_SUPPORT) { + this.vmtarget = null; // maybe updated by JVM + MethodHandleNatives.init(this, mh, argnum); + } else { + this.vmtarget = mh; + } + } + + /** For subclasses only. + */ + BoundMethodHandle(MethodType type, Object argument, int vmargslot) { + super(Access.TOKEN, type); + this.argument = argument; + this.vmargslot = vmargslot; + assert(this.getClass() != BoundMethodHandle.class); + } + + @Override + public String toString() { + return "Bound[" + super.toString() + "]"; + } +} --- /dev/null 2009-01-20 02:47:18.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/CallSiteImpl.java 2009-01-20 02:47:18.000000000 -0800 @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.*; + +/** + * The CallSite privately created by the JVM at every invokedynamic instruction. + * @author jrose + */ +class CallSiteImpl extends CallSite { + // Fields used only by the JVM. Do not use or change. + Object vmref; + long vmdata; + + private CallSiteImpl(Class caller, String name, MethodType type) { + super(caller, name, type); + } + + @Override + public void setTarget(MethodHandle mh) { + checkTarget(mh); + if (MethodHandleNatives.JVM_SUPPORT) + MethodHandleNatives.linkCallSite(this, (MethodHandle) mh); + else + super.setTarget(mh); + } + + // this is the up-call from the JVM: + static CallSiteImpl makeSite(Class caller, String name, MethodType type, + long vmdata) { + CallSiteImpl site = new CallSiteImpl(caller, name, type); + site.vmdata = vmdata; + System.out.println("DynCallSite: "+site); + return site; + } +} --- /dev/null 2009-01-20 02:47:19.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/DirectMethodHandle.java 2009-01-20 02:47:19.000000000 -0800 @@ -0,0 +1,53 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.*; +import static impl.java.dyn.MethodHandleNatives.Constants.*; + +/** + * The flavor of method handle which emulates invokespecial or invokestatic. + * @author jrose + */ +class DirectMethodHandle extends MethodHandle { + //inherited oop vmtarget; // methodOop or virtual class/interface oop + private final int vmindex; // method index within class or interface + { vmindex = VM_INDEX_UNINITIALIZED; } // JVM may change this + + // Constructors in this class *must* be package scoped or private. + DirectMethodHandle(MethodType mtype, MemberName m, boolean doDispatch, Class caller) { + super(Access.TOKEN, mtype); + + assert(m.isMethod() || !doDispatch && m.isConstructor()); + if (!m.isResolved()) + throw new InternalError(); + MethodHandleNatives.init(this, (Object) m, doDispatch, caller); + } + + boolean isValid() { + return (vmindex != VM_INDEX_UNINITIALIZED); + } +} --- /dev/null 2009-01-20 02:47:20.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/JavaMethodHandle.java 2009-01-20 02:47:20.000000000 -0800 @@ -0,0 +1,552 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import impl.java.dyn.util.*; +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; + +/** + * General-purpose method handle combinator. + * It is invoked on an argument list, and performs whatever processing + * is needed, eventually returning the required return value. + * Note the distinctions from the low-level adapters supported directly + * by the JVM. Those low-level adapters perform argument conversion + * and delegate directly to target method handles, without executing + * any Java code. These Java-level adapters execute Java code to define + * their method handle semantics; whether they delegate to other method + * handles (as they often do) is a detail of that Java code. + *

+ * If you are creating a method handle for a specific method type, + * do not use this class; simply use {@link MethodHandles#bind}. + * This class is designed for applications which must be polymorphic + * across a range of method types. + * + * @author jrose + */ +public abstract class JavaMethodHandle { + private final MethodType exactType; + private final MethodType rawType; + private final String rawName; + + // summary of the structure of type + //private final int fingerprint; + + /** Intended type of any method handle made from this adapter. */ + public final MethodType type() { + return exactType; + } + + /** The (exact) type of my invoke function. */ + protected final MethodType rawType() { + return rawType; + } + + /** The name of my categorical invoke function, e.g., invoke_L2. */ + protected final String rawName() { + return rawName; + } + + /** + * Subclasses must provide the method type which this adapter + * implements. + * @param type + */ + protected JavaMethodHandle(MethodType type) { + this.exactType = type; + this.rawType = computeRawType(type); + this.rawName = computeRawName(type, rawType); + } + + /** Return the type of one of my invoke functions. + * Can be extended by subclasses to increase the repertoire + * of raw invocation actions. + */ + protected MethodType computeRawType(MethodType type) { + return categoricalMethodType(methodTypeCategory(type)); + } + + /** Return the type of one of my invoke functions. + * By default, of the form "invoke_$X$N" where $X is a + * single basic-type character (as found in bytecode signatures) + * and $N is the (decimal) number of parameters in the raw type. + * Can be extended by subclasses to increase the repertoire + * of raw invocation actions. + */ + protected String computeRawName(MethodType exactType, MethodType rawType) { + char rc = Wrappers.basicTypeChar(rawType.returnType()); + int nargs = rawType.parameterCount(); + return "invoke_"+rc+nargs; + } + + /** Return a permutation array which tells, for each argument + * to the categorical invoker, which incoming argument + * will supply the value (perhaps after suitable conversion). + * Returns null if the conversions are simply pairwise, + * with no reordering of argument values. + */ + protected char[] computePermutation(MethodType exactType, MethodType rawType) { + return methodTypeCategoricalPermutation(exactType); + } + + protected final RuntimeException illegal() { + return new UnsupportedOperationException(); + } + + /** Produce a method handle that binds the appropriate invoke + * method of this combinator. + * @return a method handle of this combinator's type + */ + public MethodHandle handle() { + //return permuteAndRetypeArguments(rawHandle(), type(), computePermutation()); + throw new UnsupportedOperationException("NYI"); + } + + protected MethodHandle rawHandle() { + return MethodHandles.bind(this, rawName(), rawType()); + } + + // Pluggable behaviors, specific to particular arguments or return values. + protected Object res(Object x) { return exactType.returnType().cast(x); } + protected int res(int x) { return x; } + protected long res(long x) { return x; } + + // A categorical method type is one which (a) avoids boxing, + // (b) avoids passing unused arguments, and (c) can represent + // the calling sequence of an original exact type. + // + // It's good to limit the number of categorical types, + // so that we don't have to generate too many different + // code shapes for Java-based adapters. + // We (1) erase all references to Object, + // (2) widen primitives to int when they fit, + // (3) widen other primitive arguments to long, + // (4) sort the argument list (refs, ints, then longs), and + // (5) promote ints to longs if any longs are present. + // + // Because it is harmless to ignore an int return value, + // if the caller is intending a void result, void can be + // erased to int, as with other short primitives. + // The distinction in return values between reference, + // int, long, float, and double. + // + // Therefore, the grammar of signatures is: + // {Object|int|long|float|double} ( Object* {int* | long*} ) + // This gives 5(2N+1) as the number of categorical signatures + // of N arguments, or 5(N+1)^2 for signatures of <=N arguments. + // Clearly some of this needs to get code-generated lazily, + // but we supoprt a core set of signatures here. + + protected static int category(Class type) { + if (!type.isPrimitive()) return 0; + if (type == void.class) return -1; + if (Wrappers.bitWidth(type) > 32) return 2; + return 1; + } + protected static Class categoricalType(int cat) { + switch (cat) { + case 0: return Object.class; + case 1: return int.class; + case 2: return long.class; + case 3: return void.class; + } + throw new InternalError(); + } + protected static int methodTypeCategory(MethodType type) { + int nargs = type.parameterCount(); + int nprims = 0; + int nlongs = 0; + for (int i = 0; i < nargs; i++) { + int cat = category(type.parameterType(i)); + if (cat != 0) { + ++nprims; + if (cat == 2) ++nlongs; + } + } + int nrefs = nargs - nprims; + int nints = nprims - nlongs; + int rcat = category(type.returnType()); + if (nlongs != 0) { nlongs += nints; nints = 0; } // point (5) + return (nlongs << 24) | (nints << 16) | (nrefs << 8) | rcat; + } + protected static char[] methodTypeCategoricalPermutation(MethodType type) { + return methodTypeCategoricalPermutation(type, methodTypeCategory(type)); + } + protected static char[] methodTypeCategoricalPermutation(MethodType type, int category) { + int x = category; + int rcat = (x & 0xFF); x >>= 8; + int nrefs = (x & 0xFF); x >>= 8; + int nints = (x & 0xFF); x >>= 8; + int nlongs = (x & 0xFF); x >>= 8; + int nargs = type.parameterCount(); + assert(nargs == nrefs + nints + nlongs); + char[] permutation = new char[nargs]; + int nextref = 0, nextint = nrefs, nextlong = nrefs + nints; + int reflimit = nextint, intlimit = nextlong, longlimit = nargs; + boolean sawReorder = false; + for (int i = 0; i < nargs; i++) { + int cat = category(type.parameterType(i)); + int nextarg; + if (cat == 0) + nextarg = nextref++; + else if (nextint == intlimit || cat == 2) + nextarg = nextlong++; + else + nextarg = nextint++; + // Next outgoing argument (to the categorical invoke method) + // is going to come from exactType.parameterType(i) + assert(permutation[nextarg] == 0); + permutation[nextarg] = (char) i; + if (nextarg != i) sawReorder = true; + } + assert(nextref == reflimit); + assert(nextint == intlimit); + assert(nextlong == longlimit); + if (!sawReorder) return null; // this is always a special case + return permutation; + } + protected static MethodType categoricalMethodType(int category) { + int x = category; + int rcat = (x & 0xFF); x >>= 8; + int nrefs = (x & 0xFF); x >>= 8; + int nints = (x & 0xFF); x >>= 8; + int nlongs = (x & 0xFF); x >>= 8; + return categoricalMethodType(rcat, nrefs, nints, nlongs); + } + protected static MethodType categoricalMethodType(int rcat, int nrefs, int nints, int nlongs) { + int nshorts = nints + nlongs; + Class ptypes[] = new Class[nshorts + nlongs]; + int cat = 0; + Class ptype = categoricalType(cat); + for (int i = 0; i < ptypes.length; i++) { + if (i == nrefs) ptype = categoricalType(++cat); + if (i == nshorts) ptype = categoricalType(++cat); + ptypes[i] = ptype; + } + Class rtype = categoricalType(rcat); + return MethodType.make(rtype, ptypes); + } + + // Categorical methods up to arity 5 (180 of them). + // They are (arbitrarily) split up in subclasses. + /* + (defun insert-categorical-methods (max-arity &optional max-rcat) + (let* ((types (list "Object" "int " "long " "float " "double")) + (tchars (list "L" "I" "J" "F" "D")) + invoke acat nrefs + (do-case (function (lambda (rcat nargs nrefs nints nlongs) + (setq invoke (format "invoke_%s%d" (elt tchars rcat) nargs)) + (insert "\n protected " (elt types rcat) " " invoke "(") + (dotimes (arg nargs) + (setq acat (cond ((< arg nrefs) 0) ((< arg (+ nrefs nints)) 1) (t 2))) + (insert (if (= 0 arg) "" ", ") (elt types acat) (format " a%d" arg))) + (insert ") { throw illegal(); }"))))) + (dotimes (rcat (1+ (or max-rcat 4))) (dotimes (nargs (1+ max-arity)) + (setq invoke (format "%s%d" (elt tchars rcat) nargs)) + (insert "\n static class " invoke " extends JavaMethodHandle {") + (insert "\n " invoke "(MethodType type) { super(type); }") + (funcall do-case rcat nargs nargs 0 0) + (dotimes (j 2) (dotimes (i nargs) (setq nrefs (- nargs i 1)) + (if (= j 0) (funcall do-case rcat nargs nrefs (- nargs nrefs) 0) + (funcall do-case rcat nargs nrefs 0 (- nargs nrefs))))) + (insert "\n }") + )))) + */ + // (progn (insert-categorical-methods 5 4) (insert "\n}")) + /* + static class L0 extends JavaMethodHandle { + L0(MethodType type) { super(type); } + protected Object invoke_L0() { throw illegal(); } + } + static class L1 extends JavaMethodHandle { + L1(MethodType type) { super(type); } + protected Object invoke_L1(Object a0) { throw illegal(); } + protected Object invoke_L1(int a0) { throw illegal(); } + protected Object invoke_L1(long a0) { throw illegal(); } + } + static class L2 extends JavaMethodHandle { + L2(MethodType type) { super(type); } + protected Object invoke_L2(Object a0, Object a1) { throw illegal(); } + protected Object invoke_L2(Object a0, int a1) { throw illegal(); } + protected Object invoke_L2(int a0, int a1) { throw illegal(); } + protected Object invoke_L2(Object a0, long a1) { throw illegal(); } + protected Object invoke_L2(long a0, long a1) { throw illegal(); } + } + static class L3 extends JavaMethodHandle { + L3(MethodType type) { super(type); } + protected Object invoke_L3(Object a0, Object a1, Object a2) { throw illegal(); } + protected Object invoke_L3(Object a0, Object a1, int a2) { throw illegal(); } + protected Object invoke_L3(Object a0, int a1, int a2) { throw illegal(); } + protected Object invoke_L3(int a0, int a1, int a2) { throw illegal(); } + protected Object invoke_L3(Object a0, Object a1, long a2) { throw illegal(); } + protected Object invoke_L3(Object a0, long a1, long a2) { throw illegal(); } + protected Object invoke_L3(long a0, long a1, long a2) { throw illegal(); } + } + static class L4 extends JavaMethodHandle { + L4(MethodType type) { super(type); } + protected Object invoke_L4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); } + protected Object invoke_L4(Object a0, Object a1, Object a2, int a3) { throw illegal(); } + protected Object invoke_L4(Object a0, Object a1, int a2, int a3) { throw illegal(); } + protected Object invoke_L4(Object a0, int a1, int a2, int a3) { throw illegal(); } + protected Object invoke_L4(int a0, int a1, int a2, int a3) { throw illegal(); } + protected Object invoke_L4(Object a0, Object a1, Object a2, long a3) { throw illegal(); } + protected Object invoke_L4(Object a0, Object a1, long a2, long a3) { throw illegal(); } + protected Object invoke_L4(Object a0, long a1, long a2, long a3) { throw illegal(); } + protected Object invoke_L4(long a0, long a1, long a2, long a3) { throw illegal(); } + } + static class L5 extends JavaMethodHandle { + L5(MethodType type) { super(type); } + protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); } + protected Object invoke_L5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected Object invoke_L5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); } + protected Object invoke_L5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); } + protected Object invoke_L5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); } + protected Object invoke_L5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); } + } + static class I0 extends JavaMethodHandle { + I0(MethodType type) { super(type); } + protected int invoke_I0() { throw illegal(); } + } + static class I1 extends JavaMethodHandle { + I1(MethodType type) { super(type); } + protected int invoke_I1(Object a0) { throw illegal(); } + protected int invoke_I1(int a0) { throw illegal(); } + protected int invoke_I1(long a0) { throw illegal(); } + } + static class I2 extends JavaMethodHandle { + I2(MethodType type) { super(type); } + protected int invoke_I2(Object a0, Object a1) { throw illegal(); } + protected int invoke_I2(Object a0, int a1) { throw illegal(); } + protected int invoke_I2(int a0, int a1) { throw illegal(); } + protected int invoke_I2(Object a0, long a1) { throw illegal(); } + protected int invoke_I2(long a0, long a1) { throw illegal(); } + } + static class I3 extends JavaMethodHandle { + I3(MethodType type) { super(type); } + protected int invoke_I3(Object a0, Object a1, Object a2) { throw illegal(); } + protected int invoke_I3(Object a0, Object a1, int a2) { throw illegal(); } + protected int invoke_I3(Object a0, int a1, int a2) { throw illegal(); } + protected int invoke_I3(int a0, int a1, int a2) { throw illegal(); } + protected int invoke_I3(Object a0, Object a1, long a2) { throw illegal(); } + protected int invoke_I3(Object a0, long a1, long a2) { throw illegal(); } + protected int invoke_I3(long a0, long a1, long a2) { throw illegal(); } + } + static class I4 extends JavaMethodHandle { + I4(MethodType type) { super(type); } + protected int invoke_I4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); } + protected int invoke_I4(Object a0, Object a1, Object a2, int a3) { throw illegal(); } + protected int invoke_I4(Object a0, Object a1, int a2, int a3) { throw illegal(); } + protected int invoke_I4(Object a0, int a1, int a2, int a3) { throw illegal(); } + protected int invoke_I4(int a0, int a1, int a2, int a3) { throw illegal(); } + protected int invoke_I4(Object a0, Object a1, Object a2, long a3) { throw illegal(); } + protected int invoke_I4(Object a0, Object a1, long a2, long a3) { throw illegal(); } + protected int invoke_I4(Object a0, long a1, long a2, long a3) { throw illegal(); } + protected int invoke_I4(long a0, long a1, long a2, long a3) { throw illegal(); } + } + static class I5 extends JavaMethodHandle { + I5(MethodType type) { super(type); } + protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); } + protected int invoke_I5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected int invoke_I5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); } + protected int invoke_I5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); } + protected int invoke_I5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); } + protected int invoke_I5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); } + } + static class J0 extends JavaMethodHandle { + J0(MethodType type) { super(type); } + protected long invoke_J0() { throw illegal(); } + } + static class J1 extends JavaMethodHandle { + J1(MethodType type) { super(type); } + protected long invoke_J1(Object a0) { throw illegal(); } + protected long invoke_J1(int a0) { throw illegal(); } + protected long invoke_J1(long a0) { throw illegal(); } + } + static class J2 extends JavaMethodHandle { + J2(MethodType type) { super(type); } + protected long invoke_J2(Object a0, Object a1) { throw illegal(); } + protected long invoke_J2(Object a0, int a1) { throw illegal(); } + protected long invoke_J2(int a0, int a1) { throw illegal(); } + protected long invoke_J2(Object a0, long a1) { throw illegal(); } + protected long invoke_J2(long a0, long a1) { throw illegal(); } + } + static class J3 extends JavaMethodHandle { + J3(MethodType type) { super(type); } + protected long invoke_J3(Object a0, Object a1, Object a2) { throw illegal(); } + protected long invoke_J3(Object a0, Object a1, int a2) { throw illegal(); } + protected long invoke_J3(Object a0, int a1, int a2) { throw illegal(); } + protected long invoke_J3(int a0, int a1, int a2) { throw illegal(); } + protected long invoke_J3(Object a0, Object a1, long a2) { throw illegal(); } + protected long invoke_J3(Object a0, long a1, long a2) { throw illegal(); } + protected long invoke_J3(long a0, long a1, long a2) { throw illegal(); } + } + static class J4 extends JavaMethodHandle { + J4(MethodType type) { super(type); } + protected long invoke_J4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); } + protected long invoke_J4(Object a0, Object a1, Object a2, int a3) { throw illegal(); } + protected long invoke_J4(Object a0, Object a1, int a2, int a3) { throw illegal(); } + protected long invoke_J4(Object a0, int a1, int a2, int a3) { throw illegal(); } + protected long invoke_J4(int a0, int a1, int a2, int a3) { throw illegal(); } + protected long invoke_J4(Object a0, Object a1, Object a2, long a3) { throw illegal(); } + protected long invoke_J4(Object a0, Object a1, long a2, long a3) { throw illegal(); } + protected long invoke_J4(Object a0, long a1, long a2, long a3) { throw illegal(); } + protected long invoke_J4(long a0, long a1, long a2, long a3) { throw illegal(); } + } + static class J5 extends JavaMethodHandle { + J5(MethodType type) { super(type); } + protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); } + protected long invoke_J5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected long invoke_J5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); } + protected long invoke_J5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); } + protected long invoke_J5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); } + protected long invoke_J5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); } + } + static class F0 extends JavaMethodHandle { + F0(MethodType type) { super(type); } + protected float invoke_F0() { throw illegal(); } + } + static class F1 extends JavaMethodHandle { + F1(MethodType type) { super(type); } + protected float invoke_F1(Object a0) { throw illegal(); } + protected float invoke_F1(int a0) { throw illegal(); } + protected float invoke_F1(long a0) { throw illegal(); } + } + static class F2 extends JavaMethodHandle { + F2(MethodType type) { super(type); } + protected float invoke_F2(Object a0, Object a1) { throw illegal(); } + protected float invoke_F2(Object a0, int a1) { throw illegal(); } + protected float invoke_F2(int a0, int a1) { throw illegal(); } + protected float invoke_F2(Object a0, long a1) { throw illegal(); } + protected float invoke_F2(long a0, long a1) { throw illegal(); } + } + static class F3 extends JavaMethodHandle { + F3(MethodType type) { super(type); } + protected float invoke_F3(Object a0, Object a1, Object a2) { throw illegal(); } + protected float invoke_F3(Object a0, Object a1, int a2) { throw illegal(); } + protected float invoke_F3(Object a0, int a1, int a2) { throw illegal(); } + protected float invoke_F3(int a0, int a1, int a2) { throw illegal(); } + protected float invoke_F3(Object a0, Object a1, long a2) { throw illegal(); } + protected float invoke_F3(Object a0, long a1, long a2) { throw illegal(); } + protected float invoke_F3(long a0, long a1, long a2) { throw illegal(); } + } + static class F4 extends JavaMethodHandle { + F4(MethodType type) { super(type); } + protected float invoke_F4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); } + protected float invoke_F4(Object a0, Object a1, Object a2, int a3) { throw illegal(); } + protected float invoke_F4(Object a0, Object a1, int a2, int a3) { throw illegal(); } + protected float invoke_F4(Object a0, int a1, int a2, int a3) { throw illegal(); } + protected float invoke_F4(int a0, int a1, int a2, int a3) { throw illegal(); } + protected float invoke_F4(Object a0, Object a1, Object a2, long a3) { throw illegal(); } + protected float invoke_F4(Object a0, Object a1, long a2, long a3) { throw illegal(); } + protected float invoke_F4(Object a0, long a1, long a2, long a3) { throw illegal(); } + protected float invoke_F4(long a0, long a1, long a2, long a3) { throw illegal(); } + } + static class F5 extends JavaMethodHandle { + F5(MethodType type) { super(type); } + protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); } + protected float invoke_F5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected float invoke_F5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); } + protected float invoke_F5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); } + protected float invoke_F5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); } + protected float invoke_F5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); } + } + static class D0 extends JavaMethodHandle { + D0(MethodType type) { super(type); } + protected double invoke_D0() { throw illegal(); } + } + static class D1 extends JavaMethodHandle { + D1(MethodType type) { super(type); } + protected double invoke_D1(Object a0) { throw illegal(); } + protected double invoke_D1(int a0) { throw illegal(); } + protected double invoke_D1(long a0) { throw illegal(); } + } + static class D2 extends JavaMethodHandle { + D2(MethodType type) { super(type); } + protected double invoke_D2(Object a0, Object a1) { throw illegal(); } + protected double invoke_D2(Object a0, int a1) { throw illegal(); } + protected double invoke_D2(int a0, int a1) { throw illegal(); } + protected double invoke_D2(Object a0, long a1) { throw illegal(); } + protected double invoke_D2(long a0, long a1) { throw illegal(); } + } + static class D3 extends JavaMethodHandle { + D3(MethodType type) { super(type); } + protected double invoke_D3(Object a0, Object a1, Object a2) { throw illegal(); } + protected double invoke_D3(Object a0, Object a1, int a2) { throw illegal(); } + protected double invoke_D3(Object a0, int a1, int a2) { throw illegal(); } + protected double invoke_D3(int a0, int a1, int a2) { throw illegal(); } + protected double invoke_D3(Object a0, Object a1, long a2) { throw illegal(); } + protected double invoke_D3(Object a0, long a1, long a2) { throw illegal(); } + protected double invoke_D3(long a0, long a1, long a2) { throw illegal(); } + } + static class D4 extends JavaMethodHandle { + D4(MethodType type) { super(type); } + protected double invoke_D4(Object a0, Object a1, Object a2, Object a3) { throw illegal(); } + protected double invoke_D4(Object a0, Object a1, Object a2, int a3) { throw illegal(); } + protected double invoke_D4(Object a0, Object a1, int a2, int a3) { throw illegal(); } + protected double invoke_D4(Object a0, int a1, int a2, int a3) { throw illegal(); } + protected double invoke_D4(int a0, int a1, int a2, int a3) { throw illegal(); } + protected double invoke_D4(Object a0, Object a1, Object a2, long a3) { throw illegal(); } + protected double invoke_D4(Object a0, Object a1, long a2, long a3) { throw illegal(); } + protected double invoke_D4(Object a0, long a1, long a2, long a3) { throw illegal(); } + protected double invoke_D4(long a0, long a1, long a2, long a3) { throw illegal(); } + } + static class D5 extends JavaMethodHandle { + D5(MethodType type) { super(type); } + protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, Object a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, int a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, Object a2, int a3, int a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, int a2, int a3, int a4) { throw illegal(); } + protected double invoke_D5(Object a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected double invoke_D5(int a0, int a1, int a2, int a3, int a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, Object a2, Object a3, long a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, Object a2, long a3, long a4) { throw illegal(); } + protected double invoke_D5(Object a0, Object a1, long a2, long a3, long a4) { throw illegal(); } + protected double invoke_D5(Object a0, long a1, long a2, long a3, long a4) { throw illegal(); } + protected double invoke_D5(long a0, long a1, long a2, long a3, long a4) { throw illegal(); } + } + */ +} --- /dev/null 2009-01-20 02:47:22.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/MemberName.java 2009-01-20 02:47:21.000000000 -0800 @@ -0,0 +1,550 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import impl.java.dyn.util.Signatures; +import java.dyn.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import static impl.java.dyn.MethodHandleNatives.Constants.*; + +/** + * Compact information which fully characterizes a method or field reference. + * When resolved, it includes a direct pointer to JVM metadata. + * This representation is stateless and only decriptive. + * It provides no private information and no capability to use the member. + *

+ * By contrast, a java.lang.reflect.Method contains fuller information + * about the internals of a method (except its bytecodes) and also + * allows invocation. A MemberName is much lighter than a reflect.Method, + * since it contains about 7 fields to Method's 16 (plus its sub-arrays), + * and those seven fields omit much of the information in Method. + * @author jrose + */ +public final class MemberName implements Member, Cloneable { + private Class clazz; // class in which the method is defined + private String name; // may be null if not yet materialized + private Object type; // may be null if not yet materialized + private int flags; // modifier bits; see reflect.Modifier + + private Object vmtarget; // VM-specific target value + private int vmindex; // method index within class or interface + + { vmindex = VM_INDEX_UNINITIALIZED; } + + public Class getDeclaringClass() { + if (clazz == null && isResolved()) { + expandFromVM(); + } + return clazz; + } + + public ClassLoader getClassLoader() { + return clazz.getClassLoader(); + } + + public String getName() { + if (name == null) { + expandFromVM(); + if (name == null) return null; + } + return name; + } + + public MethodType getMethodType() { + if (type == null) { + expandFromVM(); + if (type == null) return null; + } + if (!isInvocable()) + throw newIllegalArgumentException("not invocable, no method type"); + if (type instanceof MethodType) { + return (MethodType) type; + } + if (type instanceof String) { + String sig = (String) type; + MethodType res = MethodType.fromBytecodeString(sig, getClassLoader()); + this.type = res; + return res; + } + if (type instanceof Object[]) { + Object[] typeInfo = (Object[]) type; + Class[] ptypes = (Class[]) typeInfo[1]; + Class rtype = (Class) typeInfo[0]; + MethodType res = MethodType.make(rtype, ptypes); + this.type = res; + return res; + } + throw new InternalError("bad method type "+type); + } + + public MethodType getInvocationType() { + MethodType itype = getMethodType(); + if (!isStatic()) + itype = itype.insertParameterType(0, clazz); + return itype; + } + + public Class[] getParameterTypes() { + return getMethodType().parameterArray(); + } + + public Class getReturnType() { + return getMethodType().returnType(); + } + + public Class getFieldType() { + if (type == null) { + expandFromVM(); + if (type == null) return null; + } + if (isInvocable()) + throw newIllegalArgumentException("not a field or nested class, no simple type"); + if (type instanceof Class) { + return (Class) type; + } + if (type instanceof String) { + String sig = (String) type; + MethodType mtype = MethodType.fromBytecodeString("()"+sig, getClassLoader()); + Class res = mtype.returnType(); + this.type = res; + return res; + } + throw new InternalError("bad field type "+type); + } + + public Object getType() { + return (isInvocable() ? getMethodType() : getFieldType()); + } + + public String getSignature() { + if (type == null) { + expandFromVM(); + if (type == null) return null; + } + if (type instanceof String) + return (String) type; + if (isInvocable()) + return Signatures.unparse(getMethodType()); + else + return Signatures.unparse(getFieldType()); + } + + public int getModifiers() { + return (flags & RECOGNIZED_MODIFIERS); + } + + private void setFlags(int flags) { + this.flags = flags; + assert(testAnyFlags(ALL_KINDS)); + } + + private boolean testFlags(int mask, int value) { + return (flags & mask) == value; + } + private boolean testAllFlags(int mask) { + return testFlags(mask, mask); + } + private boolean testAnyFlags(int mask) { + return !testFlags(mask, 0); + } + + public boolean isStatic() { + return Modifier.isStatic(flags); + } + public boolean isPublic() { + return Modifier.isPublic(flags); + } + public boolean isPrivate() { + return Modifier.isPrivate(flags); + } + public boolean isProtected() { + return Modifier.isProtected(flags); + } + public boolean isFinal() { + return Modifier.isFinal(flags); + } + public boolean isAbstract() { + return Modifier.isAbstract(flags); + } + // let the rest (native, volatile, transient, etc.) be tested via Modifier.isFoo + + // unofficial modifier flags, used by HotSpot: + static final int BRIDGE = 0x00000040; + static final int VARARGS = 0x00000080; + static final int SYNTHETIC = 0x00001000; + static final int ANNOTATION= 0x00002000; + static final int ENUM = 0x00004000; + public boolean isBridge() { + return testAllFlags(IS_METHOD | BRIDGE); + } + public boolean isVarargs() { + return testAllFlags(VARARGS) && isInvocable(); + } + public boolean isSynthetic() { + return testAllFlags(SYNTHETIC); + } + + static final String CONSTRUCTOR_NAME = ""; // the ever-popular + + // modifiers exported by the JVM: + static final int RECOGNIZED_MODIFIERS = 0xFFFF; + + // private flags, not part of RECOGNIZED_MODIFIERS: + static final int + IS_METHOD = MN_IS_METHOD, // method (not constructor) + IS_CONSTRUCTOR = MN_IS_CONSTRUCTOR, // constructor + IS_FIELD = MN_IS_FIELD, // field + IS_TYPE = MN_IS_TYPE; // nested type + static final int // for MethodHandleNatives.getMembers + SEARCH_SUPERCLASSES = MN_SEARCH_SUPERCLASSES, + SEARCH_INTERFACES = MN_SEARCH_INTERFACES; + + static final int ALL_ACCESS = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED; + static final int ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE; + static final int IS_INVOCABLE = IS_METHOD | IS_CONSTRUCTOR; + static final int IS_FIELD_OR_METHOD = IS_METHOD | IS_FIELD; + static final int SEARCH_ALL_SUPERS = SEARCH_SUPERCLASSES | SEARCH_INTERFACES; + + public boolean isInvocable() { + return testAnyFlags(IS_INVOCABLE); + } + public boolean isFieldOrMethod() { + return testAnyFlags(IS_FIELD_OR_METHOD); + } + public boolean isMethod() { + return testAllFlags(IS_METHOD); + } + public boolean isConstructor() { + return testAllFlags(IS_CONSTRUCTOR); + } + public boolean isField() { + return testAllFlags(IS_FIELD); + } + public boolean isType() { + return testAllFlags(IS_TYPE); + } + public boolean isPackage() { + return !testAnyFlags(ALL_ACCESS); + } + + /** Initialize a query. It is not resolved. */ + private void init(Class defClass, String name, Object type, int flags) { + // defining class is allowed to be null (for a naked name/type pair) + name.toString(); // null check + type.equals(type); // null check + // fill in fields: + this.clazz = defClass; + this.name = name; + this.type = type; + setFlags(flags); + assert(!isResolved()); + } + + private void expandFromVM() { + if (!isResolved()) return; + if (type instanceof Object[]) + type = null; // don't saddle JVM w/ typeInfo + MethodHandleNatives.expand(this); + } + + // Capturing information from the Core Reflection API: + private static int flagsMods(int flags, int mods) { + assert((flags & RECOGNIZED_MODIFIERS) == 0); + assert((mods & ~RECOGNIZED_MODIFIERS) == 0); + return flags | mods; + } + public MemberName(Method m) { + Object[] typeInfo = { m.getReturnType(), m.getParameterTypes() }; + init(m.getDeclaringClass(), m.getName(), typeInfo, flagsMods(IS_METHOD, m.getModifiers())); + // fill in vmtarget, vmindex while we have m in hand: + MethodHandleNatives.init(this, m); + assert(isResolved()); + } + public MemberName(Constructor ctor) { + Object[] typeInfo = { void.class, ctor.getParameterTypes() }; + init(ctor.getDeclaringClass(), CONSTRUCTOR_NAME, typeInfo, flagsMods(IS_CONSTRUCTOR, ctor.getModifiers())); + // fill in vmtarget, vmindex while we have ctor in hand: + MethodHandleNatives.init(this, ctor); + assert(isResolved()); + } + public MemberName(Field fld) { + init(fld.getDeclaringClass(), fld.getName(), fld.getType(), flagsMods(IS_FIELD, fld.getModifiers())); + // fill in vmtarget, vmindex while we have fld in hand: + MethodHandleNatives.init(this, fld); + assert(isResolved()); + } + public MemberName(Class type) { + init(type.getDeclaringClass(), type.getSimpleName(), type, flagsMods(IS_TYPE, type.getModifiers())); + vmindex = 0; // isResolved + assert(isResolved()); + } + + // bare-bones constructor; the JVM will fill it in + MemberName() { } + + // locally useful cloner + @Override protected MemberName clone() { + try { + return (MemberName) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + // %%% define equals/hashcode? + + // Construction from symbolic parts, for queries: + public MemberName(Class defClass, String name, Class type, int modifiers) { + init(defClass, name, type, IS_FIELD | (modifiers & RECOGNIZED_MODIFIERS)); + } + public MemberName(Class defClass, String name, Class type) { + this(defClass, name, type, 0); + } + public MemberName(Class defClass, String name, MethodType type, int modifiers) { + int flagBit = (name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); + init(defClass, name, type, flagBit | (modifiers & RECOGNIZED_MODIFIERS)); + } + public MemberName(Class defClass, String name, MethodType type) { + this(defClass, name, type, 0); + } + + boolean isResolved() { + return (vmindex != VM_INDEX_UNINITIALIZED); + } + + public boolean hasReceiverTypeDispatch() { + return (isMethod() && getVMIndex(Access.TOKEN) >= 0); + } + + @Override + public String toString() { + if (isType()) + return type.toString(); // class java.lang.String + // else it is a field, method, or constructor + StringBuilder buf = new StringBuilder(); + if (getDeclaringClass() != null) { + buf.append(getName(clazz)); + buf.append('.'); + } + buf.append(getName()); + if (!isInvocable()) buf.append('/'); + buf.append(getName(getType())); + /* + buf.append('/'); + // key: Public, private, pRotected, sTatic, Final, sYnchronized, + // transient/Varargs, native, (interface), abstract, sTrict, sYnthetic, + // (annotation), Enum, (unused) + final String FIELD_MOD_CHARS = "PprTF?vt????Y?E?"; + final String METHOD_MOD_CHARS = "PprTFybVn?atY???"; + String modChars = (isInvocable() ? METHOD_MOD_CHARS : FIELD_MOD_CHARS); + for (int i = 0; i < modChars.length(); i++) { + if ((flags & (1 << i)) != 0) { + char mc = modChars.charAt(i); + if (mc != '.') + buf.append(mc); + } + } + */ + return buf.toString(); + } + private static String getName(Object obj) { + if (obj instanceof Class) + return ((Class)obj).getName(); + return obj.toString(); + } + + // Queries to the JVM: + public int getVMIndex(Access token) { + Access.check(token); + if (!isResolved()) + throw newIllegalStateException("not resolved"); + return vmindex; + } +// public Object getVMTarget(Access token) { +// Access.check(token); +// if (!isResolved()) +// throw newIllegalStateException("not resolved"); +// return vmtarget; +// } + private RuntimeException newIllegalStateException(String message) { + return new IllegalStateException(message+": "+this); + } + + // handy shared exception makers (they simplify the common case code) + public static RuntimeException newIllegalArgumentException(String message) { + return new IllegalArgumentException(message); + } + public static NoAccessException newNoAccessException(MemberName name, Class caller) { + return newNoAccessException("cannot access", name, caller); + } + public static NoAccessException newNoAccessException(String message, + MemberName name, Class caller) { + message += ": " + name; + if (caller != null) message += ", from " + caller.getName(); + return new NoAccessException(message); + } + + /** Actually making a query requires an access check. */ + public static Factory getFactory(Access token) { + Access.check(token); + return Factory.INSTANCE; + } + public static Factory getFactory() { + return getFactory(Access.getToken()); + } + public static class Factory { + private Factory() { } // singleton pattern + static Factory INSTANCE = new Factory(); + + private static int ALLOWED_FLAGS = SEARCH_ALL_SUPERS | ALL_KINDS; + + /// Queries + List getMembers(Class defc, + String matchName, Object matchType, + int matchFlags, Class caller) { + matchFlags &= ALLOWED_FLAGS; + String matchSig = null; + if (matchType != null) { + matchSig = Signatures.unparse(matchType); + if (matchSig.startsWith("(")) + matchFlags &= ~(ALL_KINDS & ~IS_INVOCABLE); + else + matchFlags &= ~(ALL_KINDS & ~IS_FIELD); + } + final int BUF_MAX = 0x2000; + int len1 = matchName == null ? 10 : matchType == null ? 4 : 1; + MemberName[] buf = newMemberBuffer(len1); + int totalCount = 0; + ArrayList bufs = null; + for (;;) { + int bufCount = MethodHandleNatives.getMembers(defc, + matchName, matchSig, matchFlags, caller, + totalCount, buf); + if (bufCount <= buf.length) { + if (bufCount >= 0) + totalCount += bufCount; + break; + } + // JVM returned tp us with an intentional overflow! + totalCount += buf.length; + int excess = bufCount - buf.length; + if (bufs == null) bufs = new ArrayList(1); + bufs.add(buf); + int len2 = buf.length; + len2 = Math.max(len2, excess); + len2 = Math.max(len2, totalCount / 4); + buf = newMemberBuffer(Math.min(BUF_MAX, len2)); + } + ArrayList result = new ArrayList(totalCount); + if (bufs != null) { + for (MemberName[] buf0 : bufs) { + Collections.addAll(result, buf0); + } + } + Collections.addAll(result, buf); + // Signature matching is not the same as type matching, since + // one signature might correspond to several types. + // So if matchType is a Class or MethodType, refilter the results. + if (matchType != null && matchType != matchSig) { + for (Iterator it = result.iterator(); it.hasNext();) { + MemberName m = it.next(); + if (!matchType.equals(m.getType())) + it.remove(); + } + } + return result; + } + boolean resolveInPlace(MemberName m, boolean searchSupers, Class caller) { + MethodHandleNatives.resolve(m, caller); + if (m.isResolved()) return true; + int matchFlags = m.flags | (searchSupers ? SEARCH_ALL_SUPERS : 0); + String matchSig = m.getSignature(); + MemberName[] buf = { m }; + int n = MethodHandleNatives.getMembers(m.getDeclaringClass(), + m.getName(), matchSig, matchFlags, caller, 0, buf); + if (n != 1) return false; + return m.isResolved(); + } + public MemberName resolveOrNull(MemberName m, boolean searchSupers, Class caller) { + MemberName result = m.clone(); + if (resolveInPlace(result, searchSupers, caller)) + return result; + return null; + } + public MemberName resolveOrFail(MemberName m, boolean searchSupers, Class caller) { + MemberName result = resolveOrNull(m, searchSupers, caller); + if (result != null) + return result; + throw newNoAccessException(m, caller); + } + public List getMethods(Class defc, boolean searchSupers, + Class caller) { + return getMethods(defc, searchSupers, null, null, caller); + } + public List getMethods(Class defc, boolean searchSupers, + String name, MethodType type, Class caller) { + int matchFlags = IS_METHOD | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, name, type, matchFlags, caller); + } + public List getConstructors(Class defc, Class caller) { + return getMembers(defc, null, null, IS_CONSTRUCTOR, caller); + } + public List getFields(Class defc, boolean searchSupers, + Class caller) { + return getFields(defc, searchSupers, null, null, caller); + } + public List getFields(Class defc, boolean searchSupers, + String name, Class type, Class caller) { + int matchFlags = IS_FIELD | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, name, type, matchFlags, caller); + } + public List getNestedTypes(Class defc, boolean searchSupers, + Class caller) { + int matchFlags = IS_TYPE | (searchSupers ? SEARCH_ALL_SUPERS : 0); + return getMembers(defc, null, null, matchFlags, caller); + } + private static MemberName[] newMemberBuffer(int length) { + MemberName[] buf = new MemberName[length]; + // fill the buffer with dummy structs for the JVM to fill in + for (int i = 0; i < length; i++) + buf[i] = new MemberName(); + return buf; + } + } + +// static { +// System.out.println("Hello world! My methods are:"); +// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null)); +// } +} \ No newline at end of file --- /dev/null 2009-01-20 02:47:23.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/MethodHandleImpl.java 2009-01-20 02:47:22.000000000 -0800 @@ -0,0 +1,266 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; +import impl.java.dyn.JavaMethodHandle; +import impl.java.dyn.util.MethodHandleInvoker; +import java.dyn.NoAccessException; +import static impl.java.dyn.MemberName.newIllegalArgumentException; +import static impl.java.dyn.MemberName.newNoAccessException; + +/** + * Base class for method handles which are known to the Hotspot JVM. + * @author jrose + */ +public abstract class MethodHandleImpl { + + // Fields in MethodHandle: + private byte vmentry; // adapter stub or method entry point + //private int vmslots; // optionally, hoist type.form.vmslots + protected Object vmtarget; // VM-specific, class-specific target value + //MethodType type; // defined in MethodHandle + + // These two dummy fields are present to force 'I' and 'J' signatures + // into this class's constant pool, so they can be transferred + // to vmentry when this class is loaded. + static final int INT_FIELD = 0; + static final long LONG_FIELD = 0; + + // type is defined in java.dyn.MethodHandle, which is platform-independent + + // vmentry (a void* field) is used *only* by by the JVM. + // The JVM adjusts its type to int or long depending on system wordsize. + // Since it is statically typed as neither int nor long, it is impossible + // to use this field from Java bytecode. (Please don't try to, either.) + + // The vmentry is an assembly-language stub which is jumped to + // immediately after the method type is verified. + // For a direct MH, this stub loads the vmtarget's entry point + // and jumps to it. + + /** + * VM-based method handles must have a security token. + * This security token can only be obtained by trusted code. + * Do not create method handles directly; use factory methods. + */ + public MethodHandleImpl(Access token) { + Access.check(token); + } + + /** Initialize the method type form to participate in JVM calls. + * This is done once for each erased type. + */ + public static void init(Access token, MethodType self) { + Access.check(token); + if (MethodHandleNatives.JVM_SUPPORT) + MethodHandleNatives.init(self); + } + + /// Factory methods to create method handles: + + private static final MemberName.Factory LOOKUP = MemberName.Factory.INSTANCE; + + + /** Look up a given method. + * Callable only from java.dyn and related packages. + *

+ * The resulting method handle type will be of the given type, + * with a receiver type {@code rcvc} prepended if the member is not static. + *

+ * Access checks are made as of the given caller. + * In particular, if the method is protected and {@code defc} is in a + * different package from the caller, then {@code rcvc} must be + * caller or a subclass. + * @param token Proof that the caller has access to this package. + * @param member Resolved method or constructor to call. + * @param name Name of the desired method. + * @param rcvc Receiver type of desired non-static method (else null) + * @param doDispatch whether the method handle will test the receiver type + * @param caller if not null, access-check relative to this class ???? + * @return a direct handle to the matching method + * @throws NoAccessException if the given method cannot be accessed by caller + */ + public static + MethodHandle findMethod(Access token, MemberName method, + boolean doDispatch, Class caller) { + Access.check(token); // only trusted calls + MethodType mtype = method.getMethodType(); + if (method.isStatic()) { + doDispatch = false; + } else { + // adjust the advertised receiver type to be exactly the one requested + // (in the case of invokespecial, this will be the calling class) + mtype = mtype.insertParameterType(0, method.getDeclaringClass()); + if (method.isConstructor()) + doDispatch = true; + } + DirectMethodHandle mh = new DirectMethodHandle(mtype, method, doDispatch, caller); + if (!mh.isValid()) + throw newNoAccessException(method, caller); + return mh; + } + + public static + MethodHandle accessField(Access token, + MemberName member, boolean isSetter, + Class caller) { + Access.check(token); + // FIXME: Use sun.misc.Unsafe to dig up the dirt on the field. + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static + MethodHandle accessArrayElement(Access token, + Class arrayClass, boolean isSetter, + Class caller) { + Access.check(token); + if (!arrayClass.isArray()) + throw newIllegalArgumentException("not an array: "+arrayClass); + // FIXME: Use sun.misc.Unsafe to dig up the dirt on the array. + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Bind a predetermined first argument to the given direct method handle. + * Callable only from MethodHandles. + * @param token Proof that the caller has access to this package. + * @param target Any direct method handle. + * @param receiver Receiver (or first static method argument) to pre-bind. + * @return a BoundMethodHandle for the given DirectMethodHandle, or null if it does not exist + */ + public static + MethodHandle bindReceiver(Access token, + MethodHandle target, Object receiver) { + Access.check(token); + if (target instanceof DirectMethodHandle) + return new BoundMethodHandle((DirectMethodHandle)target, receiver, 0); + return null; // let caller try something else + } + + /** Bind a predetermined argument to the given arbitrary method handle. + * Callable only from MethodHandles. + * @param token Proof that the caller has access to this package. + * @param target Any method handle. + * @param receiver Argument (which can be a boxed primitive) to pre-bind. + * @return a suitable BoundMethodHandle + */ + public static + MethodHandle bindArgument(Access token, + MethodHandle target, int argnum, Object receiver) { + Access.check(token); + throw new UnsupportedOperationException("NYI"); + } + + public static MethodHandle convertArguments(Access token, + MethodHandle target, + MethodType newType, + MethodType oldType, + String permutationOrNull) { + Access.check(token); + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static + MethodHandle dropArguments(Access token, MethodHandle target, + MethodType newType, int argnum) { + Access.check(token); + throw new UnsupportedOperationException("NYI"); + } + + public static + MethodHandle makeGuardWithTest(Access token, + final MethodHandle test, + final MethodHandle target, + final MethodHandle fallback) { + Access.check(token); + // %%% This is just a sketch. It needs to be de-boxed. + // Adjust the handles to accept varargs lists. + MethodType type = target.type(); + Class rtype = type.returnType(); + if (type.parameterCount() != 1 || type.parameterType(0).isPrimitive()) { + MethodType vatestType = MethodType.make(boolean.class, Object[].class); + MethodType vatargetType = MethodType.make(rtype, Object[].class); + MethodHandle vaguard = makeGuardWithTest(token, + MethodHandles.spreadArguments(test, vatestType), + MethodHandles.spreadArguments(target, vatargetType), + MethodHandles.spreadArguments(fallback, vatargetType)); + return MethodHandles.collectArguments(vaguard, type); + } + if (rtype.isPrimitive()) { + MethodType boxtype = type.changeReturnType(Object.class); + MethodHandle boxguard = makeGuardWithTest(token, + test, + MethodHandles.convertArguments(target, boxtype), + MethodHandles.convertArguments(fallback, boxtype)); + return MethodHandles.convertArguments(boxguard, type); + } + // Got here? Reduced calling sequence to Object(Object). + final MethodHandleInvoker invoke1 + = MethodHandleInvoker.make(test.type()); + final MethodHandleInvoker invoke2 + = MethodHandleInvoker.make(target.type()); + class Guarder { + Object invoke(Object x) { + // If javac supports MethodHandle.invoke directly: + //z = vatest.invoke(arguments); + // If javac does not support direct MH.invoke calls: + boolean z = (Boolean) invoke1.invoke_1(test, x); + MethodHandle mh = (z ? target : fallback); + return invoke2.invoke_1(mh, x); + } + MethodHandle handle() { + MethodType invokeType = MethodType.makeGeneric(0, true); + MethodHandle vh = MethodHandles.bind(this, "invoke", invokeType); + return MethodHandles.collectArguments(vh, target.type()); + } + } + return new Guarder().handle(); + } + + public static + MethodHandle checkArguments(Access token, MethodHandle target, MethodHandle checker, int pos) { + Access.check(token); + throw new UnsupportedOperationException("Not yet implemented"); + } + + protected static String basicToString(MethodHandle target) { + MemberName name = MethodHandleNatives.getMethodName(target); + if (name == null) + name = new MemberName(null, "", target.type()); + return name.toString(); + } + + static RuntimeException newIllegalArgumentException(String string) { + return new IllegalArgumentException(string); + } + + @Override + public String toString() { + return basicToString((MethodHandle)this); + } +} --- /dev/null 2009-01-20 02:47:24.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/MethodHandleNatives.java 2009-01-20 02:47:24.000000000 -0800 @@ -0,0 +1,240 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn; + +import java.dyn.MethodHandle; +import java.dyn.MethodType; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import static impl.java.dyn.MethodHandleNatives.Constants.*; + +/** + * The JVM interface for the method handles package is all here. + * @author jrose + */ +class MethodHandleNatives { + + private MethodHandleNatives() { } // static only + + /// MethodName support + + static native void init(MemberName self, Object ref); + static native void expand(MemberName self); + static native void resolve(MemberName self, Class caller); + static native int getMembers(Class defc, String matchName, String matchSig, + int matchFlags, Class caller, int skip, MemberName[] results); + + /// MethodHandle support + + /** Initialize the method handle to adapt the call. */ + static native void init(AdapterMethodHandle self, MethodHandle target, int argnum); + /** Initialize the method handle to call the correct method, directly. */ + static native void init(BoundMethodHandle self, Object target, int argnum); + /** Initialize the method handle to call as if by an invoke* instruction. */ + static native void init(DirectMethodHandle self, Object ref, boolean doDispatch, Class caller); + + /** Initialize a method type, once per form. */ + static native void init(MethodType self); + + /** Tell the JVM that we need to change the target of an invokedynamic. */ + static native void linkCallSite(CallSiteImpl site, MethodHandle target); + + /** Fetch the vmtarget field. + * It will be sanitized as necessary to avoid exposing non-Java references. + * This routine is for debugging and reflection. + */ + static native Object getTarget(MethodHandle self, int format); + + /** Fetch the name of the handled method, if available. + * This routine is for debugging and reflection. + */ + static MemberName getMethodName(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return (MemberName) getTarget(self, ETF_METHOD_NAME); + } + + /** Fetch the reflective version of the handled method, if available. + */ + static AccessibleObject getTargetMethod(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return (AccessibleObject) getTarget(self, ETF_REFLECT_METHOD); + } + + /** Fetch the target of this method handle. + * If it directly targets a method, return a tuple of method info. + * The info is of the form new Object[]{defclass, name, sig, refclass}. + * If it is chained to another method handle, return that handle. + */ + static Object getTargetInfo(MethodHandle self) { + if (!JVM_SUPPORT) return null; + return getTarget(self, ETF_HANDLE_OR_METHOD_NAME); + } + + static Object[] makeTarget(Class defc, String name, String sig, int mods, Class refc) { + return new Object[] { defc, name, sig, mods, refc }; + } + + /** Fetch MH-related JVM parameter. + * which=0 retrieves MethodHandlePushLimit + * which=1 retrieves stack slot push size (in address units) + */ + static native int getConstant(int which); + + /** True iff this HotSpot JVM has built-in support for method handles. + * If false, some test cases might run, but functionality will be missing. + */ + public static final boolean JVM_SUPPORT; + + /** Java copy of MethodHandlePushLimit in range 2..255. */ + static final int JVM_PUSH_LIMIT; + /** JVM stack motion (in words) after one slot is pushed, usually -1. + */ + static final int JVM_STACK_MOVE_UNIT; + + private static native void registerNatives(); + static { + boolean JVM_SUPPORT_; + int JVM_PUSH_LIMIT_; + int JVM_STACK_MOVE_UNIT_; + try { + registerNatives(); + JVM_SUPPORT_ = true; + JVM_PUSH_LIMIT_ = getConstant(Constants.GC_JVM_PUSH_LIMIT); + JVM_STACK_MOVE_UNIT_ = getConstant(Constants.GC_JVM_STACK_MOVE_LIMIT); + //sun.reflect.Reflection.registerMethodsToFilter(MethodHandleImpl.class, "init"); + } catch (UnsatisfiedLinkError ee) { + // ignore; if we use init() methods later we'll see linkage errors + JVM_SUPPORT_ = false; + JVM_PUSH_LIMIT_ = 3; // arbitrary + JVM_STACK_MOVE_UNIT_ = -1; // arbitrary + } + JVM_SUPPORT = JVM_SUPPORT_; + JVM_PUSH_LIMIT = JVM_PUSH_LIMIT_; + JVM_STACK_MOVE_UNIT = JVM_STACK_MOVE_UNIT_; + if (!JVM_SUPPORT) + System.out.println("Warning: Running with JVM_SUPPORT=false"); + } + + // All compile-time constants go here. + // There is an opportunity to check them against the JVM's idea of them. + static class Constants { + Constants() { } // static only + // MethodHandleImpl + static final int // for getConstant + GC_JVM_PUSH_LIMIT = 0, + GC_JVM_STACK_MOVE_LIMIT = 1; + static final int + ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method) + ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self) + ETF_METHOD_NAME = 2, // ultimate method as MemberName + ETF_REFLECT_METHOD = 3; // ultimate method as java.lang.reflect object (sans refClass) + + // MemberName + // The JVM uses values of -2 and above for vtable indexes. + // Field values are simple positive offsets. + // Ref: src/share/vm/oops/methodOop.hpp + // This value is negative enough to avoid such numbers, + // but not too negative. + static final int + MN_IS_METHOD = 0x00010000, // method (not constructor) + MN_IS_CONSTRUCTOR = 0x00020000, // constructor + MN_IS_FIELD = 0x00040000, // field + MN_IS_TYPE = 0x00080000, // nested type + MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers + MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers + VM_INDEX_UNINITIALIZED = -99; + + // AdapterMethodHandle + /** Conversions recognized by the JVM. + * They must align with enum AdapterKind in vm/prims/methodHandles.hpp. + */ + static final int + RETYPE_ONLY = 0x000, // no argument changes; straight retype + CHECK_CAST = 0x100, // ref-to-ref conversion; requires a Class argument + PRIM_TO_PRIM = 0x200, // converts from one primitive to another + REF_TO_PRIM = 0x300, // unboxes a wrapper to produce a primitive + PRIM_TO_REF = 0x400, // boxes a primitive into a wrapper (NYI) + SWAP_ARGS = 0x500, // swap arguments (vminfo is 2nd arg) + ROT_ARGS = 0x600, // rotate arguments (vminfo is displaced arg) + DUP_ARGS = 0x700, // duplicates one or more arguments (at TOS) + DROP_ARGS = 0x800, // remove one or more argument slots + COLLECT_ARGS = 0x900, // combine one or more arguments into a varargs (NYI) + SPREAD_ARGS = 0xA00, // expand in place a varargs array (of known size) + FLYBY = 0xB00, // operate first on reified argument list (NYI) + RICOCHET = 0xC00; // run an adapter chain on the return value (NYI) + static final int + CONV_OP_MASK = 0xF00, // byte 3 contains the conversion op field + CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use + CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK + CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK + CONV_DEST_TYPE_SHIFT = 12, // byte 3 has the adapter BasicType (if needed) + CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed) + CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change + CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1; + + /** + * Basic types as encoded in the JVM. These code values are not + * intended for use outside this class. They are used as part of + * a private interface between the JVM and this class. + */ + static final int + T_BOOLEAN = 4, + T_CHAR = 5, + T_FLOAT = 6, + T_DOUBLE = 7, + T_BYTE = 8, + T_SHORT = 9, + T_INT = 10, + T_LONG = 11, + T_OBJECT = 12, + //T_ARRAY = 13 + T_VOID = 14; + //T_ADDRESS = 15 + } + + private static native int getNamedCon(int which, Object[] name); + static boolean verifyConstants() { + Object[] box = { null }; + for (int i = 0; ; i++) { + box[0] = null; + int vmval = getNamedCon(i, box); + if (box[0] == null) break; + String name = (String) box[0]; + try { + Field con = Constants.class.getDeclaredField(name); + int jval = con.getInt(null); + if (jval != vmval) + throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval); + } catch (Exception ex) { + throw new InternalError(name+": access failed, got "+ex); + } + } + return true; + } + static { + if (JVM_SUPPORT) verifyConstants(); + } +} --- /dev/null 2009-01-20 02:47:25.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/package-info.java 2009-01-20 02:47:25.000000000 -0800 @@ -0,0 +1,35 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * Implementation details for JSR 292 RI, package java.dyn. + * This particular version is specific to Hotspot. + * There is also a backport version of this sub-package which uses reflection, + * and can therefore run (slowly) on older versions of Java. + * Other JVM vendors may create their own versions of this sub-package. + * @author jrose + */ + +package impl.java.dyn; --- /dev/null 2009-01-20 02:47:26.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/MethodHandleInvoker.java 2009-01-20 02:47:26.000000000 -0800 @@ -0,0 +1,455 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn.util; + +import impl.java.dyn.Access; +import impl.java.dyn.AdapterMethodHandle; +import java.dyn.AnonymousClassLoader; +import java.dyn.ConstantPoolParser; +import java.dyn.ConstantPoolPatch; +import java.dyn.ConstantPoolVisitor; +import java.dyn.InvalidConstantPoolFormatException; +import java.dyn.MethodHandle; +import java.dyn.MethodHandles; +import java.dyn.MethodType; +import java.dyn.WrongMethodTypeException; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.IdentityHashMap; + +/** + * Emulation of method handle invocation. + * Not needed if javac supports direct invocation of MethodHandle.invoke. + * @author jrose + */ +public abstract +class MethodHandleInvoker implements Cloneable { + private static final Access IMPL_TOKEN = Access.getToken(); + + + /** Exact type for all handles targeted by this invoker. */ + protected final MethodType exactType; + + /** Condensed information about the return type, one of "VLIJFDZBSC". */ + protected final char rtypec; + + /** Adapter which converts the approximate type to the exact exactType. */ + protected final MethodHandle adapter; + + /** Maximum number of arguments allowed. + * This is an implementation limit. + */ + public static final int ARGUMENT_MAX; + /** Maximum number of double or long arguments allowed. + * This is an implementation limit. + */ + public static final int LONG_ARGUMENT_MAX; + static { + LONG_ARGUMENT_MAX = 3; // %%% depends on stack headroom + } + + public MethodType type() { return exactType; } + + protected MethodHandleInvoker(MethodType exactType, MethodHandle adapter) { + this.exactType = exactType; + this.rtypec = Wrappers.basicTypeChar(exactType.returnType()); + this.adapter = adapter; + } + + static MethodHandle makeAdapter(MethodType exactType, MethodType approxType) { + // For each argument, convert incoming Object to the exact type needed. + int len = exactType.parameterCount(); + assert(len == approxType.parameterCount()); + if (exactType.parameterSlotCount() > len + LONG_ARGUMENT_MAX) + throw new IllegalArgumentException("too many long arguments in "+exactType); + MethodHandle invoker = MethodHandles.findVirtual(MethodHandle.class, "invoke", exactType); + MethodType adapterType = approxType.insertParameterType(0, MethodHandle.class); + return AdapterMethodHandle.makePairwiseConversion(IMPL_TOKEN, adapterType, invoker); + } + + public Object invoke_0(MethodHandle mh) + { throw wrongType(mh); } + public Object invoke_1(MethodHandle mh, Object a0) + { throw wrongType(mh); } + public Object invoke_2(MethodHandle mh, Object a0, Object a1) + { throw wrongType(mh); } + public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2) + { throw wrongType(mh); } + public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3) + { throw wrongType(mh); } + public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4) + { throw wrongType(mh); } + + /** Reflective style generic invocation. This always delegates + * to one of the invoke_X methods. + * @param mh method handle to invoke (must be of exactly correct type) + * @param args array of arguments to send to method (maybe null if empty) + * @return + */ + final // try this... + public Object invoke(MethodHandle mh, Object ... args) { + switch (args == null ? 0 : args.length) { + case 0: return invoke_0(mh); + case 1: return invoke_1(mh, args[0]); + case 2: return invoke_2(mh, args[0], args[1]); + case 3: return invoke_3(mh, args[0], args[1], args[2]); + case 4: return invoke_4(mh, args[0], args[1], args[2], args[3]); + case 5: return invoke_5(mh, args[0], args[1], args[2], args[3], args[4]); + } + throw wrongType(mh); + } + + // This class is not used after compile time. + // It is renamed away to MethodHandle itself, to call the MHI.adapter. + // TO DO: Update javac so we can call directly to polymorphic MH.invoke. + private static abstract class FakeMethodHandle extends MethodHandle { + public FakeMethodHandle() { super(null, null); } + // here are all the invokes we need to link against: + public abstract void fake_invoke_V0(MethodHandle mh); + public abstract Object fake_invoke_L0(MethodHandle mh); + public abstract int fake_invoke_I0(MethodHandle mh); + public abstract long fake_invoke_J0(MethodHandle mh); + public abstract double fake_invoke_F0(MethodHandle mh); + public abstract double fake_invoke_D0(MethodHandle mh); + public abstract void fake_invoke_V1(MethodHandle mh, Object a0); + public abstract Object fake_invoke_L1(MethodHandle mh, Object a0); + public abstract int fake_invoke_I1(MethodHandle mh, Object a0); + public abstract long fake_invoke_J1(MethodHandle mh, Object a0); + public abstract float fake_invoke_F1(MethodHandle mh, Object a0); + public abstract double fake_invoke_D1(MethodHandle mh, Object a0); + public abstract void fake_invoke_V2(MethodHandle mh, Object a0, Object a1); + public abstract Object fake_invoke_L2(MethodHandle mh, Object a0, Object a1); + public abstract int fake_invoke_I2(MethodHandle mh, Object a0, Object a1); + public abstract long fake_invoke_J2(MethodHandle mh, Object a0, Object a1); + public abstract float fake_invoke_F2(MethodHandle mh, Object a0, Object a1); + public abstract double fake_invoke_D2(MethodHandle mh, Object a0, Object a1); + public abstract void fake_invoke_V3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract Object fake_invoke_L3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract int fake_invoke_I3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract long fake_invoke_J3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract float fake_invoke_F3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract double fake_invoke_D3(MethodHandle mh, Object a0, Object a1, Object a2); + public abstract void fake_invoke_V4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract Object fake_invoke_L4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract int fake_invoke_I4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract long fake_invoke_J4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract float fake_invoke_F4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract double fake_invoke_D4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3); + public abstract void fake_invoke_V5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + public abstract Object fake_invoke_L5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + public abstract int fake_invoke_I5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + public abstract long fake_invoke_J5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + public abstract float fake_invoke_F5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + public abstract double fake_invoke_D5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4); + } + private static String FMHInvokeName(MethodType approxType) { + assert(isFMHInvokeType(approxType)) : approxType; + return "fake_invoke_" + + Wrappers.basicTypeChar(approxType.returnType()) + + approxType.parameterCount(); + } + private static boolean isFMHInvokeType(MethodType approxType) { + Class rtype = approxType.returnType(); + char rtc = Wrappers.basicTypeChar(approxType.returnType()); + if ("VIJFD".indexOf(rtc) < 0 && rtype != Object.class) + return false; + int len = approxType.parameterCount(); + for (int i = 0; i < len; i++) + if (approxType.parameterType(i) != Object.class) + return false; + if (approxType.isVarArgs()) return false; + return true; + } + + protected Object wrap(int value) { + switch (rtypec) { + case 'Z': return (value != 0); + case 'B': return (byte)value; + case 'S': return (short)value; + case 'C': return (char)value; + } + return value; + } + + static class L0 extends MethodHandleInvoker { + public L0(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_0(MethodHandle mh) { + checkType(mh); + switch (rtypec) { + // Note: Could unswitch this into 5 classes, but too messy. + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L0(mh); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I0(mh)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J0(mh); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F0(mh); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D0(mh); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V0(mh); + /* Here is the sort of code we will use when we get javac support: + case 'L': return adapter.invoke(mh); + default: return wrap(adapter.invoke(mh)); + case 'J': return adapter.invoke(mh); + case 'F': return adapter.invoke(mh); + case 'D': return adapter.invoke(mh); + case 'V': adapter.invoke(mh); + */ + } + return null; + } + } + + static class L1 extends MethodHandleInvoker { + public L1(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_1(MethodHandle mh, Object a0) { + checkType(mh); + switch (rtypec) { + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L1(mh, a0); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I1(mh, a0)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J1(mh, a0); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F1(mh, a0); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D1(mh, a0); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V1(mh, a0); + } + return null; + } + } + + static class L2 extends MethodHandleInvoker { + public L2(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_2(MethodHandle mh, Object a0, Object a1) { + checkType(mh); + switch (rtypec) { + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L2(mh, a0, a1); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I2(mh, a0, a1)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J2(mh, a0, a1); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F2(mh, a0, a1); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D2(mh, a0, a1); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V2(mh, a0, a1); + } + return null; + } + } + + static class L3 extends MethodHandleInvoker { + public L3(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_3(MethodHandle mh, Object a0, Object a1, Object a2) { + checkType(mh); + switch (rtypec) { + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L3(mh, a0, a1, a2); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I3(mh, a0, a1, a2)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J3(mh, a0, a1, a2); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F3(mh, a0, a1, a2); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D3(mh, a0, a1, a2); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V3(mh, a0, a1, a2); + } + return null; + } + } + + static class L4 extends MethodHandleInvoker { + public L4(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_4(MethodHandle mh, Object a0, Object a1, Object a2, Object a3) { + checkType(mh); + switch (rtypec) { + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L4(mh, a0, a1, a2, a3); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I4(mh, a0, a1, a2, a3)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J4(mh, a0, a1, a2, a3); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F4(mh, a0, a1, a2, a3); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D4(mh, a0, a1, a2, a3); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V4(mh, a0, a1, a2, a3); + } + return null; + } + } + + static class L5 extends MethodHandleInvoker { + public L5(MethodType type, MethodHandle adapter) { super(type, adapter); } + @Override public Object invoke_5(MethodHandle mh, Object a0, Object a1, Object a2, Object a3, Object a4) { + checkType(mh); + switch (rtypec) { + case 'L': return ((FakeMethodHandle)adapter).fake_invoke_L5(mh, a0, a1, a2, a3, a4); + default: return wrap(((FakeMethodHandle)adapter).fake_invoke_I5(mh, a0, a1, a2, a3, a4)); + case 'J': return ((FakeMethodHandle)adapter).fake_invoke_J5(mh, a0, a1, a2, a3, a4); + case 'F': return ((FakeMethodHandle)adapter).fake_invoke_F5(mh, a0, a1, a2, a3, a4); + case 'D': return ((FakeMethodHandle)adapter).fake_invoke_D5(mh, a0, a1, a2, a3, a4); + case 'V': ((FakeMethodHandle)adapter).fake_invoke_V5(mh, a0, a1, a2, a3, a4); + } + return null; + } + } + + private static Class[] L_CLASSES + = { L0.class, L1.class, L2.class, L3.class, L4.class, L5.class }; + static { ARGUMENT_MAX = L_CLASSES.length + 1; } + + public static MethodHandleInvoker make(MethodType type) { + MethodHandleInvoker inv = null; + synchronized (invokers) { + inv = invokers.get(type); + } + if (inv != null) return inv; + inv = makeNew(type); + synchronized (invokers) { + MethodHandleInvoker inv2 = invokers.get(type); + if (inv2 == null) + invokers.put(type, inv); + else + inv = inv2; + } + System.out.println("new invoker: "+inv); + return inv; + } + + static MethodType approxType(MethodType exactType) { + int len = exactType.parameterCount(); + if (len > ARGUMENT_MAX || exactType.isVarArgs()) + throw new IllegalArgumentException("too many arguments for invoker: "+exactType); + // The JVM can insert casts and unboxing for us in a native adapter. + MethodType approxType = MethodType.makeGeneric(len); + // But the return type must be exact, except for subword types. + // Convert subwords to int, since the JVM an narrow them back down. + Class rtype = exactType.returnType(); + switch (Wrappers.basicTypeChar(rtype)) { + case 'L': + rtype = Object.class; break; + case 'Z': case 'B': case 'S': case 'C': + rtype = int.class; break; + } + approxType = approxType.changeReturnType(rtype); + return approxType; + } + + private static MethodHandleInvoker makeNew(MethodType exactType) { + MethodHandleInvoker inv = null; + Exception ex1 = null; + MethodType approxType = approxType(exactType); + MethodHandle adapter = makeAdapter(exactType, approxType); + Class template = null; + Class instance = null; + Constructor constr = null; + template = L_CLASSES[approxType.parameterCount()].asSubclass(MethodHandleInvoker.class); + { + try { + instance = expandTemplate(template, approxType, exactType); + // When we get rid of the fakery, it will be just + // constr = template.getConstructor + constr = instance.getConstructor(MethodType.class, MethodHandle.class); + inv = constr.newInstance(exactType, adapter); + } catch (IOException ex) { + ex1 = ex; + } catch (InvalidConstantPoolFormatException ex) { + ex1 = ex; + } catch (InstantiationException ex) { + ex1 = ex; + } catch (IllegalAccessException ex) { + ex1 = ex; + } catch (NoSuchMethodException ex) { + ex1 = ex; + } catch (IllegalArgumentException ex) { + ex1 = ex; + } catch (InvocationTargetException ex) { + ex1 = ex; + } + } + if (inv == null) { + printex(ex1); + throw new InternalError(); + } + return inv; + } + private static void printex(Exception ex) { + System.out.println("*** Unexpected exception in "+MethodHandleInvoker.class); + System.out.println(ex); + ex.printStackTrace(System.out); + } + + private static final AnonymousClassLoader LOADER + = new AnonymousClassLoader(MethodHandleInvoker.class); + + private static String utf8Name(Class cls) { + return cls.getName().replace('.', '/'); + } + + private static class TemplateExpander extends ConstantPoolVisitor { + ConstantPoolParser cp; + ConstantPoolPatch patch; + + // Pairs of strings to be rewritten: + String fakeMHName = utf8Name(FakeMethodHandle.class); + String realMHName = utf8Name(MethodHandle.class); + boolean didMHName; + + String fakeInvokeName, realInvokeName = "invoke"; + boolean didInvokeName; + + @Override + public void visitUTF8(int index, byte tag, String utf8) { + String orig = utf8; + if (utf8.equals(fakeMHName)) { + utf8 = realMHName; didMHName = true; + } + if (utf8.equals(fakeInvokeName)) { + utf8 = realInvokeName; didInvokeName = true; + } + if ((Object)utf8 != orig) + patch.putUTF8(index, utf8); + } + + public TemplateExpander(Class template, + MethodType approxType, MethodType exactType) + throws IOException, InvalidConstantPoolFormatException { + // construct a descriptor of something like: + // int fake_invoke_I2(MethodHandle, Object, Object); + fakeInvokeName = FMHInvokeName(approxType); + cp = new ConstantPoolParser(template); + patch = cp.createPatch(); + cp.parse(this); + if (!(didMHName && didInvokeName)) + throw new RuntimeException("utf8 rewrites failed: " + +(!didMHName?"":fakeMHName)+(!didInvokeName?"":fakeInvokeName)); + } + } + + static final IdentityHashMap invokers + = new IdentityHashMap(); + + private static Class + expandTemplate(Class template, + MethodType approxType, MethodType exactType) + throws IOException, InvalidConstantPoolFormatException { + TemplateExpander tex = new TemplateExpander(template, approxType, exactType); + return LOADER.loadClass(tex.patch).asSubclass(MethodHandleInvoker.class); + } + + /** Throw this if a bad entry point is taken. */ + protected RuntimeException wrongType(MethodHandle mh) { + return new WrongMethodTypeException("wrong call type for "+mh+ + " should be "+exactType+" in "+this); + } + protected void checkType(MethodHandle mh) { + if (mh.type() != exactType) + throw wrongType(mh); + } +} --- /dev/null 2009-01-20 02:47:27.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/Signatures.java 2009-01-20 02:47:27.000000000 -0800 @@ -0,0 +1,133 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn.util; + +import java.dyn.MethodType; +import java.util.ArrayList; +import java.util.List; + +public class Signatures { + + private Signatures() { } // cannot instantiate + + public static List> parseMethod(String bytecodeSignature, ClassLoader loader) { + return parseMethod(bytecodeSignature, 0, bytecodeSignature.length(), loader); + } + + public static List> parseMethod(String bytecodeSignature, + int start, int end, ClassLoader loader) { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + String str = bytecodeSignature; + int[] i = {start}; + ArrayList> ptypes = new ArrayList>(); + if (i[0] < end && str.charAt(i[0]) == '(') { + ++i[0]; // skip '(' + while (i[0] < end && str.charAt(i[0]) != ')') { + Class pt = parseSig(str, i, end, loader); + if (pt == null || pt == void.class) + parseError(str, "bad argument type"); + ptypes.add(pt); + } + ++i[0]; // skip ')' + } else { + parseError(str, "not a method type"); + } + Class rtype = parseSig(str, i, end, loader); + if (rtype == null || i[0] != end) + parseError(str, "bad return type"); + ptypes.add(rtype); + return ptypes; + } + + static private void parseError(String str, String msg) { + throw new IllegalArgumentException("bad signature: "+str+": "+msg); + } + + static private Class parseSig(String str, int[] i, int end, ClassLoader loader) { + if (i[0] == end) return null; + char c = str.charAt(i[0]++); + if (c == 'L') { + int begc = i[0], endc = str.indexOf(';', begc); + if (endc < 0) return null; + i[0] = endc+1; + String name = str.substring(begc, endc).replace('/', '.'); + try { + return loader.loadClass(name); + } catch (ClassNotFoundException ex) { + throw new TypeNotPresentException(name, ex); + } + } else if (c == '[') { + Class t = parseSig(str, i, end, loader); + if (t != null) + t = java.lang.reflect.Array.newInstance(t, 0).getClass(); + return t; + } else { + return Wrappers.basicTypeFromChar(c); + } + } + + public static String unparse(Class type) { + StringBuilder sb = new StringBuilder(); + unparseSig(type, sb); + return sb.toString(); + } + + public static String unparse(MethodType type) { + return unparseMethod(type.returnType(), type.parameterList()); + } + + public static String unparse(Object type) { + if (type instanceof Class) + return unparse((Class) type); + if (type instanceof MethodType) + return unparse((MethodType) type); + return (String) type; + } + + public static String unparseMethod(Class rtype, List> ptypes) { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (Class pt : ptypes) + unparseSig(pt, sb); + sb.append(')'); + unparseSig(rtype, sb); + return sb.toString(); + } + + static private void unparseSig(Class t, StringBuilder sb) { + char c = Wrappers.basicTypeChar(t); + if (c != 'L') { + sb.append(c); + } else { + boolean lsemi = (!t.isArray()); + if (lsemi) sb.append('L'); + sb.append(t.getName().replace('.', '/')); + if (lsemi) sb.append(';'); + } + } + +} --- /dev/null 2009-01-20 02:47:29.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/VerifyAccess.java 2009-01-20 02:47:28.000000000 -0800 @@ -0,0 +1,135 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn.util; + +import impl.java.dyn.MemberName; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; + +/** + * This class centralizes information about the JVM's linkage access control. + * @author jrose + */ +public class VerifyAccess { + + private VerifyAccess() { } // cannot instantiate + + /** + * Evaluate the JVM linkage rules for access to the given method on behalf of caller. + * Return non-null if and only if the given accessing class has at least partial + * privileges to invoke the given method. The return value {@code Object.class} + * denotes unlimited privileges. + *

+ * Some circumstances require an additional check on the + * leading parameter (the receiver) of the method, if it is non-static. + * In the case of {@code invokespecial} ({@code doDispatch} is false), + * the leading parameter must be the accessing class or a subclass. + * In the case of a call to a {@code protected} method outside the same + * package, the same constraint applies. + * @param m the proposed callee + * @param doDispatch if false, a non-static m will be invoked as if by {@code invokespecial} + * @param accessingClass the class for which the access check is being made + * @return null if the method is not accessible, else a receiver type constraint, else {@code Object.class} + */ + public static Class isAccessible(Class defc, int mods, + boolean doDispatch, Class accessingClass) { + if (!isAccessible(defc, accessingClass)) + return null; + Class constraint = Object.class; + if (!doDispatch && !Modifier.isStatic(mods)) { + constraint = accessingClass; + } + if (Modifier.isPublic(mods)) + return constraint; + if (Modifier.isPrivate(mods)) + return (defc == accessingClass) ? constraint : null; + if (isSamePackage(defc, accessingClass)) + return constraint; + if (Modifier.isProtected(mods) && defc.isAssignableFrom(accessingClass)) + return constraint; + // else it is private or package scoped, and not close enough + return null; + } + + /** + * Evaluate the JVM linkage rules for access to the given class on behalf of caller. + */ + public static boolean isAccessible(Class refc, Class accessingClass) { + int mods = refc.getModifiers(); + if (Modifier.isPublic(mods)) + return true; + if (isSamePackage(accessingClass, refc)) + return true; + return false; + } + + /** + * Test if two classes have the same class loader and package qualifier. + * @param class1 + * @param class2 + * @return whether they are in the same package + */ + public static boolean isSamePackage(Class class1, Class class2) { + if (class1 == class2) + return true; + if (class1.getClassLoader() != class2.getClassLoader()) + return false; + String name1 = class1.getName(), name2 = class2.getName(); + int dot = name1.lastIndexOf('.'); + if (dot != name2.lastIndexOf('.')) + return false; + for (int i = 0; i < dot; i++) { + if (name1.charAt(i) != name2.charAt(i)) + return false; + } + return true; + } + + /** + * Test if two classes are defined as part of the same package member (top-level class). + * If this is true, they can share private access with each other. + * @param class1 + * @param class2 + * @return whether they are identical or nested together + */ + public static boolean isSamePackageMember(Class class1, Class class2) { + if (class1 == class2) + return true; + if (!isSamePackage(class1, class2)) + return false; + if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2)) + return false; + return true; + } + + private static Class getOutermostEnclosingClass(Class c) { + Class pkgmem = c; + for (Class enc = c; (enc = enc.getEnclosingClass()) != null; ) + pkgmem = enc; + return pkgmem; + } +} --- /dev/null 2009-01-20 02:47:30.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/VerifyType.java 2009-01-20 02:47:29.000000000 -0800 @@ -0,0 +1,140 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn.util; + +import java.dyn.MethodType; + +/** + * This class centralizes information about the JVM verifier + * and its requirements about type correctness. + * @author jrose + */ +public class VerifyType { + + private VerifyType() { } // cannot instantiate + + /** + * True if a value can be stacked as the source type and unstacked as the + * destination type, without violating the JVM's type consistency. + * + * @param call the type of a stacked value + * @param recv the type by which we'd like to treat it + * @return whether the retyping can be done without motion or reformatting + */ + public static boolean isNullConversion(Class src, Class dst) { + if (src == dst) return true; + // Verifier allows any interface to be treated as Object: + if (dst.isInterface()) dst = Object.class; + if (src.isInterface()) src = Object.class; + if (src == dst) return true; // check again + if (dst == void.class) return true; // drop any return value + if (!src.isPrimitive()) return dst.isAssignableFrom(src); + // Verifier allows an int to carry byte, short, char, or even boolean: + if (dst == int.class) return Wrappers.isSubwordOrInt(src); + return false; + } + + /** + * True if a method handle can receive a call under a slightly different + * method type, without moving or reformatting any stack elements. + * + * @param call the type of call being made + * @param recv the type of the method handle receiving the call + * @return whether the retyping can be done without motion or reformatting + */ + public static boolean isNullConversion(MethodType call, MethodType recv) { + if (call == recv) return true; + int len = call.parameterCount(); + if (len != recv.parameterCount()) return false; + for (int i = 0; i < len; i++) + if (!isNullConversion(call.parameterType(i), recv.parameterType(i))) + return false; + return isNullConversion(recv.returnType(), call.returnType()); + } + + /** + * Determine if the JVM verifier allows a value of type call to be + * passed to a formal parameter (or return variable) of type recv. + * Returns 1 if the verifier allows the types to match without conversion. + * Returns -1 if the types can be made to match by a JVM-supported adapter. + * Cases supported are: + *

  • checkcast + *
  • conversion between any two integral types (but not floats) + *
  • unboxing from a wrapper to its corresponding primitive type + *
  • conversion in either direction between float and double + *
+ * (Autoboxing is not supported here; it must be done via Java code.) + * Returns 0 otherwise. + */ + public static int canPassUnchecked(Class src, Class dst) { + if (src == dst) + return 1; + + if (dst.isPrimitive()) { + if (dst == void.class) + // Return anything to a caller expecting void. + return 1; + if (src == void.class) + return 0; // void-to-something? + if (!src.isPrimitive()) + // Cannot pass a reference to any primitive type (exc. void). + return 0; + boolean swt = Wrappers.isSubwordOrInt(src); + boolean dwt = Wrappers.isSubwordOrInt(dst); + if (swt && dwt) { + if (Wrappers.bitWidth(src) >= Wrappers.bitWidth(dst)) + return -1; // truncation may be required + if (!Wrappers.isSigned(dst) && Wrappers.isSigned(src)) + return -1; // sign elimination may be required + } + if (src == float.class || dst == float.class) { + if (src == double.class || dst == double.class) + return -1; // floating conversion may be required + else + return 0; // other primitive conversions NYI + } else { + // all fixed-point conversions are supported + return 0; + } + } else if (src.isPrimitive()) { + // Cannot pass a primitive to any reference type. + // (Maybe allow null.class?) + return 0; + } + + // Handle reference types in the rest of the block: + + // The verifier treats interfaces exactly like Object. + if (dst.isInterface()) dst = Object.class; + //if (call.isInterface()) call = Object.class; + if (dst == Object.class) + // pass any reference to object or an arb. interface + return 1; + // else it's a definite "maybe" (cast is required) + return -1; + } + +} --- /dev/null 2009-01-20 02:47:31.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/Wrappers.java 2009-01-20 02:47:31.000000000 -0800 @@ -0,0 +1,272 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package impl.java.dyn.util; + +import java.util.HashMap; + +public class Wrappers { + + private Wrappers() { } // cannot instantiate + + /** If {@code type} is a primitive type, return the corresponding + * wrapper type, else return {@code type} unchanged. + */ + public static Class asWrapperType(Class type) { + if (!type.isPrimitive()) { + return type; + } + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get(type); + assert (memo != null); + return (Class) memo[0]; // unchecked warning is OK here + } + + /** If {@code type} is a wrapper type, return the corresponding + * primitive type, else return {@code type} unchanged. + */ + public static Class asPrimitiveType(Class type) { + if (type.isPrimitive()) { + return type; + } + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get(type); + if (memo == null) { + return type; + } + return (Class) memo[1]; // unchecked warning is OK here + } + + public static boolean isWrapperType(Class type) { + return asPrimitiveType(type) != type; + } + + public static boolean isPrimitiveType(Class type) { + return type.isPrimitive(); + } + + public static char basicTypeChar(Class type) { + if (!type.isPrimitive()) { + return 'L'; + } + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get(type); + assert (memo != null); + return (char) (Character) memo[2]; + } + + static final String PRIMITIVE_BITS_TABLE = "LZBCSFIZZDJ"; + // "--01234 78" + + /** Return the number of bits in the given type, or zero for refs. */ + public static int bitWidth(Class type) { + return bitWidth(basicTypeChar(type)); + } + + /** Return the number of bits in the given basic type, or zero for refs. */ + public static int bitWidth(char c) { + int i = PRIMITIVE_BITS_TABLE.indexOf(c); + if (i < 0) throw new IllegalArgumentException("not a basic type char: "+c); + i -= 2; + switch (i) { + case -2: return 0; // L + case -1: return 1; // Z + case 0: return 8; // B + default: return (i + (i & 1)) * 8; + } + } + + static final String PRIMITIVE_SIGN_TABLE = "JZICSZB"; + // "SuSuS S" + /** Return whether the given type is a signed integral type. */ + public static boolean isSigned(Class type) { + return isSigned(basicTypeChar(type)); + } + + /** Return whether the given type is a signed integral type. */ + public static boolean isSigned(char c) { + int i = PRIMITIVE_SIGN_TABLE.indexOf(c); + return (i & 1) == 0; + } + + /** Return whether the given type is a unsigned integral type. */ + public static boolean isUnsigned(Class type) { + return isUnsigned(basicTypeChar(type)); + } + + /** Return whether the given type is a unsigned integral type. */ + public static boolean isUnsigned(char c) { + int i = PRIMITIVE_SIGN_TABLE.indexOf(c); + return (i & 0x11) == 1; + } + + /** Return whether the given type is an integral type. */ + public static boolean isIntegral(Class type) { + return isIntegral(basicTypeChar(type)); + } + + /** Return whether the given type is an integral type. */ + public static boolean isIntegral(char c) { + int i = PRIMITIVE_SIGN_TABLE.indexOf(c); + return i >= 0; + } + + /** Report if the type is one of int, boolean, byte, char, or short. */ + public static boolean isSubwordOrInt(Class type) { + return isSubwordOrInt(basicTypeChar(type)); + } + + /** Report if the type char is one of "IZBCS". */ + public static boolean isSubwordOrInt(char c) { + return PRIMITIVE_SIGN_TABLE.indexOf(c) > 0; + } + + /** Return whether the given type is a floating primitive type. */ + public static boolean isFloating(Class type) { + return isFloating(basicTypeChar(type)); + } + + /** Return whether the given type is a floating primitive type. */ + public static boolean isFloating(char c) { + return c == 'F' || c == 'D'; + } + + /** Return the primitive type that corresponds to the given bytecode + * signature character. Return {@code Object.class} for the character + * 'L', and null for any non-signature character or '['. + */ + public static Class basicTypeFromChar(char c) { + if (c == 'L') { + return Object.class; + } +// if (c == '[') { +// return Object[].class; +// } + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get((Character)c); + if (memo == null) + return null; // random junk character + return (Class) memo[1]; + } + + public static Object zeroValue(Class type) { + if (!type.isPrimitive()) { + return null; + } + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get(type); + assert (memo != null); + return memo[3]; + } + + public static T wrap(Object x, Class numClass) { + if (wrappers.isEmpty()) { + fillWrappers(); + } + Object[] memo = wrappers.get(numClass); + if (memo == null) return numClass.cast(x); // no change + Class wrapType = (Class) memo[0]; // unchecked warning is OK here + return wrapType.cast(wrap(x, (Character) memo[2])); + } + public static Object wrap(Object x, char c) { + Number xn = numberValue(x); + switch (c) { + case 'I': return Integer.valueOf(xn.intValue()); + case 'J': return Long.valueOf(xn.longValue()); + case 'F': return Float.valueOf(xn.floatValue()); + case 'D': return Double.valueOf(xn.doubleValue()); + case 'S': return Short.valueOf((short) xn.intValue()); + case 'B': return Byte.valueOf((byte) xn.intValue()); + case 'C': return Character.valueOf((char) xn.intValue()); + case 'Z': return Boolean.valueOf(boolValue(xn.longValue())); + case 'V': return null; + } + return xn; + } + + private static Number numberValue(Object x) { + if (x instanceof Number) return (Number)x; + if (x instanceof Character) return (int)(Character)x; + if (x instanceof Boolean) return (Boolean)x ? 1 : 0; + // Remaining allowed case of void: Must be a null reference. + return (Number)x; + } + private static boolean boolValue(long bits) { + bits &= 1; // simple 31-bit zero extension + return (bits != 0); + } + + private static final HashMap wrappers + = new HashMap(20); + + private static void fillWrappers() { + Object[][] memos = { + {Boolean.class, Boolean.TYPE, 'Z', (Boolean) false}, + {Character.class, Character.TYPE, 'C', (Character) '\000'}, + {Byte.class, Byte.TYPE, 'B', (Byte) (byte) 0}, + {Short.class, Short.TYPE, 'S', (Short) (short) 0}, + {Integer.class, Integer.TYPE, 'I', (Integer) 0}, + {Long.class, Long.TYPE, 'J', (Long) 0L}, + {Float.class, Float.TYPE, 'F', (Float) 0.0F}, + {Double.class, Double.TYPE, 'D', (Double) 0.0}, + {Void.class, Void.TYPE, 'V', null} + }; + for (Object[] memo : memos) { + wrappers.put(memo[0], memo); + wrappers.put(memo[1], memo); + wrappers.put(memo[2], memo); + } + } + + // TO DO: Put these into a unit test. + private static Class PTYPE = int.class, WTYPE = Integer.class; + static { + assert(PTYPE != WTYPE); + assert(asPrimitiveType(PTYPE) == PTYPE); + assert(asPrimitiveType(WTYPE) == PTYPE); + assert( asWrapperType(PTYPE) == WTYPE); + assert( asWrapperType(WTYPE) == WTYPE); + + assert(bitWidth(String.class) == 0); + assert(bitWidth(Integer.class) == 0); + assert(bitWidth(int.class) == 32); + assert(bitWidth(char.class) == 16); + assert(bitWidth(short.class) == 16); + assert(bitWidth(byte.class) == 8); + assert(bitWidth(boolean.class) == 1); + assert(bitWidth(double.class) == 64); + assert(bitWidth(float.class) == 32); + } +} --- /dev/null 2009-01-20 02:47:32.000000000 -0800 +++ new/src/share/projects/meth/src/impl/java/dyn/util/package-info.java 2009-01-20 02:47:32.000000000 -0800 @@ -0,0 +1,31 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * Extra support for using JSR 292 RI, package java.dyn. + * @author jrose + */ + +package impl.java.dyn.util; --- /dev/null 2009-01-20 02:47:34.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/CallSite.java 2009-01-20 02:47:33.000000000 -0800 @@ -0,0 +1,116 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * An invokedynamic call site, as reified to the bootstrap method. + * Every instance of a call site corresponds to a distinct instance + * of the invokedynamic instruction. + * Call sites have state, one reference word, called the target, + * and typed as a {@link MethodHandle}. When this state is null (as it is + * initially) the call site is in the unlinked state. Otherwise, it is said + * to be linked to its target. + *

+ * When an unlinked call site is executed, a bootstrap routine is called + * to finish the execution of the call site, and optionally to link + * the call site. + *

+ * @author John Rose, JSR 292 EG + */ +public abstract class CallSite { + protected MethodHandle target; + protected final Object caller; // usually a class + protected final String name; + protected final MethodType type; + + protected CallSite(Object caller, String name, MethodType type) { + this.caller = caller; + this.name = name; + this.type = type; + } + + /** + * Report the current linkage state of the call site. (This is mutable.) + * The value is null if and only if the call site is currently unlinked. + * @return the current linkage state of the call site + */ + public MethodHandle getTarget() { + return target; + } + + /** + * Link or relink the call site, by setting its target method. + * @param target the new target, or null if it is to be unlinked + * @throws WrongMethodTypeException if the new target is not null + * and has a method type that differs from the call site type + */ + public void setTarget(MethodHandle target) { + checkTarget(target); + this.target = target; + } + + protected void checkTarget(MethodHandle target) { + if (!canSetTarget(target)) + throw new WrongMethodTypeException(String.valueOf(target)); + } + + protected boolean canSetTarget(MethodHandle target) { + return (target == null || target.type() == type()); + } + + /** + * Report the class containing the call site. + * This is immutable static context. + * @return class containing the call site + */ + public Class callerClass() { + return (Class) caller; + } + + /** + * Report the method name specified in the {@code invokedynamic} instruction. + * This is immutable static context. + * @return method name specified by the call site + */ + public String name() { + return name; + } + + /* + * Report the result and parameter types, derived from the invocation descriptor, and resolved + * against the calling class's class loader. + * This is immutable static context. + * @return method type specified by the call site + */ + public MethodType type() { + return type; + } + + @Override + public String toString() { + return "CallSite#"+hashCode()+"["+name+type+" => "+target+"]"; + } +} --- /dev/null 2009-01-20 02:47:35.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/Dynamic.java 2009-01-20 02:47:35.000000000 -0800 @@ -0,0 +1,50 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * Syntactic marker interface to request javac to emit an {@code invokedynamic} instruction. + * A language compiler might use this interface to express invokedynamic instructions as follows: + *

+ * Dynamic.greet("hello world", 123);
+ * // previous line generates invokedynamic "greet" "(Ljava/lang/String;I)Ljava/lang/Object;"
+ * Dynamic.println(123);
+ * // previous line generates invokedynamic "println" "(I)V"
+ * Dynamic.#"long:strange:name"();
+ * // previous line generates invokedynamic "long:strange:name" "()Ljava/lang/Object;"
+ * 
+ *

+ * This type has no particular meaning as a class or interface supertype, and need never be implemented by any class. + * Logically, it denotes a dynamically typed reference to any object. + * As such it may be viewed as logically containing all methods on any of those types. + *

+ * This type may be used as a marker interface for arguments to method handles, in order + * to distinguish the static type {@code Object} from a dynamically typed reference. + * @author John Rose, JSR 292 EG + */ +public interface Dynamic { + // no methods +} --- /dev/null 2009-01-20 02:47:36.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/InvokeDynamicBootstrapError.java 2009-01-20 02:47:36.000000000 -0800 @@ -0,0 +1,53 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * Thrown to indicate that an {@code invokedynamic} instruction has + * failed to find its bootstrap method. + * This must have been declared during a class's initialization + * by a call to {@link Linkage#registerBootstrapMethod}. + * + * @author John Rose, JSR 292 EG + */ +public class InvokeDynamicBootstrapError extends LinkageError { + /** + * Constructs a {@code InvokeDynamicBootstrapError} with no detail message. + */ + public InvokeDynamicBootstrapError() { + super(); + } + + /** + * Constructs a {@code InvokeDynamicBootstrapError} with the specified + * detail message. + * + * @param s the detail message. + */ + public InvokeDynamicBootstrapError(String s) { + super(s); + } +} --- /dev/null 2009-01-20 02:47:37.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/Linkage.java 2009-01-20 02:47:37.000000000 -0800 @@ -0,0 +1,211 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import impl.java.dyn.util.MethodHandleInvoker; +import java.util.WeakHashMap; +import sun.reflect.Reflection; + +/** + * Static methods which control the linkage of invokedynamic call sites. + * @author John Rose, JSR 292 EG + */ +public class Linkage { + private Linkage() {} // do not instantiate + + /** + * Register a bootstrap method for use for a given caller class. + * The method handle must be of a type equivalent to {@link Linkage#bootstrapInvokeDynamic}. + *

+ * The operation will fail with an exception if any of the following conditions hold: + *

    + *
  • The caller of this method is in a different package than the {@code callerClass}, + * and there is a security manager, and its {@code checkPermission} call throws + * when passed {@link LinkagePermission}("registerBootstrapMethod",callerClass). + *
  • The given class already has a bootstrap method, either from an embedded + * {@code BootstrapInvokeDynamic} classfile attribute, or from a previous + * call to this method. + *
  • The given class is already fully initialized. + *
  • The given class is in the process of initialization, in another thread. + *
+ * Because of these rules, a class may install its own bootstrap method in + * a static initializer. + */ + public static + void registerBootstrapMethod(Class callerClass, MethodHandle mh) { + Class callc = Reflection.getCallerClass(2); + checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod"); + if (mh != null && mh.type() != BOOTSTRAP_METHOD_TYPE) + throw new WrongMethodTypeException(mh.type().toString()); + synchronized (bootstrapMethods) { + if (bootstrapMethods.containsKey(callerClass)) + throw new IllegalStateException("bootstrap method already declared in "+callerClass); + bootstrapMethods.put(callerClass, mh); + } + } + + /** + * Report the bootstrap method registered for a given class. + * Returns null if the class has never yet registered a bootstrap method, + * or if the class has explicitly registered a null bootstrap method. + * Only callers privileged to set the bootstrap method may inquire + * about it, because a bootstrap method is potentially a back-door entry + * points to its class. + */ + public static + MethodHandle getBootstrapMethod(Class callerClass) { + Class callc = Reflection.getCallerClass(2); + checkPackagePrivilege(callc, callerClass, "registerBootstrapMethod"); + synchronized (bootstrapMethods) { + return bootstrapMethods.get(callerClass); + } + } + + /** The type of any bootstrap method is (CallSite,Object...)Object. + * The varargs marker is required. + */ + public static final MethodType BOOTSTRAP_METHOD_TYPE + = MethodType.make(Object.class, + CallSite.class, Object[].class).changeVarArgs(true); + + private static MethodHandleInvoker bootstrapMethodInvoker; + + private static final WeakHashMap bootstrapMethods = + new WeakHashMap(); + + /** Determine if the caller class has declared or registered its own bootstrap method. + * If so, delegate this call to it. Otherwise, throw an IncompatibleClassChangeError. + */ + public static + Object bootstrapInvokeDynamic(CallSite site, Object... receiverAndArguments) { + Class callerClass = site.callerClass(); + MethodHandle mh; + synchronized (bootstrapMethods) { + mh = bootstrapMethods.get(callerClass); + } + if (mh == null) + throw new IllegalStateException("no bootstrap method declared in "+callerClass); + + System.out.println(site+": calling bootstrap "+mh); + if (bootstrapMethodInvoker == null) + bootstrapMethodInvoker = MethodHandleInvoker.make(BOOTSTRAP_METHOD_TYPE); + return bootstrapMethodInvoker.invoke(mh, site, receiverAndArguments); + } + + /** + * Invalidate all invokedynamic call sites everywhere. + *

+ * When this method returns, every invokedynamic instruction + * will invoke its bootstrap method on next call. + *

+ * It is unspecified whether call sites already known to the Java + * code will continue to be associated with invokedynamic + * instructions. If any call site is still so associated, its + * {@link CallSite#getTarget()} method is guaranteed to return null + * the invalidation operation completes. + *

+ * Invalidation operations are likely to be slow. Use them sparingly. + */ + public static + Object invalidateAll() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(new LinkagePermission("invalidateAll")); + } + throw new UnsupportedOperationException("NYI"); + } + + /** + * Invalidate all invokedynamic call sites associated + * with the given class. + * (These are exactly those sites which report the given class + * via the {@link CallSite#callerClass()} method.) + *

+ * When this method returns, every matching invokedynamic + * instruction will invoke its bootstrap method on next call. + *

+ * For additional semantics of call site invalidation, + * see {@link #invalidateAll()}. + */ + public static + Object invalidateCallerClass(Class callerClass) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(new LinkagePermission("invalidateAll", callerClass)); + } + throw new UnsupportedOperationException("NYI"); + } + + /** + * Ensure the requesting class have privileges to perform invokedynamic + * linkage operations on subjectClass. True if requestingClass is + * null (meaning the request originates from the JVM) or if the + * classes are in the same package and have consistent class loaders. + * (The subject class loader must be identical with or be a child of + * the requesting class loader.) + * @param requestingClass + * @param subjectClass + * @return + */ + static void checkPackagePrivilege(Class requestingClass, Class subjectClass, + String permissionName) { + if (requestingClass == null) return; + if (requestingClass == subjectClass) return; + SecurityManager security = System.getSecurityManager(); + if (security == null) return; // open season + ClassLoader rcl = requestingClass.getClassLoader(); + ClassLoader scl = subjectClass.getClassLoader(); + if (isParent(rcl, scl)) { + String rn = requestingClass.getName(); + if (rn.startsWith("java.dyn.")) return; + String sn = subjectClass.getName(); + if (samePackage(rn, sn)) return; + } + security.checkPermission(new LinkagePermission(permissionName, requestingClass)); + } + + static + MethodHandle findBootstrapMethod(Class callerClass, Class searchBootstrapClass) { + if (searchBootstrapClass != null) throw new UnsupportedOperationException("NYI"); + MethodHandle mh = getBootstrapMethod(callerClass); + System.out.println("reporting bootstrap method to JVM: "+mh); + return mh; + } + + private static boolean isParent(ClassLoader rcl, ClassLoader scl) { + while (scl != null && scl != rcl) + scl = scl.getParent(); + return (scl == rcl); + } + + private static boolean samePackage(String rn, String sn) { + assert((rn.indexOf('/') & sn.indexOf('/')) < 0); // no bytecode names + int lastDot = rn.lastIndexOf('.'); + if (lastDot != sn.lastIndexOf('.')) return false; + return rn.startsWith(sn.substring(0, lastDot+1)); + } + +} --- /dev/null 2009-01-20 02:47:38.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/LinkagePermission.java 2009-01-20 02:47:38.000000000 -0800 @@ -0,0 +1,111 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import java.security.*; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; + +/** + * This class is for runtime permissions. A RuntimePermission + * contains a name (also referred to as a "target name") but + * no actions list; you either have the named permission + * or you don't. + * + *

+ * The target name is the name of the runtime permission (see below). The + * naming convention follows the hierarchical property naming convention. + * Also, an asterisk + * may appear at the end of the name, following a ".", or by itself, to + * signify a wildcard match. For example: "loadLibrary.*" or "*" is valid, + * "*loadLibrary" or "a*b" is not valid. + *

+ * The following table lists all the possible RuntimePermission target names, + * and for each provides a description of what the permission allows + * and a discussion of the risks of granting code the permission. + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Permission Target NameWhat the Permission AllowsRisks of Allowing this Permission
registerBootstrapMethod.{class name}Specifying a bootstrap method for invokedynamic, within a class of the given nameAn attacker could attempt to attach a bootstrap method to a class which + * has just been loaded, thus gaining control of its invokedynamic calls.
invalidateAllForce the relinking of invokedynamic call sites everywhere.This could allow an attacker to slow down the system, or perhaps surface timing bugs in a dynamic language implementations, by forcing redundant relinking operations.
invalidateCallerClass.{class name}Force the relinking of invokedynamic call sites in the given class.See {@code invalidateAll}.
+ * + * @see java.security.BasicPermission + * @see java.lang.SecurityManager + * + * @author John Rose, JSR 292 EG + */ + +public final class LinkagePermission extends BasicPermission { + /** + * Create a new LinkagePermission with the given name. + * The name is the symbolic name of the LinkagePermission, such as + * "registerBootstrapMethod", "invalidateClass.*", etc. An asterisk + * may appear at the end of the name, following a ".", or by itself, to + * signify a wildcard match. + * + * @param name the name of the LinkagePermission + */ + public LinkagePermission(String name) { + super(name); + } + + /** + * Create a new LinkagePermission with the given name on the given class. + * Equivalent to {@code LinkagePermission(name+"."+clazz.getName())}. + * + * @param name the name of the LinkagePermission + * @param clazz the class affected by the permission + */ + public LinkagePermission(String name, Class clazz) { + super(name + "." + clazz.getName()); + } +} --- /dev/null 2009-01-20 02:47:39.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/MethodHandle.java 2009-01-20 02:47:39.000000000 -0800 @@ -0,0 +1,129 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +//import java.dyn.emu.*; +import impl.java.dyn.*; + +/** + * A method handle is a typed reference to the entry point of a method. + *

+ * Method handles are strongly typed according to signature. + * They are not distinguished by method name or enclosing class. + * A method handle must be invoked under a signature which exactly matches + * the method handle's own type. + *

+ * Every method handle confesses its type via the type accessor. + * The structure of this type is a series of classes, one of which is + * the return type of the method (or void.class if none). + *

+ * Every method handle appears as an object containing a method named + * invoke, whose signature exactly matches + * the method handle's type. + * A normal Java method call (using the invokevirtual instruction) + * can invoke this method from Java source code (if language support is present). + *

+ * Every call to a method handle specifies an intended method type, + * which must exactly match the type of the method handle. + * (The type is specified in the invokevirtual instruction, + * via a {@code CONSTANT_NameAndType} constant pool entry.) + * The call looks within the receiver object for a method + * named invoke of the intended method type. + * The call fails with a {@link WrongMethodTypeException} + * if the method does not exist, even if there is an invoke + * method of a closely similar signature. + *

+ * A method handle is an unrestricted capability to call a method. + * A method handle can be formed on a non-public method by a class + * that has access to that method; the resulting handle can be used + * in any place by any caller who receives a reference to it. Thus, access + * checking is performed when the method handle is created, not + * (as in reflection) every time it is called. Handles to non-public + * methods, or in non-public classes, should generally be kept secret. + * They should not be passed to untrusted code. + *

+ * Bytecode in an extended JVM can directly call a method handle's + * invoke from an invokevirtual instruction. + * The receiver class type must be MethodHandle and the method name + * must be invoke. The signature of the invocation + * (after resolving symbolic type names) must exactly match the method type + * of the target method. + *

+ * Bytecode in an extended JVM can directly obtain a method handle + * for any accessible method from a ldc instruction + * which refers to a CONSTANT_Methodref or + * CONSTANT_InterfaceMethodref constant pool entry. + *

+ * All JVMs can also use a reflective API called MethodHandles + * for creating and calling method handles. + *

+ * A method reference may refer either to a static or non-static method. + * In the non-static case, the method handle type includes an explicit + * receiver argument, prepended before any other arguments. + * In the method handle's type, the initial receiver argument is typed + * according to the class under which the method was initially requested. + * (E.g., if a non-static method handle is obtained via ldc, + * the type of the receiver is the class named in the constant pool entry.) + *

+ * When a method handle to a virtual method is invoked, the method is + * always looked up in the receiver (that is, the first argument). + *

+ * A non-virtual method handles to a specific virtual method implementation + * can also be created. These do not perform virtual lookup based on + * receiver type. Such a method handle simulates the effect of + * an invokespecial instruction to the same method. + * + * @see MethodType + * @see MethodHandles + * @author John Rose, JSR 292 EG + */ +public class MethodHandle extends MethodHandleImpl { + // interface MethodHandle> + // { T type(); public R invoke(A...); } + + final private MethodType type; + + /** + * Report the type of this method handle. + * Every invocation of this method handle must exactly match this type. + * @return the method handle type + */ + public MethodType type() { + return type; + } + + /** + * The constructor for MethodHandle may only be called by privileged code. + * Subclasses may be in other packages, but must possess + * a token which they obtained from MH with a security check. + * @param token non-null object which proves access permission + * @param type type (permanently assigned) of the new method handle + */ + protected MethodHandle(Access token, MethodType type) { + super(token); + this.type = type; + } +} --- /dev/null 2009-01-20 02:47:41.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/MethodHandles.java 2009-01-20 02:47:40.000000000 -0800 @@ -0,0 +1,940 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import impl.java.dyn.Access; +import impl.java.dyn.MemberName; +import impl.java.dyn.MethodHandleImpl; +import impl.java.dyn.util.MethodHandleInvoker; +import impl.java.dyn.util.VerifyAccess; +import impl.java.dyn.util.Wrappers; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import sun.reflect.Reflection; +import static impl.java.dyn.MemberName.newIllegalArgumentException; +import static impl.java.dyn.MemberName.newNoAccessException; + +/** + * Fundamental operations and utilities for MethodHandle. + *

+ * API Note: The matching of method types in this API cannot + * be completely checked by Java's generic type system for three reasons: + *

    + *
  1. Method types range over all possible arities, + * from no arguments to an arbitrary number of arguments. + * Generics are not variadic, and so cannot represent this.
  2. + *
  3. Method types can specify arguments of primitive types, + * which Java generic types cannot range over.
  4. + *
  5. Method types can optionally specify varargs (ellipsis).
  6. + *
+ * @author John Rose, JSR 292 EG + */ +public class MethodHandles { + + private MethodHandles() { } // do not instantiate + + private static final Access IMPL_TOKEN = Access.getToken(); + private static final MemberName.Factory IMPL_LOOKUP = MemberName.getFactory(IMPL_TOKEN); + + //// Method handle creation from ordinary methods. + + /** + * Produce a method handle for a static method. + * The type of the method handle will be that of the method. + * The method and all its argument types must be accessible to the caller. + * If the method's class has not yet been initialized, that is done + * immediately, before the method handle is returned. + * @param defc the class from which the method is accessed + * @param name the name of the method + * @param type the type of the method + * @return the desired method handle, or null if no such method exists + * @exception SecurityException TBD + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle findStatic(Class defc, String name, MethodType type) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type, Modifier.STATIC), true, caller); + checkStatic(true, method, caller); + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, caller); + } + + private static void checkStatic(boolean wantStatic, MemberName m, Class caller) { + if (wantStatic != m.isStatic()) { + String message = wantStatic ? "expected a static method" : "expected a non-static method"; + throw newNoAccessException(message, m, caller); + } + } + + /** + * Produce a method handle for a virtual method. + * The type of the method handle will be that of the method, + * with the receiver type ({@code defc}) prepended. + * The method and all its argument types must be accessible to the caller. + *

+ * When called, the handle will treat the first argument as a receiver + * and dispatch on the receiver's type to determine which method + * implementation to enter. + * (The dispatching action is identical with that performed by an + * {@code invokevirtual} or {@code invokeinterface} instruction.) + * @param defc the class or interface from which the method is accessed + * @param name the name of the method + * @param type the type of the method, with the receiver argument omitted + * @return the desired method handle, or null if no such method exists + * @exception SecurityException TBD + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle findVirtual(Class defc, String name, MethodType type) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), true, caller); + checkStatic(false, method, caller); + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller); + } + + /** + * Produce an early-bound method handle for a virtual method, + * or a handle for a constructor, as if called from an {@code invokespecial} + * instruction from {@code caller}. + * The type of the method handle will be that of the method or constructor, + * with a suitably restricted receiver type (such as {@code caller}) prepended. + * The method or constructor and all its argument types must be accessible + * to the caller. + *

+ * When called, the handle will treat the first argument as a receiver, + * but will not dispatch on the receiver's type. + * (This direct invocation action is identical with that performed by an + * {@code invokespecial} instruction.) + *

+ * If the explicitly specified caller class is not identical with the actual + * caller of {@code findSpecial}, a security check TBD is performed. + * @param defc the class or interface from which the method is accessed + * @param name the name of the method, or "" for a constructor + * @param type the type of the method, with the receiver argument omitted + * @param specialCaller the proposed calling class to perform the {@code invokespecial} + * @return the desired method handle, or null if no such method exists + * @exception SecurityException TBD + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle findSpecial(Class defc, String name, MethodType type, + Class specialCaller) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + checkSpecialCaller(specialCaller, caller); + MemberName method = IMPL_LOOKUP.resolveOrFail(new MemberName(defc, name, type), false, specialCaller); + checkStatic(false, method, caller); + if (name.equals("")) { + if (defc != specialCaller) + throw newNoAccessException("constructor must be local to caller", method, caller); + } else if (defc.isInterface() || !defc.isAssignableFrom(specialCaller)) { + throw newNoAccessException("method must be in a superclass of caller", method, caller); + } + return MethodHandleImpl.findMethod(IMPL_TOKEN, method, false, specialCaller); + } + + /** + * Produce an early-bound method handle for a non-static method. + * The receiver must have a supertype {@code defc} in which a method + * of the given name and type is accessible to the caller. + * The method and all its argument types must be accessible to the caller. + * The type of the method handle will be that of the method. + * The given receiver will be bound into the method handle. + *

+ * Equivalent to the following expression: + * + * {@link #insertArgument}({@link #findVirtual}(defc, name, type), receiver) + * + * @param receiver the object from which the method is accessed + * @param name the name of the method + * @param type the type of the method, with the receiver argument omitted + * @return the desired method handle, or null if no such method exists + * @exception SecurityException TBD + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle bind(Object receiver, String name, MethodType type) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + Class rcvc = receiver.getClass(); // may get NPE + MemberName reference = new MemberName(rcvc, name, type); + MemberName method = IMPL_LOOKUP.resolveOrFail(reference, true, caller); + checkStatic(false, method, caller); + MethodHandle dmh = MethodHandleImpl.findMethod(IMPL_TOKEN, method, true, caller); + MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver); + if (bmh == null) + throw newNoAccessException(method, caller); + return bmh; + } + + /** + * Make a direct method handle to m, if the current caller has permission. + * If m is non-static, the receiver argument is treated as an initial argument. + * If m is virtual, overriding is respected on every call. + * Unlike the Core Reflection API, exceptions are not wrapped. + * The type of the method handle will be that of the method, + * with the receiver type prepended (but only if it is non-static). + * If the method's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the caller. + * If m is not public, do not share the resulting handle with untrusted callers. + * @param m the reflected method + * @return a method handle which can invoke the reflected method + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle unreflect(Method m) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + return unreflect(new MemberName(m), m.isAccessible(), true, caller); + } + + /** + * Produce a method handle for a reflected method. + * It will bypass checks for overriding methods on the receiver, + * as if by the {@code invokespecial} instruction. + * The type of the method handle will be that of the method, + * with the receiver type prepended. + * If the method's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the caller, + * as if {@code invokespecial} instruction were being linked. + * @param m the reflected method + * @return a method handle which can invoke the reflected method + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle unreflectSpecial(Method m, Class specialCaller) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + checkSpecialCaller(specialCaller, caller); + MemberName mname = new MemberName(m); + checkStatic(false, mname, caller); + return unreflect(mname, m.isAccessible(), false, specialCaller); + } + +// /** +// * Produce a method handle for a reflected constructor. +// * It will allow direct access to the constructor, +// * as if by the {@code invokespecial} instruction. +// * The type of the method handle will be that of the method, +// * with the receiver type prepended. +// * If the constructor's {@code accessible} flag is not set, +// * access checking is performed immediately on behalf of the caller, +// * as if {@code invokespecial} instruction were being linked. +// * @param ctor the reflected constructor +// * @param specialCaller the proposed calling class to perform the {@code invokespecial} +// * @return a method handle which can invoke the reflected constructor +// * @exception NoAccessException if access checking fails +// */ +// public static +// MethodHandle unreflectSpecial(Constructor ctor, Class specialCaller) throws NoAccessException { +// Class caller = Reflection.getCallerClass(2); +// checkSpecialCaller(specialCaller, caller); +// MemberName m = new MemberName(ctor); +// checkStatic(false, m, caller); +// return unreflect(m, ctor.isAccessible(), false, specialCaller); +// } + + private static + void checkSpecialCaller(Class specialCaller, Class caller) { + if (caller != null && !VerifyAccess.isSamePackageMember(specialCaller, caller)) + throw newNoAccessException("no private access", new MemberName(specialCaller), caller); + } + + // Helper for creating handles on reflected methods and constructors. + private static + MethodHandle unreflect(MemberName m, boolean isAccessible, boolean doDispatch, Class caller) { + MethodType mtype = m.getInvocationType(); + Class defc = m.getDeclaringClass(); + int mods = m.getModifiers(); + if (m.isStatic()) { + if (!isAccessible && + VerifyAccess.isAccessible(defc, mods, false, caller) == null) + throw newNoAccessException(m, caller); + } else { + Class constraint; + if (isAccessible) { + // abbreviated access check for "unlocked" method + constraint = doDispatch ? defc : caller; + } else { + constraint = VerifyAccess.isAccessible(defc, mods, doDispatch, caller); + } + if (constraint != defc && !constraint.isAssignableFrom(defc)) { + if (!defc.isAssignableFrom(constraint)) + throw newNoAccessException("receiver must be in caller class", m, caller); + mtype = mtype.changeParameterType(0, constraint); + } + } + return MethodHandleImpl.findMethod(IMPL_TOKEN, m, doDispatch, caller); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle giving read access to a reflected field. + * The type of the method handle will have a return type of the field's + * value type. Its sole argument will be the field's containing class + * (but only if it is non-static). + * If the method's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the caller. + * @param f the reflected field + * @return a method handle which can load values from the reflected field + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle unreflectGetter(Field f) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), false, (Class)caller); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle giving write access to a reflected field. + * The type of the method handle will have a void return type. + * Its last argument will be the field's value type. + * Its other argument will be the field's containing class + * (but only if it is non-static). + * If the method's {@code accessible} flag is not set, + * access checking is performed immediately on behalf of the caller. + * @param f the reflected field + * @return a method handle which can store values into the reflected field + * @exception NoAccessException if access checking fails + */ + public static + MethodHandle unreflectSetter(Field f) throws NoAccessException { + Class caller = Reflection.getCallerClass(2); + return MethodHandleImpl.accessField(IMPL_TOKEN, new MemberName(f), true, (Class)caller); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle giving read access to elements of an array. + * The type of the method handle will have a return type of the array's + * element type. Its first argument will be the array type, + * and the second will be {@code int}. + * @param arrayClass an array type + * @return a method handle which can load values from the given array type + * @throws IllegalArgumentException if arrayClass is not an array type + */ + public static + MethodHandle arrayElementGetter(Class arrayClass) throws IllegalArgumentException { + Class caller = Reflection.getCallerClass(2); + return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, false, (Class)caller); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle giving write access to elements of an array. + * The type of the method handle will have a void return type. + * Its last argument will be the array's element type. + * The first and second arguments will be the array type and int. + * @return a method handle which can store values into the array type + * @throws IllegalArgumentException if arrayClass is not an array type + */ + public static + MethodHandle arrayElementSetter(Class arrayClass) throws IllegalArgumentException { + Class caller = Reflection.getCallerClass(2); + return MethodHandleImpl.accessArrayElement(IMPL_TOKEN, arrayClass, true, (Class)caller); + } + + + /// method handle invocation (reflective style) + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Call the {@code invoke} method of a given method handle. + * The given arguments must exactly match the parameter types of the method handle. + * TBD: Fill in the details. + * No conversions are performed except reference casting and unboxing of primitives. + * @param target method handle to invoke + * @param arguments arguments to pass to the target (with any primitives wrapped) + * @return the result of the method (with any primitive wrapped) + */ + public static + Object invoke(MethodHandle target, Object... arguments) { + // TO DO: Remove this checking logic; must be part of the invoker anyway. + int length = arguments.length; + MethodType type = target.type(); + if (type.parameterCount() != length) + throw new WrongMethodTypeException("wrong number of arguments"); + for (int i = 0; i < length; i++) { + Object argument = arguments[i]; + Class ptype = type.parameterType(i); + if (ptype.isPrimitive()) { + argument.getClass(); // provoke NPE if null + ptype = Wrappers.asWrapperType(ptype); + } else if (ptype.isInterface()) { + ptype = Object.class; // no check + } + if (ptype != Object.class) { + ptype.cast(argument); + } + } + // End of checking logic. + return MethodHandleInvoker.make(target.type()).invoke(target, arguments); + } + + /** + * WORK IN PROGRESS: + * Perform value checking, exactly as if for an adapted method handle. + * It is assumed that the given value is either null, of type T0, + * or (if T0 is primitive) of the wrapper type corresponding to T0. + * The following checks and conversions are made: + *

    + *
  • If T0 and T1 are references, then a cast to T1 is applied. + * (The types do not need to be related in any particular way.) + *
  • If T0 and T1 are primitives, then a widening or narrowing + * conversion is applied, if one exists. + *
  • If T0 is a primitive and T1 a reference, and + * T0 has a wrapper type TW, a boxing conversion to TW is applied, + * possibly followed by a reference conversion. + * T1 must be TW or a supertype. + *
  • If T0 is a reference and T1 a primitive, and + * T1 has a wrapper type TW, an unboxing conversion is applied, + * possibly preceded by a reference conversion. + * T0 must be TW or a supertype. + *
  • If T1 is void, the return value is discarded + *
  • If T0 is void and T1 a reference, a null value is introduced. + *
  • If T0 is void and T1 a primitive, a zero value is introduced. + *
+ * If the value is discarded, null will be returned. + * @param valueType + * @param value + * @return the value, converted if necessary + * @throws java.lang.ClassCastException if a cast fails + */ + static + Object checkValue(Class T0, Class T1, Object value) + throws ClassCastException + { + if (T0 == T1) { + // no conversion needed + return value; + } + boolean prim0 = T0.isPrimitive(), prim1 = T1.isPrimitive(); + Class TW; + if (!prim0) { + if (!prim1) { + return T1.cast(T0.cast(value)); + } + // convert reference to primitive by unboxing + TW = Wrappers.asWrapperType(T0); + return T1.cast(TW.cast(value)); + } + if (!prim1) { + // convert primitive to reference type by boxing + TW = Wrappers.asWrapperType(T1); + return TW.cast(T0.cast(value)); + } + // convert primitive to primitive; this requires a real value change + if (value == null) + return Wrappers.zeroValue(T1); + // convert non-Number primitives to Integer: + if (value instanceof Character) { + char ch = (char) (Character) value; + value = Integer.valueOf(ch); + if (T1 == Integer.class) + return value; + } else if (value instanceof Boolean) { + boolean z = (boolean) (Boolean) value; + value = Integer.valueOf(z ? 1 : 0); + if (T1 == Integer.class) + return value; + } + Number numval = (Number) value; + switch (Wrappers.basicTypeChar(T1)) { + case 'Z': return (numval.intValue() != 0); + case 'B': return numval.byteValue(); + case 'C': return (char) numval.intValue(); + case 'S': return numval.shortValue(); + case 'I': return numval.intValue(); + case 'J': return numval.longValue(); + case 'F': return numval.floatValue(); + case 'D': return numval.doubleValue(); + } + return null; + } + + static + Object checkValue(Class T1, Object value) + throws ClassCastException + { + Class T0; + if (value == null) + T0 = Object.class; + else + T0 = value.getClass(); + return checkValue(T0, T1, value); + } + + /// method handle modification (creation from other method handles) + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which adapts the type of the + * given method handle to a new type, by pairwise argument conversion, + * and/or varargs conversion. + * The original type and new type must have the same number of + * arguments, or else one or both them the must be varargs types. + * The resulting method handle is guaranteed to confess a type + * which is equal to the desired new type, with any varargs property erased. + *

+ * If the original type and new type are equal, returns target. + *

+ * The following conversions are applied as needed both to + * arguments and return types. Let T0 and T1 be the differing + * new and old parameter types (or old and new return types) + * for corresponding values passed by the new and old method types. + *

+ * If an ordinary (non-varargs) parameter of the new type is + * to be boxed in a varargs parameter of the old type of type T1[], + * then T1 is the element type of the varargs array. + * Otherwise, if a varargs parameter of the new type of type T0[] + * is to be spread into one or more outgoing old type parameters, + * then T0 is the element type of the + * If the new type is varargs and the old type is not, the varargs + * argument will be checked and must be a non-null array of exactly + * the right length. If there are no parameters in the old type + * corresponding to the new varargs parameter, the varargs argument + * is also allowed to be null. + *

+ * Given those types T0, T1, one of the following conversions is applied + * if possible: + *

    + *
  • If T0 and T1 are references, then a cast to T2 is applied, + * where T2 is Object if T1 is an interface, else T1. + * (The types do not need to be related in any particular way. + * The treatment of interfaces follows the usage of the bytecode verifier.) + *
  • If T0 and T1 are primitives, then a Java casting + * conversion (JLS 5.5) is applied, if one exists. + *
  • If T0 and T1 are primitives and one is boolean, + * the boolean is treated as a one-bit unsigned integer. + * (This treatment follows the usage of the bytecode verifier.) + * A conversion from another primitive type behaves as if + * it first converts to byte, and then masks all but the low bit. + *
  • If T0 is a primitive and T1 a reference, a boxing + * conversion is applied if one exists, possibly followed by + * an reference conversion to a superclass. + * T1 must be a wrapper class or a supertype of one. + * If T1 is a wrapper class, T0 is converted if necessary + * to T1's primitive type by one of the preceding conversions. + * Otherwise, T0 is boxed, and its wrapper converted to T1. + *
  • If T0 is a reference and T1 a primitive, an unboxing + * conversion is applied if one exists, possibly preceded by + * a reference conversion to a wrapper class. + * T0 must be a wrapper class or a supertype of one. + * If T0 is a wrapper class, its primitive value is converted + * if necessary to T1 by one of the preceding conversions. + * Otherwise, T0 is converted directly to the wrapper type for T1, + * which is then unboxed. + *
  • If T1 is void, any returned value is discarded + *
  • If T0 is void and T1 a reference, a null value is introduced. + *
  • If T0 is void and T1 a primitive, a zero value is introduced. + *
+ * @param target the method handle to invoke after arguments are retyped + * @param newType the expected type of the new method handle + * @return a method handle which delegates to {@code target} after performing + * any necessary argument conversions, and arranges for any + * necessary return value conversions + */ + public static + MethodHandle convertArguments(MethodHandle target, MethodType newType) { + MethodType oldType = target.type(); + if (oldType.equals(newType)) + return target; + return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, + newType, target.type(), null); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which adapts the calling sequence of the + * given method handle to a new type, by reordering the arguments. + * Duplication and omission is allowed. + * The resulting method handle is guaranteed to confess a type + * which is equal to the desired new type. + *

+ * The given permutation string controls the reordering. + * The characters of the string are treated as small integers. + * All the characters must be in the range zero (i.e., '\0') to + * the number of incoming arguments (the parameter count of the new type). + * The length of the string must be equal to the number of outgoing + * parameters (the parameter count of the original method handle's type). + * If the n-th character of the string denotes the integer k, + * then the n-th incoming argument becomes the k-th outgoing argument. + *

+ * Pairwise conversions are applied as needed to arguments and return + * values, as with {@link #convertArguments}. + * @param target the method handle to invoke after arguments are reordered + * @param newType the expected type of the new method handle + * @param permutation a string which controls the reordering + * @return a method handle which delegates to {@code target} after performing + * any necessary argument motion and conversions, and arranges for any + * necessary return value conversions + */ + public static + MethodHandle permuteArguments(MethodHandle target, MethodType newType, String permutation) { + MethodType oldType = target.type(); + return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, + newType, target.type(), + permutation); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which adapts the type of the + * given method handle to a new type, by spreading the final argument. + * The resulting method handle is guaranteed to confess a type + * which is equal to the desired new type. + *

+ * The final parameter type of the new type must be an array type T[]. + * This is the type of what is called the spread argument. + * All other arguments of the new type are called ordinary arguments. + *

+ * The ordinary arguments of the new type are pairwise converted + * to the initial parameter types of the old type, according to the + * rules in {@link #convertArguments}. + * Any additional arguments in the old type + * are converted from the array element type T, + * again according to the rules in {@link #convertArguments}. + * The return value is converted according likewise. + *

+ * The call verifies that the spread argument is in fact an array + * of exactly the type length, i.e., the excess number of + * arguments in the old type over the ordinary arguments in the new type. + * If there are no excess arguments, the spread argument is also + * allowed to be null. + * @param target the method handle to invoke after the argument is prepended + * @param newType the expected type of the new method handle + * @return a new method handle which spreads its final argument, + * before calling the original method handle + */ + public static + MethodHandle spreadArguments(MethodHandle target, MethodType newType) { + MethodType oldType = target.type(); + int inargs = newType.parameterCount(); + int outargs = oldType.parameterCount(); + int spreadPos = inargs - 1; + int numSpread = (outargs - spreadPos); + if (spreadPos < 0 || numSpread < 0) + throw newIllegalArgumentException("wrong number of arguments"); + newType = newType.changeVarArgs(true); + return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, newType, oldType, null); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which adapts the type of the + * given method handle to a new type, by collecting a series of + * trailing arguments into an array. + * The resulting method handle is guaranteed to confess a type + * which is equal to the desired new type. + *

+ * This method is inverse to {@link #spreadArguments}. + * The final parameter type of the old type must be an array type T[], + * which is the type of what is called the spread argument. + * The trailing arguments of the new type which correspond to + * the spread argument are all converted to type T and collected + * into an array before the original method is called. + * @param target the method handle to invoke after the argument is prepended + * @param newType the expected type of the new method handle + * @return a new method handle which collects some trailings argument + * into an array, before calling the original method handle + */ + public static + MethodHandle collectArguments(MethodHandle target, MethodType newType) { + MethodType oldType = target.type(); + int inargs = newType.parameterCount(); + int outargs = oldType.parameterCount(); + int collectPos = outargs - 1; + int numCollect = (inargs - collectPos); + if (collectPos < 0 || numCollect < 0) + throw newIllegalArgumentException("wrong number of arguments"); + oldType = oldType.changeVarArgs(true); + return MethodHandleImpl.convertArguments(IMPL_TOKEN, target, newType, oldType, null); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which calls the original method handle, + * after inserting the given argument as the first argument. + * The type of the new method handle will drop the first argument + * type from the original handle's type. + *

+ * Equivalent to {@code insertArgument(target, value, 0)}. + */ + public static + MethodHandle insertArgument(MethodHandle target, Object value) { + return insertArgument(target, value, 0); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which calls the original method handle, + * after appending the given argument as the final argument. + * The type of the new method handle will drop the last argument + * type from the original handle's type. + *

+ * Equivalent to {@code insertArgument(target, value, N)}, + * where N is the number of arguments to target. + */ + public static + MethodHandle appendArgument(MethodHandle target, Object value) { + return insertArgument(target, value, target.type().parameterCount()); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which calls the original method handle, + * after inserting the given argument at the given position. + * The type of the new method handle will drop the corresponding argument + * type from the original handle's type. + *

+ * The given argument object must match the dropped argument type. + * If the dropped argument type is a primitive, the argument object + * must be a wrapper, and is unboxed to produce the primitive. + *

+ * The pos may range between zero and N (inclusively), + * where N is the number of argument types in target, + * meaning to insert the new argument as the first or last (respectively), + * or somewhere in between. + * @param target the method handle to invoke after the argument is inserted + * @param value the argument to insert + * @param pos where to insert the argument (zero for the first) + * @return a new method handle which inserts an additional argument, + * before calling the original method handle + */ + public static + MethodHandle insertArgument(MethodHandle target, Object value, int pos) { + MethodType oldType = target.type(); + ArrayList> ptypes = + new ArrayList>(oldType.parameterList()); + int outargs = oldType.parameterCount(); + int inargs = outargs - 1; + if (pos < 0 || pos >= outargs) + throw newIllegalArgumentException("no argument type to append"); + Class valueType = ptypes.remove(pos); + value = checkValue(valueType, value); + if (pos == 0 && !valueType.isPrimitive()) { + // At least for now, make bound method handles a special case. + // This lets us get by with minimal JVM support, at the expense + // of generating signature-specific adapters as Java bytecodes. + MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, target, value); + if (bmh != null) return bmh; + // else fall through to general adapter machinery + } + return MethodHandleImpl.bindArgument(IMPL_TOKEN, target, pos, value); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Produce a method handle which calls the original method handle, + * after dropping the given argument(s) at the given position. + * The type of the new method handle will insert the given argument + * type(s), at that position, into the original handle's type. + *

+ * The pos may range between zero and N-1, + * where N is the number of argument types in target, + * meaning to drop the first or last argument (respectively), + * or an argument somewhere in between. + * @param target the method handle to invoke after the argument is dropped + * @param valueTypes the type(s) of the argument to drop + * @param pos which argument to drop (zero for the first) + * @return a new method handle which drops an argument of the given type, + * before calling the original method handle + */ + public static + MethodHandle dropArguments(MethodHandle target, int pos, Class... valueTypes) { + if (valueTypes.length == 0) return target; + MethodType oldType = target.type(); + int outargs = oldType.parameterCount(); + int inargs = outargs + valueTypes.length; + if (pos < 0 || pos >= inargs) + throw newIllegalArgumentException("no argument type to remove"); + ArrayList> ptypes = + new ArrayList>(oldType.parameterList()); + ptypes.addAll(pos, Arrays.asList(valueTypes)); + MethodType newType = MethodType.make(oldType.returnType(), ptypes); + return MethodHandleImpl.dropArguments(IMPL_TOKEN, target, newType, pos); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Make a method handle which adapts a target method handle, + * by guarding it with a test, a boolean-valued method handle. + * If the guard fails, a fallback handle is called instead. + * All three method handles must have the same corresponding + * argument and return types, except that the return type + * of the test must be boolean. + *

Here is pseudocode for the resulting adapter: + *

+     * signature T(A...);
+     * boolean test(A...);
+     * T target(A...);
+     * T fallback(A...);
+     * T adapter(A... a) {
+     *   if (test(a...))
+     *     return target(a...);
+     *   else
+     *     return fallback(a...);
+     * }
+     * 
+ * @param test method handle used for test, must return boolean + * @param target method handle to call if test passes + * @param fallback method handle to call if test fails + * @return method handle which incorporates the specified if/then/else logic + * @throws IllegalArgumentException if {@code test} does not return boolean, + * or if all three method types do not match (with the return + * type of {@code test} changed to match that of {@code target}). + */ + public static + MethodHandle guardWithTest(MethodHandle test, + MethodHandle target, + MethodHandle fallback) { + if (target.type() != fallback.type()) + throw newIllegalArgumentException("target and fallback types do not match"); + if (target.type().changeReturnType(boolean.class) != test.type()) + throw newIllegalArgumentException("target and test types do not match"); + /* { + MethodHandle choose(boolean z, MethodHandle t, MethodHandle f) { + return z ? t : f; + } + MethodHandle choose = findVirtual(MethodHandles.class, "choose", + MethodType.make(boolean.class, MethodHandle.class, MethodHandle.class)); + choose = appendArgument(choose, target); + choose = appendArgument(choose, fallback); + choose = combineArguments(choose, test); + MethodHandle invoke = findVirtual(MethodHandle.class, "invoke", target.type()); + return checkArguments(invoke, choose, 0); + } */ + return MethodHandleImpl.makeGuardWithTest(IMPL_TOKEN, test, target, fallback); + } + + /** + * PROVISIONAL API, WORK IN PROGRESS: + * Adapt a target method handle {@code target} by first checking its arguments, + * and then calling the target. + * The check is performed by a second method handle, the {@code checker}. + * After this, control passes to the target method handle, with the same + * arguments. + *

+ * The return value of the checker is inserted into the argument list + * for {@code target} at the indicated position {@code pos}, if it is non-negative. + * Except for this inserted argument (if any), the argument types of + * the target {@code target} and the {@code checker} must be identical. + *

+ * The checker handle must have the same argument types as the + * target handle, but must return {@link MethodHandle} instead of + * the ultimate return type. The returned method handle, in turn, + * is required to have exactly the given final method type. + *

Here is pseudocode for the resulting adapter: + *

+     * signature V(A[pos]..., B...);
+     * signature T(A[pos]..., V, B...);
+     * T target(A... a, V, B... b);
+     * U checker(A..., B...);
+     * T adapter(A... a, B... b) {
+     *   V v = checker(a..., b...);
+     *   return target(a..., v, b...);
+     * }
+     * 
+ * @param target the method handle to invoke after arguments are checked + * @param checker method handle to call initially on the incoming arguments + * @param pos where the return value of {@code checker} is to + * be inserted as an argument to {@code target} + * @return method handle which incorporates the specified dispatch logic + * @throws IllegalArgumentException if {@code checker} does not itself + * return either void or the {@code pos}-th argument of {@code target}, + * or does not have the same argument types as {@code target} + * (minus the inserted argument) + */ + public static + MethodHandle checkArguments(MethodHandle target, MethodHandle checker, int pos) { + MethodType mhType = target.type(); + Class checkType = checker.type().returnType(); + MethodType incomingArgs; + if (pos < 0) { + // No inserted argument; target & checker must have same argument types. + incomingArgs = mhType; + if (!incomingArgs.changeReturnType(checkType).equals(checker.type())) + throw newIllegalArgumentException("target and checker types do not match"); + } else { + // Inserted argument. + if (pos >= mhType.parameterCount() + || mhType.parameterType(pos) != checkType) + throw newIllegalArgumentException("inserted checker argument does not match target"); + incomingArgs = mhType.deleteParameterType(pos); + } + if (!incomingArgs.changeReturnType(checkType).equals(checker.type())) { + throw newIllegalArgumentException("target and checker types do not match"); + } + return MethodHandleImpl.checkArguments(IMPL_TOKEN, target, checker, pos); + } + + /// standard method handles + + /** + * Identity function. + * @param x an arbitrary reference value + * @return the same value x + */ + /*public*/ static + T identity(T x) { + return x; + } + + /** + * A method handle for {@link #identity} + */ + /*public*/ static + MethodHandle identityMethod() { + if (IDENTITY == null) + IDENTITY = findStatic(MethodHandle.class, "identity", IDENTITY_TYPE); + return IDENTITY; + } + private static + MethodHandle IDENTITY; + private static final + MethodType IDENTITY_TYPE = MethodType.make(Object.class, Object.class); + + /** + * Empty function. + */ + /*public*/ static + void empty() { + } + + /** + * A method handle for {@link #empty} + */ + /*public*/ static + MethodHandle emptyMethod() { + if (EMPTY == null) + EMPTY = findStatic(MethodHandle.class, "empty", EMPTY_TYPE); + return EMPTY; + } + private static + MethodHandle EMPTY; + private static final + MethodType EMPTY_TYPE = MethodType.make(void.class); +} --- /dev/null 2009-01-20 02:47:42.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/MethodType.java 2009-01-20 02:47:42.000000000 -0800 @@ -0,0 +1,579 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import impl.java.dyn.*; +import impl.java.dyn.util.Signatures; +import static impl.java.dyn.MemberName.newIllegalArgumentException; + +/** + * Run-time token used to match call sites with method handles. + * The structure is a return type accompanied by any number of parameter types. + * The types (primitive, void, and reference) are represented by Class objects. + * All instances of MethodType are immutable. + * Two instances are completely interchangeable if they compare equal. + * Equality depends exactly on the return and parameter types, plus the varargs bit. + *

+ * This type can be created only by factory methods, which manage interning. + * + * @author John Rose, JSR 292 EG + */ +public final +class MethodType { + final Class rtype; + final Class[] ptypes; + MethodTypeForm form; // erased form, plus cached data about primitives + MethodType wrapAlt; // alternative wrapped/unwrapped version + + private static final Access IMPL_TOKEN = Access.getToken(); + + private MethodType(Class rtype, Class[] ptypes, boolean varargs) { + checkRtype(rtype); + checkPtypes(ptypes); + this.rtype = rtype; + this.ptypes = ptypes; + if (!varargs) { + this.form = MethodTypeForm.FAKE[0]; + } else { + this.form = MethodTypeForm.FAKE[1]; + checkVarargs(ptypes); + } + assert(this.isVarArgs() == varargs); + } + + private void checkRtype(Class rtype) { + rtype.equals(rtype); // null check + } + private void checkPtypes(Class[] ptypes) { + for (Class ptype : ptypes) { + ptype.equals(ptype); // null check + if (ptype == void.class) + throw newIllegalArgumentException("void parameter: "+this); + } + } + private void checkVarargs(Class[] ptypes) { + if (ptypes.length == 0 || !ptypes[ptypes.length-1].isArray()) + throw newIllegalArgumentException("not varargs: "+this); + } + + static final HashMap internTable + = new HashMap(); + + static final Class[] NO_PTYPES = {}; + + /** Find or create an instance of the given method type. + * @param rtype the return type + * @param ptypes the parameter types + * @return the interned method type with the given parts + * @throws NullPointerException if rtype or any ptype is null + * @throws IllegalArgumentException if any of the ptypes is void, + */ + public static + MethodType make(Class rtype, Class[] ptypes) { + return makeImpl(rtype, ptypes, false, false); + } + + /** Find or create an instance of the given method type. + * @param rtype the return type + * @param ptypes the parameter types + * @param varargs whether the method type will be varargs + * @return the interned method type with the given parts + * @throws NullPointerException if rtype or any ptype is null + * @throws IllegalArgumentException if any of the ptypes is void, + * or if varargs is true and the last parameter type is not an array + */ + public static + MethodType make(Class rtype, Class[] ptypes, boolean varargs) { + return makeImpl(rtype, ptypes, varargs, false); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. */ + public static + MethodType make(Class rtype, List> ptypes) { + return makeImpl(rtype, ptypes.toArray(NO_PTYPES), false, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The leading parameter type is prepended to the remaining array. + */ + public static + MethodType make(Class rtype, Class ptype0, Class... ptypes) { + Class[] ptypes1 = new Class[1+ptypes.length]; + ptypes1[0] = ptype0; + System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length); + return makeImpl(rtype, ptypes1, false, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has no parameter types. + */ + public static + MethodType make(Class rtype) { + return makeImpl(rtype, NO_PTYPES, false, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has the single given parameter type. + */ + public static + MethodType make(Class rtype, Class ptype0) { + return makeImpl(rtype, new Class[]{ ptype0 }, false, true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * The resulting method has the same parameter types as {@code ptypes}, + * and the specified return type. + */ + public static + MethodType make(Class rtype, MethodType ptypes) { + return makeImpl(rtype, ptypes.ptypes, false, true); + } + + /** + * Sole factory method to find or create an interned method type. + * @param rtype desired return type + * @param ptypes desired parameter types + * @param varargs whether the method type will be varargs + * @param trusted whether the ptypes can be used without cloning + * @return the unique method type of the desired structure + */ + static + MethodType makeImpl(Class rtype, Class[] ptypes, boolean varargs, boolean trusted) { + if (ptypes == null || ptypes.length == 0) { + ptypes = NO_PTYPES; trusted = true; + } + MethodType mt1 = new MethodType(rtype, ptypes, varargs); + MethodType mt0; + synchronized (internTable) { + mt0 = internTable.get(mt1); + if (mt0 != null) + return mt0; + } + if (!trusted) + // defensively copy the array passed in by the user + mt1 = new MethodType(rtype, ptypes.clone(), varargs); + // promote the object to the Real Thing, and reprobe + mt1.form = MethodTypeForm.findForm(mt1, varargs); + if (mt1.form.erasedType == mt1) + MethodHandleImpl.init(IMPL_TOKEN, mt1); + assert(mt1.isVarArgs() == varargs); + synchronized (internTable) { + mt0 = internTable.get(mt1); + if (mt0 != null) + return mt0; + internTable.put(mt1, mt1); + } + return mt1; + } + + private static final MethodType[] objectOnlyTypes = new MethodType[20]; + + /** + * Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * All parameters and the return type will be Object, except the final varargs parameter if any. + * @param objectArgCount number of parameters (excluding the varargs parameter if any) + * @param varargs whether there will be a varargs parameter + * @return a totally generic method type, given only its count of parameters and varargs. + */ + public static + MethodType makeGeneric(int objectArgCount, boolean varargs) { + MethodType mt; + int ivarargs = (!varargs ? 0 : 1); + int ootIndex = objectArgCount*2 + ivarargs; + if (ootIndex < objectOnlyTypes.length) { + mt = objectOnlyTypes[ootIndex]; + if (mt != null) return mt; + } + Class[] ptypes = new Class[objectArgCount + ivarargs]; + Arrays.fill(ptypes, Object.class); + if (ivarargs != 0) ptypes[objectArgCount] = Object[].class; + mt = makeImpl(Object.class, ptypes, varargs, true); + if (ootIndex < objectOnlyTypes.length) { + objectOnlyTypes[ootIndex] = mt; // cache it here also! + } + return mt; + } + + /** + * Convenience method for {@link #makeGeneric(int, boolean)}, defaulting {@code varargs} to false. + */ + public static + MethodType makeGeneric(int objectArgCount) { + return makeGeneric(objectArgCount, false); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the index (zero-based) of the parameter type to change + * @param nptype a new parameter type to replace the old one with + * @return the same type, except with the selected parameter changed + */ + public MethodType changeParameterType(int num, Class nptype) { + if (parameterType(num) == nptype) return this; + Class[] nptypes = ptypes.clone(); + nptypes[num] = nptype; + return makeImpl(rtype, nptypes, isVarArgs(), true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the position (zero-based) of the inserted parameter type + * @param nptype a new parameter type to insert into the parameter list + * @return the same type, except with the selected parameter inserted + */ + public MethodType insertParameterType(int num, Class nptype) { + int len = ptypes.length; + Class[] nptypes = Arrays.copyOfRange(ptypes, 0, len+1); + System.arraycopy(nptypes, num, nptypes, num+1, len-num); + nptypes[num] = nptype; + return makeImpl(rtype, nptypes, isVarArgs(), true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param num the index (zero-based) of the parameter type to remove + * @return the same type, except with the selected parameter removed + */ + public MethodType deleteParameterType(int num) { + int len = ptypes.length; + Class[] nptypes; + if (num == 0) { + nptypes = Arrays.copyOfRange(ptypes, 1, len); + } else { + nptypes = Arrays.copyOfRange(ptypes, 0, len-1); + System.arraycopy(ptypes, num+1, nptypes, num, (len-1)-num); + } + return makeImpl(rtype, nptypes, isVarArgs(), true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param nrtype a return parameter type to replace the old one with + * @return the same type, except with the return type change + */ + public MethodType changeReturnType(Class nrtype) { + if (returnType() == nrtype) return this; + return makeImpl(nrtype, ptypes, isVarArgs(), true); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[], boolean)}. + * @param varargs a new setting of the varargs flag + * @return the same type, except with the varargs change + * @throws IllegalArgumentException if varargs is true and the last parameter type is not an array + */ + public MethodType changeVarArgs(boolean varargs) { + if (isVarArgs() == varargs) return this; + return makeImpl(rtype, ptypes, varargs, true); + } + + /** Convenience method. + * Report if this type contains a primitive argument or return value. + * @return true if any of the types are primitives + */ + public boolean hasPrimitives() { + return form.primCounts != 0; + } + + /** Convenience method. + * Report if this type contains a wrapper argument or return value. + * Wrappers are types which box primitive values, such as {@link Integer}. + * @return true if any of the types are wrappers + */ + public boolean hasWrappers() { + return unwrap() != this; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Erase all reference types to Object. + * @return a version of the original type with all reference types replaced + */ + public MethodType erase() { + return form.erasedType; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Erase all reference types to Object, and all primitive types to wrappers. + * @return a version of the original type with references erasedType and primitives wrapped + */ + public MethodType eraseWrap() { + return form.wrappedType; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Convert all types, both reference and primitive, to Object. + * @return a version of the original type with all types replaced + * @see #makeGeneric(int) + */ + public MethodType generic() { + return form.wrappedType.erase(); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Convert all primitive types to their corresponding wrapper types. + * @return a version of the original type with all primitive types replaced + */ + public MethodType wrap() { + return hasPrimitives() ? wrapWithPrims(this) : this; + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Convert all wrapper types to their corresponding primitive types. + * @return a version of the original type with all wrapper types replaced + */ + public MethodType unwrap() { + MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this); + return unwrapWithNoPrims(noprims); + } + + private static MethodType wrapWithPrims(MethodType pt) { + assert(pt.hasPrimitives()); + MethodType wt = pt.wrapAlt; + if (wt == null) { + // fill in lazily + wt = MethodTypeForm.canonType(pt, MethodTypeForm.WRAP); + assert(wt != null); + pt.wrapAlt = wt; + } + return wt; + } + + private static MethodType unwrapWithNoPrims(MethodType wt) { + assert(!wt.hasPrimitives()); + MethodType uwt = wt.wrapAlt; + if (uwt == null) { + // fill in lazily + uwt = MethodTypeForm.canonType(wt, MethodTypeForm.UNWRAP); + if (uwt == null) + uwt = wt; // type has no wrappers or prims at all + wt.wrapAlt = uwt; + } + return uwt; + } + + /** @param num the index (zero-based) of the desired parameter type + * @return the selected parameter type + */ + public Class parameterType(int num) { + return ptypes[num]; + } + /** @return the number of parameter types */ + public int parameterCount() { + return ptypes.length; + } + /** @return the return type */ + public Class returnType() { + return rtype; + } + /** @return whether this is a varargs type */ + public boolean isVarArgs() { + return form.varargs; + } + + /** + * Convenience method to present the arguments as a list. + * @return the parameter types (as an immutable list) + */ + public List> parameterList() { + return Collections.unmodifiableList(Arrays.asList(ptypes)); + } + + /** + * Convenience method to present the arguments as an array. + * @return the parameter types (as a fresh copy if necessary) + */ + public Class[] parameterArray() { + return ptypes.clone(); + } + + /** + * Compares the specified object with this type for equality. + * That is, it returns true if and only if the specified object + * is also a method type with exactly the same parameters and return type, + * and agree on their varargs flag. + * @param x object to compare + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object x) { + return this == x || x instanceof MethodType && equals((MethodType)x); + } + + private boolean equals(MethodType that) { + return this.rtype == that.rtype + && this.isVarArgs() == that.isVarArgs() + && Arrays.equals(this.ptypes, that.ptypes); + } + + /** + * Returns the hash code value for this method type. + * It is defined to be the same as the hashcode of a List + * whose elements are the return type followed by the + * parameter types, plus an additional void type if varargs is set. + * @return the hash code value for this method type + * @see Object#hashCode() + * @see #equals(Object) + * @see List#hashCode() + */ + @Override + public int hashCode() { + int hashCode = 31 + rtype.hashCode(); + for (Class ptype : ptypes) + hashCode = 31*hashCode + ptype.hashCode(); + if (isVarArgs()) + hashCode = 31*hashCode + void.class.hashCode(); + return hashCode; + } + + /** + * The string representation of a method type is a + * parenthesis enclosed, comma separated list of type names, + * followed immediately by the return type. + * The last argument type name is followed by "..." + * if the type is vararags. + *

+ * If a type name is array, it the base type followed + * by [], rather than the Class.getName of the array type. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("("); + int vanum = isVarArgs() ? ptypes.length-1 : -1 ; + for (int i = 0; i < ptypes.length; i++) { + if (i > 0) sb.append(","); + if (i == vanum) { + putName(sb, ptypes[i].getComponentType()); + sb.append("..."); + break; + } + putName(sb, ptypes[i]); + } + sb.append(")"); + putName(sb, rtype); + return sb.toString(); + } + + static void putName(StringBuilder sb, Class cls) { + int brackets = 0; + while (cls.isArray()) { + cls = cls.getComponentType(); + brackets++; + } + String n = cls.getName(); + /* + if (n.startsWith("java.lang.")) { + String nb = n.substring("java.lang.".length()); + if (nb.indexOf('.') < 0) n = nb; + } else if (n.indexOf('.') < 0) { + n = "."+n; // anonymous package + } + */ + sb.append(n); + while (brackets > 0) { + sb.append("[]"); + brackets--; + } + } + + /// Queries which have to do with the bytecode architecture + + /** The number of JVM stack slots required to invoke a method + * of this type. Note that (for historic reasons) the JVM requires + * a second stack slot to pass long and double arguments. + * So this method returns {@link #parameterCount()} plus the + * number of long and double parameters (if any). + * @return the number of JVM stack slots for this type's parameters + */ + public int parameterSlotCount() { + return form.parameterSlotCount(); + } + + /** The JVM stack slot which carries the given parameter. + * The last parameter ({@link #parameterCount()}-1) is carried in the + * the most shallowly stacked argument slot, which is numbered as slot zero. + * The first parameter is carried in the most deeply stacked argument slot, + * which is {@link #parameterSlotCount()} minus the number of slots used + * by that parameter. + *

+ * As a useful edge case, {@code parameterSlot(-1)} returns + * {@link #parameterSlotCount()}. No other negative values are accepted. + *

+ * In general, the number returned is the number of arguments after + * the given parameter, plus the number of long or double arguments + * after the argument for the given parameter. + * @param num the index (zero-based) of the desired parameter type + * @return the index of the (shallowest) JVM stack slot transmitting the + * given parameter + */ + public int parameterSlot(int num) { + return form.parameterToArgSlot(num); + } + + /** The number of JVM stack slots required to receive a return value + * from a method of this type. + * If the {@link #returnType() return type} is void, it will be zero, + * else if the return type is long or double, it will be two, else one. + * @return the number of JVM stack slots (0, 1, or 2) for this type's return value + */ + public int returnSlotCount() { + return form.returnSlotCount(); + } + + /** Convenience method for {@link #make(java.lang.Class, java.lang.Class[])}. + * Find or create an instance (interned) of the given method type. + * Any class or interface name embedded in the signature string + * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)} + * on the given loader (or if it is null, on the system class loader). + *

+ * Note that it is possible to build method types which cannot be + * constructed by this method, because their component types are + * not all reachable from a common class loader. + * @param bytecodeSignature a bytecode-level signature string "(T...)T" + * @param loader the class loader in which to look up the types + * @return a method type matching the bytecode-level signature + * @throws IllegalArgumentException if the string is not well-formed + * @throws TypeNotPresentException if a named type cannot be found + */ + public static MethodType fromBytecodeString(String bytecodeSignature, ClassLoader loader) + throws IllegalArgumentException, TypeNotPresentException + { + List> types = Signatures.parseMethod(bytecodeSignature, loader); + Class rtype = types.remove(types.size() - 1); + Class[] ptypes = types.toArray(NO_PTYPES); + return makeImpl(rtype, ptypes, false, true); + } + + /** + * Create a bytecode signature representation of the type. + * Note that this is not a strict inverse of + * {@link #fromBytecodeString(java.lang.String, java.lang.ClassLoader)}, + * because the latter requires a suitable class loader argument. + * @return the bytecode signature representation + */ + public String toBytecodeString() { + return Signatures.unparse(this); + } +} --- /dev/null 2009-01-20 02:47:43.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/MethodTypeForm.java 2009-01-20 02:47:43.000000000 -0800 @@ -0,0 +1,270 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +import impl.java.dyn.util.Wrappers; + +/** + * Shared information for a group of method types, which differ + * only by reference types, and therefore share a common erasure + * and wrapping. + *

+ * For an empirical discussion of the structure of method types, + * see + * the thread "Avoiding Boxing" on jvm-languages. + * There are approximately 2000 distinct erased method types in the JDK. + * There are a little over 10 times that number of unerased types. + * No more than half of these are likely to be loaded at once. + * @author John Rose + */ +class MethodTypeForm { + final int[] argToSlotTable, slotToArgTable; + final long argCounts; // packed slot & value counts + final long primCounts; // packed prim & double counts + final boolean varargs; // is this a "..." type? + final int vmslots; // total number of parameter slots + final MethodType erasedType; // the canonical erasure + final MethodType wrappedType; // erasure, with primitives wrapped + + public static MethodTypeForm of(MethodType type) { + return type.form; + } + + private MethodTypeForm(MethodType erase, boolean varargs) { + this.erasedType = erase; + this.varargs = varargs; + MethodType wt = canonType(erase, WRAP); + this.wrappedType = (wt == null) ? erase : wt; + + int ptypeCount = erase.ptypes.length; + int pslotCount = ptypeCount; // temp. estimate + int rtypeCount = 1; // temp. estimate + int rslotCount = 1; // temp. estimate + + assert(varargs == (ptypeCount != 0 && erase.ptypes[ptypeCount-1].isArray())); + + int[] argToSlotTab = null, slotToArgTab = null; + + // Walk the argument types, looking for primitives. + if (wt != null) { + int pac = 0, lac = 0, prc = 0, lrc = 0; + Class epts[] = erase.ptypes; + for (int i = 0; i < epts.length; i++) { + Class pt = epts[i]; + if (pt != Object.class) { + assert(pt.isPrimitive()); + ++pac; + if (hasTwoArgSlots(pt)) ++lac; + } + } + pslotCount += lac; // #slots = #args + #longs + Class rt = erase.rtype; + if (rt != Object.class) { + ++prc; // even void.class counts as a prim here + if (hasTwoArgSlots(rt)) ++lrc; + // adjust #slots, #args + if (rt == void.class) + rtypeCount = rslotCount = 0; + else + rslotCount += lrc; + } + if (lac != 0) { + int slot = ptypeCount + lac; + slotToArgTab = new int[slot+1]; + argToSlotTab = new int[1+ptypeCount]; + argToSlotTab[0] = slot; // argument "-1" is past end of slots + for (int i = 0; i < epts.length; i++) { + Class pt = epts[i]; + if (hasTwoArgSlots(pt)) --slot; + --slot; + slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note + argToSlotTab[1+i] = slot; + } + assert(slot == 0); // filled the table + } + this.primCounts = pack(lrc, prc, lac, pac); + } else { + this.primCounts = 0; + } + this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); + if (slotToArgTab == null) { + int slot = ptypeCount; // first arg is deepest in stack + slotToArgTab = new int[slot+1]; + argToSlotTab = new int[1+ptypeCount]; + argToSlotTab[0] = slot; // argument "-1" is past end of slots + for (int i = 0; i < ptypeCount; i++) { + --slot; + slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note + argToSlotTab[1+i] = slot; + } + } + this.argToSlotTable = argToSlotTab; + this.slotToArgTable = slotToArgTab; + + if (pslotCount >= 256) throw new IllegalArgumentException("too many arguments"); + + // send a few bits down to the JVM: + this.vmslots = parameterSlotCount(); + } + + private static boolean hasTwoArgSlots(Class type) { + return type == long.class || type == double.class; + } + + private static long pack(int a, int b, int c, int d) { + assert(((a|b|c|d) & ~0xFFFF) == 0); + long hw = ((a << 16) | b), lw = ((c << 16) | d); + return (hw << 32) | lw; + } + private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d + assert(word <= 3); + return (char)(packed >> ((3-word) * 16)); + } + + // used only for bootstrapping: + static final MethodTypeForm[] FAKE = { + new MethodTypeForm(false), new MethodTypeForm(true) + }; + // used only for making FAKE[]: + private MethodTypeForm(boolean varargs) { + this.varargs = varargs; + primCounts = argCounts = -1; + erasedType = wrappedType = null; + vmslots = -1; + argToSlotTable = slotToArgTable = null; + } + + public int parameterCount() { // # outgoing values + return unpack(argCounts, 3); + } + public int parameterSlotCount() { // # outgoing interpreter slots + return unpack(argCounts, 2); + } + public int returnCount() { // = 0 (V), or 1 + return unpack(argCounts, 1); + } + public int returnSlotCount() { // = 0 (V), 2 (J/D), or 1 + return unpack(argCounts, 0); + } + public int primitiveParameterCount() { + return unpack(primCounts, 3); + } + public int longPrimitiveParameterCount() { + return unpack(primCounts, 2); + } + public int primitiveReturnCount() { // = 0 (obj), or 1 + return unpack(primCounts, 1); + } + public int longPrimitiveReturnCount() { // = 1 (J/D), or 0 + return unpack(primCounts, 0); + } + public int parameterToArgSlot(int i) { + return argToSlotTable[1+i]; + } + public int argSlotToParameter(int argSlot) { + // Note: Empty slots are represented by zero in this table. + // Valid arguments slots contain incremented entries, so as to be non-zero. + // We return -1 the caller to mean an empty slot. + return slotToArgTable[argSlot] - 1; + } + + static final int ERASE = 1, WRAP = 2, UNWRAP = 4, ARGS_ONLY = 8, KEEP_VARARG = 16; + + static MethodTypeForm findForm(MethodType mt, boolean varargs) { + MethodType erased = canonType(mt, ERASE | (varargs ? KEEP_VARARG : 0)); + if (erased == null) { + // It is already erased. Make a new MethodTypeForm. + return new MethodTypeForm(mt, varargs); + } else { + // Share the MethodTypeForm with the erased version. + return erased.form; + } + } + + /** Canonicalize the types in the given method type. + * If any types change, intern the new type, and return it. + * Otherwise return null. + */ + static MethodType canonType(MethodType mt, int how) { + Class[] ptc = MethodTypeForm.canonTypes(mt.ptypes, how); + Class rtc = null; + if ((how & ARGS_ONLY) == 0) + rtc = MethodTypeForm.canonType(mt.rtype, how); + if (ptc == null && rtc == null) { + // It is already canonical. + return null; + } + // Find the erased version of the method type: + if (rtc == null) rtc = mt.rtype; + if (ptc == null) ptc = mt.ptypes; + return MethodType.makeImpl(rtc, ptc, mt.isVarArgs(), true); + } + + /** Canonicalize the given return or param type. + * Return null if the type is already canonicalized. + */ + static Class canonType(Class t, int how) { + if (t == Object.class) { + // no change, ever + } else if (!t.isPrimitive()) { + if ((how & UNWRAP) != 0) { + Class pt = Wrappers.asPrimitiveType(t); + if (pt != t) + return pt; + } + if ((how & ERASE) != 0) + return Object.class; + } else { + if ((how & WRAP) != 0) { + if ((how & ERASE) != 0) + return Object.class; + return Wrappers.asWrapperType(t); + } + } + // no change; return null to signify + return null; + } + + /** Canonicalize each param type in the given array. + * Return null if all types are already canonicalized. + */ + static Class[] canonTypes(Class[] ts, int how) { + Class[] cs = null; + for (int imax = ts.length, i = 0; i < imax; i++) { + Class c = canonType(ts[i], how); + if (c != null) { + if ((how & KEEP_VARARG) != 0) { + // if this is a final 'vararg', stop canonicalizing now + if (i+1 == imax && ts[i].isArray()) break; + } + if (cs == null) + cs = ts.clone(); + cs[i] = c; + } + } + return cs; + } +} --- /dev/null 2009-01-20 02:47:44.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/NoAccessException.java 2009-01-20 02:47:44.000000000 -0800 @@ -0,0 +1,75 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * Thrown to indicate that a caller has attempted to create a method handle + * which calls a method to which the caller does not have access. + * This unchecked exception is analogous to {@link IllegalAccessException}, + * which is a checked exception thrown when reflective invocation fails + * because of an access check. With method handles, this same access + * checking is performed on behalf of the method handle creator, + * at the time of creation. + * @author John Rose, JSR 292 EG + */ +public class NoAccessException extends RuntimeException { + /** + * Constructs a {@code NoAccessException} with no detail message. + */ + public NoAccessException() { + super(); + } + + /** + * Constructs a {@code NoAccessException} with the specified + * detail message. + * + * @param s the detail message + */ + public NoAccessException(String s) { + super(s); + } + + /** + * Constructs a {@code NoAccessException} with the specified cause. + * + * @param cause the underlying cause of the exception + */ + public NoAccessException(Throwable cause) { + super(cause); + } + + /** + * Constructs a {@code NoAccessException} with the specified + * detail message and cause. + * + * @param s the detail message + * @param cause the underlying cause of the exception + */ + public NoAccessException(String s, Throwable cause) { + super(s, cause); + } +} --- /dev/null 2009-01-20 02:47:45.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/WrongMethodTypeException.java 2009-01-20 02:47:45.000000000 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.dyn; + +/** + * Thrown to indicate that code has attempted to call a method handle + * via the wrong method type. As with the bytecode representation of + * normal Java method calls, method handle calls are strongly typed + * to a specific signature associated with a call site. + *

+ * This exception may also be thrown when two method handles are + * composed, and the system detects that their types cannot be + * matched up correctly. This amounts to an early evaluation + * of the type mismatch, at method handle construction time, + * instead of when the mismatched method handle is called. + * + * @author John Rose, JSR 292 EG + */ +public class WrongMethodTypeException extends RuntimeException { + /** + * Constructs a {@code WrongMethodTypeException} with no detail message. + */ + public WrongMethodTypeException() { + super(); + } + + /** + * Constructs a {@code WrongMethodTypeException} with the specified + * detail message. + * + * @param s the detail message. + */ + public WrongMethodTypeException(String s) { + super(s); + } +} --- /dev/null 2009-01-20 02:47:46.000000000 -0800 +++ new/src/share/projects/meth/src/java/dyn/package-info.java 2009-01-20 02:47:46.000000000 -0800 @@ -0,0 +1,32 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * This package contains dynamic language support provided directly by + * the Java core class libraries and virtual machine. + * @author John Rose, JSR 292 EG + */ + +package java.dyn; --- /dev/null 2009-01-20 02:47:48.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/InvokeDynamicDemo.java 2009-01-20 02:47:47.000000000 -0800 @@ -0,0 +1,298 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import java.dyn.*; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.regex.Pattern; +import org.junit.*; +//import static org.junit.Assert.*; +import static jdk.java.dyn.JUnitAssert.*; + +public class InvokeDynamicDemo { + + private interface FakeDynamic extends Dynamic { + int foo(int x); + String bar(String x, int y); + } + + public static int myFoo(Object recv, int z) { + println("myFoo "+recv+" "+z); + return z * 1000000; + } + + public static String myBar(Object recv, String x, int y) { + println("myBar "+recv+" "+x+" "+y); + return "myBar"+Arrays.asList(recv, x, y); + } + + static class IDUser implements Runnable { + static String options = "FAKE_OPTIONS"; + static boolean hasOption(String x) { + return options.indexOf(x) >= 0; + } + + @Override + public void run() { + useit("baz", "bat", 123); + } + + void useit(Object x, Object y, int z) { + int fooval = fakeIdentity(x).foo(z); + println("foo => "+fooval); + assertEquals(myFoo(x, z), fooval); + String barval = fakeIdentity(x).bar((String)y, z); + println("bar => "+barval); + assertEquals(myBar(x, (String)y, z), barval); + } + + public // %%% FIXME: findStatic only finds public methods right now + static Object bootstrap(CallSite site, Object... args) { + println("bootstrap "+site+Arrays.asList(args)); + // a bootstrap method can be very stupid: + if (hasOption("boot-gc")) + Runtime.getRuntime().gc(); + if (hasOption("boot-null")) + return null; + if (hasOption("boot-bad")) + return Arrays.asList(args); // neither a String nor an Integer + if (hasOption("boot-string")) + return "boot"; + if (hasOption("boot-int")) + return 8007; + if (hasOption("boot-throw")) + throw new RuntimeException("boot-throw!"); + // or it can actually look at the call site: + Object name = site.name(); + if (name == "foo") { + if (hasOption("boot-con")) + return 8007000; // it does not need to set a target... + MethodHandle myFoo = MethodHandles.findStatic(InvokeDynamicDemo.class, "myFoo", site.type()); + println("setTarget myFoo: "+myFoo); + site.setTarget(myFoo); + return myFoo(args[0], (Integer) args[1]); + } + if (name == "bar") { + if (hasOption("boot-con")) + return "boot con!"; // it does not need to set a target... + MethodHandle myBar = MethodHandles.findStatic(InvokeDynamicDemo.class, "myBar", site.type()); + println("setTarget myBar: "+myBar); + site.setTarget(myBar); + return myBar(args[0], (String) args[1], (Integer) args[2]); + } + throw new RuntimeException("name not recognized: "+name); + } + + static { + println("options: "+options); + MethodType type = Linkage.BOOTSTRAP_METHOD_TYPE; + MethodHandle mh = MethodHandles.findStatic(IDUser.class, "bootstrap", type); + if (mh == null) + println("missing bootstrap"+type); + else + println("register bootstrap: "+mh); + Linkage.registerBootstrapMethod(IDUser.class, mh); + } + } + static FakeDynamic fakeIdentity(Object x) { + return (FakeDynamic)x; + } + static Object identity(Object x) { + return x; + } + + static boolean verbose; + static void println(String x) { + if (verbose) System.out.println(x); + } + + @Test public void run() throws Exception { run(new String[0]); } + + public static void run(String... av) throws Exception { + final HashMap utf8Map = new HashMap(); + utf8Map.put("fakeIdentity", "identity"); + String objbcn = Object.class.getName().replace('.', '/'); + String objsig = "L"+objbcn+";"; + String fdbcn = FakeDynamic.class.getName().replace('.', '/'); + String rdbcn = Dynamic.class.getName().replace('.', '/'); + utf8Map.put(fdbcn, rdbcn); + String fakesig = "("+objsig+")L"+fdbcn+";"; + utf8Map.put(fakesig, fakesig.replaceAll(Pattern.quote(fdbcn), objbcn)); + utf8Map.put("FAKE_OPTIONS", Arrays.asList(av).toString()); + System.out.println("utf8Map: "+utf8Map); + final ConstantPoolPatch patch = new ConstantPoolPatch(IDUser.class); + patch.putPatches(utf8Map, null, null, true); + if (!utf8Map.isEmpty()) + throw new AssertionError("Map not empty: "+utf8Map); + Class anonk = new AnonymousClassLoader(IDUser.class).loadClass(patch); + Object user = anonk.newInstance(); + System.out.println("Running test on new object "+user); + int count = 1; + verbose = true; + for (String arg : av) { + if (arg.startsWith("count-")) + count = Integer.parseInt(arg.substring("count-".length(), arg.length())); + } + if (count != 1) System.out.println("run count: "+count); + for (int i = 0; i < count; i++) { + if (i > 10) { + if (i > count - 10) + verbose = true; + else if (verbose) { + println("...shutting up..."); + verbose = false; + } + } + ((Runnable)user).run(); + } + } + + // Extra entry point for standalone use. Use as follows: + //junit="$NetBeansResources/NetBeans/java2/modules/ext/junit-4.1.jar" + //anonk="../AnonymousClass/dist/AnonymousClass.jar" + //cpath="$anonk:build/classes:build/test/classes:$junit" + //java -Xbootclasspath/p:"$cpath" jdk.java.dyn.MethodHandleBytecodeTest + public static void main(String... av) throws Throwable { + System.out.println("running ID demo"); + run(av); + //new JUnit4TestAdapter(InvokeDynamicDemo.class).run(null); + } +} + +/* --- SAMPLE OUTPUT --- +-------- + ./gamma -XX:+{MethodHandles,InvokeDynamic} -Xbootclasspath/p:"$cpath" jdk.java.dyn.InvokeDynamicDemo count-1000 +VM option '+PrintCompilation' +VM option '+VerifyBeforeGC' +VM option '+MethodHandles' +VM option '+InvokeDynamic' +[Verifying threads permgen tenured generation def new generation remset ref_proc syms strs zone dict hand C-heap ] +running ID demo +utf8Map: {fakeIdentity=identity, jdk/java/dyn/InvokeDynamicDemo$FakeDynamic=java/dyn/Dynamic, (Ljava/lang/Object;)Ljdk/java/dyn/InvokeDynamicDemo$FakeDynamic;=(Ljava/lang/Object;)Ljava/lang/Object;, FAKE_OPTIONS=[count-1000]} + 1 java.lang.String::charAt (33 bytes) + 2 java.lang.String::hashCode (60 bytes) +Running test on new object jdk.java.dyn.InvokeDynamicDemo$IDUser/29596205@eb7859 +run count: 1000 +reporting bootstrap method to JVM: bootstrap:(java.dyn.CallSite,java.lang.Object...)java.lang.Object +DynCallSite: CallSite#23491286[foo(java.lang.Object,int)int => null] +bootstrap CallSite#23491286[foo(java.lang.Object,int)int => null][baz, 123] +setTarget myFoo: myFoo:(java.lang.Object,int)int +myFoo baz 123 +foo => 123000000 +DynCallSite: CallSite#21061094[bar(java.lang.Object,java.lang.String,int)java.lang.String => null] +bootstrap CallSite#21061094[bar(java.lang.Object,java.lang.String,int)java.lang.String => null][baz, bat, 123] +setTarget myBar: myBar:(java.lang.Object,java.lang.String,int)java.lang.String +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +...shutting up... + 3 java.lang.Object:: (1 bytes) + VerifyBeforeGC:[Verifying threads permgen tenured generation def new generation remset ref_proc syms strs zone dict hand C-heap ] +--- n java.lang.System::arraycopy (static) + 4 java.lang.String::getChars (66 bytes) + 5 java.lang.AbstractStringBuilder::append (60 bytes) + 6 java.lang.StringBuilder::append (8 bytes) + 7 java.lang.Integer::getChars (131 bytes) + 8 java.lang.AbstractStringBuilder::stringSizeOfInt (21 bytes) +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +myFoo baz 123 +foo => 123000000 +myBar baz bat 123 +bar => myBar[baz, bat, 123] +-------- + * --- */ --- /dev/null 2009-01-20 02:47:49.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/JUnitAssert.java 2009-01-20 02:47:49.000000000 -0800 @@ -0,0 +1,125 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Local stub for running without junit.jar. + * To use, copy this file to your package, and add this to the per-file imports: + * + * //import static org.junit.Assert.*; + * import static whatever.this.package.is.JUnitAssert.*; + * + * @author jrose + */ +class JUnitAssert { + static void assertSame(Object expected, Object actual) { + Method m = findMethod("assertSame", Object.class, Object.class); + if (m != null) { + callMethod(m, expected, actual); + } else if (expected != actual) { + error("not same", expected, actual); + } + } + + static void assertEquals(Object expected, Object actual) { + if (!equals(expected, actual)) + assertSame(expected, actual); + } + + static boolean equals(Object x, Object y) { + return (x == y) || (x != null && y != null && x.equals(y)); + } + + static void error(String message, Object expected, Object actual) { + throw new AssertionError(message+": expected ["+expected+"]"+ + ", but result was ["+actual+"]"); + } + + /// the rest is brute hackery... + + static final Class JUNIT_ASSERT; + static { + Class JUNIT_ASSERT_ = null; + final String cname = "org.junit.Assert"; + try { + JUNIT_ASSERT_ = Class.forName(cname); + } catch (ClassNotFoundException ee) { + System.out.println("Using backup methods instead of "+cname); + } + JUNIT_ASSERT = JUNIT_ASSERT_; + } + + static final HashMap, Method> methods + = new HashMap, Method>(); + static final Method NO_METHOD; + static { + Method noMethod = null; + try { + noMethod = JUnitAssert.class.getDeclaredMethod("noMethod"); + } catch (Exception ee) { + } + NO_METHOD = noMethod; + } + static private void noMethod() { } + + static Method findMethod(String name, Class... params) { + if (JUNIT_ASSERT == null) return null; + ArrayList key = new ArrayList(Arrays.asList(params)); + key.add(0, name); + Method m = methods.get(key); + if (m != null) return (m == NO_METHOD ? null : m); + try { + m = JUNIT_ASSERT.getMethod(name, params); + } catch (NoSuchMethodException ee) { + System.out.println("Missing method: "+name+Arrays.asList(params)); + } + methods.put(key, (m == null ? NO_METHOD : m)); + return m; + } + static Object callMethod(Method m, Object... params) { + Throwable bad; + try { + return m.invoke(null, params); + } catch (InvocationTargetException ite) { + Throwable ee = ite.getTargetException(); + if (ee instanceof RuntimeException) + throw (RuntimeException)ee; + if (ee instanceof Error) + throw (Error)ee; + bad = ee; + } catch (Exception ee) { + bad = ee; + } + throw new Error("unexpected reflection problem", bad); + } +} --- /dev/null 2009-01-20 02:47:50.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/MethodHandleBytecodeTest.java 2009-01-20 02:47:50.000000000 -0800 @@ -0,0 +1,83 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import java.dyn.*; +import impl.java.dyn.util.*; +import java.lang.reflect.Method; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestCase; +import org.junit.*; +import static org.junit.Assert.*; + +/** + * + * @author jrose + */ +public class MethodHandleBytecodeTest { + private Method toString, compareTo, getBytes; + + @Before + public void setUp() throws Exception { + toString = Object.class.getDeclaredMethod("toString"); + compareTo = Comparable.class.getDeclaredMethod("compareTo", Object.class); + getBytes = String.class.getDeclaredMethod("getBytes", String.class); + } + + @After + public void tearDown() throws Exception { + } + + /** + * Test of invoke pseudo-method, of class MethodHandle. + */ + @Test + public void testToString() { + System.out.println("invoke toString"); + MethodHandle instance = MethodHandles.findVirtual( + Object.class, "toString", MethodType.make(String.class) ); + String foo = "foo"; + String expResult = foo.toString(); + String result = invokeToString(instance, foo); + System.out.println("result = "+result); + assertEquals(expResult, result); + } + + private String invokeToString(MethodHandle mh, Object obj) { + MethodHandleInvoker inv = MethodHandleInvoker.make(mh.type()); + System.out.println("invoke mh="+mh); + return (String) inv.invoke_LL(mh, obj); + } + + // Extra entry point for standalone use. Use as follows: + //junit="$NetBeansResources/NetBeans/java2/modules/ext/junit-4.1.jar" + //anonk="../AnonymousClass/dist/AnonymousClass.jar" + //cpath="$anonk:build/classes:build/test/classes:$junit" + //java -Xbootclasspath/p:"$cpath" jdk.java.dyn.MethodHandleBytecodeTest + public static void main(String... av) throws Throwable { + new JUnit4TestAdapter(MethodHandleBytecodeTest.class).run(null); + } +} --- /dev/null 2009-01-20 02:47:51.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/MethodHandleDemo.java 2009-01-20 02:47:51.000000000 -0800 @@ -0,0 +1,170 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import java.dyn.*; +import impl.java.dyn.util.MethodHandleInvoker; + +import org.junit.*; +//import static org.junit.Assert.*; +import static jdk.java.dyn.JUnitAssert.*; + +public class MethodHandleDemo { + static String options = ""; + static boolean hasOption(String x) { + return options.indexOf(x) >= 0; + } + static boolean verbose = true; + static void println(Object x) { + if (verbose) System.out.println(x); + } + + public static void main(String... av) { + assertSame("hello", "hello"); // exercise assertion stuff first + + options = java.util.Arrays.asList(av).toString(); + int count = 1; + verbose = true; + for (String arg : av) { + if (arg.startsWith("count-")) + count = Integer.parseInt(arg.substring("count-".length(), arg.length())); + } + if (count != 1) println("run count: "+count); + for (int i = 0; i < count; i++) { + if (i > 10) { + if (i > count - 10) + verbose = true; + else if (verbose) { + println("...shutting up..."); + verbose = false; + } + } + test(); + } + } + + static void test() { + Class caller = MethodHandleDemo.class; + Class returnType = String.class; + Class[] signature = {String.class}; + String result; + MethodHandle mh; + mh = MethodHandles.findVirtual(Object.class, + "toString", MethodType.make(String.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LL(mh, "foo"); + assertSame("foo", result); + + mh = MethodHandles.findStatic(MethodHandleDemo.class, + "test0", MethodType.make(String.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_L(mh); + assertEquals("[test0]", result); + + mh = MethodHandles.findStatic(MethodHandleDemo.class, + "test1", MethodType.make(String.class, String.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LL(mh, "X"); + assertEquals("[test1 X]", result); + + mh = MethodHandles.insertArgument(mh, "Bounderby"); + println("calling bound "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_L(mh); + assertEquals("[test1 Bounderby]", result); + + mh = MethodHandles.findStatic(MethodHandleDemo.class, + "test2", MethodType.make(String.class, String.class, int.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LLI(mh, "X", 123); + assertEquals("[test2 X #123]", result); + + mh = MethodHandles.insertArgument(mh, "Buster"); + println("calling bound "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LI(mh, 456); + assertEquals("[test2 Buster #456]", result); + + Obj obj = new Obj("X"); + mh = MethodHandles.findVirtual(I.class, + "test1", MethodType.make(String.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LL(mh, obj); + assertEquals("[Obj.test1 X]", result); + + mh = MethodHandles.findVirtual(I.class, + "test2", MethodType.make(String.class, int.class)); + println("calling "+mh); + result = (String) MethodHandleInvoker.make(mh.type()).invoke_LLI(mh, obj, 123); + assertEquals("[Obj.test2 X #123]", result); + } + + static void eachCall() { + if (hasOption("call-gc")) + Runtime.getRuntime().gc(); + } + + // static methods we make handles for: + public static String test0() { + eachCall(); + return "[test0]"; + } + + public static String test1(String x) { + eachCall(); + return "[test1 " + x + "]"; + } + + public static String test2(String x, int y) { + println("y == 0x"+Integer.toHexString(y)); + eachCall(); + return "[test2 " + x + " #" + y + "]"; + } + + interface I { + String test1(); + String test2(int y); + } + + // virtual methods we make handles for: + static class Obj implements I { + String x; + Obj(String x) { this.x = x; } + public String test1() { + eachCall(); + return "[Obj.test1 " + x + "]"; + } + public String test2(int y) { + println("y == 0x"+Integer.toHexString(y)); + eachCall(); + return "[Obj.test2 " + x + " #" + y + "]"; + } + } + + // JUnit trimmings: + @Test + public void testMain() { + main(); + } +} --- /dev/null 2009-01-20 02:47:52.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/MethodHandlesTest.java 2009-01-20 02:47:52.000000000 -0800 @@ -0,0 +1,630 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import impl.java.dyn.MemberName; +import impl.java.dyn.util.Wrappers; +import java.dyn.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author jrose + */ +public class MethodHandlesTest { + + public MethodHandlesTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + calledLog.clear(); + calledLog.add(null); + nextArg = 1000000; + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + static List calledLog = new ArrayList(); + static Object logEntry(String name, Object... args) { + return Arrays.asList(name, Arrays.asList(args)); + } + static Object called(String name, Object... args) { + 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); + assertEquals("previous method call", expected, actual); + } + static void printCalled(MethodHandle target, String name, Object... args) { + System.out.println("calling "+logEntry(name, args)+" on "+target); + } + + static int nextArg; + static Object randomArg(Class param) { + if (param.isPrimitive() || Wrappers.isWrapperType(param)) + return Wrappers.wrap(nextArg++, param); + else if (param.isInterface() || param.isAssignableFrom(String.class)) + return "#"+(nextArg++); + else + try { + return param.newInstance(); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + 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 T[] array(Class atype, E... a) { + return Arrays.copyOf(a, a.length, atype); + } + static 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; + } + + // Subject methods... + static class Example implements IntExample { + final String name; + public Example() { name = "Example#"+(nextArg++); } + protected Example(String name) { this.name = name; } + protected Example(int x) { this(); called("protected ", this, x); } + @Override public String toString() { return name; } + + public void v0() { called("v0", this); } + void pkg_v0() { called("pkg_v0", this); } + private void pri_v0() { called("pri_v0", this); } + public static void s0() { called("s0"); } + 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); } + + static MethodHandle findStatic(Class defc, String name, MethodType type) { + return MethodHandles.findStatic(defc, name, type); + } + static MethodHandle findVirtual(Class defc, String name, MethodType type) { + return MethodHandles.findVirtual(defc, name, type); + } + static MethodHandle unreflect(Method rmethod) { + return MethodHandles.unreflect(rmethod); + } + } + static class SubExample extends Example { + @Override public void v0() { called("Sub/v0", this); } + @Override void pkg_v0() { called("Sub/pkg_v0", this); } + private SubExample(int x) { called("", this, x); } + public SubExample() { super("SubExample#"+(nextArg++)); } + static MethodHandle findSpecial(Class defc, String name, MethodType type) { + return MethodHandles.findSpecial(defc, name, type, SubExample.class); + } + } + static interface IntExample { + public void v0(); + static class Impl implements IntExample { + public void v0() { called("Int/v0", this); } + final String name; + public Impl() { name = "Example#"+(nextArg++); } + } + } + + static boolean CAN_SKIP_WORKING = false; + + @Test + public void testFindStatic() { + if (CAN_SKIP_WORKING) return; + System.out.println("findStatic"); + testFindStatic(Example.class, void.class, "s0"); + testFindStatic(Example.class, void.class, "pkg_s0"); + testFindStatic(Example.class, void.class, "pri_s0"); + + 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(false, Example.class, void.class, "bogus"); + } + + void testFindStatic(Class defc, Class ret, String name, Class... params) { + testFindStatic(true, defc, ret, name, params); + } + void testFindStatic(boolean positive, Class defc, Class ret, String name, Class... params) { + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = Example.findStatic(defc, name, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + System.out.println("findStatic "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(type, target.type()); + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testFindVirtual() { + if (CAN_SKIP_WORKING) return; + System.out.println("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); + testFindVirtual(false, Example.class, Example.class, void.class, "bogus"); + // 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) { + testFindVirtual(defc, defc, ret, name, params); + } + void testFindVirtual(Class rcvc, Class defc, Class ret, String name, Class... params) { + testFindVirtual(true, rcvc, defc, ret, name, params); + } + void testFindVirtual(boolean positive, Class rcvc, Class defc, Class ret, String name, Class... params) { + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = MethodHandles.findVirtual(defc, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + System.out.println("findVirtual "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsWithSelf = cat(array(Class[].class, defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + assertEquals(typeWithSelf, target.type()); + Object[] argsWithSelf = randomArgs(paramsWithSelf); + if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); + printCalled(target, name, argsWithSelf); + MethodHandles.invoke(target, argsWithSelf); + assertCalled(name, argsWithSelf); + System.out.print(':'); + } + + @Test + public void testFindSpecial() { + if (CAN_SKIP_WORKING) return; + System.out.println("findSpecial"); + testFindSpecial(Example.class, void.class, "v0"); + testFindSpecial(Example.class, void.class, "pkg_v0"); + testFindSpecial(SubExample.class, void.class, "", int.class); + testFindSpecial(false, Example.class, void.class, "", int.class); + testFindSpecial(false, Example.class, void.class, "bogus"); + } + + void testFindSpecial(Class defc, Class ret, String name, Class... params) { + testFindSpecial(true, defc, ret, name, params); + } + void testFindSpecial(boolean positive, Class defc, Class ret, String name, Class... params) { + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = SubExample.findSpecial(defc, name, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + System.out.println("findSpecial "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsWithSelf = cat(array(Class[].class, defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + assertEquals(typeWithSelf, target.type()); + Object[] args = randomArgs(paramsWithSelf); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testBind() { + if (CAN_SKIP_WORKING) return; + System.out.println("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, 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) { + testBind(true, defc, ret, name, params); + } + void testBind(boolean positive, Class defc, Class ret, String name, Class... params) { + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + Object receiver = randomArg(defc); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = MethodHandles.bind(receiver, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(type, target.type()); + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); + assertCalled(name, argsWithReceiver); + System.out.print(':'); + } + + @Test + public void testUnreflect() { + if (CAN_SKIP_WORKING) return; + System.out.println("unreflect"); + testUnreflect(Example.class, true, void.class, "s0"); + 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); + //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); + + 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); + } + + void testUnreflect(Class defc, boolean isStatic, Class ret, String name, Class... params) { + testUnreflect(true, defc, isStatic, ret, name, params); + } + void testUnreflect(boolean positive, Class defc, boolean isStatic, Class ret, String name, Class... params) { + MethodType type = MethodType.make(ret, params); + Method rmethod = null; + MethodHandle target = null; + RuntimeException noAccess = null; + try { + rmethod = defc.getDeclaredMethod(name, params); + } catch (NoSuchMethodException ex) { + throw new NoAccessException(ex); + } + MemberName mname = new MemberName(rmethod); + assertEquals(isStatic, mname.isStatic()); + try { + target = Example.unreflect(rmethod); + } catch (NoAccessException ex) { + noAccess = ex; + } + System.out.println("unreflect "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsMaybeWithSelf = params; + if (!isStatic) { + paramsMaybeWithSelf = cat(array(Class[].class, defc), params); + } + MethodType typeMaybeWithSelf = MethodType.make(ret, paramsMaybeWithSelf); + assertEquals(typeMaybeWithSelf, target.type()); + Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); + printCalled(target, name, argsMaybeWithSelf); + MethodHandles.invoke(target, argsMaybeWithSelf); + assertCalled(name, argsMaybeWithSelf); + System.out.print(':'); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSpecial() { + System.out.println("unreflectSpecial"); + Method m = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.unreflectSpecial(m, Example.class); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectGetter() { + System.out.println("unreflectGetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.unreflectGetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSetter() { + System.out.println("unreflectSetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.unreflectSetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementGetter() { + System.out.println("arrayElementGetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementGetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementSetter() { + System.out.println("arrayElementSetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementSetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + 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) + return ofType(MethodType.make(rtype, Object[].class)); + return ofType(MethodType.makeGeneric(n).changeReturnType(rtype)); + } + static MethodHandle ofType(Class rtype, Class... ptypes) { + return ofType(MethodType.make(rtype, ptypes)); + } + 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"; + return MethodHandles.findStatic(Callee.class, name, type); + } + } + + @Test + public void testConvertArguments_pairwise() { + System.out.println("convertArguments/pairwise"); + testConvert(Callee.ofType(1), null, "id", String.class); + testConvert(Callee.ofType(1), null, "id", Integer.class); + testConvert(Callee.ofType(1), null, "id", int.class); + testConvert(Callee.ofType(1), null, "id", short.class); + } + + void testConvert(MethodHandle id, Class rtype, String name, Class... params) { + testConvert(true, id, rtype, name, params); + } + + void testConvert(boolean positive, MethodHandle id, Class rtype, String name, Class... params) { + 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); + } + MethodType type = MethodType.make(rtype, params); + MethodHandle target = null; + RuntimeException error = null; + try { + target = MethodHandles.convertArguments(id, type); + } catch (RuntimeException ex) { + error = ex; + } + System.out.println("convert "+id+ " to "+type+" => "+target + +(error == null ? "" : " !! "+error)); + if (positive && error != null) throw error; + assertEquals(positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(type, target.type()); + Object[] args = randomArgs(type.parameterArray()); + printCalled(target, id.toString(), args); + Object result = MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test @Ignore("unimplemented") + public void testSpreadArguments() { + System.out.println("spreadArguments"); + MethodHandle target = null; + MethodType newType = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.spreadArguments(target, newType); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testCollectArguments() { + System.out.println("collectArguments"); + MethodHandle target = null; + MethodType newType = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.collectArguments(target, newType); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testInsertArgument_MethodHandle_Object() { + System.out.println("insertArgument"); + MethodHandle target = null; + Object value = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.insertArgument(target, value); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testAppendArgument() { + System.out.println("appendArgument"); + MethodHandle target = null; + Object value = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.appendArgument(target, value); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testInsertArgument_3args() { + System.out.println("insertArgument"); + MethodHandle target = null; + Object value = null; + int pos = 0; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.insertArgument(target, value, pos); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testDropArguments() { + System.out.println("dropArguments"); + MethodHandle target = null; + int pos = 0; + Class[] valueTypes = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.dropArguments(target, pos, valueTypes); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testGuardWithTest() { + System.out.println("guardWithTest"); + MethodHandle test = null; + MethodHandle target = null; + MethodHandle fallback = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.guardWithTest(test, target, fallback); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testCheckArguments() { + System.out.println("checkArguments"); + MethodHandle target = null; + MethodHandle checker = null; + int pos = 0; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.checkArguments(target, checker, pos); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + +} \ No newline at end of file --- /dev/null 2009-01-20 02:47:53.000000000 -0800 +++ new/src/share/projects/meth/test/jdk/java/dyn/MethodTypeTest.java 2009-01-20 02:47:53.000000000 -0800 @@ -0,0 +1,424 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * 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 + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package jdk.java.dyn; + +import impl.java.dyn.MemberName; +import java.dyn.MethodType; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.*; +import static org.junit.Assert.*; + +/** + * + * @author jrose + */ +public class MethodTypeTest { + + private Class rtype; + private Class[] ptypes; + private MethodType mt_viS, mt_OO2, mt_vv, mt_Vv, mt_Ov; + private MethodType mt_iSI, mt_ISi, mt_ISI, mt_iSi; + private MethodType mt_viO, mt_iO2, mt_OOi, mt_iOi; + private MethodType mt_VIO, mt_IO2, mt_OOI, mt_IOI, mt_VIS; + private Method compareTo; + + @Before + public void setUp() throws Exception { + rtype = void.class; + ptypes = new Class[] { int.class, String.class }; + + mt_viS = MethodType.make(void.class, int.class, String.class); + mt_OO2 = MethodType.make(Object.class, Object.class, Object.class); + mt_vv = MethodType.make(void.class); + mt_Vv = MethodType.make(Void.class); + mt_Ov = MethodType.make(Object.class); + mt_iSI = MethodType.make(int.class, String.class, Integer.class); + mt_ISi = MethodType.make(Integer.class, String.class, int.class); + mt_ISI = MethodType.make(Integer.class, String.class, Integer.class); + mt_iSi = MethodType.make(int.class, String.class, int.class); + + compareTo = String.class.getDeclaredMethod("compareTo", String.class); + + mt_viO = MethodType.make(void.class, int.class, Object.class); + mt_iO2 = MethodType.make(int.class, Object.class, Object.class); + mt_OOi = MethodType.make(Object.class, Object.class, int.class); + mt_iOi = MethodType.make(int.class, Object.class, int.class); + + mt_VIO = MethodType.make(Void.class, Integer.class, Object.class); + mt_IO2 = MethodType.make(Integer.class, Object.class, Object.class); + mt_OOI = MethodType.make(Object.class, Object.class, Integer.class); + mt_IOI = MethodType.make(Integer.class, Object.class, Integer.class); + mt_VIS = MethodType.make(Void.class, Integer.class, String.class); + + } + + @After + public void tearDown() throws Exception { + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class_ClassArr() { + System.out.println("make (from type array)"); + MethodType result = MethodType.make(rtype, ptypes); + assertEquals(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class_List() { + System.out.println("make (from type list)"); + MethodType result = MethodType.make(rtype, Arrays.asList(ptypes)); + assertEquals(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_3args() { + System.out.println("make (from type with varargs)"); + MethodType result = MethodType.make(rtype, ptypes[0], ptypes[1]); + assertEquals(mt_viS, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Class() { + System.out.println("make (from single type)"); + Class rt = Integer.class; + MethodType expResult = MethodType.make(rt, new Class[0]); + MethodType result = MethodType.make(rt); + assertEquals(expResult, result); + } + + @Test + public void testMakeGeneric() { + System.out.println("makeGeneric"); + int objectArgCount = 2; + MethodType expResult = mt_OO2; + MethodType result = MethodType.makeGeneric(objectArgCount); + assertEquals(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_Method() { + System.out.println("make (via MemberName.getMethodType)"); + MethodType expResult = MethodType.make(int.class, String.class, String.class); + MemberName name = new MemberName(compareTo); + MethodType result = name.getMethodType(); + assertEquals(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_MethodType() { + System.out.println("make (from rtype, MethodType)"); + MethodType expResult = mt_iO2; + MethodType result = MethodType.make(int.class, mt_IO2); + assertEquals(expResult, result); + } + + /** + * Test of make method, of class MethodType. + */ + @Test + public void testMake_String_ClassLoader() { + System.out.println("make (from bytecode signature)"); + ClassLoader loader = null; + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + String obj = "Ljava/lang/Object;"; + assertEquals(obj, concat(Object.class)); + String[] expResults = { + "(ILjava/lang/String;)V", + concat("(", obj, 2, ")", Object.class), + "()V", "()"+obj, + concat("(", String.class, Integer.class, ")I"), + concat("(", String.class, "I)", Integer.class), + concat("(", String.class, Integer.class, ")", Integer.class), + concat("(", String.class, "I)I") + }; + for (int i = 0; i < instances.length; i++) { + MethodType instance = instances[i]; + String result = instance.toBytecodeString(); + assertEquals("#"+i, expResults[i], result); + MethodType parsed = MethodType.fromBytecodeString(result, loader); + assertEquals("--#"+i, instance, parsed); + } + } + private static String concat(Object... parts) { + StringBuilder sb = new StringBuilder(); + Object prevPart = ""; + for (Object part : parts) { + if (part instanceof Class) { + part = "L"+((Class)part).getName()+";"; + } + if (part instanceof Integer) { + for (int n = (Integer) part; n > 1; n--) + sb.append(prevPart); + part = ""; + } + sb.append(part); + prevPart = part; + } + return sb.toString().replace('.', '/'); + } + + @Test + public void testHasPrimitives() { + System.out.println("hasPrimitives"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + boolean[] expResults = {true, false, true, false, true, true, false, true}; + for (int i = 0; i < instances.length; i++) { + boolean result = instances[i].hasPrimitives(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testHasWrappers() { + System.out.println("hasWrappers"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + boolean[] expResults = {false, false, false, false, true, true, true, false}; + for (int i = 0; i < instances.length; i++) { + boolean result = instances[i].hasWrappers(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testErase() { + System.out.println("erase"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_viO, mt_OO2, mt_vv, mt_Ov, mt_iO2, mt_OOi, mt_OO2, mt_iOi}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].erase(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testEraseWrap() { + System.out.println("eraseWrap"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_VIO, mt_OO2, mt_Vv, mt_Ov, mt_IO2, mt_OOI, mt_OO2, mt_IOI}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].eraseWrap(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testGeneric() { + System.out.println("generic"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_OO2, mt_OO2, mt_Ov, mt_Ov, mt_OO2, mt_OO2, mt_OO2, mt_OO2}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].generic(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testWrap() { + System.out.println("wrap"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_VIS, mt_OO2, mt_Vv, mt_Ov, mt_ISI, mt_ISI, mt_ISI, mt_ISI}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].wrap(); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testUnwrap() { + System.out.println("unwrap"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + MethodType[] expResults = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSi, mt_iSi, mt_iSi, mt_iSi}; + for (int i = 0; i < instances.length; i++) { + MethodType result = instances[i].unwrap(); + assertEquals("#"+i, expResults[i], result); + } + } + + /** + * Test of parameterType method, of class MethodType. + */ + @Test + public void testParameterType() { + System.out.println("parameterType"); + for (int num = 0; num < ptypes.length; num++) { + MethodType instance = mt_viS; + Class expResult = ptypes[num]; + Class result = instance.parameterType(num); + assertEquals(expResult, result); + } + } + + /** + * Test of parameterCount method, of class MethodType. + */ + @Test + public void testParameterCount() { + System.out.println("parameterCount"); + MethodType instance = mt_viS; + int expResult = 2; + int result = instance.parameterCount(); + assertEquals(expResult, result); + } + + /** + * Test of returnType method, of class MethodType. + */ + @Test + public void testReturnType() { + System.out.println("returnType"); + MethodType instance = mt_viS; + Class expResult = void.class; + Class result = instance.returnType(); + assertEquals(expResult, result); + } + + /** + * Test of parameterList method, of class MethodType. + */ + @Test + public void testParameterList() { + System.out.println("parameterList"); + MethodType instance = mt_viS; + List> expResult = Arrays.asList(ptypes); + List> result = instance.parameterList(); + assertEquals(expResult, result); + } + + /** + * Test of parameterArray method, of class MethodType. + */ + @Test + public void testParameterArray() { + System.out.println("parameterArray"); + MethodType instance = mt_viS; + Class[] expResult = ptypes; + Class[] result = instance.parameterArray(); + assertEquals(Arrays.asList(expResult), Arrays.asList(result)); + } + + /** + * Test of equals method, of class MethodType. + */ + @Test + public void testEquals_Object() { + System.out.println("equals"); + Object x = null; + MethodType instance = mt_viS; + boolean expResult = false; + boolean result = instance.equals(x); + assertEquals(expResult, result); + } + + /** + * Test of equals method, of class MethodType. + */ + @Test + public void testEquals_MethodType() { + System.out.println("equals"); + MethodType that = mt_viS; + MethodType instance = mt_viS; + boolean expResult = true; + boolean result = instance.equals(that); + assertEquals(expResult, result); + } + + /** + * Test of hashCode method, of class MethodType. + */ + @Test + public void testHashCode() { + System.out.println("hashCode"); + MethodType instance = mt_viS; + ArrayList> types = new ArrayList>(); + types.add(instance.returnType()); + types.addAll(instance.parameterList()); + int expResult = types.hashCode(); + int result = instance.hashCode(); + assertEquals(expResult, result); + } + + /** + * Test of toString method, of class MethodType. + */ + @Test + public void testToString() { + System.out.println("toString"); + MethodType[] instances = {mt_viS, mt_OO2, mt_vv, mt_Ov, mt_iSI, mt_ISi, mt_ISI, mt_iSi}; + //String expResult = "void[int, class java.lang.String]"; + String[] expResults = { + "(int,java.lang.String)void", + "(java.lang.Object,java.lang.Object)java.lang.Object", + "()void", + "()java.lang.Object", + "(java.lang.String,java.lang.Integer)int", + "(java.lang.String,int)java.lang.Integer", + "(java.lang.String,java.lang.Integer)java.lang.Integer", + "(java.lang.String,int)int" + }; + for (int i = 0; i < instances.length; i++) { + MethodType instance = instances[i]; + String result = instance.toString(); + System.out.println("#"+i+":"+result); + assertEquals("#"+i, expResults[i], result); + } + } + + @Test + public void testToString_va() { + System.out.println("toString"); + MethodType instance = MethodType.make(Object.class, int[].class); + for (int i = 0; i <= 1; i++) { + boolean va = (i != 0); + String expResult = "(int"+(va?"...":"[]")+")java.lang.Object"; + String result = instance.changeVarArgs(va).toString(); + System.out.println(result); + assertEquals(expResult, result); + } + } + +}