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 }