1 /*
   2  * Copyright (c) 1998, 2003, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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<Method> methodsByName(ReferenceType refType,
 195                                       String name, int kind) {
 196         List<Method> list = refType.methodsByName(name);
 197         Iterator<Method> iter = list.iterator();
 198         while (iter.hasNext()) {
 199             Method method = iter.next();
 200             boolean isStatic = method.isStatic();
 201             if (((kind == STATIC) && !isStatic) ||
 202                 ((kind == INSTANCE) && isStatic)) {
 203                 iter.remove();
 204             }
 205         }
 206         return list;
 207     }
 208 
 209     static List<String> primitiveTypeNames = new ArrayList<String>();
 210     static {
 211         primitiveTypeNames.add("boolean");
 212         primitiveTypeNames.add("byte");
 213         primitiveTypeNames.add("char");
 214         primitiveTypeNames.add("short");
 215         primitiveTypeNames.add("int");
 216         primitiveTypeNames.add("long");
 217         primitiveTypeNames.add("float");
 218         primitiveTypeNames.add("double");
 219     }
 220 
 221 
 222     static final int SAME = 0;
 223     static final int ASSIGNABLE = 1;
 224     static final int DIFFERENT = 2;
 225     /*
 226      * Return SAME, DIFFERENT or ASSIGNABLE.
 227      * SAME means each arg type is the same as type of the corr. arg.
 228      * ASSIGNABLE means that not all the pairs are the same, but
 229      * for those that aren't, at least the argType is assignable
 230      * from the type of the argument value.
 231      * DIFFERENT means that in at least one pair, the
 232      * argType is not assignable from the type of the argument value.
 233      * IE, one is an Apple and the other is an Orange.
 234      */
 235     static int argumentsMatch(List<Type> argTypes, List<Value> arguments) {
 236         if (argTypes.size() != arguments.size()) {
 237             return DIFFERENT;
 238         }
 239 
 240         Iterator<Type> typeIter = argTypes.iterator();
 241         Iterator<Value> valIter = arguments.iterator();
 242         int result = SAME;
 243 
 244         // If any pair aren't the same, change the
 245         // result to ASSIGNABLE.  If any pair aren't
 246         // assignable, return DIFFERENT
 247         while (typeIter.hasNext()) {
 248             Type argType = typeIter.next();
 249             Value value = valIter.next();
 250             if (value == null) {
 251                 // Null values can be passed to any non-primitive argument
 252                 if (primitiveTypeNames.contains(argType.name())) {
 253                     return DIFFERENT;
 254                 }
 255                 // Else, we will assume that a null value
 256                 // exactly matches an object type.
 257             }
 258             if (!value.type().equals(argType)) {
 259                 if (isAssignableTo(value.type(), argType)) {
 260                     result = ASSIGNABLE;
 261                 } else {
 262                     return DIFFERENT;
 263                 }
 264             }
 265         }
 266         return result;
 267     }
 268 
 269 
 270     // These is...AssignableTo methods are based on similar code in the JDI
 271     // implementations of ClassType, ArrayType, and InterfaceType
 272 
 273     static boolean isComponentAssignable(Type fromType, Type toType) {
 274         if (fromType instanceof PrimitiveType) {
 275             // Assignment of primitive arrays requires identical
 276             // component types.
 277             return fromType.equals(toType);
 278         }
 279         if (toType instanceof PrimitiveType) {
 280             return false;
 281         }
 282         // Assignment of object arrays requires availability
 283         // of widening conversion of component types
 284         return isAssignableTo(fromType, toType);
 285     }
 286 
 287     static boolean isArrayAssignableTo(ArrayType fromType, Type toType) {
 288         if (toType instanceof ArrayType) {
 289             try {
 290                 Type toComponentType = ((ArrayType)toType).componentType();
 291                 return isComponentAssignable(fromType.componentType(), toComponentType);
 292             } catch (ClassNotLoadedException e) {
 293                 // One or both component types has not yet been
 294                 // loaded => can't assign
 295                 return false;
 296             }
 297         }
 298         if (toType instanceof InterfaceType) {
 299             // Only valid InterfaceType assignee is Cloneable
 300             return toType.name().equals("java.lang.Cloneable");
 301         }
 302         // Only valid ClassType assignee is Object
 303         return toType.name().equals("java.lang.Object");
 304     }
 305 
 306     static boolean isAssignableTo(Type fromType, Type toType) {
 307         if (fromType.equals(toType)) {
 308             return true;
 309         }
 310 
 311         // If one is boolean, so must be the other.
 312         if (fromType instanceof BooleanType) {
 313             if (toType instanceof BooleanType) {
 314                 return true;
 315             }
 316             return false;
 317         }
 318         if (toType instanceof BooleanType) {
 319             return false;
 320         }
 321 
 322         // Other primitive types are intermixable only with each other.
 323         if (fromType instanceof PrimitiveType) {
 324             if (toType instanceof PrimitiveType) {
 325                 return true;
 326             }
 327             return false;
 328         }
 329         if (toType instanceof PrimitiveType) {
 330             return false;
 331         }
 332 
 333         // neither one is primitive.
 334         if (fromType instanceof ArrayType) {
 335             return isArrayAssignableTo((ArrayType)fromType, toType);
 336         }
 337         List<InterfaceType> interfaces;
 338         if (fromType instanceof ClassType) {
 339             ClassType superclazz = ((ClassType)fromType).superclass();
 340             if ((superclazz != null) && isAssignableTo(superclazz, toType)) {
 341                 return true;
 342             }
 343             interfaces = ((ClassType)fromType).interfaces();
 344         } else {
 345             // fromType must be an InterfaceType
 346             interfaces = ((InterfaceType)fromType).superinterfaces();
 347         }
 348         for (InterfaceType interfaze : interfaces) {
 349             if (isAssignableTo(interfaze, toType)) {
 350                 return true;
 351             }
 352         }
 353         return false;
 354     }
 355 
 356     static Method resolveOverload(List<Method> overloads,
 357                                   List<Value> 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 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         Method retVal = null;
 378         int assignableCount = 0;
 379         for (Method mm : overloads) {
 380             List<Type> argTypes;
 381             try {
 382                 argTypes = mm.argumentTypes();
 383             } catch (ClassNotLoadedException ee) {
 384                 // This probably won't happen for the
 385                 // method that we are really supposed to
 386                 // call.
 387                 continue;
 388             }
 389             int compare = argumentsMatch(argTypes, arguments);
 390             if (compare == SAME) {
 391                 return mm;
 392             }
 393             if (compare == DIFFERENT) {
 394                 continue;
 395             }
 396             // Else, it is assignable.  Remember it.
 397             retVal = mm;
 398             assignableCount++;
 399         }
 400 
 401         // At this point, we didn't find an exact match,
 402         // but we found one for which the args are assignable.
 403         //
 404         if (retVal != null) {
 405             if (assignableCount == 1) {
 406                 return retVal;
 407             }
 408             throw new ParseException("Arguments match multiple methods");
 409         }
 410         throw new ParseException("Arguments match no method");
 411     }
 412 
 413     private static class LValueLocal extends LValue {
 414         final StackFrame frame;
 415         final LocalVariable var;
 416 
 417         LValueLocal(StackFrame frame, LocalVariable var) {
 418             this.frame = frame;
 419             this.var = var;
 420         }
 421 
 422         Value getValue() {
 423             if (jdiValue == null) {
 424                 jdiValue = frame.getValue(var);
 425             }
 426             return jdiValue;
 427         }
 428 
 429         void setValue0(Value val) throws InvalidTypeException,
 430                                          ClassNotLoadedException {
 431             frame.setValue(var, val);
 432             jdiValue = val;
 433         }
 434 
 435         void invokeWith(List<Value> arguments) throws ParseException {
 436             throw new ParseException(var.name() + " is not a method");
 437         }
 438     }
 439 
 440     private static class LValueInstanceMember extends LValue {
 441         final ObjectReference obj;
 442         final ThreadReference thread;
 443         final Field matchingField;
 444         final List<Method> overloads;
 445         Method matchingMethod = null;
 446         List<Value> methodArguments = null;
 447 
 448         LValueInstanceMember(Value value,
 449                             String memberName,
 450                             ThreadReference thread) throws ParseException {
 451             if (!(value instanceof ObjectReference)) {
 452                 throw new ParseException(
 453                        "Cannot access field of primitive type: " + value);
 454             }
 455             this.obj = (ObjectReference)value;
 456             this.thread = thread;
 457             ReferenceType refType = obj.referenceType();
 458             /*
 459              * Can't tell yet whether this LValue will be accessed as a
 460              * field or method, so we keep track of all the possibilities
 461              */
 462             matchingField = LValue.fieldByName(refType, memberName,
 463                                                LValue.INSTANCE);
 464             overloads = LValue.methodsByName(refType, memberName,
 465                                               LValue.INSTANCE);
 466             if ((matchingField == null) && overloads.size() == 0) {
 467                 throw new ParseException("No instance field or method with the name "
 468                                + memberName + " in " + refType.name());
 469             }
 470         }
 471 
 472         Value getValue() throws InvocationException, InvalidTypeException,
 473                                 ClassNotLoadedException, IncompatibleThreadStateException,
 474                                 ParseException {
 475             if (jdiValue != null) {
 476                 return jdiValue;
 477             }
 478             if (matchingMethod == null) {
 479                 if (matchingField == null) {
 480                     throw new ParseException("No such field in " + obj.referenceType().name());
 481                 }
 482                 return jdiValue = obj.getValue(matchingField);
 483             } else {
 484                 return jdiValue = obj.invokeMethod(thread, matchingMethod, methodArguments, 0);
 485             }
 486         }
 487 
 488         void setValue0(Value val) throws ParseException,
 489                                          InvalidTypeException,
 490                                         ClassNotLoadedException {
 491             if (matchingMethod != null) {
 492                 throw new ParseException("Cannot assign to a method invocation");
 493             }
 494             obj.setValue(matchingField, val);
 495             jdiValue = val;
 496         }
 497 
 498         void invokeWith(List<Value> arguments) throws ParseException {
 499             if (matchingMethod != null) {
 500                 throw new ParseException("Invalid consecutive invocations");
 501             }
 502             methodArguments = arguments;
 503             matchingMethod = LValue.resolveOverload(overloads, arguments);
 504         }
 505     }
 506 
 507     private static class LValueStaticMember extends LValue {
 508         final ReferenceType refType;
 509         final ThreadReference thread;
 510         final Field matchingField;
 511         final List<Method> overloads;
 512         Method matchingMethod = null;
 513         List<Value> methodArguments = null;
 514 
 515         LValueStaticMember(ReferenceType refType,
 516                           String memberName,
 517                           ThreadReference thread) throws ParseException {
 518             this.refType = refType;
 519             this.thread = thread;
 520             /*
 521              * Can't tell yet whether this LValue will be accessed as a
 522              * field or method, so we keep track of all the possibilities
 523              */
 524             matchingField = LValue.fieldByName(refType, memberName,
 525                                                LValue.STATIC);
 526             overloads = LValue.methodsByName(refType, memberName,
 527                                               LValue.STATIC);
 528             if ((matchingField == null) && overloads.size() == 0) {
 529                 throw new ParseException("No static field or method with the name "
 530                                + memberName + " in " + refType.name());
 531             }
 532         }
 533 
 534         Value getValue() throws InvocationException, InvalidTypeException,
 535                                 ClassNotLoadedException, IncompatibleThreadStateException,
 536                                 ParseException {
 537             if (jdiValue != null) {
 538                 return jdiValue;
 539             }
 540             if (matchingMethod == null) {
 541                 return jdiValue = refType.getValue(matchingField);
 542             } else if (refType instanceof ClassType) {
 543                 ClassType clazz = (ClassType)refType;
 544                 return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0);
 545             } else {
 546                 throw new InvalidTypeException("Cannot invoke static method on " +
 547                                          refType.name());
 548             }
 549         }
 550 
 551         void setValue0(Value val)
 552                            throws ParseException, InvalidTypeException,
 553                                   ClassNotLoadedException {
 554             if (matchingMethod != null) {
 555                 throw new ParseException("Cannot assign to a method invocation");
 556             }
 557             if (!(refType instanceof ClassType)) {
 558                 throw new ParseException(
 559                        "Cannot set interface field: " + refType);
 560             }
 561             ((ClassType)refType).setValue(matchingField, val);
 562             jdiValue = val;
 563         }
 564 
 565         void invokeWith(List<Value> arguments) throws ParseException {
 566             if (matchingMethod != null) {
 567                 throw new ParseException("Invalid consecutive invocations");
 568             }
 569             methodArguments = arguments;
 570             matchingMethod = LValue.resolveOverload(overloads, arguments);
 571         }
 572     }
 573 
 574     private static class LValueArrayLength extends LValue {
 575         /*
 576          * Since one can code "int myLen = myArray.length;",
 577          * one might expect that these JDI calls would get a Value
 578          * object for the length of an array in the debugee:
 579          *    Field xxx = ArrayType.fieldByName("length")
 580          *    Value lenVal= ArrayReference.getValue(xxx)
 581          *
 582          * However, this doesn't work because the array length isn't
 583          * really stored as a field, and can't be accessed as such
 584          * via JDI.  Instead, the arrayRef.length() method has to be
 585          * used.
 586          */
 587         final ArrayReference arrayRef;
 588         LValueArrayLength (ArrayReference value) {
 589             this.arrayRef = value;
 590         }
 591 
 592         Value getValue() {
 593             if (jdiValue == null) {
 594                 jdiValue = arrayRef.virtualMachine().mirrorOf(arrayRef.length());
 595             }
 596             return jdiValue;
 597         }
 598 
 599         void setValue0(Value value) throws ParseException  {
 600             throw new ParseException("Cannot set constant: " + value);
 601         }
 602 
 603         void invokeWith(List<Value> arguments) throws ParseException {
 604             throw new ParseException("Array element is not a method");
 605         }
 606     }
 607 
 608     private static class LValueArrayElement extends LValue {
 609         final ArrayReference array;
 610         final int index;
 611 
 612         LValueArrayElement(Value value, int index) throws ParseException {
 613             if (!(value instanceof ArrayReference)) {
 614                 throw new ParseException(
 615                        "Must be array type: " + value);
 616             }
 617             this.array = (ArrayReference)value;
 618             this.index = index;
 619         }
 620 
 621         Value getValue() {
 622             if (jdiValue == null) {
 623                 jdiValue = array.getValue(index);
 624             }
 625             return jdiValue;
 626         }
 627 
 628         void setValue0(Value val) throws InvalidTypeException,
 629                                          ClassNotLoadedException  {
 630             array.setValue(index, val);
 631             jdiValue = val;
 632         }
 633 
 634         void invokeWith(List<Value> arguments) throws ParseException {
 635             throw new ParseException("Array element is not a method");
 636         }
 637     }
 638 
 639     private static class LValueConstant extends LValue {
 640         final Value value;
 641 
 642         LValueConstant(Value value) {
 643             this.value = value;
 644         }
 645 
 646         Value getValue() {
 647             if (jdiValue == null) {
 648                 jdiValue = value;
 649             }
 650             return jdiValue;
 651         }
 652 
 653         void setValue0(Value val) throws ParseException {
 654             throw new ParseException("Cannot set constant: " + value);
 655         }
 656 
 657         void invokeWith(List<Value> arguments) throws ParseException {
 658             throw new ParseException("Constant is not a method");
 659         }
 660     }
 661 
 662     static LValue make(VirtualMachine vm, boolean val) {
 663         return new LValueConstant(vm.mirrorOf(val));
 664     }
 665 
 666     static LValue make(VirtualMachine vm, byte val) {
 667         return new LValueConstant(vm.mirrorOf(val));
 668     }
 669 
 670     static LValue make(VirtualMachine vm, char val) {
 671         return new LValueConstant(vm.mirrorOf(val));
 672     }
 673 
 674     static LValue make(VirtualMachine vm, short val) {
 675         return new LValueConstant(vm.mirrorOf(val));
 676     }
 677 
 678     static LValue make(VirtualMachine vm, int val) {
 679         return new LValueConstant(vm.mirrorOf(val));
 680     }
 681 
 682     static LValue make(VirtualMachine vm, long val) {
 683         return new LValueConstant(vm.mirrorOf(val));
 684     }
 685 
 686     static LValue make(VirtualMachine vm, float val) {
 687         return new LValueConstant(vm.mirrorOf(val));
 688     }
 689 
 690     static LValue make(VirtualMachine vm, double val) {
 691         return new LValueConstant(vm.mirrorOf(val));
 692     }
 693 
 694     static LValue make(VirtualMachine vm, String val) throws ParseException {
 695         return new LValueConstant(vm.mirrorOf(val));
 696     }
 697 
 698     static LValue makeBoolean(VirtualMachine vm, Token token) {
 699         return make(vm, token.image.charAt(0) == 't');
 700     }
 701 
 702     static LValue makeCharacter(VirtualMachine vm, Token token) {
 703         return make(vm, token.image.charAt(1));
 704     }
 705 
 706     static LValue makeFloat(VirtualMachine vm, Token token) {
 707         return make(vm, Float.valueOf(token.image).floatValue());
 708     }
 709 
 710     static LValue makeDouble(VirtualMachine vm, Token token) {
 711         return make(vm, Double.valueOf(token.image).doubleValue());
 712     }
 713 
 714     static LValue makeInteger(VirtualMachine vm, Token token) {
 715         return make(vm, Integer.parseInt(token.image));
 716     }
 717 
 718     static LValue makeShort(VirtualMachine vm, Token token) {
 719         return make(vm, Short.parseShort(token.image));
 720     }
 721 
 722     static LValue makeLong(VirtualMachine vm, Token token) {
 723         return make(vm, Long.parseLong(token.image));
 724     }
 725 
 726     static LValue makeByte(VirtualMachine vm, Token token) {
 727         return make(vm, Byte.parseByte(token.image));
 728     }
 729 
 730     static LValue makeString(VirtualMachine vm,
 731                              Token token) throws ParseException {
 732         int len = token.image.length();
 733         return make(vm, token.image.substring(1,len-1));
 734     }
 735 
 736     static LValue makeNull(VirtualMachine vm,
 737                            Token token) throws ParseException {
 738         return new LValueConstant(null);
 739     }
 740 
 741     static LValue makeThisObject(VirtualMachine vm,
 742                                  ExpressionParser.GetFrame frameGetter,
 743                                  Token token) throws ParseException {
 744         if (frameGetter == null) {
 745             throw new ParseException("No current thread");
 746         } else {
 747             try {
 748                 StackFrame frame = frameGetter.get();
 749                 ObjectReference thisObject = frame.thisObject();
 750 
 751                 if (thisObject==null) {
 752                         throw new ParseException(
 753                             "No 'this'.  In native or static method");
 754                 } else {
 755                         return new LValueConstant(thisObject);
 756                 }
 757             } catch (IncompatibleThreadStateException exc) {
 758                 throw new ParseException("Thread not suspended");
 759             }
 760         }
 761     }
 762 
 763     static LValue makeNewObject(VirtualMachine vm,
 764                                  ExpressionParser.GetFrame frameGetter,
 765                                 String className, List<Value> arguments) throws ParseException {
 766         List<ReferenceType> classes = vm.classesByName(className);
 767         if (classes.size() == 0) {
 768             throw new ParseException("No class named: " + className);
 769         }
 770 
 771         if (classes.size() > 1) {
 772             throw new ParseException("More than one class named: " +
 773                                      className);
 774         }
 775         ReferenceType refType = classes.get(0);
 776 
 777 
 778         if (!(refType instanceof ClassType)) {
 779             throw new ParseException("Cannot create instance of interface " +
 780                                      className);
 781         }
 782 
 783         ClassType classType = (ClassType)refType;
 784         List<Method> methods = new ArrayList<Method>(classType.methods()); // writable
 785         Iterator<Method> iter = methods.iterator();
 786         while (iter.hasNext()) {
 787             Method method = iter.next();
 788             if (!method.isConstructor()) {
 789                 iter.remove();
 790             }
 791         }
 792         Method constructor = LValue.resolveOverload(methods, arguments);
 793 
 794         ObjectReference newObject;
 795         try {
 796             ThreadReference thread = frameGetter.get().thread();
 797             newObject = classType.newInstance(thread, constructor, arguments, 0);
 798         } catch (InvocationException ie) {
 799             throw new ParseException("Exception in " + className + " constructor: " +
 800                                      ie.exception().referenceType().name());
 801         } catch (IncompatibleThreadStateException exc) {
 802             throw new ParseException("Thread not suspended");
 803         } catch (Exception e) {
 804             /*
 805              * TO DO: Better error handling
 806              */
 807             throw new ParseException("Unable to create " + className + " instance");
 808         }
 809         return new LValueConstant(newObject);
 810     }
 811 
 812     private static LValue nFields(LValue lval,
 813                                   StringTokenizer izer,
 814                                   ThreadReference thread)
 815                                           throws ParseException {
 816         if (!izer.hasMoreTokens()) {
 817             return lval;
 818         } else {
 819             return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread);
 820         }
 821     }
 822 
 823     static LValue makeName(VirtualMachine vm,
 824                            ExpressionParser.GetFrame frameGetter,
 825                            String name) throws ParseException {
 826         StringTokenizer izer = new StringTokenizer(name, ".");
 827         String first = izer.nextToken();
 828         // check local variables
 829         if (frameGetter != null) {
 830             try {
 831                 StackFrame frame = frameGetter.get();
 832                 ThreadReference thread = frame.thread();
 833                 LocalVariable var;
 834                 try {
 835                     var = frame.visibleVariableByName(first);
 836                 } catch (AbsentInformationException e) {
 837                     var = null;
 838                 }
 839                 if (var != null) {
 840                     return nFields(new LValueLocal(frame, var), izer, thread);
 841                 } else {
 842                     ObjectReference thisObject = frame.thisObject();
 843                     if (thisObject != null) {
 844                         // check if it is a field of 'this'
 845                         LValue thisLValue = new LValueConstant(thisObject);
 846                         LValue fv;
 847                         try {
 848                             fv = thisLValue.memberLValue(first, thread);
 849                         } catch (ParseException exc) {
 850                             fv = null;
 851                         }
 852                         if (fv != null) {
 853                             return nFields(fv, izer, thread);
 854                         }
 855                     }
 856                 }
 857                 // check for class name
 858                 while (izer.hasMoreTokens()) {
 859                     List<ReferenceType> classes = vm.classesByName(first);
 860                     if (classes.size() > 0) {
 861                         if (classes.size() > 1) {
 862                             throw new ParseException("More than one class named: " +
 863                                                      first);
 864                         } else {
 865                             ReferenceType refType = classes.get(0);
 866                             LValue lval = new LValueStaticMember(refType,
 867                                                             izer.nextToken(), thread);
 868                             return nFields(lval, izer, thread);
 869                         }
 870                     }
 871                     first = first + '.' + izer.nextToken();
 872                 }
 873             } catch (IncompatibleThreadStateException exc) {
 874                 throw new ParseException("Thread not suspended");
 875             }
 876         }
 877         throw new ParseException("Name unknown: " + name);
 878     }
 879 
 880     static String stringValue(LValue lval, ExpressionParser.GetFrame frameGetter
 881                               ) throws ParseException {
 882         Value val = lval.getMassagedValue(frameGetter);
 883         if (val == null) {
 884             return "null";
 885         }
 886         if (val instanceof StringReference) {
 887             return ((StringReference)val).value();
 888         }
 889         return val.toString();  // is this correct in all cases?
 890     }
 891 
 892     static LValue booleanOperation(VirtualMachine vm, Token token,
 893                             LValue rightL,
 894                             LValue leftL) throws ParseException {
 895         String op = token.image;
 896         Value right = rightL.interiorGetValue();
 897         Value left = leftL.interiorGetValue();
 898         if ( !(right instanceof PrimitiveValue) ||
 899              !(left instanceof PrimitiveValue) ) {
 900             if (op.equals("==")) {
 901                 return make(vm, right.equals(left));
 902             } else if (op.equals("!=")) {
 903                 return make(vm, !right.equals(left));
 904             } else {
 905                 throw new ParseException("Operands or '" + op +
 906                                      "' must be primitive");
 907             }
 908         }
 909         // can compare any numeric doubles
 910         double rr = ((PrimitiveValue)right).doubleValue();
 911         double ll = ((PrimitiveValue)left).doubleValue();
 912         boolean res;
 913         if (op.equals("<")) {
 914             res = rr < ll;
 915         } else 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 {
 926             throw new ParseException("Unknown operation: " + op);
 927         }
 928         return make(vm, res);
 929     }
 930 
 931     static LValue operation(VirtualMachine vm, Token token,
 932                             LValue rightL, LValue leftL,
 933                             ExpressionParser.GetFrame frameGetter
 934                             ) throws ParseException {
 935         String op = token.image;
 936         Value right = rightL.interiorGetValue();
 937         Value left = leftL.interiorGetValue();
 938         if ((right instanceof StringReference) ||
 939                               (left instanceof StringReference)) {
 940             if (op.equals("+")) {
 941                 // If one is an ObjectRef, we will need to invoke
 942                 // toString on it, so we need the thread.
 943                 return make(vm, stringValue(rightL, frameGetter) +
 944                             stringValue(leftL, frameGetter));
 945             }
 946         }
 947         if ((right instanceof ObjectReference) ||
 948                               (left instanceof ObjectReference)) {
 949             if (op.equals("==")) {
 950                 return make(vm, right.equals(left));
 951             } else if (op.equals("!=")) {
 952                 return make(vm, !right.equals(left));
 953             } else {
 954                 throw new ParseException("Invalid operation '" +
 955                                          op + "' on an Object");
 956             }
 957         }
 958         if ((right instanceof BooleanValue) ||
 959                               (left instanceof BooleanValue)) {
 960             throw new ParseException("Invalid operation '" +
 961                                      op + "' on a Boolean");
 962         }
 963         // from here on, we know it is a integer kind of type
 964         PrimitiveValue primRight = (PrimitiveValue)right;
 965         PrimitiveValue primLeft = (PrimitiveValue)left;
 966         if ((primRight instanceof DoubleValue) ||
 967                               (primLeft instanceof DoubleValue)) {
 968             double rr = primRight.doubleValue();
 969             double ll = primLeft.doubleValue();
 970             double res;
 971             if (op.equals("+")) {
 972                 res = rr + ll;
 973             } else 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 {
 980                 throw new ParseException("Unknown operation: " + op);
 981             }
 982             return make(vm, res);
 983         }
 984         if ((primRight instanceof FloatValue) ||
 985                               (primLeft instanceof FloatValue)) {
 986             float rr = primRight.floatValue();
 987             float ll = primLeft.floatValue();
 988             float res;
 989             if (op.equals("+")) {
 990                 res = rr + ll;
 991             } else 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 {
 998                 throw new ParseException("Unknown operation: " + op);
 999             }
1000             return make(vm, res);
1001         }
1002         if ((primRight instanceof LongValue) ||
1003                               (primLeft instanceof LongValue)) {
1004             long rr = primRight.longValue();
1005             long ll = primLeft.longValue();
1006             long res;
1007             if (op.equals("+")) {
1008                 res = rr + ll;
1009             } else 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 {
1016                 throw new ParseException("Unknown operation: " + op);
1017             }
1018             return make(vm, res);
1019         } else {
1020             int rr = primRight.intValue();
1021             int ll = primLeft.intValue();
1022             int res;
1023             if (op.equals("+")) {
1024                 res = rr + ll;
1025             } else 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 {
1032                 throw new ParseException("Unknown operation: " + op);
1033             }
1034             return make(vm, res);
1035         }
1036     }
1037 }