1/*
2 * Copyright (c) 1998, 2011, 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.*;
29
30import java.util.*;
31
32final public class ClassTypeImpl extends InvokableTypeImpl
33    implements ClassType
34{
35    private static class IResult implements InvocationResult {
36        final private JDWP.ClassType.InvokeMethod rslt;
37
38        public IResult(JDWP.ClassType.InvokeMethod rslt) {
39            this.rslt = rslt;
40        }
41
42        @Override
43        public ObjectReferenceImpl getException() {
44            return rslt.exception;
45        }
46
47        @Override
48        public ValueImpl getResult() {
49            return rslt.returnValue;
50        }
51    }
52
53    private boolean cachedSuperclass = false;
54    private ClassType superclass = null;
55    private int lastLine = -1;
56    private List<InterfaceType> interfaces = null;
57
58    protected ClassTypeImpl(VirtualMachine aVm,long aRef) {
59        super(aVm, aRef);
60    }
61
62    public ClassType superclass() {
63        if(!cachedSuperclass)  {
64            ClassTypeImpl sup = null;
65            try {
66                sup = JDWP.ClassType.Superclass.
67                    process(vm, this).superclass;
68            } catch (JDWPException exc) {
69                throw exc.toJDIException();
70            }
71
72            /*
73             * If there is a superclass, cache its
74             * ClassType here. Otherwise,
75             * leave the cache reference null.
76             */
77            if (sup != null) {
78                superclass = sup;
79            }
80            cachedSuperclass = true;
81        }
82
83        return superclass;
84    }
85
86    @Override
87    public List<InterfaceType> interfaces()  {
88        if (interfaces == null) {
89            interfaces = getInterfaces();
90        }
91        return interfaces;
92    }
93
94    @Override
95    public List<InterfaceType> allInterfaces() {
96        return getAllInterfaces();
97    }
98
99    public List<ClassType> subclasses() {
100        List<ClassType> subs = new ArrayList<ClassType>();
101        for (ReferenceType refType : vm.allClasses()) {
102            if (refType instanceof ClassType) {
103                ClassType clazz = (ClassType)refType;
104                ClassType superclass = clazz.superclass();
105                if ((superclass != null) && superclass.equals(this)) {
106                    subs.add((ClassType)refType);
107                }
108            }
109        }
110
111        return subs;
112    }
113
114    public boolean isEnum() {
115        ClassType superclass = superclass();
116        if (superclass != null &&
117            superclass.name().equals("java.lang.Enum")) {
118            return true;
119        }
120        return false;
121    }
122
123    public void setValue(Field field, Value value)
124        throws InvalidTypeException, ClassNotLoadedException {
125
126        validateMirror(field);
127        validateMirrorOrNull(value);
128        validateFieldSet(field);
129
130        // More validation specific to setting from a ClassType
131        if(!field.isStatic()) {
132            throw new IllegalArgumentException(
133                            "Must set non-static field through an instance");
134        }
135
136        try {
137            JDWP.ClassType.SetValues.FieldValue[] values =
138                          new JDWP.ClassType.SetValues.FieldValue[1];
139            values[0] = new JDWP.ClassType.SetValues.FieldValue(
140                    ((FieldImpl)field).ref(),
141                    // validate and convert if necessary
142                    ValueImpl.prepareForAssignment(value, (FieldImpl)field));
143
144            try {
145                JDWP.ClassType.SetValues.process(vm, this, values);
146            } catch (JDWPException exc) {
147                throw exc.toJDIException();
148            }
149        } catch (ClassNotLoadedException e) {
150            /*
151             * Since we got this exception,
152             * the field type must be a reference type. The value
153             * we're trying to set is null, but if the field's
154             * class has not yet been loaded through the enclosing
155             * class loader, then setting to null is essentially a
156             * no-op, and we should allow it without an exception.
157             */
158            if (value != null) {
159                throw e;
160            }
161        }
162    }
163
164    PacketStream sendNewInstanceCommand(final ThreadReferenceImpl thread,
165                                   final MethodImpl method,
166                                   final ValueImpl[] args,
167                                   final int options) {
168        CommandSender sender =
169            new CommandSender() {
170                public PacketStream send() {
171                    return JDWP.ClassType.NewInstance.enqueueCommand(
172                                          vm, ClassTypeImpl.this, thread,
173                                          method.ref(), args, options);
174                }
175        };
176
177        PacketStream stream;
178        if ((options & INVOKE_SINGLE_THREADED) != 0) {
179            stream = thread.sendResumingCommand(sender);
180        } else {
181            stream = vm.sendResumingCommand(sender);
182        }
183        return stream;
184    }
185
186    public ObjectReference newInstance(ThreadReference threadIntf,
187                                       Method methodIntf,
188                                       List<? extends Value> origArguments,
189                                       int options)
190                                   throws InvalidTypeException,
191                                          ClassNotLoadedException,
192                                          IncompatibleThreadStateException,
193                                          InvocationException {
194        validateMirror(threadIntf);
195        validateMirror(methodIntf);
196        validateMirrorsOrNulls(origArguments);
197
198        MethodImpl method = (MethodImpl)methodIntf;
199        ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
200
201        validateConstructorInvocation(method);
202
203        List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(
204                                                       origArguments);
205        ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
206        JDWP.ClassType.NewInstance ret = null;
207        try {
208            PacketStream stream =
209                sendNewInstanceCommand(thread, method, args, options);
210            ret = JDWP.ClassType.NewInstance.waitForReply(vm, stream);
211        } catch (JDWPException exc) {
212            if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
213                throw new IncompatibleThreadStateException();
214            } else {
215                throw exc.toJDIException();
216            }
217        }
218
219        /*
220         * There is an implict VM-wide suspend at the conclusion
221         * of a normal (non-single-threaded) method invoke
222         */
223        if ((options & INVOKE_SINGLE_THREADED) == 0) {
224            vm.notifySuspend();
225        }
226
227        if (ret.exception != null) {
228            throw new InvocationException(ret.exception);
229        } else {
230            return ret.newObject;
231        }
232    }
233
234    public Method concreteMethodByName(String name, String signature)  {
235       Method method = null;
236       for (Method candidate : visibleMethods()) {
237           if (candidate.name().equals(name) &&
238               candidate.signature().equals(signature) &&
239               !candidate.isAbstract()) {
240
241               method = candidate;
242               break;
243           }
244       }
245       return method;
246   }
247
248    void validateConstructorInvocation(Method method)
249                                   throws InvalidTypeException,
250                                          InvocationException {
251        /*
252         * Method must be in this class.
253         */
254        ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
255        if (!declType.equals(this)) {
256            throw new IllegalArgumentException("Invalid constructor");
257        }
258
259        /*
260         * Method must be a constructor
261         */
262        if (!method.isConstructor()) {
263            throw new IllegalArgumentException("Cannot create instance with non-constructor");
264        }
265    }
266
267
268    public String toString() {
269       return "class " + name() + " (" + loaderString() + ")";
270    }
271
272    @Override
273    CommandSender getInvokeMethodSender(ThreadReferenceImpl thread,
274                                        MethodImpl method,
275                                        ValueImpl[] args,
276                                        int options) {
277        return () ->
278            JDWP.ClassType.InvokeMethod.enqueueCommand(vm,
279                                                       ClassTypeImpl.this,
280                                                       thread,
281                                                       method.ref(),
282                                                       args,
283                                                       options);
284    }
285
286    @Override
287    InvocationResult waitForReply(PacketStream stream) throws JDWPException {
288        return new IResult(JDWP.ClassType.InvokeMethod.waitForReply(vm, stream));
289    }
290
291    @Override
292    boolean canInvoke(Method method) {
293        // Method must be in this class or a superclass.
294        return ((ReferenceTypeImpl)method.declaringType()).isAssignableFrom(this);
295    }
296}
297