1/*
2 * Copyright (c) 2014, 2017, 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 java.util.ArrayList;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Map;
32import java.util.Set;
33
34import com.sun.jdi.ClassNotLoadedException;
35import com.sun.jdi.ClassType;
36import com.sun.jdi.IncompatibleThreadStateException;
37import com.sun.jdi.InterfaceType;
38import com.sun.jdi.InvalidTypeException;
39import com.sun.jdi.InvocationException;
40import com.sun.jdi.Method;
41import com.sun.jdi.ReferenceType;
42import com.sun.jdi.ThreadReference;
43import com.sun.jdi.VMCannotBeModifiedException;
44import com.sun.jdi.Value;
45import com.sun.jdi.VirtualMachine;
46
47/**
48 * A supertype for ReferenceTypes allowing method invocations
49 */
50abstract 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 InvalidTypeException if any argument in the
81     * argument list is not assignable to the corresponding method argument
82     * type.
83     * @throws ClassNotLoadedException if any argument type has not yet been loaded
84     * through the appropriate class loader.
85     * @throws IncompatibleThreadStateException if the specified thread has not
86     * been suspended by an event.
87     * @throws InvocationException if the method invocation resulted in
88     * an exception in the target VM.
89     * @throws InvalidTypeException If the arguments do not meet this requirement --
90     *         Object arguments must be assignment compatible with the argument
91     *         type.  This implies that the argument type must be
92     *         loaded through the enclosing class's class loader.
93     *         Primitive arguments must be either assignment compatible with the
94     *         argument type or must be convertible to the argument type without loss
95     *         of information. See JLS section 5.2 for more information on assignment
96     *         compatibility.
97     * @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}.
98     */
99    final public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
100                                    List<? extends Value> origArguments, int options)
101                                        throws InvalidTypeException,
102                                               ClassNotLoadedException,
103                                               IncompatibleThreadStateException,
104                                               InvocationException {
105        validateMirror(threadIntf);
106        validateMirror(methodIntf);
107        validateMirrorsOrNulls(origArguments);
108        MethodImpl method = (MethodImpl) methodIntf;
109        ThreadReferenceImpl thread = (ThreadReferenceImpl) threadIntf;
110        validateMethodInvocation(method);
111        List<? extends Value> arguments = method.validateAndPrepareArgumentsForInvoke(origArguments);
112        ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
113        InvocationResult ret;
114        try {
115            PacketStream stream = sendInvokeCommand(thread, method, args, options);
116            ret = waitForReply(stream);
117        } catch (JDWPException exc) {
118            if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
119                throw new IncompatibleThreadStateException();
120            } else {
121                throw exc.toJDIException();
122            }
123        }
124        /*
125         * There is an implict VM-wide suspend at the conclusion
126         * of a normal (non-single-threaded) method invoke
127         */
128        if ((options & ClassType.INVOKE_SINGLE_THREADED) == 0) {
129            vm.notifySuspend();
130        }
131        if (ret.getException() != null) {
132            throw new InvocationException(ret.getException());
133        } else {
134            return ret.getResult();
135        }
136    }
137
138    @Override
139    boolean isAssignableTo(ReferenceType type) {
140        ClassTypeImpl superclazz = (ClassTypeImpl) superclass();
141        if (this.equals(type)) {
142            return true;
143        } else if ((superclazz != null) && superclazz.isAssignableTo(type)) {
144            return true;
145        } else {
146            List<InterfaceType> interfaces = interfaces();
147            Iterator<InterfaceType> iter = interfaces.iterator();
148            while (iter.hasNext()) {
149                InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
150                if (interfaze.isAssignableTo(type)) {
151                    return true;
152                }
153            }
154            return false;
155        }
156    }
157
158    @Override
159    final void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces) {
160        /*
161         * Add methods from
162         * parent types first, so that the methods in this class will
163         * overwrite them in the hash table
164         */
165        Iterator<InterfaceType> iter = interfaces().iterator();
166        while (iter.hasNext()) {
167            InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
168            if (!seenInterfaces.contains(interfaze)) {
169                interfaze.addVisibleMethods(methodMap, seenInterfaces);
170                seenInterfaces.add(interfaze);
171            }
172        }
173        ClassTypeImpl clazz = (ClassTypeImpl) superclass();
174        if (clazz != null) {
175            clazz.addVisibleMethods(methodMap, seenInterfaces);
176        }
177        addToMethodMap(methodMap, methods());
178    }
179
180    final void addInterfaces(List<InterfaceType> list) {
181        List<InterfaceType> immediate = interfaces();
182        list.addAll(interfaces());
183        Iterator<InterfaceType> iter = immediate.iterator();
184        while (iter.hasNext()) {
185            InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
186            interfaze.addInterfaces(list);
187        }
188        ClassTypeImpl superclass = (ClassTypeImpl) superclass();
189        if (superclass != null) {
190            superclass.addInterfaces(list);
191        }
192    }
193
194    /**
195     * Returns all the implemented interfaces recursively
196     * @return A list of all the implemented interfaces (recursively)
197     */
198    final List<InterfaceType> getAllInterfaces() {
199        List<InterfaceType> all = new ArrayList<>();
200        addInterfaces(all);
201        return all;
202    }
203
204    /**
205     * Shared implementation of {@linkplain ClassType#allMethods()} and
206     * {@linkplain InterfaceType#allMethods()}
207     * @return A list of all methods (recursively)
208     */
209    public final List<Method> allMethods() {
210        ArrayList<Method> list = new ArrayList<>(methods());
211        ClassType clazz = superclass();
212        while (clazz != null) {
213            list.addAll(clazz.methods());
214            clazz = clazz.superclass();
215        }
216        /*
217         * Avoid duplicate checking on each method by iterating through
218         * duplicate-free allInterfaces() rather than recursing
219         */
220        for (InterfaceType interfaze : getAllInterfaces()) {
221            list.addAll(interfaze.methods());
222        }
223        return list;
224    }
225
226    @Override
227    final List<ReferenceType> inheritedTypes() {
228        List<ReferenceType> inherited = new ArrayList<>();
229        if (superclass() != null) {
230            inherited.add(0, superclass()); /* insert at front */
231        }
232        for (ReferenceType rt : interfaces()) {
233            inherited.add(rt);
234        }
235        return inherited;
236    }
237
238    private PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
239                                           final MethodImpl method,
240                                           final ValueImpl[] args,
241                                           final int options) {
242        /*
243         * Cache the values of args when TRACE_SENDS is enabled, for later printing.
244         * If not cached, printing causes a remote call while synchronized, and deadlock.
245         */
246        if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) {
247           for (ValueImpl arg: args) {
248              arg.toString();
249           }
250        }
251        CommandSender sender = getInvokeMethodSender(thread, method, args, options);
252        PacketStream stream;
253        if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) {
254            stream = thread.sendResumingCommand(sender);
255        } else {
256            stream = vm.sendResumingCommand(sender);
257        }
258        return stream;
259    }
260
261    private void validateMethodInvocation(Method method)
262                                            throws InvalidTypeException,
263                                                   InvocationException {
264        if (!canInvoke(method)) {
265            throw new IllegalArgumentException("Invalid method");
266        }
267        /*
268         * Method must be a static and not a static initializer
269         */
270        if (!method.isStatic()) {
271            throw new IllegalArgumentException("Cannot invoke instance method on a class/interface type");
272        } else if (method.isStaticInitializer()) {
273            throw new IllegalArgumentException("Cannot invoke static initializer");
274        }
275    }
276
277    /**
278     * A subclass will provide specific {@linkplain CommandSender}
279     * @param thread the current invocation thread
280     * @param method the method to invoke
281     * @param args the arguments to pass to the method
282     * @param options the integer bit flag options
283     * @return the specific {@literal CommandSender} instance
284     */
285    abstract CommandSender getInvokeMethodSender(ThreadReferenceImpl thread,
286                                                 MethodImpl method,
287                                                 ValueImpl[] args,
288                                                 int options);
289
290    /**
291     * Waits for the reply to the last sent command
292     * @param stream the stream to listen for the reply on
293     * @return the {@linkplain InvocationResult} instance
294     * @throws JDWPException when something goes wrong in JDWP
295     */
296    abstract InvocationResult waitForReply(PacketStream stream) throws JDWPException;
297
298    /**
299     * Get the {@linkplain ReferenceType} superclass
300     * @return the superclass or null
301     */
302    abstract ClassType superclass();
303
304    /**
305     * Get the implemented/extended interfaces
306     * @return the list of implemented/extended interfaces
307     */
308    abstract List<InterfaceType> interfaces();
309
310    /**
311     * Checks the provided method whether it can be invoked
312     * @param method the method to check
313     * @return {@code TRUE} if the implementation knows how to invoke the method,
314     *         {@code FALSE} otherwise
315     */
316    abstract boolean canInvoke(Method method);
317}
318