1/*
2 * Copyright (c) 2014, 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
26package com.sun.tools.jdi;
27
28import com.sun.jdi.ClassNotLoadedException;
29import com.sun.jdi.ClassType;
30import com.sun.jdi.IncompatibleThreadStateException;
31import com.sun.jdi.InterfaceType;
32import com.sun.jdi.InvalidTypeException;
33import com.sun.jdi.InvocationException;
34import com.sun.jdi.Method;
35import com.sun.jdi.ReferenceType;
36import com.sun.jdi.ThreadReference;
37import com.sun.jdi.Value;
38import com.sun.jdi.VirtualMachine;
39import java.util.ArrayList;
40import java.util.Iterator;
41import java.util.List;
42import java.util.Map;
43import java.util.Set;
44
45/**
46 * A supertype for ReferenceTypes allowing method invocations
47 */
48abstract class InvokableTypeImpl extends ReferenceTypeImpl {
49    /**
50     * The invocation result wrapper
51     * It is necessary because both ClassType and InterfaceType
52     * use their own type to represent the invocation result
53     */
54    static interface InvocationResult {
55        ObjectReferenceImpl getException();
56        ValueImpl getResult();
57    }
58
59    InvokableTypeImpl(VirtualMachine aVm, long aRef) {
60        super(aVm, aRef);
61    }
62
63    /**
64     * Method invocation support.
65     * Shared by ClassType and InterfaceType
66     * @param threadIntf the thread in which to invoke.
67     * @param methodIntf method the {@link Method} to invoke.
68     * @param origArguments the list of {@link Value} arguments bound to the
69     * invoked method. Values from the list are assigned to arguments
70     * in the order they appear in the method signature.
71     * @param options the integer bit flag options.
72     * @return a {@link Value} mirror of the invoked method's return value.
73     * @throws java.lang.IllegalArgumentException if the method is not
74     * a member of this type, if the size of the argument list
75     * does not match the number of declared arguments for the method, or
76     * if the method is not static or is a static initializer.
77     * @throws {@link InvalidTypeException} if any argument in the
78     * argument list is not assignable to the corresponding method argument
79     * type.
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 & VirtualMachineImpl.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}
315