InvokeDynamicPrintArgs.java revision 4251:f09930d526ba
1/*
2 * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @summary smoke test for invokedynamic instructions
26 * @build indify.Indify
27 * @compile InvokeDynamicPrintArgs.java
28 * @run main/othervm
29 *      indify.Indify
30 *      --verify-specifier-count=3
31 *      --expand-properties --classpath ${test.classes}
32 *      --java test.java.lang.invoke.InvokeDynamicPrintArgs --check-output
33 */
34
35package test.java.lang.invoke;
36
37import org.junit.Test;
38
39import java.util.*;
40import java.io.*;
41
42import java.lang.invoke.*;
43import static java.lang.invoke.MethodHandles.*;
44import static java.lang.invoke.MethodType.*;
45
46public class InvokeDynamicPrintArgs {
47    public static void main(String... av) throws Throwable {
48        if (av.length > 0)  openBuf();  // --check-output mode
49        System.out.println("Printing some argument lists, starting with a empty one:");
50        INDY_nothing().invokeExact();                 // BSM specifier #0 = {bsm}
51        INDY_bar().invokeExact("bar arg", 1);         // BSM specifier #1 = {bsm2, Void.class, "void type"}
52        INDY_bar2().invokeExact("bar2 arg", 222);     // BSM specifier #1 = (same)
53        INDY_baz().invokeExact("baz arg", 2, 3.14);   // BSM specifier #2 = {bsm2, 1234.5}
54        INDY_foo().invokeExact("foo arg");            // BSM specifier #0 = (same)
55        // Hence, BSM specifier count should be 3.  See "--verify-specifier-count=3" above.
56        System.out.println("Done printing argument lists.");
57        closeBuf();
58    }
59
60    @Test
61    public void testInvokeDynamicPrintArgs() throws IOException {
62        System.err.println(System.getProperties());
63        String testClassPath = System.getProperty("build.test.classes.dir");
64        if (testClassPath == null)  throw new RuntimeException();
65        String[] args = new String[]{
66            "--verify-specifier-count=3",
67            "--verbose",
68            "--expand-properties", "--classpath", testClassPath,
69            "--java", "test.java.lang.invoke.InvokeDynamicPrintArgs", "--check-output"
70        };
71        System.err.println("Indify: "+Arrays.toString(args));
72        indify.Indify.main(args);
73    }
74
75    private static PrintStream oldOut;
76    private static ByteArrayOutputStream buf;
77    private static void openBuf() {
78        oldOut = System.out;
79        buf = new ByteArrayOutputStream();
80        System.setOut(new PrintStream(buf));
81    }
82    private static void closeBuf() {
83        if (buf == null)  return;
84        System.out.flush();
85        System.setOut(oldOut);
86        String[] haveLines = new String(buf.toByteArray()).split("[\n\r]+");
87        for (String line : haveLines)  System.out.println(line);
88        Iterator<String> iter = Arrays.asList(haveLines).iterator();
89        for (String want : EXPECT_OUTPUT) {
90            String have = iter.hasNext() ? iter.next() : "[EOF]";
91            if (want.equals(have))  continue;
92            System.err.println("want line: "+want);
93            System.err.println("have line: "+have);
94            throw new AssertionError("unexpected output: "+have);
95        }
96        if (iter.hasNext())
97            throw new AssertionError("unexpected output: "+iter.next());
98    }
99    private static final String[] EXPECT_OUTPUT = {
100        "Printing some argument lists, starting with a empty one:",
101        "[test.java.lang.invoke.InvokeDynamicPrintArgs, nothing, ()void][]",
102        "[test.java.lang.invoke.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
103        "[test.java.lang.invoke.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
104        "[test.java.lang.invoke.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
105        "[test.java.lang.invoke.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
106        "Done printing argument lists."
107    };
108
109    private static boolean doPrint = true;
110    private static void printArgs(Object bsmInfo, Object... args) {
111        String message = bsmInfo+Arrays.deepToString(args);
112        if (doPrint)  System.out.println(message);
113    }
114    private static MethodHandle MH_printArgs() throws ReflectiveOperationException {
115        shouldNotCallThis();
116        return lookup().findStatic(lookup().lookupClass(),
117                                   "printArgs", methodType(void.class, Object.class, Object[].class));
118    }
119
120    private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
121        // ignore caller and name, but match the type:
122        Object bsmInfo = Arrays.asList(caller, name, type);
123        return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
124    }
125    private static MethodType MT_bsm() {
126        shouldNotCallThis();
127        return methodType(CallSite.class, Lookup.class, String.class, MethodType.class);
128    }
129    private static MethodHandle MH_bsm() throws ReflectiveOperationException {
130        shouldNotCallThis();
131        return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
132    }
133
134    /* Example of a constant call site with user-data.
135     * In this case, the user data is exactly the BSM data.
136     * Note that a CCS with user data must use the "hooked" constructor
137     * to bind the CCS itself into the resulting target.
138     * A normal constructor would not allow a circular relation
139     * between the CCS and its target.
140     */
141    public static class PrintingCallSite extends ConstantCallSite {
142        final Lookup caller;
143        final String name;
144        final Object[] staticArgs;
145
146        PrintingCallSite(Lookup caller, String name, MethodType type, Object... staticArgs) throws Throwable {
147            super(type, MH_createTarget());
148            this.caller = caller;
149            this.name = name;
150            this.staticArgs = staticArgs;
151        }
152
153        public MethodHandle createTarget() {
154            try {
155                return lookup().bind(this, "runTarget", genericMethodType(0, true)).asType(type());
156            } catch (ReflectiveOperationException ex) {
157                throw new RuntimeException(ex);
158            }
159        }
160
161        public Object runTarget(Object... dynamicArgs) {
162            List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type()));
163            bsmInfo.addAll(Arrays.asList(staticArgs));
164            printArgs(bsmInfo, dynamicArgs);
165            return null;
166        }
167
168        private static MethodHandle MH_createTarget() throws ReflectiveOperationException {
169            shouldNotCallThis();
170            return lookup().findVirtual(lookup().lookupClass(), "createTarget", methodType(MethodHandle.class));
171        }
172    }
173    private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws Throwable {
174        // ignore caller and name, but match the type:
175        return new PrintingCallSite(caller, name, type, arg);
176    }
177    private static MethodType MT_bsm2() {
178        shouldNotCallThis();
179        return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class);
180    }
181    private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
182        shouldNotCallThis();
183        return lookup().findStatic(lookup().lookupClass(), "bsm2", MT_bsm2());
184    }
185
186    private static MethodHandle INDY_nothing() throws Throwable {
187        shouldNotCallThis();
188        return ((CallSite) MH_bsm().invoke(lookup(),
189                                                  "nothing", methodType(void.class)
190                                                  )).dynamicInvoker();
191    }
192    private static MethodHandle INDY_foo() throws Throwable {
193        shouldNotCallThis();
194        return ((CallSite) MH_bsm().invoke(lookup(),
195                                                  "foo", methodType(void.class, String.class)
196                                                  )).dynamicInvoker();
197    }
198    private static MethodHandle INDY_bar() throws Throwable {
199        shouldNotCallThis();
200        return ((CallSite) MH_bsm2().invoke(lookup(),
201                                                  "bar", methodType(void.class, String.class, int.class)
202                                                  , Void.class, "void type!", 1, 234.5F, 67.5, (long)89
203                                                  )).dynamicInvoker();
204    }
205    private static MethodHandle INDY_bar2() throws Throwable {
206        shouldNotCallThis();
207        return ((CallSite) MH_bsm2().invoke(lookup(),
208                                                  "bar2", methodType(void.class, String.class, int.class)
209                                                  , Void.class, "void type!", 1, 234.5F, 67.5, (long)89
210                                                  )).dynamicInvoker();
211    }
212    private static MethodHandle INDY_baz() throws Throwable {
213        shouldNotCallThis();
214        return ((CallSite) MH_bsm2().invoke(lookup(),
215                                                  "baz", methodType(void.class, String.class, int.class, double.class)
216                                                  , 1234.5
217                                                  )).dynamicInvoker();
218    }
219
220    private static void shouldNotCallThis() {
221        // if this gets called, the transformation has not taken place
222        if (System.getProperty("InvokeDynamicPrintArgs.allow-untransformed") != null)  return;
223        throw new AssertionError("this code should be statically transformed away by Indify");
224    }
225}
226