2 * @(#)LValue.java 1.17 99/05/21
4 * Copyright (c) 1997-1999 by Sun Microsystems, Inc. All Rights Reserved.
6 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
7 * modify and redistribute this software in source and binary code form,
8 * provided that i) this copyright notice and license appear on all copies of
9 * the software; and ii) Licensee does not utilize the software in a manner
10 * which is disparaging to Sun.
12 * This software is provided "AS IS," without a warranty of any kind. ALL
13 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
14 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
15 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
16 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
17 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
18 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
19 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
20 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
21 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
22 * POSSIBILITY OF SUCH DAMAGES.
24 * This software is not designed or intended for use in on-line control of
25 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
26 * the design, construction, operation or maintenance of any nuclear
27 * facility. Licensee represents and warrants that it will not use or
28 * redistribute the Software for such purposes.
31 package jde.debugger.expr;
36 abstract class LValue {
38 abstract Value getValue() throws InvocationException,
39 IncompatibleThreadStateException,
41 ClassNotLoadedException;
43 abstract void setValue0(Value value)
44 throws ParseException, InvalidTypeException,
45 ClassNotLoadedException;
47 abstract void invokeWith(List arguments) throws ParseException;
49 void setValue(Value value) throws ParseException {
52 } catch (InvalidTypeException exc) {
53 throw new ParseException(
54 "Attempt to set value of incorrect type" +
56 } catch (ClassNotLoadedException exc) {
57 throw new ParseException(
58 "Attempt to set value before " + exc.className() + " was loaded" +
63 void setValue(LValue lval) throws ParseException {
64 setValue(lval.interiorGetValue());
67 LValue memberLValue(ExpressionParser.GetFrame frameGetter,
68 String fieldName) throws ParseException {
70 return memberLValue(fieldName, frameGetter.get().thread());
71 } catch (IncompatibleThreadStateException exc) {
72 throw new ParseException("Thread not suspended");
76 LValue memberLValue(String fieldName, ThreadReference thread) throws ParseException {
77 return new LValueInstanceMember(interiorGetValue(), fieldName, thread);
80 Value interiorGetValue() throws ParseException {
84 } catch (InvocationException e) {
85 throw new ParseException("Unable to complete expression. Exception " +
86 e.exception() + " thrown");
87 } catch (IncompatibleThreadStateException itse) {
88 throw new ParseException("Unable to complete expression. Thread " +
89 "not suspended for method invoke");
90 } catch (InvalidTypeException ite) {
91 throw new ParseException("Unable to complete expression. Method " +
92 "argument type mismatch");
93 } catch (ClassNotLoadedException tnle) {
94 throw new ParseException("Unable to complete expression. Method " +
95 "argument type " + tnle.className() +
98 // if (value == null) {
99 // throw new ParseException("Cannot invoke a void method within an expression");
104 LValue arrayElementLValue(LValue lval) throws ParseException {
105 Value indexValue = lval.interiorGetValue();
107 if ( (indexValue instanceof IntegerValue) ||
108 (indexValue instanceof ShortValue) ||
109 (indexValue instanceof ByteValue) ||
110 (indexValue instanceof CharValue) ) {
111 index = ((PrimitiveValue)indexValue).intValue();
113 throw new ParseException("Array index must be a integer type");
115 return new LValueArrayElement(interiorGetValue(), index);
118 public String toString() {
120 return interiorGetValue().toString();
121 } catch (ParseException e) {
122 return "<Parse Exception>";
126 static final int STATIC = 0;
127 static final int INSTANCE = 1;
129 static Field fieldByName(ReferenceType refType, String name, int kind) {
131 * TO DO: Note that this currently fails to find superclass
132 * or implemented interface fields. This is due to a temporary
133 * limititation of RefType.fieldByName. Once that method is
134 * fixed, superclass fields will be found.
136 Field field = refType.fieldByName(name);
138 boolean isStatic = field.isStatic();
139 if (((kind == STATIC) && !isStatic) ||
140 ((kind == INSTANCE) && isStatic)) {
145 System.err.println("fieldByName: " + refType.name() + " " +
153 static List methodsByName(ReferenceType refType, String name, int kind) {
154 List list = refType.methodsByName(name);
155 Iterator iter = list.iterator();
156 while (iter.hasNext()) {
157 Method method = (Method)iter.next();
158 boolean isStatic = method.isStatic();
159 if (((kind == STATIC) && !isStatic) ||
160 ((kind == INSTANCE) && isStatic)) {
167 static List primitiveTypeNames = new ArrayList();
169 primitiveTypeNames.add("boolean");
170 primitiveTypeNames.add("byte");
171 primitiveTypeNames.add("char");
172 primitiveTypeNames.add("short");
173 primitiveTypeNames.add("int");
174 primitiveTypeNames.add("long");
175 primitiveTypeNames.add("float");
176 primitiveTypeNames.add("double");
179 static boolean argumentsMatch(List argNames, List arguments) {
180 if (argNames.size() != arguments.size()) {
183 Iterator nameIter = argNames.iterator();
184 Iterator valIter = arguments.iterator();
185 while (nameIter.hasNext()) {
186 String argTypeName = (String)nameIter.next();
187 Value value = (Value)valIter.next();
189 * For now we require exact match
192 // Null values can be passed to any non-primitive argument
193 if (primitiveTypeNames.contains(argTypeName)) {
196 } else if (!argTypeName.equals(value.type().name())) {
203 static Method resolveOverload(List overloads, List arguments)
204 throws ParseException {
205 Iterator iter = overloads.iterator();
206 while (iter.hasNext()) {
207 Method method = (Method)iter.next();
208 List argNames = method.argumentTypeNames();
209 if (argumentsMatch(argNames, arguments)) {
213 throw new ParseException("Arguments match no method");
216 private static class LValueLocal extends LValue {
217 final StackFrame frame;
218 final LocalVariable var;
220 LValueLocal(StackFrame frame, LocalVariable var) {
226 return frame.getValue(var);
229 void setValue0(Value val) throws InvalidTypeException,
230 ClassNotLoadedException {
231 frame.setValue(var, val);
234 void invokeWith(List arguments) throws ParseException {
235 throw new ParseException(var.name() + " is not a method");
239 private static class LValueInstanceMember extends LValue {
240 final ObjectReference obj;
241 final ThreadReference thread;
242 final Field matchingField;
243 final List overloads;
244 Method matchingMethod = null;
245 List methodArguments = null;
247 LValueInstanceMember(Value value,
249 ThreadReference thread) throws ParseException {
250 if (!(value instanceof ObjectReference)) {
251 throw new ParseException(
252 "Cannot access field of primitive type: " + value);
254 this.obj = (ObjectReference)value;
255 this.thread = thread;
256 ReferenceType refType = obj.referenceType();
258 * Can't tell yet whether this LValue will be accessed as a
259 * field or method, so we keep track of all the possibilities
261 matchingField = LValue.fieldByName(refType, memberName,
263 overloads = LValue.methodsByName(refType, memberName,
265 if ((matchingField == null) && overloads.size() == 0) {
266 throw new ParseException("No instance field or method with the name "
267 + memberName + " in " + refType.name());
271 Value getValue() throws InvocationException, InvalidTypeException,
272 ClassNotLoadedException, IncompatibleThreadStateException {
273 if (matchingMethod == null) {
274 return obj.getValue(matchingField);
276 return obj.invokeMethod(thread, matchingMethod, methodArguments, 0);
280 void setValue0(Value val) throws ParseException,
281 InvalidTypeException,
282 ClassNotLoadedException {
283 if (matchingMethod != null) {
284 throw new ParseException("Cannot assign to a method invocation");
286 obj.setValue(matchingField, val);
289 void invokeWith(List arguments) throws ParseException {
290 if (matchingMethod != null) {
291 throw new ParseException("Invalid consecutive invocations");
293 methodArguments = arguments;
294 matchingMethod = LValue.resolveOverload(overloads, arguments);
298 private static class LValueStaticMember extends LValue {
299 final ReferenceType refType;
300 final ThreadReference thread;
301 final Field matchingField;
302 final List overloads;
303 Method matchingMethod = null;
304 List methodArguments = null;
306 LValueStaticMember(ReferenceType refType,
308 ThreadReference thread) throws ParseException {
309 this.refType = refType;
310 this.thread = thread;
312 * Can't tell yet whether this LValue will be accessed as a
313 * field or method, so we keep track of all the possibilities
315 matchingField = LValue.fieldByName(refType, memberName,
317 overloads = LValue.methodsByName(refType, memberName,
319 if ((matchingField == null) && overloads.size() == 0) {
320 throw new ParseException("No static field or method with the name "
321 + memberName + " in " + refType.name());
325 Value getValue() throws InvocationException, InvalidTypeException,
326 ClassNotLoadedException, IncompatibleThreadStateException {
327 if (matchingMethod == null) {
328 return refType.getValue(matchingField);
329 } else if (refType instanceof ClassType) {
330 ClassType clazz = (ClassType)refType;
331 return clazz.invokeMethod(thread, matchingMethod, methodArguments, 0);
333 throw new InvalidTypeException("Cannot invoke static method on " +
338 void setValue0(Value val)
339 throws ParseException, InvalidTypeException,
340 ClassNotLoadedException {
341 if (matchingMethod != null) {
342 throw new ParseException("Cannot assign to a method invocation");
344 if (!(refType instanceof ClassType)) {
345 throw new ParseException(
346 "Cannot set interface field: " + refType);
348 ((ClassType)refType).setValue(matchingField, val);
351 void invokeWith(List arguments) throws ParseException {
352 if (matchingMethod != null) {
353 throw new ParseException("Invalid consecutive invocations");
355 methodArguments = arguments;
356 matchingMethod = LValue.resolveOverload(overloads, arguments);
360 private static class LValueArrayElement extends LValue {
361 final ArrayReference array;
364 LValueArrayElement(Value value, int index) throws ParseException {
365 if (!(value instanceof ArrayReference)) {
366 throw new ParseException(
367 "Must be array type: " + value);
369 this.array = (ArrayReference)value;
374 return array.getValue(index);
377 void setValue0(Value val) throws InvalidTypeException,
378 ClassNotLoadedException {
379 array.setValue(index, val);
382 void invokeWith(List arguments) throws ParseException {
383 throw new ParseException("Array element is not a method");
387 private static class LValueConstant extends LValue {
390 LValueConstant(Value value) {
398 void setValue0(Value val) throws ParseException {
399 throw new ParseException("Cannot set constant: " + value);
402 void invokeWith(List arguments) throws ParseException {
403 throw new ParseException("Constant is not a method");
407 static LValue make(VirtualMachine vm, boolean val) {
408 return new LValueConstant(vm.mirrorOf(val));
411 static LValue make(VirtualMachine vm, byte val) {
412 return new LValueConstant(vm.mirrorOf(val));
415 static LValue make(VirtualMachine vm, char val) {
416 return new LValueConstant(vm.mirrorOf(val));
419 static LValue make(VirtualMachine vm, short val) {
420 return new LValueConstant(vm.mirrorOf(val));
423 static LValue make(VirtualMachine vm, int val) {
424 return new LValueConstant(vm.mirrorOf(val));
427 static LValue make(VirtualMachine vm, long val) {
428 return new LValueConstant(vm.mirrorOf(val));
431 static LValue make(VirtualMachine vm, float val) {
432 return new LValueConstant(vm.mirrorOf(val));
435 static LValue make(VirtualMachine vm, double val) {
436 return new LValueConstant(vm.mirrorOf(val));
439 static LValue make(VirtualMachine vm, String val) throws ParseException {
440 return new LValueConstant(vm.mirrorOf(val));
443 static LValue makeBoolean(VirtualMachine vm, Token token) {
444 return make(vm, token.image.charAt(0) == 't');
447 static LValue makeCharacter(VirtualMachine vm, Token token) {
448 return make(vm, token.image.charAt(1));
451 static LValue makeFloat(VirtualMachine vm, Token token) {
452 return make(vm, Float.valueOf(token.image).floatValue());
455 static LValue makeDouble(VirtualMachine vm, Token token) {
456 return make(vm, Double.valueOf(token.image).doubleValue());
459 static LValue makeInteger(VirtualMachine vm, Token token) {
460 return make(vm, Integer.parseInt(token.image));
463 static LValue makeShort(VirtualMachine vm, Token token) {
464 return make(vm, Short.parseShort(token.image));
467 static LValue makeLong(VirtualMachine vm, Token token) {
468 return make(vm, Long.parseLong(token.image));
471 static LValue makeByte(VirtualMachine vm, Token token) {
472 return make(vm, Byte.parseByte(token.image));
475 static LValue makeString(VirtualMachine vm,
476 Token token) throws ParseException {
477 int len = token.image.length();
478 return make(vm, token.image.substring(1,len-1));
481 static LValue makeNull(VirtualMachine vm,
482 Token token) throws ParseException {
483 return new LValueConstant(null);
486 static LValue makeThisObject(VirtualMachine vm,
487 ExpressionParser.GetFrame frameGetter,
488 Token token) throws ParseException {
489 if (frameGetter == null) {
490 throw new ParseException("No current thread");
493 StackFrame frame = frameGetter.get();
494 ObjectReference thisObject = frame.thisObject();
495 if (thisObject == null) {
496 throw new ParseException(
497 "No 'this'. In native or static method");
499 return new LValueConstant(thisObject);
501 } catch (IncompatibleThreadStateException exc) {
502 throw new ParseException("Thread not suspended");
507 static LValue makeNewObject(VirtualMachine vm,
508 ExpressionParser.GetFrame frameGetter,
509 String className, List arguments) throws ParseException {
510 List classes = vm.classesByName(className);
511 if (classes.size() == 0) {
512 throw new ParseException("No class named: " + className);
515 if (classes.size() > 1) {
516 throw new ParseException("More than one class named: " +
519 ReferenceType refType = (ReferenceType)classes.get(0);
522 if (!(refType instanceof ClassType)) {
523 throw new ParseException("Cannot create instance of interface " +
527 ClassType classType = (ClassType)refType;
528 List methods = new ArrayList(classType.methods()); // writable
529 Iterator iter = methods.iterator();
530 while (iter.hasNext()) {
531 Method method = (Method)iter.next();
532 if (!method.isConstructor()) {
536 Method constructor = LValue.resolveOverload(methods, arguments);
538 ObjectReference newObject;
540 ThreadReference thread = frameGetter.get().thread();
541 newObject = classType.newInstance(thread, constructor, arguments, 0);
542 } catch (InvocationException ie) {
543 throw new ParseException("Exception in " + className + " constructor: " +
544 ie.exception().referenceType().name());
545 } catch (IncompatibleThreadStateException exc) {
546 throw new ParseException("Thread not suspended");
547 } catch (Exception e) {
549 * TO DO: Better error handling
551 throw new ParseException("Unable to create " + className + " instance");
553 return new LValueConstant(newObject);
556 private static LValue nFields(LValue lval,
557 StringTokenizer izer,
558 ThreadReference thread)
559 throws ParseException {
560 if (!izer.hasMoreTokens()) {
563 return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread);
567 static LValue makeName(VirtualMachine vm,
568 ExpressionParser.GetFrame frameGetter,
569 String name) throws ParseException {
570 StringTokenizer izer = new StringTokenizer(name, ".");
571 String first = izer.nextToken();
573 // check local variables
574 if (frameGetter != null) {
576 StackFrame frame = frameGetter.get();
577 ThreadReference thread = frame.thread();
580 var = frame.visibleVariableByName(first);
581 } catch (AbsentInformationException e) {
585 return nFields(new LValueLocal(frame, var), izer, thread);
587 ObjectReference thisObject = frame.thisObject();
588 if (thisObject != null) {
589 // check if it is a field of 'this'
590 LValue thisLValue = new LValueConstant(thisObject);
593 fv = thisLValue.memberLValue(first, thread);
594 } catch (ParseException exc) {
598 return nFields(fv, izer, thread);
602 // check for class name
603 while (izer.hasMoreTokens()) {
604 List classes = vm.classesByName(first);
605 if (classes.size() > 0) {
606 if (classes.size() > 1) {
607 throw new ParseException("More than one class named: " +
610 ReferenceType refType = (ReferenceType)classes.get(0);
611 LValue lval = new LValueStaticMember(refType,
612 izer.nextToken(), thread);
613 return nFields(lval, izer, thread);
616 first = first + '.' + izer.nextToken();
618 } catch (IncompatibleThreadStateException exc) {
619 throw new ParseException("Thread not suspended");
622 throw new ParseException("Name unknown: " + name);
625 static String stringValue(Value val) {
626 if (val instanceof StringReference) {
627 return ((StringReference)val).value();
628 } else if (val instanceof ObjectReference) {
629 return ((ObjectReference)val).toString(); // TODO
630 } else if (val == null) {
633 return val.toString(); // TODO not correct in all cases
637 static LValue booleanOperation(VirtualMachine vm, Token token,
639 LValue leftL) throws ParseException {
640 String op = token.image;
641 Value right = rightL.interiorGetValue();
642 Value left = leftL.interiorGetValue();
643 if ( !(right instanceof PrimitiveValue) ||
644 !(left instanceof PrimitiveValue) ) {
645 if (op.equals("==")) {
646 return make(vm, right.equals(left));
647 } else if (op.equals("!=")) {
648 return make(vm, !right.equals(left));
650 throw new ParseException("Operands or '" + op +
651 "' must be primitive");
654 // can compare any numeric doubles
655 double rr = ((PrimitiveValue)right).doubleValue();
656 double ll = ((PrimitiveValue)left).doubleValue();
658 if (op.equals("<")) {
660 } else if (op.equals(">")) {
662 } else if (op.equals("<=")) {
664 } else if (op.equals(">=")) {
666 } else if (op.equals("==")) {
668 } else if (op.equals("!=")) {
671 throw new ParseException("Unknown operation: " + op);
673 return make(vm, res);
676 static LValue operation(VirtualMachine vm, Token token,
678 LValue leftL) throws ParseException {
679 String op = token.image;
680 Value right = rightL.interiorGetValue();
681 Value left = leftL.interiorGetValue();
682 if ((right instanceof StringReference) ||
683 (left instanceof StringReference)) {
684 if (op.equals("+")) {
685 return make(vm, stringValue(right) + stringValue(left));
688 if ((right instanceof ObjectReference) ||
689 (left instanceof ObjectReference)) {
690 if (op.equals("==")) {
691 return make(vm, right.equals(left));
692 } else if (op.equals("!=")) {
693 return make(vm, !right.equals(left));
695 throw new ParseException("Invalid operation '" +
696 op + "' on an Object");
699 if ((right instanceof BooleanValue) ||
700 (left instanceof BooleanValue)) {
701 throw new ParseException("Invalid operation '" +
702 op + "' on a Boolean");
704 // from here on, we know it is a integer kind of type
705 PrimitiveValue primRight = (PrimitiveValue)right;
706 PrimitiveValue primLeft = (PrimitiveValue)left;
707 if ((primRight instanceof DoubleValue) ||
708 (primLeft instanceof DoubleValue)) {
709 double rr = primRight.doubleValue();
710 double ll = primLeft.doubleValue();
712 if (op.equals("+")) {
714 } else if (op.equals("-")) {
716 } else if (op.equals("*")) {
718 } else if (op.equals("/")) {
721 throw new ParseException("Unknown operation: " + op);
723 return make(vm, res);
725 if ((primRight instanceof FloatValue) ||
726 (primLeft instanceof FloatValue)) {
727 float rr = primRight.floatValue();
728 float ll = primLeft.floatValue();
730 if (op.equals("+")) {
732 } else if (op.equals("-")) {
734 } else if (op.equals("*")) {
736 } else if (op.equals("/")) {
739 throw new ParseException("Unknown operation: " + op);
741 return make(vm, res);
743 if ((primRight instanceof LongValue) ||
744 (primLeft instanceof LongValue)) {
745 long rr = primRight.longValue();
746 long ll = primLeft.longValue();
748 if (op.equals("+")) {
750 } else if (op.equals("-")) {
752 } else if (op.equals("*")) {
754 } else if (op.equals("/")) {
757 throw new ParseException("Unknown operation: " + op);
759 return make(vm, res);
761 int rr = primRight.intValue();
762 int ll = primLeft.intValue();
764 if (op.equals("+")) {
766 } else if (op.equals("-")) {
768 } else if (op.equals("*")) {
770 } else if (op.equals("/")) {
773 throw new ParseException("Unknown operation: " + op);
775 return make(vm, res);