blob: fbb613b119aaaef6e4dc77a39c4d689c7b8d26f9 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2004 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.tools.jdi;
27
28import com.sun.jdi.*;
29
30import java.util.*;
31
32public class ClassTypeImpl extends ReferenceTypeImpl
33 implements ClassType
34{
35 private boolean cachedSuperclass = false;
36 private ClassType superclass = null;
37 private int lastLine = -1;
38 private List<InterfaceType> interfaces = null;
39
40 protected ClassTypeImpl(VirtualMachine aVm,long aRef) {
41 super(aVm, aRef);
42 }
43
44 public ClassType superclass() {
45 if(!cachedSuperclass) {
46 ClassTypeImpl sup = null;
47 try {
48 sup = JDWP.ClassType.Superclass.
49 process(vm, this).superclass;
50 } catch (JDWPException exc) {
51 throw exc.toJDIException();
52 }
53
54 /*
55 * If there is a superclass, cache its
56 * ClassType here. Otherwise,
57 * leave the cache reference null.
58 */
59 if (sup != null) {
60 superclass = sup;
61 }
62 cachedSuperclass = true;
63 }
64
65 return superclass;
66 }
67
68 public List<InterfaceType> interfaces() {
69 if (interfaces == null) {
70 interfaces = getInterfaces();
71 }
72 return interfaces;
73 }
74
75 void addInterfaces(List<InterfaceType> list) {
76 List<InterfaceType> immediate = interfaces();
77 list.addAll(interfaces());
78
79 Iterator iter = immediate.iterator();
80 while (iter.hasNext()) {
81 InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
82 interfaze.addSuperinterfaces(list);
83 }
84
85 ClassTypeImpl superclass = (ClassTypeImpl)superclass();
86 if (superclass != null) {
87 superclass.addInterfaces(list);
88 }
89 }
90
91 public List<InterfaceType> allInterfaces() {
92 List<InterfaceType> all = new ArrayList<InterfaceType>();
93 addInterfaces(all);
94 return all;
95 }
96
97 public List<ClassType> subclasses() {
98 List<ReferenceType> all = vm.allClasses();
99 List<ClassType> subs = new ArrayList<ClassType>();
100 Iterator iter = all.iterator();
101 while (iter.hasNext()) {
102 ReferenceType refType = (ReferenceType)iter.next();
103 if (refType instanceof ClassType) {
104 ClassType clazz = (ClassType)refType;
105 ClassType superclass = clazz.superclass();
106 if ((superclass != null) && superclass.equals(this)) {
107 subs.add((ClassType)refType);
108 }
109 }
110 }
111
112 return subs;
113 }
114
115 public boolean isEnum() {
116 ClassType superclass = superclass();
117 if (superclass != null &&
118 superclass.name().equals("java.lang.Enum")) {
119 return true;
120 }
121 return false;
122 }
123
124 public void setValue(Field field, Value value)
125 throws InvalidTypeException, ClassNotLoadedException {
126
127 validateMirror(field);
128 validateMirrorOrNull(value);
129 validateFieldSet(field);
130
131 // More validation specific to setting from a ClassType
132 if(!field.isStatic()) {
133 throw new IllegalArgumentException(
134 "Must set non-static field through an instance");
135 }
136
137 try {
138 JDWP.ClassType.SetValues.FieldValue[] values =
139 new JDWP.ClassType.SetValues.FieldValue[1];
140 values[0] = new JDWP.ClassType.SetValues.FieldValue(
141 ((FieldImpl)field).ref(),
142 // validate and convert if necessary
143 ValueImpl.prepareForAssignment(value, (FieldImpl)field));
144
145 try {
146 JDWP.ClassType.SetValues.process(vm, this, values);
147 } catch (JDWPException exc) {
148 throw exc.toJDIException();
149 }
150 } catch (ClassNotLoadedException e) {
151 /*
152 * Since we got this exception,
153 * the field type must be a reference type. The value
154 * we're trying to set is null, but if the field's
155 * class has not yet been loaded through the enclosing
156 * class loader, then setting to null is essentially a
157 * no-op, and we should allow it without an exception.
158 */
159 if (value != null) {
160 throw e;
161 }
162 }
163 }
164
165 PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
166 final MethodImpl method,
167 final ValueImpl[] args,
168 final int options) {
169 CommandSender sender =
170 new CommandSender() {
171 public PacketStream send() {
172 return JDWP.ClassType.InvokeMethod.enqueueCommand(
173 vm, ClassTypeImpl.this, thread,
174 method.ref(), args, options);
175 }
176 };
177
178 PacketStream stream;
179 if ((options & INVOKE_SINGLE_THREADED) != 0) {
180 stream = thread.sendResumingCommand(sender);
181 } else {
182 stream = vm.sendResumingCommand(sender);
183 }
184 return stream;
185 }
186
187 PacketStream sendNewInstanceCommand(final ThreadReferenceImpl thread,
188 final MethodImpl method,
189 final ValueImpl[] args,
190 final int options) {
191 CommandSender sender =
192 new CommandSender() {
193 public PacketStream send() {
194 return JDWP.ClassType.NewInstance.enqueueCommand(
195 vm, ClassTypeImpl.this, thread,
196 method.ref(), args, options);
197 }
198 };
199
200 PacketStream stream;
201 if ((options & INVOKE_SINGLE_THREADED) != 0) {
202 stream = thread.sendResumingCommand(sender);
203 } else {
204 stream = vm.sendResumingCommand(sender);
205 }
206 return stream;
207 }
208
209 public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
210 List<? extends Value> origArguments, int options)
211 throws InvalidTypeException,
212 ClassNotLoadedException,
213 IncompatibleThreadStateException,
214 InvocationException {
215 validateMirror(threadIntf);
216 validateMirror(methodIntf);
217 validateMirrorsOrNulls(origArguments);
218
219 MethodImpl method = (MethodImpl)methodIntf;
220 ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
221
222 validateMethodInvocation(method);
223
224 List<? extends Value> arguments = method.validateAndPrepareArgumentsForInvoke(origArguments);
225
226 ValueImpl[] args = (ValueImpl[])arguments.toArray(new ValueImpl[0]);
227 JDWP.ClassType.InvokeMethod ret;
228 try {
229 PacketStream stream =
230 sendInvokeCommand(thread, method, args, options);
231 ret = JDWP.ClassType.InvokeMethod.waitForReply(vm, stream);
232 } catch (JDWPException exc) {
233 if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
234 throw new IncompatibleThreadStateException();
235 } else {
236 throw exc.toJDIException();
237 }
238 }
239
240 /*
241 * There is an implict VM-wide suspend at the conclusion
242 * of a normal (non-single-threaded) method invoke
243 */
244 if ((options & INVOKE_SINGLE_THREADED) == 0) {
245 vm.notifySuspend();
246 }
247
248 if (ret.exception != null) {
249 throw new InvocationException(ret.exception);
250 } else {
251 return ret.returnValue;
252 }
253 }
254
255 public ObjectReference newInstance(ThreadReference threadIntf,
256 Method methodIntf,
257 List<? extends Value> origArguments,
258 int options)
259 throws InvalidTypeException,
260 ClassNotLoadedException,
261 IncompatibleThreadStateException,
262 InvocationException {
263 validateMirror(threadIntf);
264 validateMirror(methodIntf);
265 validateMirrorsOrNulls(origArguments);
266
267 MethodImpl method = (MethodImpl)methodIntf;
268 ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
269
270 validateConstructorInvocation(method);
271
272 List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(
273 origArguments);
274 ValueImpl[] args = (ValueImpl[])arguments.toArray(new ValueImpl[0]);
275 JDWP.ClassType.NewInstance ret = null;
276 try {
277 PacketStream stream =
278 sendNewInstanceCommand(thread, method, args, options);
279 ret = JDWP.ClassType.NewInstance.waitForReply(vm, stream);
280 } catch (JDWPException exc) {
281 if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
282 throw new IncompatibleThreadStateException();
283 } else {
284 throw exc.toJDIException();
285 }
286 }
287
288 /*
289 * There is an implict VM-wide suspend at the conclusion
290 * of a normal (non-single-threaded) method invoke
291 */
292 if ((options & INVOKE_SINGLE_THREADED) == 0) {
293 vm.notifySuspend();
294 }
295
296 if (ret.exception != null) {
297 throw new InvocationException(ret.exception);
298 } else {
299 return ret.newObject;
300 }
301 }
302
303 public Method concreteMethodByName(String name, String signature) {
304 List methods = visibleMethods();
305 Method method = null;
306 Iterator iter = methods.iterator();
307 while (iter.hasNext()) {
308 Method candidate = (Method)iter.next();
309 if (candidate.name().equals(name) &&
310 candidate.signature().equals(signature) &&
311 !candidate.isAbstract()) {
312
313 method = candidate;
314 break;
315 }
316 }
317 return method;
318 }
319
320 public List<Method> allMethods() {
321 ArrayList<Method> list = new ArrayList<Method>(methods());
322
323 ClassType clazz = superclass();
324 while (clazz != null) {
325 list.addAll(clazz.methods());
326 clazz = clazz.superclass();
327 }
328
329 /*
330 * Avoid duplicate checking on each method by iterating through
331 * duplicate-free allInterfaces() rather than recursing
332 */
333 Iterator iter = allInterfaces().iterator();
334 while (iter.hasNext()) {
335 InterfaceType interfaze = (InterfaceType)iter.next();
336 list.addAll(interfaze.methods());
337 }
338
339 return list;
340 }
341
342 List<ReferenceType> inheritedTypes() {
343 List<ReferenceType> inherited = new ArrayList<ReferenceType>();
344 if (superclass() != null) {
345 inherited.add(0, (ReferenceType)superclass()); /* insert at front */
346 }
347 for (ReferenceType rt : interfaces()) {
348 inherited.add(rt);
349 }
350 return inherited;
351 }
352
353 void validateMethodInvocation(Method method)
354 throws InvalidTypeException,
355 InvocationException {
356 /*
357 * Method must be in this class or a superclass.
358 */
359 ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
360 if (!declType.isAssignableFrom(this)) {
361 throw new IllegalArgumentException("Invalid method");
362 }
363
364 /*
365 * Method must be a static and not a static initializer
366 */
367 if (!method.isStatic()) {
368 throw new IllegalArgumentException("Cannot invoke instance method on a class type");
369 } else if (method.isStaticInitializer()) {
370 throw new IllegalArgumentException("Cannot invoke static initializer");
371 }
372 }
373
374 void validateConstructorInvocation(Method method)
375 throws InvalidTypeException,
376 InvocationException {
377 /*
378 * Method must be in this class.
379 */
380 ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
381 if (!declType.equals(this)) {
382 throw new IllegalArgumentException("Invalid constructor");
383 }
384
385 /*
386 * Method must be a constructor
387 */
388 if (!method.isConstructor()) {
389 throw new IllegalArgumentException("Cannot create instance with non-constructor");
390 }
391 }
392
393 void addVisibleMethods(Map<String, Method> methodMap) {
394 /*
395 * Add methods from
396 * parent types first, so that the methods in this class will
397 * overwrite them in the hash table
398 */
399
400 Iterator iter = interfaces().iterator();
401 while (iter.hasNext()) {
402 InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
403 interfaze.addVisibleMethods(methodMap);
404 }
405
406 ClassTypeImpl clazz = (ClassTypeImpl)superclass();
407 if (clazz != null) {
408 clazz.addVisibleMethods(methodMap);
409 }
410
411 addToMethodMap(methodMap, methods());
412 }
413
414 boolean isAssignableTo(ReferenceType type) {
415 ClassTypeImpl superclazz = (ClassTypeImpl)superclass();
416 if (this.equals(type)) {
417 return true;
418 } else if ((superclazz != null) && superclazz.isAssignableTo(type)) {
419 return true;
420 } else {
421 List<InterfaceType> interfaces = interfaces();
422 Iterator iter = interfaces.iterator();
423 while (iter.hasNext()) {
424 InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
425 if (interfaze.isAssignableTo(type)) {
426 return true;
427 }
428 }
429 return false;
430 }
431 }
432
433 public String toString() {
434 return "class " + name() + " (" + loaderString() + ")";
435 }
436}