1 /* 2 * Copyright (c) 2013, 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.runtime.defmeth.shared.executor; 25 26 import nsk.share.Pair; 27 import nsk.share.TestFailure; 28 import nsk.share.test.TestUtils; 29 import vm.runtime.defmeth.shared.data.AbstractVisitor; 30 import vm.runtime.defmeth.shared.DefMethTest; 31 import vm.runtime.defmeth.shared.MemoryClassLoader; 32 import vm.runtime.defmeth.shared.Util; 33 import vm.runtime.defmeth.shared.data.Visitor; 34 import vm.runtime.defmeth.shared.data.Clazz; 35 import vm.runtime.defmeth.shared.data.Tester; 36 import vm.runtime.defmeth.shared.data.method.body.CallMethod; 37 import vm.runtime.defmeth.shared.data.method.param.*; 38 import vm.runtime.defmeth.shared.data.method.result.IntResult; 39 import vm.runtime.defmeth.shared.data.method.result.ThrowExResult; 40 41 import java.lang.invoke.MethodHandle; 42 import java.lang.invoke.MethodHandles; 43 import java.lang.invoke.MethodType; 44 import java.lang.reflect.InvocationTargetException; 45 import java.lang.reflect.Method; 46 import java.util.Arrays; 47 import java.util.Collection; 48 49 /** 50 * Test runner for invocation mode through MethodHandle.invokeWithArguments(...). 51 */ 52 public class MHInvokeWithArgsTest extends AbstractReflectionTest { 53 54 55 public MHInvokeWithArgsTest(MemoryClassLoader cl, DefMethTest testInstance, 56 Collection<? extends Tester> tests) { 57 super(testInstance, cl, tests); 58 } 59 60 public MHInvokeWithArgsTest(MemoryClassLoader cl, DefMethTest testInstance, Tester... tests) { 61 super(testInstance, cl, Arrays.asList(tests)); 62 } 63 64 private MethodType descToMethodType(String desc) throws ClassNotFoundException { 65 Pair<String[],String> p = Util.parseDesc(desc); 66 Class rtype = Util.decodeClass(p.second, cl); 67 if (p.first.length > 0) { 68 Class[] ptypes = new Class[p.first.length]; 69 for (int i = 0; i < ptypes.length; i++) { 70 ptypes[i] = Util.decodeClass(p.first[i], cl); 71 } 72 return MethodType.methodType(rtype, ptypes); 73 } else { 74 return MethodType.methodType(rtype); 75 } 76 } 77 private class InvokeWithArgsVisitor extends AbstractVisitor implements Visitor { 78 private CallMethod call; 79 80 private CallMethod.Invoke invokeType; 81 Class<?> declaringClass; 82 String methodName; 83 MethodType methodType; 84 private Object[] args; 85 86 @Override 87 public void visitTester(Tester t) { 88 // Resolve targetMethod & prepare invocation parameters 89 t.getCall().visit(this); 90 91 // Invoke resolved targetMethod and check returned value 92 t.getResult().visit(this); 93 } 94 95 private void prepareForInvocation() 96 throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException { 97 final Clazz staticClass = call.staticClass(); 98 final Clazz receiverClass = call.receiverClass(); 99 100 final Param[] params = call.params(); 101 102 // MethodHandle construction is skipped deliberately 103 // Need to perform the lookup in correct context 104 invokeType = call.invokeInsn(); 105 declaringClass = resolve(staticClass); 106 methodName = call.methodName(); 107 methodType = descToMethodType(call.methodDesc()); 108 109 Object[] values = values(params); 110 111 if (call.invokeInsn() != CallMethod.Invoke.STATIC) { 112 // Prepare receiver for non-static call 113 args = new Object[params.length+1]; 114 Class recClass = resolve(receiverClass); 115 args[0] = recClass.newInstance(); 116 System.arraycopy(values, 0, args, 1, values.length); 117 } else { 118 // No need for a receiver for static call 119 args = values; 120 } 121 } 122 123 @Override 124 public void visitCallMethod(CallMethod call) { 125 // Cache call site info for future uses 126 this.call = call; 127 128 // NB! don't call prepareForInvocation yet - it can throw exceptions expected by a test 129 } 130 131 @Override 132 public void visitResultInt(IntResult res) { 133 try { 134 prepareForInvocation(); 135 int result = (int) invokeInTestContext(); 136 TestUtils.assertEquals(res.getExpected(), result); 137 } catch (Throwable e) { 138 throw new TestFailure("Unexpected exception", e); 139 } 140 } 141 142 @Override 143 public void visitResultThrowExc(ThrowExResult res) { 144 String expectedExcName = res.getExc().name(); 145 String originalExpectedExcName = expectedExcName; 146 // *Error <==> *Exception correspondence for reflection invocation 147 switch (expectedExcName) { 148 case "java.lang.IllegalAccessError": 149 case "java.lang.InstantiationError": 150 expectedExcName = expectedExcName.replace("Error", "Exception"); 151 break; 152 } 153 154 try { 155 prepareForInvocation(); // can throw an exception expected by a test 156 invokeInTestContext(); 157 throw new TestFailure("No exception was thrown: " + expectedExcName); 158 } catch (Throwable ex) { 159 // Need to dig 1 level down (MH.invoke* doesn't wrap exceptions), since there are 2 levels of indirection 160 // during invocation: 161 // invokeInTestContext(...) => TestContext.invoke(...) => Method.invoke(...) 162 Throwable target = ex; 163 Class<?> actualExc = (target.getCause() != null) ? target.getCause().getClass() 164 : target.getClass(); 165 Class<?> expectedExc; 166 try { 167 expectedExc = cl.loadClass(expectedExcName); 168 } catch (ClassNotFoundException e) { 169 throw new Error(e); 170 } 171 if (!expectedExc.isAssignableFrom(actualExc) && 172 !originalExpectedExcName.equals(actualExc.getName()) && 173 !expectedExc.isAssignableFrom(target.getClass())) { 174 throw new TestFailure( 175 String.format("Caught exception as expected, but it's type is wrong: expected: %s; actual: %s.", 176 expectedExcName, actualExc.getName()), target); 177 } 178 } 179 } 180 181 @Override 182 public void visitResultIgnore() { 183 try { 184 prepareForInvocation(); 185 invokeInTestContext(); 186 } catch (Throwable e) { 187 throw new TestFailure("Unexpected exception", e); 188 } 189 } 190 191 private Object invokeInTestContext() throws Throwable { 192 Class<?> context = cl.getTestContext(); 193 // Invoke target method from TestContext using: 194 // public static Object invokeWithArguments( 195 // CallMethod.Invoke invokeType, Class<?> declaringClass, String methodName, MethodType type, 196 // Object... arguments) throws Throwable 197 MethodHandle invoker; 198 try { 199 invoker = MethodHandles.lookup(). 200 findStatic(context, "invokeWithArguments", 201 MethodType.methodType(Object.class, CallMethod.Invoke.class, Class.class, 202 String.class, MethodType.class, Object[].class)); 203 } catch (NoSuchMethodException | IllegalAccessException e) { 204 throw new TestFailure("Exception during reflection invocation", e.getCause()); 205 } 206 207 return invoker.invokeExact(invokeType, declaringClass, methodName, methodType, args); 208 209 } 210 } 211 212 /** 213 * Run individual assertion for the test by it's name. 214 * 215 * @param test 216 * @throws Throwable 217 */ 218 public void run(Tester test) throws Throwable { 219 test.visit(new InvokeWithArgsVisitor()); 220 } 221 }