|
1 /* |
|
2 * Copyright 1998-2003 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 |
|
26 package com.sun.tools.example.debug.expr; |
|
27 |
|
28 import com.sun.jdi.*; |
|
29 import java.util.*; |
|
30 |
|
31 abstract class LValue { |
|
32 |
|
33 // The JDI Value object for this LValue. Once we have this Value, |
|
34 // we have to remember it since after we return the LValue object |
|
35 // to the ExpressionParser, it might decide that it needs |
|
36 // the 'toString' value for the LValue in which case it will |
|
37 // call getMassagedValue to get this toString value. At that |
|
38 // point, we don't want to call JDI a 2nd time to get the Value |
|
39 // for the LValue. This is especially wrong when the LValue |
|
40 // represents a member function. We would end up calling it |
|
41 // a 2nd time. |
|
42 // |
|
43 // Unfortunately, there are several levels of calls to |
|
44 // get/set values in this file. To minimize confusion, |
|
45 // jdiValue is set/tested at the lowest level - right |
|
46 // next to the actual calls to JDI methods to get/set the |
|
47 // value in the debuggee. |
|
48 protected Value jdiValue; |
|
49 |
|
50 abstract Value getValue() throws InvocationException, |
|
51 IncompatibleThreadStateException, |
|
52 InvalidTypeException, |
|
53 ClassNotLoadedException, |
|
54 ParseException; |
|
55 |
|
56 abstract void setValue0(Value value) |
|
57 throws ParseException, InvalidTypeException, |
|
58 ClassNotLoadedException; |
|
59 |
|
60 abstract void invokeWith(List<Value> arguments) throws ParseException; |
|
61 |
|
62 void setValue(Value value) throws ParseException { |
|
63 try { |
|
64 setValue0(value); |
|
65 } catch (InvalidTypeException exc) { |
|
66 throw new ParseException( |
|
67 "Attempt to set value of incorrect type" + |
|
68 exc); |
|
69 } catch (ClassNotLoadedException exc) { |
|
70 throw new ParseException( |
|
71 "Attempt to set value before " + exc.className() + " was loaded" + |
|
72 exc); |
|
73 } |
|
74 } |
|
75 |
|
76 void setValue(LValue lval) throws ParseException { |
|
77 setValue(lval.interiorGetValue()); |
|
78 } |
|
79 |
|
80 LValue memberLValue(ExpressionParser.GetFrame frameGetter, |
|
81 String fieldName) throws ParseException { |
|
82 try { |
|
83 return memberLValue(fieldName, frameGetter.get().thread()); |
|
84 } catch (IncompatibleThreadStateException exc) { |
|
85 throw new ParseException("Thread not suspended"); |
|
86 } |
|
87 } |
|
88 |
|
89 LValue memberLValue(String fieldName, ThreadReference thread) throws ParseException { |
|
90 |
|
91 Value val = interiorGetValue(); |
|
92 if ((val instanceof ArrayReference) && |
|
93 "length".equals(fieldName)){ |
|
94 return new LValueArrayLength((ArrayReference)val); |
|
95 } |
|
96 return new LValueInstanceMember(val, fieldName, thread); |
|
97 } |
|
98 |
|
99 // Return the Value for this LValue that would be used to concatenate |
|
100 // to a String. IE, if it is an Object, call toString in the debuggee. |
|
101 Value getMassagedValue(ExpressionParser.GetFrame frameGetter) throws ParseException { |
|
102 Value vv = interiorGetValue(); |
|
103 |
|
104 // If vv is an ObjectReference, then we have to |
|
105 // do the implicit call to toString(). |
|
106 if (vv instanceof ObjectReference && |
|
107 !(vv instanceof StringReference) && |
|
108 !(vv instanceof ArrayReference)) { |
|
109 StackFrame frame; |
|
110 try { |
|
111 frame = frameGetter.get(); |
|
112 } catch (IncompatibleThreadStateException exc) { |
|
113 throw new ParseException("Thread not suspended"); |
|
114 } |
|
115 |
|
116 ThreadReference thread = frame.thread(); |
|
117 LValue toStringMember = memberLValue("toString", thread); |
|
118 toStringMember.invokeWith(new ArrayList<Value>()); |
|
119 return toStringMember.interiorGetValue(); |
|
120 } |
|
121 return vv; |
|
122 } |
|
123 |
|
124 Value interiorGetValue() throws ParseException { |
|
125 Value value; |
|
126 try { |
|
127 value = getValue(); |
|
128 } catch (InvocationException e) { |
|
129 throw new ParseException("Unable to complete expression. Exception " + |
|
130 e.exception() + " thrown"); |
|
131 } catch (IncompatibleThreadStateException itse) { |
|
132 throw new ParseException("Unable to complete expression. Thread " + |
|
133 "not suspended for method invoke"); |
|
134 } catch (InvalidTypeException ite) { |
|
135 throw new ParseException("Unable to complete expression. Method " + |
|
136 "argument type mismatch"); |
|
137 } catch (ClassNotLoadedException tnle) { |
|
138 throw new ParseException("Unable to complete expression. Method " + |
|
139 "argument type " + tnle.className() + |
|
140 " not yet loaded"); |
|
141 } |
|
142 return value; |
|
143 } |
|
144 |
|
145 LValue arrayElementLValue(LValue lval) throws ParseException { |
|
146 Value indexValue = lval.interiorGetValue(); |
|
147 int index; |
|
148 if ( (indexValue instanceof IntegerValue) || |
|
149 (indexValue instanceof ShortValue) || |
|
150 (indexValue instanceof ByteValue) || |
|
151 (indexValue instanceof CharValue) ) { |
|
152 index = ((PrimitiveValue)indexValue).intValue(); |
|
153 } else { |
|
154 throw new ParseException("Array index must be a integer type"); |
|
155 } |
|
156 return new LValueArrayElement(interiorGetValue(), index); |
|
157 } |
|
158 |
|
159 public String toString() { |
|
160 try { |
|
161 return interiorGetValue().toString(); |
|
162 } catch (ParseException e) { |
|
163 return "<Parse Exception>"; |
|
164 } |
|
165 } |
|
166 |
|
167 static final int STATIC = 0; |
|
168 static final int INSTANCE = 1; |
|
169 |
|
170 static Field fieldByName(ReferenceType refType, String name, int kind) { |
|
171 /* |
|
172 * TO DO: Note that this currently fails to find superclass |
|
173 * or implemented interface fields. This is due to a temporary |
|
174 * limititation of RefType.fieldByName. Once that method is |
|
175 * fixed, superclass fields will be found. |
|
176 */ |
|
177 Field field = refType.fieldByName(name); |
|
178 if (field != null) { |
|
179 boolean isStatic = field.isStatic(); |
|
180 if (((kind == STATIC) && !isStatic) || |
|
181 ((kind == INSTANCE) && isStatic)) { |
|
182 field = null; |
|
183 } |
|
184 } |
|
185 /*** |
|
186 System.err.println("fieldByName: " + refType.name() + " " + |
|
187 name + " " + |
|
188 kind + " " + |
|
189 (field != null)); |
|
190 ***/ |
|
191 return field; |
|
192 } |
|
193 |
|
194 static List methodsByName(ReferenceType refType, String name, int kind) { |
|
195 List list = refType.methodsByName(name); |
|
196 Iterator iter = list.iterator(); |
|
197 while (iter.hasNext()) { |
|
198 Method method = (Method)iter.next(); |
|
199 boolean isStatic = method.isStatic(); |
|
200 if (((kind == STATIC) && !isStatic) || |
|
201 ((kind == INSTANCE) && isStatic)) { |
|
202 iter.remove(); |
|
203 } |
|
204 } |
|
205 return list; |
|
206 } |
|
207 |
|
208 static List<String> primitiveTypeNames = new ArrayList<String>(); |
|
209 static { |
|
210 primitiveTypeNames.add("boolean"); |
|
211 primitiveTypeNames.add("byte"); |
|
212 primitiveTypeNames.add("char"); |
|
213 primitiveTypeNames.add("short"); |
|
214 primitiveTypeNames.add("int"); |
|
215 primitiveTypeNames.add("long"); |
|
216 primitiveTypeNames.add("float"); |
|
217 primitiveTypeNames.add("double"); |
|
218 } |
|
219 |
|
220 |
|
221 static final int SAME = 0; |
|
222 static final int ASSIGNABLE = 1; |
|
223 static final int DIFFERENT = 2; |
|
224 /* |
|
225 * Return SAME, DIFFERENT or ASSIGNABLE. |
|
226 * SAME means each arg type is the same as type of the corr. arg. |
|
227 * ASSIGNABLE means that not all the pairs are the same, but |
|
228 * for those that aren't, at least the argType is assignable |
|
229 * from the type of the argument value. |
|
230 * DIFFERENT means that in at least one pair, the |
|
231 * argType is not assignable from the type of the argument value. |
|
232 * IE, one is an Apple and the other is an Orange. |
|
233 */ |
|
234 static int argumentsMatch(List argTypes, List arguments) { |
|
235 if (argTypes.size() != arguments.size()) { |
|
236 return DIFFERENT; |
|
237 } |
|
238 |
|
239 Iterator typeIter = argTypes.iterator(); |
|
240 Iterator valIter = arguments.iterator(); |
|
241 int result = SAME; |
|
242 |
|
243 // If any pair aren't the same, change the |
|
244 // result to ASSIGNABLE. If any pair aren't |
|
245 // assignable, return DIFFERENT |
|
246 while (typeIter.hasNext()) { |
|
247 Type argType = (Type)typeIter.next(); |
|
248 Value value = (Value)valIter.next(); |
|
249 if (value == null) { |
|
250 // Null values can be passed to any non-primitive argument |
|
251 if (primitiveTypeNames.contains(argType.name())) { |
|
252 return DIFFERENT; |
|
253 } |
|
254 // Else, we will assume that a null value |
|
255 // exactly matches an object type. |
|
256 } |
|
257 if (!value.type().equals(argType)) { |
|
258 if (isAssignableTo(value.type(), argType)) { |
|
259 result = ASSIGNABLE; |
|
260 } else { |
|
261 return DIFFERENT; |
|
262 } |
|
263 } |
|
264 } |
|
265 return result; |
|
266 } |
|
267 |
|
268 |
|
269 // These is...AssignableTo methods are based on similar code in the JDI |
|
270 // implementations of ClassType, ArrayType, and InterfaceType |
|
271 |
|
272 static boolean isComponentAssignable(Type fromType, Type toType) { |
|
273 if (fromType instanceof PrimitiveType) { |
|
274 // Assignment of primitive arrays requires identical |
|
275 // component types. |
|
276 return fromType.equals(toType); |
|
277 } |
|
278 if (toType instanceof PrimitiveType) { |
|
279 return false; |
|
280 } |
|
281 // Assignment of object arrays requires availability |
|
282 // of widening conversion of component types |
|
283 return isAssignableTo(fromType, toType); |
|
284 } |
|
285 |
|
286 static boolean isArrayAssignableTo(ArrayType fromType, Type toType) { |
|
287 if (toType instanceof ArrayType) { |
|
288 try { |
|
289 Type toComponentType = ((ArrayType)toType).componentType(); |
|
290 return isComponentAssignable(fromType.componentType(), toComponentType); |
|
291 } catch (ClassNotLoadedException e) { |
|
292 // One or both component types has not yet been |
|
293 // loaded => can't assign |
|
294 return false; |
|
295 } |
|
296 } |
|
297 if (toType instanceof InterfaceType) { |
|
298 // Only valid InterfaceType assignee is Cloneable |
|
299 return toType.name().equals("java.lang.Cloneable"); |
|
300 } |
|
301 // Only valid ClassType assignee is Object |
|
302 return toType.name().equals("java.lang.Object"); |
|
303 } |
|
304 |
|
305 static boolean isAssignableTo(Type fromType, Type toType) { |
|
306 if (fromType.equals(toType)) { |
|
307 return true; |
|
308 } |
|
309 |
|
310 // If one is boolean, so must be the other. |
|
311 if (fromType instanceof BooleanType) { |
|
312 if (toType instanceof BooleanType) { |
|
313 return true; |
|
314 } |
|
315 return false; |
|
316 } |
|
317 if (toType instanceof BooleanType) { |
|
318 return false; |
|
319 } |
|
320 |
|
321 // Other primitive types are intermixable only with each other. |
|
322 if (fromType instanceof PrimitiveType) { |
|
323 if (toType instanceof PrimitiveType) { |
|
324 return true; |
|
325 } |
|
326 return false; |
|
327 } |
|
328 if (toType instanceof PrimitiveType) { |
|
329 return false; |
|
330 } |
|
331 |
|
332 // neither one is primitive. |
|
333 if (fromType instanceof ArrayType) { |
|
334 return isArrayAssignableTo((ArrayType)fromType, toType); |
|
335 } |
|
336 List interfaces; |
|
337 if (fromType instanceof ClassType) { |
|
338 ClassType superclazz = ((ClassType)fromType).superclass(); |
|
339 if ((superclazz != null) && isAssignableTo(superclazz, toType)) { |
|
340 return true; |
|
341 } |
|
342 interfaces = ((ClassType)fromType).interfaces(); |
|
343 } else { |
|
344 // fromType must be an InterfaceType |
|
345 interfaces = ((InterfaceType)fromType).superinterfaces(); |
|
346 } |
|
347 Iterator iter = interfaces.iterator(); |
|
348 while (iter.hasNext()) { |
|
349 InterfaceType interfaze = (InterfaceType)iter.next(); |
|
350 if (isAssignableTo(interfaze, toType)) { |
|
351 return true; |
|
352 } |
|
353 } |
|
354 return false; |
|
355 } |
|
356 |
|
357 static Method resolveOverload(List overloads, List arguments) |
|
358 throws ParseException { |
|
359 |
|
360 // If there is only one method to call, we'll just choose |
|
361 // that without looking at the args. If they aren't right |
|
362 // the invoke will return a better error message than we |
|
363 // could generate here. |
|
364 if (overloads.size() == 1) { |
|
365 return (Method)overloads.get(0); |
|
366 } |
|
367 |
|
368 // Resolving overloads is beyond the scope of this exercise. |
|
369 // So, we will look for a method that matches exactly the |
|
370 // types of the arguments. If we can't find one, then |
|
371 // if there is exactly one method whose param types are assignable |
|
372 // from the arg types, we will use that. Otherwise, |
|
373 // it is an error. We won't guess which of multiple possible |
|
374 // methods to call. And, since casts aren't implemented, |
|
375 // the user can't use them to pick a particular overload to call. |
|
376 // IE, the user is out of luck in this case. |
|
377 Iterator iter = overloads.iterator(); |
|
378 Method retVal = null; |
|
379 int assignableCount = 0; |
|
380 while (iter.hasNext()) { |
|
381 Method mm = (Method)iter.next(); |
|
382 List argTypes; |
|
383 try { |
|
384 argTypes = mm.argumentTypes(); |
|
385 } catch (ClassNotLoadedException ee) { |
|
386 // This probably won't happen for the |
|
387 // method that we are really supposed to |
|
388 // call. |
|
389 continue; |
|
390 } |
|
391 int compare = argumentsMatch(argTypes, arguments); |
|
392 if (compare == SAME) { |
|
393 return mm; |
|
394 } |
|
395 if (compare == DIFFERENT) { |
|
396 continue; |
|
397 } |
|
398 // Else, it is assignable. Remember it. |
|
399 retVal = mm; |
|
400 assignableCount++; |
|
401 } |
|
402 |
|
403 // At this point, we didn't find an exact match, |
|
404 // but we found one for which the args are assignable. |
|
405 // |
|
406 if (retVal != null) { |
|
407 if (assignableCount == 1) { |
|
408 return retVal; |
|
409 } |
|
410 throw new ParseException("Arguments match multiple methods"); |
|
411 } |
|
412 throw new ParseException("Arguments match no method"); |
|
413 } |
|
414 |
|
415 private static class LValueLocal extends LValue { |
|
416 final StackFrame frame; |
|
417 final LocalVariable var; |
|
418 |
|
419 LValueLocal(StackFrame frame, LocalVariable var) { |
|
420 this.frame = frame; |
|
421 this.var = var; |
|
422 } |
|
423 |
|
424 Value getValue() { |
|
425 if (jdiValue == null) { |
|
426 jdiValue = frame.getValue(var); |
|
427 } |
|
428 return jdiValue; |
|
429 } |
|
430 |
|
431 void setValue0(Value val) throws InvalidTypeException, |
|
432 ClassNotLoadedException { |
|
433 frame.setValue(var, val); |
|
434 jdiValue = val; |
|
435 } |
|
436 |
|
437 void invokeWith(List<Value> arguments) throws ParseException { |
|
438 throw new ParseException(var.name() + " is not a method"); |
|
439 } |
|
440 } |
|
441 |
|
442 private static class LValueInstanceMember extends LValue { |
|
443 final ObjectReference obj; |
|
444 final ThreadReference thread; |
|
445 final Field matchingField; |
|
446 final List overloads; |
|
447 Method matchingMethod = null; |
|
448 List<Value> methodArguments = null; |
|
449 |
|
450 LValueInstanceMember(Value value, |
|
451 String memberName, |
|
452 ThreadReference thread) throws ParseException { |
|
453 if (!(value instanceof ObjectReference)) { |
|
454 throw new ParseException( |
|
455 "Cannot access field of primitive type: " + value); |
|
456 } |
|
457 this.obj = (ObjectReference)value; |
|
458 this.thread = thread; |
|
459 ReferenceType refType = obj.referenceType(); |
|
460 /* |
|
461 * Can't tell yet whether this LValue will be accessed as a |
|
462 * field or method, so we keep track of all the possibilities |
|
463 */ |
|
464 matchingField = LValue.fieldByName(refType, memberName, |
|
465 LValue.INSTANCE); |
|
466 overloads = LValue.methodsByName(refType, memberName, |
|
467 LValue.INSTANCE); |
|
468 if ((matchingField == null) && overloads.size() == 0) { |
|
469 throw new ParseException("No instance field or method with the name " |
|
470 + memberName + " in " + refType.name()); |
|
471 } |
|
472 } |
|
473 |
|
474 Value getValue() throws InvocationException, InvalidTypeException, |
|
475 ClassNotLoadedException, IncompatibleThreadStateException, |
|
476 ParseException { |
|
477 if (jdiValue != null) { |
|
478 return jdiValue; |
|
479 } |
|
480 if (matchingMethod == null) { |
|
481 if (matchingField == null) { |
|
482 throw new ParseException("No such field in " + obj.referenceType().name()); |
|
483 } |
|
484 return jdiValue = obj.getValue(matchingField); |
|
485 } else { |
|
486 return jdiValue = obj.invokeMethod(thread, matchingMethod, methodArguments, 0); |
|
487 } |
|
488 } |
|
489 |
|
490 void setValue0(Value val) throws ParseException, |
|
491 InvalidTypeException, |
|
492 ClassNotLoadedException { |
|
493 if (matchingMethod != null) { |
|
494 throw new ParseException("Cannot assign to a method invocation"); |
|
495 } |
|
496 obj.setValue(matchingField, val); |
|
497 jdiValue = val; |
|
498 } |
|
499 |
|
500 void invokeWith(List<Value> arguments) throws ParseException { |
|
501 if (matchingMethod != null) { |
|
502 throw new ParseException("Invalid consecutive invocations"); |
|
503 } |
|
504 methodArguments = arguments; |
|
505 matchingMethod = LValue.resolveOverload(overloads, arguments); |
|
506 } |
|
507 } |
|
508 |
|
509 private static class LValueStaticMember extends LValue { |
|
510 final ReferenceType refType; |
|
511 final ThreadReference thread; |
|
512 final Field matchingField; |
|
513 final List overloads; |
|
514 Method matchingMethod = null; |
|
515 List<Value> methodArguments = null; |
|
516 |
|
517 LValueStaticMember(ReferenceType refType, |
|
518 String memberName, |
|
519 ThreadReference thread) throws ParseException { |
|
520 this.refType = refType; |
|
521 this.thread = thread; |
|
522 /* |
|
523 * Can't tell yet whether this LValue will be accessed as a |
|
524 * field or method, so we keep track of all the possibilities |
|
525 */ |
|
526 matchingField = LValue.fieldByName(refType, memberName, |
|
527 LValue.STATIC); |
|
528 overloads = LValue.methodsByName(refType, memberName, |
|
529 LValue.STATIC); |
|
530 if ((matchingField == null) && overloads.size() == 0) { |
|
531 throw new ParseException("No static field or method with the name " |
|
532 + memberName + " in " + refType.name()); |
|
533 } |
|
534 } |
|
535 |
|
536 Value getValue() throws InvocationException, InvalidTypeException, |
|
537 ClassNotLoadedException, IncompatibleThreadStateException, |
|
538 ParseException { |
|
539 if (jdiValue != null) { |
|
540 return jdiValue; |
|
541 } |
|
542 if (matchingMethod == null) { |
|
543 return jdiValue = refType.getValue(matchingField); |
|
544 } else if (refType instanceof ClassType) { |
|
545 ClassType clazz = (ClassType)refType; |
|
546 return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0); |
|
547 } else { |
|
548 throw new InvalidTypeException("Cannot invoke static method on " + |
|
549 refType.name()); |
|
550 } |
|
551 } |
|
552 |
|
553 void setValue0(Value val) |
|
554 throws ParseException, InvalidTypeException, |
|
555 ClassNotLoadedException { |
|
556 if (matchingMethod != null) { |
|
557 throw new ParseException("Cannot assign to a method invocation"); |
|
558 } |
|
559 if (!(refType instanceof ClassType)) { |
|
560 throw new ParseException( |
|
561 "Cannot set interface field: " + refType); |
|
562 } |
|
563 ((ClassType)refType).setValue(matchingField, val); |
|
564 jdiValue = val; |
|
565 } |
|
566 |
|
567 void invokeWith(List<Value> arguments) throws ParseException { |
|
568 if (matchingMethod != null) { |
|
569 throw new ParseException("Invalid consecutive invocations"); |
|
570 } |
|
571 methodArguments = arguments; |
|
572 matchingMethod = LValue.resolveOverload(overloads, arguments); |
|
573 } |
|
574 } |
|
575 |
|
576 private static class LValueArrayLength extends LValue { |
|
577 /* |
|
578 * Since one can code "int myLen = myArray.length;", |
|
579 * one might expect that these JDI calls would get a Value |
|
580 * object for the length of an array in the debugee: |
|
581 * Field xxx = ArrayType.fieldByName("length") |
|
582 * Value lenVal= ArrayReference.getValue(xxx) |
|
583 * |
|
584 * However, this doesn't work because the array length isn't |
|
585 * really stored as a field, and can't be accessed as such |
|
586 * via JDI. Instead, the arrayRef.length() method has to be |
|
587 * used. |
|
588 */ |
|
589 final ArrayReference arrayRef; |
|
590 LValueArrayLength (ArrayReference value) { |
|
591 this.arrayRef = value; |
|
592 } |
|
593 |
|
594 Value getValue() { |
|
595 if (jdiValue == null) { |
|
596 jdiValue = arrayRef.virtualMachine().mirrorOf(arrayRef.length()); |
|
597 } |
|
598 return jdiValue; |
|
599 } |
|
600 |
|
601 void setValue0(Value value) throws ParseException { |
|
602 throw new ParseException("Cannot set constant: " + value); |
|
603 } |
|
604 |
|
605 void invokeWith(List<Value> arguments) throws ParseException { |
|
606 throw new ParseException("Array element is not a method"); |
|
607 } |
|
608 } |
|
609 |
|
610 private static class LValueArrayElement extends LValue { |
|
611 final ArrayReference array; |
|
612 final int index; |
|
613 |
|
614 LValueArrayElement(Value value, int index) throws ParseException { |
|
615 if (!(value instanceof ArrayReference)) { |
|
616 throw new ParseException( |
|
617 "Must be array type: " + value); |
|
618 } |
|
619 this.array = (ArrayReference)value; |
|
620 this.index = index; |
|
621 } |
|
622 |
|
623 Value getValue() { |
|
624 if (jdiValue == null) { |
|
625 jdiValue = array.getValue(index); |
|
626 } |
|
627 return jdiValue; |
|
628 } |
|
629 |
|
630 void setValue0(Value val) throws InvalidTypeException, |
|
631 ClassNotLoadedException { |
|
632 array.setValue(index, val); |
|
633 jdiValue = val; |
|
634 } |
|
635 |
|
636 void invokeWith(List<Value> arguments) throws ParseException { |
|
637 throw new ParseException("Array element is not a method"); |
|
638 } |
|
639 } |
|
640 |
|
641 private static class LValueConstant extends LValue { |
|
642 final Value value; |
|
643 |
|
644 LValueConstant(Value value) { |
|
645 this.value = value; |
|
646 } |
|
647 |
|
648 Value getValue() { |
|
649 if (jdiValue == null) { |
|
650 jdiValue = value; |
|
651 } |
|
652 return jdiValue; |
|
653 } |
|
654 |
|
655 void setValue0(Value val) throws ParseException { |
|
656 throw new ParseException("Cannot set constant: " + value); |
|
657 } |
|
658 |
|
659 void invokeWith(List<Value> arguments) throws ParseException { |
|
660 throw new ParseException("Constant is not a method"); |
|
661 } |
|
662 } |
|
663 |
|
664 static LValue make(VirtualMachine vm, boolean val) { |
|
665 return new LValueConstant(vm.mirrorOf(val)); |
|
666 } |
|
667 |
|
668 static LValue make(VirtualMachine vm, byte val) { |
|
669 return new LValueConstant(vm.mirrorOf(val)); |
|
670 } |
|
671 |
|
672 static LValue make(VirtualMachine vm, char val) { |
|
673 return new LValueConstant(vm.mirrorOf(val)); |
|
674 } |
|
675 |
|
676 static LValue make(VirtualMachine vm, short val) { |
|
677 return new LValueConstant(vm.mirrorOf(val)); |
|
678 } |
|
679 |
|
680 static LValue make(VirtualMachine vm, int val) { |
|
681 return new LValueConstant(vm.mirrorOf(val)); |
|
682 } |
|
683 |
|
684 static LValue make(VirtualMachine vm, long val) { |
|
685 return new LValueConstant(vm.mirrorOf(val)); |
|
686 } |
|
687 |
|
688 static LValue make(VirtualMachine vm, float val) { |
|
689 return new LValueConstant(vm.mirrorOf(val)); |
|
690 } |
|
691 |
|
692 static LValue make(VirtualMachine vm, double val) { |
|
693 return new LValueConstant(vm.mirrorOf(val)); |
|
694 } |
|
695 |
|
696 static LValue make(VirtualMachine vm, String val) throws ParseException { |
|
697 return new LValueConstant(vm.mirrorOf(val)); |
|
698 } |
|
699 |
|
700 static LValue makeBoolean(VirtualMachine vm, Token token) { |
|
701 return make(vm, token.image.charAt(0) == 't'); |
|
702 } |
|
703 |
|
704 static LValue makeCharacter(VirtualMachine vm, Token token) { |
|
705 return make(vm, token.image.charAt(1)); |
|
706 } |
|
707 |
|
708 static LValue makeFloat(VirtualMachine vm, Token token) { |
|
709 return make(vm, Float.valueOf(token.image).floatValue()); |
|
710 } |
|
711 |
|
712 static LValue makeDouble(VirtualMachine vm, Token token) { |
|
713 return make(vm, Double.valueOf(token.image).doubleValue()); |
|
714 } |
|
715 |
|
716 static LValue makeInteger(VirtualMachine vm, Token token) { |
|
717 return make(vm, Integer.parseInt(token.image)); |
|
718 } |
|
719 |
|
720 static LValue makeShort(VirtualMachine vm, Token token) { |
|
721 return make(vm, Short.parseShort(token.image)); |
|
722 } |
|
723 |
|
724 static LValue makeLong(VirtualMachine vm, Token token) { |
|
725 return make(vm, Long.parseLong(token.image)); |
|
726 } |
|
727 |
|
728 static LValue makeByte(VirtualMachine vm, Token token) { |
|
729 return make(vm, Byte.parseByte(token.image)); |
|
730 } |
|
731 |
|
732 static LValue makeString(VirtualMachine vm, |
|
733 Token token) throws ParseException { |
|
734 int len = token.image.length(); |
|
735 return make(vm, token.image.substring(1,len-1)); |
|
736 } |
|
737 |
|
738 static LValue makeNull(VirtualMachine vm, |
|
739 Token token) throws ParseException { |
|
740 return new LValueConstant(null); |
|
741 } |
|
742 |
|
743 static LValue makeThisObject(VirtualMachine vm, |
|
744 ExpressionParser.GetFrame frameGetter, |
|
745 Token token) throws ParseException { |
|
746 if (frameGetter == null) { |
|
747 throw new ParseException("No current thread"); |
|
748 } else { |
|
749 try { |
|
750 StackFrame frame = frameGetter.get(); |
|
751 ObjectReference thisObject = frame.thisObject(); |
|
752 |
|
753 if (thisObject==null) { |
|
754 throw new ParseException( |
|
755 "No 'this'. In native or static method"); |
|
756 } else { |
|
757 return new LValueConstant(thisObject); |
|
758 } |
|
759 } catch (IncompatibleThreadStateException exc) { |
|
760 throw new ParseException("Thread not suspended"); |
|
761 } |
|
762 } |
|
763 } |
|
764 |
|
765 static LValue makeNewObject(VirtualMachine vm, |
|
766 ExpressionParser.GetFrame frameGetter, |
|
767 String className, List<Value> arguments) throws ParseException { |
|
768 List classes = vm.classesByName(className); |
|
769 if (classes.size() == 0) { |
|
770 throw new ParseException("No class named: " + className); |
|
771 } |
|
772 |
|
773 if (classes.size() > 1) { |
|
774 throw new ParseException("More than one class named: " + |
|
775 className); |
|
776 } |
|
777 ReferenceType refType = (ReferenceType)classes.get(0); |
|
778 |
|
779 |
|
780 if (!(refType instanceof ClassType)) { |
|
781 throw new ParseException("Cannot create instance of interface " + |
|
782 className); |
|
783 } |
|
784 |
|
785 ClassType classType = (ClassType)refType; |
|
786 List<Method> methods = new ArrayList<Method>(classType.methods()); // writable |
|
787 Iterator iter = methods.iterator(); |
|
788 while (iter.hasNext()) { |
|
789 Method method = (Method)iter.next(); |
|
790 if (!method.isConstructor()) { |
|
791 iter.remove(); |
|
792 } |
|
793 } |
|
794 Method constructor = LValue.resolveOverload(methods, arguments); |
|
795 |
|
796 ObjectReference newObject; |
|
797 try { |
|
798 ThreadReference thread = frameGetter.get().thread(); |
|
799 newObject = classType.newInstance(thread, constructor, arguments, 0); |
|
800 } catch (InvocationException ie) { |
|
801 throw new ParseException("Exception in " + className + " constructor: " + |
|
802 ie.exception().referenceType().name()); |
|
803 } catch (IncompatibleThreadStateException exc) { |
|
804 throw new ParseException("Thread not suspended"); |
|
805 } catch (Exception e) { |
|
806 /* |
|
807 * TO DO: Better error handling |
|
808 */ |
|
809 throw new ParseException("Unable to create " + className + " instance"); |
|
810 } |
|
811 return new LValueConstant(newObject); |
|
812 } |
|
813 |
|
814 private static LValue nFields(LValue lval, |
|
815 StringTokenizer izer, |
|
816 ThreadReference thread) |
|
817 throws ParseException { |
|
818 if (!izer.hasMoreTokens()) { |
|
819 return lval; |
|
820 } else { |
|
821 return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread); |
|
822 } |
|
823 } |
|
824 |
|
825 static LValue makeName(VirtualMachine vm, |
|
826 ExpressionParser.GetFrame frameGetter, |
|
827 String name) throws ParseException { |
|
828 StringTokenizer izer = new StringTokenizer(name, "."); |
|
829 String first = izer.nextToken(); |
|
830 // check local variables |
|
831 if (frameGetter != null) { |
|
832 try { |
|
833 StackFrame frame = frameGetter.get(); |
|
834 ThreadReference thread = frame.thread(); |
|
835 LocalVariable var; |
|
836 try { |
|
837 var = frame.visibleVariableByName(first); |
|
838 } catch (AbsentInformationException e) { |
|
839 var = null; |
|
840 } |
|
841 if (var != null) { |
|
842 return nFields(new LValueLocal(frame, var), izer, thread); |
|
843 } else { |
|
844 ObjectReference thisObject = frame.thisObject(); |
|
845 if (thisObject != null) { |
|
846 // check if it is a field of 'this' |
|
847 LValue thisLValue = new LValueConstant(thisObject); |
|
848 LValue fv; |
|
849 try { |
|
850 fv = thisLValue.memberLValue(first, thread); |
|
851 } catch (ParseException exc) { |
|
852 fv = null; |
|
853 } |
|
854 if (fv != null) { |
|
855 return nFields(fv, izer, thread); |
|
856 } |
|
857 } |
|
858 } |
|
859 // check for class name |
|
860 while (izer.hasMoreTokens()) { |
|
861 List classes = vm.classesByName(first); |
|
862 if (classes.size() > 0) { |
|
863 if (classes.size() > 1) { |
|
864 throw new ParseException("More than one class named: " + |
|
865 first); |
|
866 } else { |
|
867 ReferenceType refType = (ReferenceType)classes.get(0); |
|
868 LValue lval = new LValueStaticMember(refType, |
|
869 izer.nextToken(), thread); |
|
870 return nFields(lval, izer, thread); |
|
871 } |
|
872 } |
|
873 first = first + '.' + izer.nextToken(); |
|
874 } |
|
875 } catch (IncompatibleThreadStateException exc) { |
|
876 throw new ParseException("Thread not suspended"); |
|
877 } |
|
878 } |
|
879 throw new ParseException("Name unknown: " + name); |
|
880 } |
|
881 |
|
882 static String stringValue(LValue lval, ExpressionParser.GetFrame frameGetter |
|
883 ) throws ParseException { |
|
884 Value val = lval.getMassagedValue(frameGetter); |
|
885 if (val == null) { |
|
886 return "null"; |
|
887 } |
|
888 if (val instanceof StringReference) { |
|
889 return ((StringReference)val).value(); |
|
890 } |
|
891 return val.toString(); // is this correct in all cases? |
|
892 } |
|
893 |
|
894 static LValue booleanOperation(VirtualMachine vm, Token token, |
|
895 LValue rightL, |
|
896 LValue leftL) throws ParseException { |
|
897 String op = token.image; |
|
898 Value right = rightL.interiorGetValue(); |
|
899 Value left = leftL.interiorGetValue(); |
|
900 if ( !(right instanceof PrimitiveValue) || |
|
901 !(left instanceof PrimitiveValue) ) { |
|
902 if (op.equals("==")) { |
|
903 return make(vm, right.equals(left)); |
|
904 } else if (op.equals("!=")) { |
|
905 return make(vm, !right.equals(left)); |
|
906 } else { |
|
907 throw new ParseException("Operands or '" + op + |
|
908 "' must be primitive"); |
|
909 } |
|
910 } |
|
911 // can compare any numeric doubles |
|
912 double rr = ((PrimitiveValue)right).doubleValue(); |
|
913 double ll = ((PrimitiveValue)left).doubleValue(); |
|
914 boolean res; |
|
915 if (op.equals("<")) { |
|
916 res = rr < ll; |
|
917 } else if (op.equals(">")) { |
|
918 res = rr > ll; |
|
919 } else if (op.equals("<=")) { |
|
920 res = rr <= ll; |
|
921 } else if (op.equals(">=")) { |
|
922 res = rr >= ll; |
|
923 } else if (op.equals("==")) { |
|
924 res = rr == ll; |
|
925 } else if (op.equals("!=")) { |
|
926 res = rr != ll; |
|
927 } else { |
|
928 throw new ParseException("Unknown operation: " + op); |
|
929 } |
|
930 return make(vm, res); |
|
931 } |
|
932 |
|
933 static LValue operation(VirtualMachine vm, Token token, |
|
934 LValue rightL, LValue leftL, |
|
935 ExpressionParser.GetFrame frameGetter |
|
936 ) throws ParseException { |
|
937 String op = token.image; |
|
938 Value right = rightL.interiorGetValue(); |
|
939 Value left = leftL.interiorGetValue(); |
|
940 if ((right instanceof StringReference) || |
|
941 (left instanceof StringReference)) { |
|
942 if (op.equals("+")) { |
|
943 // If one is an ObjectRef, we will need to invoke |
|
944 // toString on it, so we need the thread. |
|
945 return make(vm, stringValue(rightL, frameGetter) + |
|
946 stringValue(leftL, frameGetter)); |
|
947 } |
|
948 } |
|
949 if ((right instanceof ObjectReference) || |
|
950 (left instanceof ObjectReference)) { |
|
951 if (op.equals("==")) { |
|
952 return make(vm, right.equals(left)); |
|
953 } else if (op.equals("!=")) { |
|
954 return make(vm, !right.equals(left)); |
|
955 } else { |
|
956 throw new ParseException("Invalid operation '" + |
|
957 op + "' on an Object"); |
|
958 } |
|
959 } |
|
960 if ((right instanceof BooleanValue) || |
|
961 (left instanceof BooleanValue)) { |
|
962 throw new ParseException("Invalid operation '" + |
|
963 op + "' on a Boolean"); |
|
964 } |
|
965 // from here on, we know it is a integer kind of type |
|
966 PrimitiveValue primRight = (PrimitiveValue)right; |
|
967 PrimitiveValue primLeft = (PrimitiveValue)left; |
|
968 if ((primRight instanceof DoubleValue) || |
|
969 (primLeft instanceof DoubleValue)) { |
|
970 double rr = primRight.doubleValue(); |
|
971 double ll = primLeft.doubleValue(); |
|
972 double res; |
|
973 if (op.equals("+")) { |
|
974 res = rr + ll; |
|
975 } else if (op.equals("-")) { |
|
976 res = rr - ll; |
|
977 } else if (op.equals("*")) { |
|
978 res = rr * ll; |
|
979 } else if (op.equals("/")) { |
|
980 res = rr / ll; |
|
981 } else { |
|
982 throw new ParseException("Unknown operation: " + op); |
|
983 } |
|
984 return make(vm, res); |
|
985 } |
|
986 if ((primRight instanceof FloatValue) || |
|
987 (primLeft instanceof FloatValue)) { |
|
988 float rr = primRight.floatValue(); |
|
989 float ll = primLeft.floatValue(); |
|
990 float res; |
|
991 if (op.equals("+")) { |
|
992 res = rr + ll; |
|
993 } else if (op.equals("-")) { |
|
994 res = rr - ll; |
|
995 } else if (op.equals("*")) { |
|
996 res = rr * ll; |
|
997 } else if (op.equals("/")) { |
|
998 res = rr / ll; |
|
999 } else { |
|
1000 throw new ParseException("Unknown operation: " + op); |
|
1001 } |
|
1002 return make(vm, res); |
|
1003 } |
|
1004 if ((primRight instanceof LongValue) || |
|
1005 (primLeft instanceof LongValue)) { |
|
1006 long rr = primRight.longValue(); |
|
1007 long ll = primLeft.longValue(); |
|
1008 long res; |
|
1009 if (op.equals("+")) { |
|
1010 res = rr + ll; |
|
1011 } else if (op.equals("-")) { |
|
1012 res = rr - ll; |
|
1013 } else if (op.equals("*")) { |
|
1014 res = rr * ll; |
|
1015 } else if (op.equals("/")) { |
|
1016 res = rr / ll; |
|
1017 } else { |
|
1018 throw new ParseException("Unknown operation: " + op); |
|
1019 } |
|
1020 return make(vm, res); |
|
1021 } else { |
|
1022 int rr = primRight.intValue(); |
|
1023 int ll = primLeft.intValue(); |
|
1024 int res; |
|
1025 if (op.equals("+")) { |
|
1026 res = rr + ll; |
|
1027 } else if (op.equals("-")) { |
|
1028 res = rr - ll; |
|
1029 } else if (op.equals("*")) { |
|
1030 res = rr * ll; |
|
1031 } else if (op.equals("/")) { |
|
1032 res = rr / ll; |
|
1033 } else { |
|
1034 throw new ParseException("Unknown operation: " + op); |
|
1035 } |
|
1036 return make(vm, res); |
|
1037 } |
|
1038 } |
|
1039 } |