1 /*
2  * Copyright (c) 2011, 2018, 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 package vm.mlvm.meth.share.transform.v2;
25 
26 import java.lang.invoke.MethodHandle;
27 import java.lang.invoke.MethodHandles;
28 import java.lang.invoke.MethodType;
29 import java.util.Arrays;
30 
31 import nsk.share.test.LazyObjectArrayToString;
32 import nsk.share.test.LazyFormatString;
33 import vm.mlvm.meth.share.Argument;
34 import vm.mlvm.meth.share.Arguments;
35 import vm.mlvm.meth.share.MHUtils;
36 import vm.mlvm.share.Env;
37 
38 public class MHCall {
39 
40     private static final boolean TRACE = false;
41     private static final boolean TRACE_COMPLAIN_VALUE_MISMATCHES = false;
42 
43     private final MHTF _target;
44     private final Argument[] _args;
45     private final Argument _retVal;
46     private final MethodHandle _targetMH;
47 
MHCall(Argument retVal, MHTF target, MethodHandle targetMH, Argument[] args)48     MHCall(Argument retVal, MHTF target, MethodHandle targetMH, Argument[] args) {
49         _args = args;
50         _target = target;
51         _retVal = retVal;
52 
53         if ( TRACE ) {
54             try {
55                 targetMH = MethodHandles.explicitCastArguments(
56                         MethodHandles.lookup().findVirtual(MHCall.class, "trace", MethodType.methodType(Object.class, MethodHandle.class, Object[].class))
57                         .bindTo(this)
58                         .bindTo(targetMH.asSpreader(Object[].class, targetMH.type().parameterCount()))
59                         .asCollector(Object[].class, targetMH.type().parameterCount()),
60                         targetMH.type());
61             } catch ( Exception e ) {
62                 Env.complain(e, "Can't add tracing to MHCall %s", this);
63             }
64         }
65 
66         _targetMH = targetMH;
67         Env.traceDebug("Created MHCall: %s", this);
68     }
69 
70     @SuppressWarnings("unused")
trace(MethodHandle targetMH, Object[] args)71     private Object trace(MethodHandle targetMH, Object[] args) throws Throwable {
72         try {
73             Env.traceNormal("Invoking %s\n\t\targuments=%s", _target, new LazyObjectArrayToString(args));
74 
75             for ( int i = 0; i < args.length; i++ ) {
76                 Object actualArg = args[i];
77                 Object goldenArg = _args[i].getValue();
78                 if ( actualArg != null && ! actualArg.equals(goldenArg) || actualArg == null && goldenArg != null ) {
79                     if ( TRACE_COMPLAIN_VALUE_MISMATCHES )
80                         Env.complain("\t\tArgument " + i + " mismatch: actual=%s, required=%s", actualArg, goldenArg);
81                     else
82                         Env.traceNormal("\t\tArgument " + i + " mismatch: actual=%s, required=%s", actualArg, goldenArg);
83                 }
84             }
85 
86             Object result = targetMH.invoke((Object[]) args);
87 
88             Env.traceNormal("Returning from %s\n\t\tresult=%s", _target, result);
89 
90             Object requiredRetVal = _retVal.getValue();
91             if ( result != null && ! result.equals(requiredRetVal) || result == null && requiredRetVal != null ) {
92                 if ( TRACE_COMPLAIN_VALUE_MISMATCHES )
93                     Env.complain("\t\tResult mismatch: actual=%s, required=%s", result, requiredRetVal);
94                 else
95                     Env.traceNormal("\t\tResult mismatch: actual=%s, required=%s", result, requiredRetVal);
96             }
97 
98             return result;
99         } catch ( Throwable t ){
100             Env.traceNormal(t, "Exception caught after calling %s", _target);
101             throw t;
102         }
103     }
104 
getRetVal()105     public Argument getRetVal() {
106         return _retVal;
107     }
108 
109     /**
110      * @return May return null if target is not a transformation (but, say, a user's MH)
111      */
getTarget()112     public MHTF getTarget() {
113         return _target;
114     }
115 
getTargetMH()116     public MethodHandle getTargetMH() {
117         return _targetMH;
118     }
119 
getArgs()120     public Argument[] getArgs() {
121         return _args;
122     }
123 
check()124     public void check() throws IllegalArgumentException {
125         MethodType mt = _targetMH.type();
126         for ( int i = 0; i < mt.parameterCount(); i++ ) {
127             MHUtils.assertAssignableType(new LazyFormatString("argument %i in %s", i, this), mt.parameterType(i), _args[i].getType());
128         }
129 
130         MHUtils.assertAssignableType(new LazyFormatString("return type in %s", this), mt.returnType(), _retVal.getType());
131     }
132 
call()133     public Object call() throws Throwable {
134         if ( ! _retVal.getType().equals(void.class) )
135             return (Object) _targetMH.invokeWithArguments(Arguments.valuesArray(_args));
136         else {
137             _targetMH.invokeWithArguments(Arguments.valuesArray(_args));
138             return null;
139         }
140     }
141 
callAndCheckRetVal()142     public Object callAndCheckRetVal() throws Throwable {
143         Object r = call();
144 
145         if ( ! _retVal.getType().equals(void.class) && ! r.equals(_retVal.getValue()) )
146             throw new IllegalArgumentException("Call returned wrong value: "
147                     + "actual=" + r
148                     + "; expected=" + _retVal.getValue());
149         return r;
150     }
151 
152     @Override
toString()153     public String toString() {
154         return "MHCall: target=" + _target + "; args=" + Arrays.toString(_args) + "; retVal=" + _retVal + "; targetMH=" + _targetMH;
155     }
156 
prettyPrint(String topPrefix, String subPrefix)157     String prettyPrint(String topPrefix, String subPrefix) {
158         return topPrefix + "MHCall:   target = " + _target + "\n"
159              + subPrefix + "       arguments = " + Arrays.toString(_args) + "\n"
160              + subPrefix + "       retVal    = " + _retVal + "\n"
161              + subPrefix + "       targetMH  = " + _targetMH;
162     }
163 }
164