2
|
1 |
/*
|
715
|
2 |
* Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
|
2
|
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 |
|
|
26 |
package com.sun.tools.jdi;
|
|
27 |
|
|
28 |
import com.sun.jdi.*;
|
|
29 |
|
|
30 |
import java.util.*;
|
|
31 |
import java.util.ArrayList;
|
|
32 |
|
|
33 |
public class ObjectReferenceImpl extends ValueImpl
|
|
34 |
implements ObjectReference, VMListener {
|
|
35 |
|
|
36 |
protected long ref;
|
|
37 |
private ReferenceType type = null;
|
|
38 |
private int gcDisableCount = 0;
|
|
39 |
boolean addedListener = false;
|
|
40 |
|
|
41 |
// This is cached only while the VM is suspended
|
|
42 |
protected static class Cache {
|
|
43 |
JDWP.ObjectReference.MonitorInfo monitorInfo = null;
|
|
44 |
}
|
|
45 |
|
|
46 |
private static final Cache noInitCache = new Cache();
|
|
47 |
private static final Cache markerCache = new Cache();
|
|
48 |
private Cache cache = noInitCache;
|
|
49 |
|
|
50 |
private void disableCache() {
|
|
51 |
synchronized (vm.state()) {
|
|
52 |
cache = null;
|
|
53 |
}
|
|
54 |
}
|
|
55 |
|
|
56 |
private void enableCache() {
|
|
57 |
synchronized (vm.state()) {
|
|
58 |
cache = markerCache;
|
|
59 |
}
|
|
60 |
}
|
|
61 |
|
|
62 |
// Override in subclasses
|
|
63 |
protected Cache newCache() {
|
|
64 |
return new Cache();
|
|
65 |
}
|
|
66 |
|
|
67 |
protected Cache getCache() {
|
|
68 |
synchronized (vm.state()) {
|
|
69 |
if (cache == noInitCache) {
|
|
70 |
if (vm.state().isSuspended()) {
|
|
71 |
// Set cache now, otherwise newly created objects are
|
|
72 |
// not cached until resuspend
|
|
73 |
enableCache();
|
|
74 |
} else {
|
|
75 |
disableCache();
|
|
76 |
}
|
|
77 |
}
|
|
78 |
if (cache == markerCache) {
|
|
79 |
cache = newCache();
|
|
80 |
}
|
|
81 |
return cache;
|
|
82 |
}
|
|
83 |
}
|
|
84 |
|
|
85 |
// Return the ClassTypeImpl upon which to invoke a method.
|
|
86 |
// By default it is our very own referenceType() but subclasses
|
|
87 |
// can override.
|
|
88 |
protected ClassTypeImpl invokableReferenceType(Method method) {
|
|
89 |
return (ClassTypeImpl)referenceType();
|
|
90 |
}
|
|
91 |
|
|
92 |
ObjectReferenceImpl(VirtualMachine aVm,long aRef) {
|
|
93 |
super(aVm);
|
|
94 |
|
|
95 |
ref = aRef;
|
|
96 |
}
|
|
97 |
|
|
98 |
protected String description() {
|
|
99 |
return "ObjectReference " + uniqueID();
|
|
100 |
}
|
|
101 |
|
|
102 |
/*
|
|
103 |
* VMListener implementation
|
|
104 |
*/
|
|
105 |
public boolean vmSuspended(VMAction action) {
|
|
106 |
enableCache();
|
|
107 |
return true;
|
|
108 |
}
|
|
109 |
|
|
110 |
public boolean vmNotSuspended(VMAction action) {
|
|
111 |
// make sure that cache and listener management are synchronized
|
|
112 |
synchronized (vm.state()) {
|
|
113 |
if (cache != null && (vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
|
114 |
vm.printTrace("Clearing temporary cache for " + description());
|
|
115 |
}
|
|
116 |
disableCache();
|
|
117 |
if (addedListener) {
|
|
118 |
/*
|
|
119 |
* If a listener was added (i.e. this is not a
|
|
120 |
* ObjectReference that adds a listener on startup),
|
|
121 |
* remove it here.
|
|
122 |
*/
|
|
123 |
addedListener = false;
|
|
124 |
return false; // false says remove
|
|
125 |
} else {
|
|
126 |
return true;
|
|
127 |
}
|
|
128 |
}
|
|
129 |
}
|
|
130 |
|
|
131 |
public boolean equals(Object obj) {
|
|
132 |
if ((obj != null) && (obj instanceof ObjectReferenceImpl)) {
|
|
133 |
ObjectReferenceImpl other = (ObjectReferenceImpl)obj;
|
|
134 |
return (ref() == other.ref()) &&
|
|
135 |
super.equals(obj);
|
|
136 |
} else {
|
|
137 |
return false;
|
|
138 |
}
|
|
139 |
}
|
|
140 |
|
|
141 |
public int hashCode() {
|
|
142 |
return(int)ref();
|
|
143 |
}
|
|
144 |
|
|
145 |
public Type type() {
|
|
146 |
return referenceType();
|
|
147 |
}
|
|
148 |
|
|
149 |
public ReferenceType referenceType() {
|
|
150 |
if (type == null) {
|
|
151 |
try {
|
|
152 |
JDWP.ObjectReference.ReferenceType rtinfo =
|
|
153 |
JDWP.ObjectReference.ReferenceType.process(vm, this);
|
|
154 |
type = vm.referenceType(rtinfo.typeID,
|
|
155 |
rtinfo.refTypeTag);
|
|
156 |
} catch (JDWPException exc) {
|
|
157 |
throw exc.toJDIException();
|
|
158 |
}
|
|
159 |
}
|
|
160 |
return type;
|
|
161 |
}
|
|
162 |
|
|
163 |
public Value getValue(Field sig) {
|
|
164 |
List<Field> list = new ArrayList<Field>(1);
|
|
165 |
list.add(sig);
|
|
166 |
Map map = getValues(list);
|
|
167 |
return(Value)map.get(sig);
|
|
168 |
}
|
|
169 |
|
|
170 |
public Map<Field,Value> getValues(List<? extends Field> theFields) {
|
|
171 |
validateMirrors(theFields);
|
|
172 |
|
|
173 |
List<Field> staticFields = new ArrayList<Field>(0);
|
|
174 |
int size = theFields.size();
|
|
175 |
List<Field> instanceFields = new ArrayList<Field>(size);
|
|
176 |
|
|
177 |
for (int i=0; i<size; i++) {
|
|
178 |
Field field = (Field)theFields.get(i);
|
|
179 |
|
|
180 |
// Make sure the field is valid
|
|
181 |
((ReferenceTypeImpl)referenceType()).validateFieldAccess(field);
|
|
182 |
|
|
183 |
// FIX ME! We need to do some sanity checking
|
|
184 |
// here; make sure the field belongs to this
|
|
185 |
// object.
|
|
186 |
if (field.isStatic())
|
|
187 |
staticFields.add(field);
|
|
188 |
else {
|
|
189 |
instanceFields.add(field);
|
|
190 |
}
|
|
191 |
}
|
|
192 |
|
|
193 |
Map<Field, Value> map;
|
|
194 |
if (staticFields.size() > 0) {
|
|
195 |
map = referenceType().getValues(staticFields);
|
|
196 |
} else {
|
|
197 |
map = new HashMap<Field, Value>(size);
|
|
198 |
}
|
|
199 |
|
|
200 |
size = instanceFields.size();
|
|
201 |
|
|
202 |
JDWP.ObjectReference.GetValues.Field[] queryFields =
|
|
203 |
new JDWP.ObjectReference.GetValues.Field[size];
|
|
204 |
for (int i=0; i<size; i++) {
|
|
205 |
FieldImpl field = (FieldImpl)instanceFields.get(i);/* thanks OTI */
|
|
206 |
queryFields[i] = new JDWP.ObjectReference.GetValues.Field(
|
|
207 |
field.ref());
|
|
208 |
}
|
|
209 |
ValueImpl[] values;
|
|
210 |
try {
|
|
211 |
values = JDWP.ObjectReference.GetValues.
|
|
212 |
process(vm, this, queryFields).values;
|
|
213 |
} catch (JDWPException exc) {
|
|
214 |
throw exc.toJDIException();
|
|
215 |
}
|
|
216 |
|
|
217 |
if (size != values.length) {
|
|
218 |
throw new InternalException(
|
|
219 |
"Wrong number of values returned from target VM");
|
|
220 |
}
|
|
221 |
for (int i=0; i<size; i++) {
|
|
222 |
FieldImpl field = (FieldImpl)instanceFields.get(i);
|
|
223 |
map.put(field, values[i]);
|
|
224 |
}
|
|
225 |
|
|
226 |
return map;
|
|
227 |
}
|
|
228 |
|
|
229 |
public void setValue(Field field, Value value)
|
|
230 |
throws InvalidTypeException, ClassNotLoadedException {
|
|
231 |
|
|
232 |
validateMirror(field);
|
|
233 |
validateMirrorOrNull(value);
|
|
234 |
|
|
235 |
// Make sure the field is valid
|
|
236 |
((ReferenceTypeImpl)referenceType()).validateFieldSet(field);
|
|
237 |
|
|
238 |
if (field.isStatic()) {
|
|
239 |
ReferenceType type = referenceType();
|
|
240 |
if (type instanceof ClassType) {
|
|
241 |
((ClassType)type).setValue(field, value);
|
|
242 |
return;
|
|
243 |
} else {
|
|
244 |
throw new IllegalArgumentException(
|
|
245 |
"Invalid type for static field set");
|
|
246 |
}
|
|
247 |
}
|
|
248 |
|
|
249 |
try {
|
|
250 |
JDWP.ObjectReference.SetValues.FieldValue[] fvals =
|
|
251 |
new JDWP.ObjectReference.SetValues.FieldValue[1];
|
|
252 |
fvals[0] = new JDWP.ObjectReference.SetValues.FieldValue(
|
|
253 |
((FieldImpl)field).ref(),
|
|
254 |
// Validate and convert if necessary
|
|
255 |
ValueImpl.prepareForAssignment(value,
|
|
256 |
(FieldImpl)field));
|
|
257 |
try {
|
|
258 |
JDWP.ObjectReference.SetValues.process(vm, this, fvals);
|
|
259 |
} catch (JDWPException exc) {
|
|
260 |
throw exc.toJDIException();
|
|
261 |
}
|
|
262 |
} catch (ClassNotLoadedException e) {
|
|
263 |
/*
|
|
264 |
* Since we got this exception,
|
|
265 |
* the field type must be a reference type. The value
|
|
266 |
* we're trying to set is null, but if the field's
|
|
267 |
* class has not yet been loaded through the enclosing
|
|
268 |
* class loader, then setting to null is essentially a
|
|
269 |
* no-op, and we should allow it without an exception.
|
|
270 |
*/
|
|
271 |
if (value != null) {
|
|
272 |
throw e;
|
|
273 |
}
|
|
274 |
}
|
|
275 |
}
|
|
276 |
|
|
277 |
void validateMethodInvocation(Method method, int options)
|
|
278 |
throws InvalidTypeException,
|
|
279 |
InvocationException {
|
|
280 |
|
|
281 |
/*
|
|
282 |
* Method must be in this object's class, a superclass, or
|
|
283 |
* implemented interface
|
|
284 |
*/
|
|
285 |
ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
|
|
286 |
if (!declType.isAssignableFrom(this)) {
|
|
287 |
throw new IllegalArgumentException("Invalid method");
|
|
288 |
}
|
|
289 |
|
|
290 |
ClassTypeImpl clazz = invokableReferenceType(method);
|
|
291 |
|
|
292 |
/*
|
|
293 |
* Method must be a non-constructor
|
|
294 |
*/
|
|
295 |
if (method.isConstructor()) {
|
|
296 |
throw new IllegalArgumentException("Cannot invoke constructor");
|
|
297 |
}
|
|
298 |
|
|
299 |
/*
|
|
300 |
* For nonvirtual invokes, method must have a body
|
|
301 |
*/
|
|
302 |
if ((options & INVOKE_NONVIRTUAL) != 0) {
|
|
303 |
if (method.declaringType() instanceof InterfaceType) {
|
|
304 |
throw new IllegalArgumentException("Interface method");
|
|
305 |
} else if (method.isAbstract()) {
|
|
306 |
throw new IllegalArgumentException("Abstract method");
|
|
307 |
}
|
|
308 |
}
|
|
309 |
|
|
310 |
/*
|
|
311 |
* Get the class containing the method that will be invoked.
|
|
312 |
* This class is needed only for proper validation of the
|
|
313 |
* method argument types.
|
|
314 |
*/
|
|
315 |
ClassTypeImpl invokedClass;
|
|
316 |
if ((options & INVOKE_NONVIRTUAL) != 0) {
|
|
317 |
// No overrides in non-virtual invokes
|
|
318 |
invokedClass = clazz;
|
|
319 |
} else {
|
|
320 |
/*
|
|
321 |
* For virtual invokes, find any override of the method.
|
|
322 |
* Since we are looking for a method with a real body, we
|
|
323 |
* don't need to bother with interfaces/abstract methods.
|
|
324 |
*/
|
|
325 |
Method invoker = clazz.concreteMethodByName(method.name(),
|
|
326 |
method.signature());
|
|
327 |
// isAssignableFrom check above guarantees non-null
|
|
328 |
invokedClass = (ClassTypeImpl)invoker.declaringType();
|
|
329 |
}
|
|
330 |
/* The above code is left over from previous versions.
|
|
331 |
* We haven't had time to divine the intent. jjh, 7/31/2003
|
|
332 |
*/
|
|
333 |
}
|
|
334 |
|
|
335 |
PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
|
|
336 |
final ClassTypeImpl refType,
|
|
337 |
final MethodImpl method,
|
|
338 |
final ValueImpl[] args,
|
|
339 |
final int options) {
|
|
340 |
CommandSender sender =
|
|
341 |
new CommandSender() {
|
|
342 |
public PacketStream send() {
|
|
343 |
return JDWP.ObjectReference.InvokeMethod.enqueueCommand(
|
|
344 |
vm, ObjectReferenceImpl.this,
|
|
345 |
thread, refType,
|
|
346 |
method.ref(), args, options);
|
|
347 |
}
|
|
348 |
};
|
|
349 |
|
|
350 |
PacketStream stream;
|
|
351 |
if ((options & INVOKE_SINGLE_THREADED) != 0) {
|
|
352 |
stream = thread.sendResumingCommand(sender);
|
|
353 |
} else {
|
|
354 |
stream = vm.sendResumingCommand(sender);
|
|
355 |
}
|
|
356 |
return stream;
|
|
357 |
}
|
|
358 |
|
|
359 |
public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
|
|
360 |
List<? extends Value> origArguments, int options)
|
|
361 |
throws InvalidTypeException,
|
|
362 |
IncompatibleThreadStateException,
|
|
363 |
InvocationException,
|
|
364 |
ClassNotLoadedException {
|
|
365 |
validateMirror(threadIntf);
|
|
366 |
validateMirror(methodIntf);
|
|
367 |
validateMirrorsOrNulls(origArguments);
|
|
368 |
|
|
369 |
MethodImpl method = (MethodImpl)methodIntf;
|
|
370 |
ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
|
|
371 |
|
|
372 |
if (method.isStatic()) {
|
|
373 |
if (referenceType() instanceof ClassType) {
|
|
374 |
ClassType type = (ClassType)referenceType();
|
|
375 |
return type.invokeMethod(thread, method, origArguments, options);
|
|
376 |
} else {
|
|
377 |
throw new IllegalArgumentException("Invalid type for static method invocation");
|
|
378 |
}
|
|
379 |
}
|
|
380 |
|
|
381 |
validateMethodInvocation(method, options);
|
|
382 |
|
|
383 |
List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(
|
|
384 |
origArguments);
|
|
385 |
|
51
|
386 |
ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
|
2
|
387 |
JDWP.ObjectReference.InvokeMethod ret;
|
|
388 |
try {
|
|
389 |
PacketStream stream =
|
|
390 |
sendInvokeCommand(thread, invokableReferenceType(method),
|
|
391 |
method, args, options);
|
|
392 |
ret = JDWP.ObjectReference.InvokeMethod.waitForReply(vm, stream);
|
|
393 |
} catch (JDWPException exc) {
|
|
394 |
if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
|
|
395 |
throw new IncompatibleThreadStateException();
|
|
396 |
} else {
|
|
397 |
throw exc.toJDIException();
|
|
398 |
}
|
|
399 |
}
|
|
400 |
|
|
401 |
/*
|
|
402 |
* There is an implict VM-wide suspend at the conclusion
|
|
403 |
* of a normal (non-single-threaded) method invoke
|
|
404 |
*/
|
|
405 |
if ((options & INVOKE_SINGLE_THREADED) == 0) {
|
|
406 |
vm.notifySuspend();
|
|
407 |
}
|
|
408 |
|
|
409 |
if (ret.exception != null) {
|
|
410 |
throw new InvocationException(ret.exception);
|
|
411 |
} else {
|
|
412 |
return ret.returnValue;
|
|
413 |
}
|
|
414 |
}
|
|
415 |
|
|
416 |
/* leave synchronized to keep count accurate */
|
|
417 |
public synchronized void disableCollection() {
|
|
418 |
if (gcDisableCount == 0) {
|
|
419 |
try {
|
|
420 |
JDWP.ObjectReference.DisableCollection.process(vm, this);
|
|
421 |
} catch (JDWPException exc) {
|
|
422 |
throw exc.toJDIException();
|
|
423 |
}
|
|
424 |
}
|
|
425 |
gcDisableCount++;
|
|
426 |
}
|
|
427 |
|
|
428 |
/* leave synchronized to keep count accurate */
|
|
429 |
public synchronized void enableCollection() {
|
|
430 |
gcDisableCount--;
|
|
431 |
|
|
432 |
if (gcDisableCount == 0) {
|
|
433 |
try {
|
|
434 |
JDWP.ObjectReference.EnableCollection.process(vm, this);
|
|
435 |
} catch (JDWPException exc) {
|
|
436 |
// If already collected, no harm done, no exception
|
|
437 |
if (exc.errorCode() != JDWP.Error.INVALID_OBJECT) {
|
|
438 |
throw exc.toJDIException();
|
|
439 |
}
|
|
440 |
return;
|
|
441 |
}
|
|
442 |
}
|
|
443 |
}
|
|
444 |
|
|
445 |
public boolean isCollected() {
|
|
446 |
try {
|
|
447 |
return JDWP.ObjectReference.IsCollected.process(vm, this).
|
|
448 |
isCollected;
|
|
449 |
} catch (JDWPException exc) {
|
|
450 |
throw exc.toJDIException();
|
|
451 |
}
|
|
452 |
}
|
|
453 |
|
|
454 |
public long uniqueID() {
|
|
455 |
return ref();
|
|
456 |
}
|
|
457 |
|
|
458 |
JDWP.ObjectReference.MonitorInfo jdwpMonitorInfo()
|
|
459 |
throws IncompatibleThreadStateException {
|
|
460 |
JDWP.ObjectReference.MonitorInfo info = null;
|
|
461 |
try {
|
|
462 |
Cache local;
|
|
463 |
|
|
464 |
// getCache() and addlistener() must be synchronized
|
|
465 |
// so that no events are lost.
|
|
466 |
synchronized (vm.state()) {
|
|
467 |
local = getCache();
|
|
468 |
|
|
469 |
if (local != null) {
|
|
470 |
info = local.monitorInfo;
|
|
471 |
|
|
472 |
// Check if there will be something to cache
|
|
473 |
// and there is not already a listener
|
|
474 |
if (info == null && !vm.state().hasListener(this)) {
|
|
475 |
/* For other, less numerous objects, this is done
|
|
476 |
* in the constructor. Since there can be many
|
|
477 |
* ObjectReferences, the VM listener is installed
|
|
478 |
* and removed as needed.
|
|
479 |
* Listener must be installed before process()
|
|
480 |
*/
|
|
481 |
vm.state().addListener(this);
|
|
482 |
addedListener = true;
|
|
483 |
}
|
|
484 |
}
|
|
485 |
}
|
|
486 |
if (info == null) {
|
|
487 |
info = JDWP.ObjectReference.MonitorInfo.process(vm, this);
|
|
488 |
if (local != null) {
|
|
489 |
local.monitorInfo = info;
|
|
490 |
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
|
491 |
vm.printTrace("ObjectReference " + uniqueID() +
|
|
492 |
" temporarily caching monitor info");
|
|
493 |
}
|
|
494 |
}
|
|
495 |
}
|
|
496 |
} catch (JDWPException exc) {
|
|
497 |
if (exc.errorCode() == JDWP.Error.THREAD_NOT_SUSPENDED) {
|
|
498 |
throw new IncompatibleThreadStateException();
|
|
499 |
} else {
|
|
500 |
throw exc.toJDIException();
|
|
501 |
}
|
|
502 |
}
|
|
503 |
return info;
|
|
504 |
}
|
|
505 |
|
|
506 |
public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException {
|
|
507 |
return Arrays.asList((ThreadReference[])jdwpMonitorInfo().waiters);
|
|
508 |
}
|
|
509 |
|
|
510 |
public ThreadReference owningThread() throws IncompatibleThreadStateException {
|
|
511 |
return jdwpMonitorInfo().owner;
|
|
512 |
}
|
|
513 |
|
|
514 |
public int entryCount() throws IncompatibleThreadStateException {
|
|
515 |
return jdwpMonitorInfo().entryCount;
|
|
516 |
}
|
|
517 |
|
|
518 |
|
|
519 |
public List<ObjectReference> referringObjects(long maxReferrers) {
|
|
520 |
if (!vm.canGetInstanceInfo()) {
|
|
521 |
throw new UnsupportedOperationException(
|
|
522 |
"target does not support getting referring objects");
|
|
523 |
}
|
|
524 |
|
|
525 |
if (maxReferrers < 0) {
|
|
526 |
throw new IllegalArgumentException("maxReferrers is less than zero: "
|
|
527 |
+ maxReferrers);
|
|
528 |
}
|
|
529 |
|
|
530 |
int intMax = (maxReferrers > Integer.MAX_VALUE)?
|
|
531 |
Integer.MAX_VALUE: (int)maxReferrers;
|
|
532 |
// JDWP can't currently handle more than this (in mustang)
|
|
533 |
|
|
534 |
try {
|
|
535 |
return Arrays.asList((ObjectReference[])JDWP.ObjectReference.ReferringObjects.
|
|
536 |
process(vm, this, intMax).referringObjects);
|
|
537 |
} catch (JDWPException exc) {
|
|
538 |
throw exc.toJDIException();
|
|
539 |
}
|
|
540 |
}
|
|
541 |
|
|
542 |
long ref() {
|
|
543 |
return ref;
|
|
544 |
}
|
|
545 |
|
|
546 |
boolean isClassObject() {
|
|
547 |
/*
|
|
548 |
* Don't need to worry about subclasses since java.lang.Class is final.
|
|
549 |
*/
|
|
550 |
return referenceType().name().equals("java.lang.Class");
|
|
551 |
}
|
|
552 |
|
|
553 |
ValueImpl prepareForAssignmentTo(ValueContainer destination)
|
|
554 |
throws InvalidTypeException,
|
|
555 |
ClassNotLoadedException {
|
|
556 |
|
|
557 |
validateAssignment(destination);
|
|
558 |
return this; // conversion never necessary
|
|
559 |
}
|
|
560 |
|
|
561 |
void validateAssignment(ValueContainer destination)
|
|
562 |
throws InvalidTypeException, ClassNotLoadedException {
|
|
563 |
|
|
564 |
/*
|
|
565 |
* Do these simpler checks before attempting a query of the destination's
|
|
566 |
* type which might cause a confusing ClassNotLoadedException if
|
|
567 |
* the destination is primitive or an array.
|
|
568 |
*/
|
|
569 |
/*
|
|
570 |
* TO DO: Centralize JNI signature knowledge
|
|
571 |
*/
|
|
572 |
if (destination.signature().length() == 1) {
|
|
573 |
throw new InvalidTypeException("Can't assign object value to primitive");
|
|
574 |
}
|
|
575 |
if ((destination.signature().charAt(0) == '[') &&
|
|
576 |
(type().signature().charAt(0) != '[')) {
|
|
577 |
throw new InvalidTypeException("Can't assign non-array value to an array");
|
|
578 |
}
|
|
579 |
if ("void".equals(destination.typeName())) {
|
|
580 |
throw new InvalidTypeException("Can't assign object value to a void");
|
|
581 |
}
|
|
582 |
|
|
583 |
// Validate assignment
|
|
584 |
ReferenceType destType = (ReferenceTypeImpl)destination.type();
|
|
585 |
ReferenceTypeImpl myType = (ReferenceTypeImpl)referenceType();
|
51
|
586 |
if (!myType.isAssignableTo(destType)) {
|
2
|
587 |
JNITypeParser parser = new JNITypeParser(destType.signature());
|
|
588 |
String destTypeName = parser.typeName();
|
|
589 |
throw new InvalidTypeException("Can't assign " +
|
|
590 |
type().name() +
|
|
591 |
" to " + destTypeName);
|
|
592 |
}
|
|
593 |
}
|
|
594 |
|
|
595 |
|
|
596 |
public String toString() {
|
|
597 |
return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")";
|
|
598 |
}
|
|
599 |
|
|
600 |
byte typeValueKey() {
|
|
601 |
return JDWP.Tag.OBJECT;
|
|
602 |
}
|
|
603 |
}
|