jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java
changeset 2 90ce3da70b43
child 51 6fe31bc95bbc
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     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 }