1 /* 2 * Copyright (c) 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdi; 27 28 import java.util.ArrayList; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Set; 33 34 import com.sun.jdi.ClassNotLoadedException; 35 import com.sun.jdi.ClassType; 36 import com.sun.jdi.IncompatibleThreadStateException; 37 import com.sun.jdi.InterfaceType; 38 import com.sun.jdi.InvalidTypeException; 39 import com.sun.jdi.InvocationException; 40 import com.sun.jdi.Method; 41 import com.sun.jdi.ReferenceType; 42 import com.sun.jdi.ThreadReference; 43 import com.sun.jdi.VMCannotBeModifiedException; 44 import com.sun.jdi.Value; 45 import com.sun.jdi.VirtualMachine; 46 47 /** 48 * A supertype for ReferenceTypes allowing method invocations 49 */ 50 abstract class InvokableTypeImpl extends ReferenceTypeImpl { 51 52 /** 53 * The invocation result wrapper 54 * It is necessary because both ClassType and InterfaceType 55 * use their own type to represent the invocation result 56 */ 57 static interface InvocationResult { 58 ObjectReferenceImpl getException(); 59 ValueImpl getResult(); 60 } 61 62 InvokableTypeImpl(VirtualMachine aVm, long aRef) { 63 super(aVm, aRef); 64 } 65 66 /** 67 * Method invocation support. 68 * Shared by ClassType and InterfaceType 69 * @param threadIntf the thread in which to invoke. 70 * @param methodIntf method the {@link Method} to invoke. 71 * @param origArguments the list of {@link Value} arguments bound to the 72 * invoked method. Values from the list are assigned to arguments 73 * in the order they appear in the method signature. 74 * @param options the integer bit flag options. 75 * @return a {@link Value} mirror of the invoked method's return value. 76 * @throws java.lang.IllegalArgumentException if the method is not 77 * a member of this type, if the size of the argument list 78 * does not match the number of declared arguments for the method, or 79 * if the method is not static or is a static initializer. 80 * @throws ClassNotLoadedException if any argument type has not yet been loaded 81 * through the appropriate class loader. 82 * @throws IncompatibleThreadStateException if the specified thread has not 83 * been suspended by an event. 84 * @throws InvocationException if the method invocation resulted in 85 * an exception in the target VM. 86 * @throws InvalidTypeException If the arguments do not meet this requirement -- 87 * Object arguments must be assignment compatible with the argument 88 * type. This implies that the argument type must be 89 * loaded through the enclosing class's class loader. 90 * Primitive arguments must be either assignment compatible with the 91 * argument type or must be convertible to the argument type without loss 92 * of information. See JLS section 5.2 for more information on assignment 93 * compatibility. 94 * @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}. 95 */ 96 final public Value invokeMethod(ThreadReference threadIntf, Method methodIntf, 97 List<? extends Value> origArguments, int options) 98 throws InvalidTypeException, 99 ClassNotLoadedException, 100 IncompatibleThreadStateException, 101 InvocationException { 102 validateMirror(threadIntf); 103 validateMirror(methodIntf); 104 validateMirrorsOrNulls(origArguments); 105 MethodImpl method = (MethodImpl) methodIntf; 106 ThreadReferenceImpl thread = (ThreadReferenceImpl) threadIntf; 107 validateMethodInvocation(method); 108 List<? extends Value> arguments = method.validateAndPrepareArgumentsForInvoke(origArguments); 109 ValueImpl[] args = arguments.toArray(new ValueImpl[0]); 110 InvocationResult ret; 111 try { 112 PacketStream stream = sendInvokeCommand(thread, method, args, options); 113 ret = waitForReply(stream); 114 } catch (JDWPException exc) { 115 if (exc.errorCode() == JDWP.Error.INVALID_THREAD) { 116 throw new IncompatibleThreadStateException(); 117 } else { 118 throw exc.toJDIException(); 119 } 120 } 121 /* 122 * There is an implict VM-wide suspend at the conclusion 123 * of a normal (non-single-threaded) method invoke 124 */ 125 if ((options & ClassType.INVOKE_SINGLE_THREADED) == 0) { 126 vm.notifySuspend(); 127 } 128 if (ret.getException() != null) { 129 throw new InvocationException(ret.getException()); 130 } else { 131 return ret.getResult(); 132 } 133 } 134 135 @Override 136 boolean isAssignableTo(ReferenceType type) { 137 ClassTypeImpl superclazz = (ClassTypeImpl) superclass(); 138 if (this.equals(type)) { 139 return true; 140 } else if ((superclazz != null) && superclazz.isAssignableTo(type)) { 141 return true; 142 } else { 143 List<InterfaceType> interfaces = interfaces(); 144 Iterator<InterfaceType> iter = interfaces.iterator(); 145 while (iter.hasNext()) { 146 InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next(); 147 if (interfaze.isAssignableTo(type)) { 148 return true; 149 } 150 } 151 return false; 152 } 153 } 154 155 @Override 156 final void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces) { 157 /* 158 * Add methods from 159 * parent types first, so that the methods in this class will 160 * overwrite them in the hash table 161 */ 162 Iterator<InterfaceType> iter = interfaces().iterator(); 163 while (iter.hasNext()) { 164 InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next(); 165 if (!seenInterfaces.contains(interfaze)) { 166 interfaze.addVisibleMethods(methodMap, seenInterfaces); 167 seenInterfaces.add(interfaze); 168 } 169 } 170 ClassTypeImpl clazz = (ClassTypeImpl) superclass(); 171 if (clazz != null) { 172 clazz.addVisibleMethods(methodMap, seenInterfaces); 173 } 174 addToMethodMap(methodMap, methods()); 175 } 176 177 final void addInterfaces(List<InterfaceType> list) { 178 List<InterfaceType> immediate = interfaces(); 179 list.addAll(interfaces()); 180 Iterator<InterfaceType> iter = immediate.iterator(); 181 while (iter.hasNext()) { 182 InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next(); 183 interfaze.addInterfaces(list); 184 } 185 ClassTypeImpl superclass = (ClassTypeImpl) superclass(); 186 if (superclass != null) { 187 superclass.addInterfaces(list); 188 } 189 } 190 191 /** 192 * Returns all the implemented interfaces recursively 193 * @return A list of all the implemented interfaces (recursively) 194 */ 195 final List<InterfaceType> getAllInterfaces() { 196 List<InterfaceType> all = new ArrayList<>(); 197 addInterfaces(all); 198 return all; 199 } 200 201 /** 202 * Shared implementation of {@linkplain ClassType#allMethods()} and 203 * {@linkplain InterfaceType#allMethods()} 204 * @return A list of all methods (recursively) 205 */ 206 public final List<Method> allMethods() { 207 ArrayList<Method> list = new ArrayList<>(methods()); 208 ClassType clazz = superclass(); 209 while (clazz != null) { 210 list.addAll(clazz.methods()); 211 clazz = clazz.superclass(); 212 } 213 /* 214 * Avoid duplicate checking on each method by iterating through 215 * duplicate-free allInterfaces() rather than recursing 216 */ 217 for (InterfaceType interfaze : getAllInterfaces()) { 218 list.addAll(interfaze.methods()); 219 } 220 return list; 221 } 222 223 @Override 224 final List<ReferenceType> inheritedTypes() { 225 List<ReferenceType> inherited = new ArrayList<>(); 226 if (superclass() != null) { 227 inherited.add(0, superclass()); /* insert at front */ 228 } 229 for (ReferenceType rt : interfaces()) { 230 inherited.add(rt); 231 } 232 return inherited; 233 } 234 235 private PacketStream sendInvokeCommand(final ThreadReferenceImpl thread, 236 final MethodImpl method, 237 final ValueImpl[] args, 238 final int options) { 239 /* 240 * Cache the values of args when TRACE_SENDS is enabled, for later printing. 241 * If not cached, printing causes a remote call while synchronized, and deadlock. 242 */ 243 if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) { 244 for (ValueImpl arg: args) { 245 arg.toString(); 246 } 247 } 248 CommandSender sender = getInvokeMethodSender(thread, method, args, options); 249 PacketStream stream; 250 if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) { 251 stream = thread.sendResumingCommand(sender); 252 } else { 253 stream = vm.sendResumingCommand(sender); 254 } 255 return stream; 256 } 257 258 private void validateMethodInvocation(Method method) 259 throws InvalidTypeException, 260 InvocationException { 261 if (!canInvoke(method)) { 262 throw new IllegalArgumentException("Invalid method"); 263 } 264 /* 265 * Method must be a static and not a static initializer 266 */ 267 if (!method.isStatic()) { 268 throw new IllegalArgumentException("Cannot invoke instance method on a class/interface type"); 269 } else if (method.isStaticInitializer()) { 270 throw new IllegalArgumentException("Cannot invoke static initializer"); 271 } 272 } 273 274 /** 275 * A subclass will provide specific {@linkplain CommandSender} 276 * @param thread the current invocation thread 277 * @param method the method to invoke 278 * @param args the arguments to pass to the method 279 * @param options the integer bit flag options 280 * @return the specific {@literal CommandSender} instance 281 */ 282 abstract CommandSender getInvokeMethodSender(ThreadReferenceImpl thread, 283 MethodImpl method, 284 ValueImpl[] args, 285 int options); 286 287 /** 288 * Waits for the reply to the last sent command 289 * @param stream the stream to listen for the reply on 290 * @return the {@linkplain InvocationResult} instance 291 * @throws JDWPException when something goes wrong in JDWP 292 */ 293 abstract InvocationResult waitForReply(PacketStream stream) throws JDWPException; 294 295 /** 296 * Get the {@linkplain ReferenceType} superclass 297 * @return the superclass or null 298 */ 299 abstract ClassType superclass(); 300 301 /** 302 * Get the implemented/extended interfaces 303 * @return the list of implemented/extended interfaces 304 */ 305 abstract List<InterfaceType> interfaces(); 306 307 /** 308 * Checks the provided method whether it can be invoked 309 * @param method the method to check 310 * @return {@code TRUE} if the implementation knows how to invoke the method, 311 * {@code FALSE} otherwise 312 */ 313 abstract boolean canInvoke(Method method); 314 }