jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java
changeset 2 90ce3da70b43
child 51 6fe31bc95bbc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1039 @@
+/*
+ * Copyright 1998-2003 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.example.debug.expr;
+
+import com.sun.jdi.*;
+import java.util.*;
+
+abstract class LValue {
+
+    // The JDI Value object for this LValue.  Once we have this Value,
+    // we have to remember it since after we return the LValue object
+    // to the ExpressionParser, it might decide that it needs
+    // the 'toString' value for the LValue in which case it will
+    // call getMassagedValue to get this toString value.  At that
+    // point, we don't want to call JDI a 2nd time to get the Value
+    // for the LValue.  This is especially wrong when the LValue
+    // represents a member function.  We would end up calling it
+    // a 2nd time.
+    //
+    // Unfortunately, there are several levels of calls to
+    // get/set values in this file.  To minimize confusion,
+    // jdiValue is set/tested at the lowest level - right
+    // next to the actual calls to JDI methods to get/set the
+    // value in the debuggee.
+    protected Value jdiValue;
+
+    abstract Value getValue() throws InvocationException,
+                                     IncompatibleThreadStateException,
+                                     InvalidTypeException,
+                                     ClassNotLoadedException,
+                                     ParseException;
+
+    abstract void setValue0(Value value)
+                   throws ParseException, InvalidTypeException,
+                          ClassNotLoadedException;
+
+    abstract void invokeWith(List<Value> arguments) throws ParseException;
+
+    void setValue(Value value) throws ParseException {
+        try {
+            setValue0(value);
+        } catch (InvalidTypeException exc) {
+            throw new ParseException(
+                "Attempt to set value of incorrect type" +
+                exc);
+        } catch (ClassNotLoadedException exc) {
+            throw new ParseException(
+                "Attempt to set value before " + exc.className() + " was loaded" +
+                exc);
+        }
+    }
+
+    void setValue(LValue lval) throws ParseException {
+        setValue(lval.interiorGetValue());
+    }
+
+    LValue memberLValue(ExpressionParser.GetFrame frameGetter,
+                        String fieldName) throws ParseException {
+        try {
+            return memberLValue(fieldName, frameGetter.get().thread());
+        } catch (IncompatibleThreadStateException exc) {
+            throw new ParseException("Thread not suspended");
+        }
+    }
+
+    LValue memberLValue(String fieldName, ThreadReference thread) throws ParseException {
+
+        Value val = interiorGetValue();
+        if ((val instanceof ArrayReference) &&
+            "length".equals(fieldName)){
+            return new LValueArrayLength((ArrayReference)val);
+        }
+        return new LValueInstanceMember(val, fieldName, thread);
+    }
+
+    // Return the Value for this LValue that would be used to concatenate
+    // to a String.  IE, if it is an Object, call toString in the debuggee.
+    Value getMassagedValue(ExpressionParser.GetFrame frameGetter) throws ParseException {
+        Value vv = interiorGetValue();
+
+        // If vv is an ObjectReference, then we have to
+        // do the implicit call to toString().
+        if (vv instanceof ObjectReference &&
+            !(vv instanceof StringReference) &&
+            !(vv instanceof ArrayReference)) {
+            StackFrame frame;
+            try {
+                frame = frameGetter.get();
+            } catch (IncompatibleThreadStateException exc) {
+                throw new ParseException("Thread not suspended");
+            }
+
+            ThreadReference thread = frame.thread();
+            LValue toStringMember = memberLValue("toString", thread);
+            toStringMember.invokeWith(new ArrayList<Value>());
+            return toStringMember.interiorGetValue();
+        }
+        return vv;
+    }
+
+    Value interiorGetValue() throws ParseException {
+        Value value;
+        try {
+            value = getValue();
+        } catch (InvocationException e) {
+            throw new ParseException("Unable to complete expression. Exception " +
+                                     e.exception() + " thrown");
+        } catch (IncompatibleThreadStateException itse) {
+            throw new ParseException("Unable to complete expression. Thread " +
+                                     "not suspended for method invoke");
+        } catch (InvalidTypeException ite) {
+            throw new ParseException("Unable to complete expression. Method " +
+                                     "argument type mismatch");
+        } catch (ClassNotLoadedException tnle) {
+            throw new ParseException("Unable to complete expression. Method " +
+                                     "argument type " + tnle.className() +
+                                     " not yet loaded");
+        }
+        return value;
+    }
+
+    LValue arrayElementLValue(LValue lval) throws ParseException {
+        Value indexValue = lval.interiorGetValue();
+        int index;
+        if ( (indexValue instanceof IntegerValue) ||
+             (indexValue instanceof ShortValue) ||
+             (indexValue instanceof ByteValue) ||
+             (indexValue instanceof CharValue) ) {
+            index = ((PrimitiveValue)indexValue).intValue();
+        } else {
+            throw new ParseException("Array index must be a integer type");
+        }
+        return new LValueArrayElement(interiorGetValue(), index);
+    }
+
+    public String toString() {
+        try {
+            return interiorGetValue().toString();
+        } catch (ParseException e) {
+            return "<Parse Exception>";
+        }
+    }
+
+    static final int STATIC = 0;
+    static final int INSTANCE = 1;
+
+    static Field fieldByName(ReferenceType refType, String name, int kind) {
+        /*
+         * TO DO: Note that this currently fails to find superclass
+         * or implemented interface fields. This is due to a temporary
+         * limititation of RefType.fieldByName. Once that method is
+         * fixed, superclass fields will be found.
+         */
+        Field field = refType.fieldByName(name);
+        if (field != null) {
+            boolean isStatic = field.isStatic();
+            if (((kind == STATIC) && !isStatic) ||
+                ((kind == INSTANCE) && isStatic)) {
+                field = null;
+            }
+        }
+/***
+        System.err.println("fieldByName: " + refType.name() + " " +
+                                             name + " " +
+                                             kind + " " +
+                                             (field != null));
+***/
+        return field;
+    }
+
+    static List methodsByName(ReferenceType refType, String name, int kind) {
+        List list = refType.methodsByName(name);
+        Iterator iter = list.iterator();
+        while (iter.hasNext()) {
+            Method method = (Method)iter.next();
+            boolean isStatic = method.isStatic();
+            if (((kind == STATIC) && !isStatic) ||
+                ((kind == INSTANCE) && isStatic)) {
+                iter.remove();
+            }
+        }
+        return list;
+    }
+
+    static List<String> primitiveTypeNames = new ArrayList<String>();
+    static {
+        primitiveTypeNames.add("boolean");
+        primitiveTypeNames.add("byte");
+        primitiveTypeNames.add("char");
+        primitiveTypeNames.add("short");
+        primitiveTypeNames.add("int");
+        primitiveTypeNames.add("long");
+        primitiveTypeNames.add("float");
+        primitiveTypeNames.add("double");
+    }
+
+
+    static final int SAME = 0;
+    static final int ASSIGNABLE = 1;
+    static final int DIFFERENT = 2;
+    /*
+     * Return SAME, DIFFERENT or ASSIGNABLE.
+     * SAME means each arg type is the same as type of the corr. arg.
+     * ASSIGNABLE means that not all the pairs are the same, but
+     * for those that aren't, at least the argType is assignable
+     * from the type of the argument value.
+     * DIFFERENT means that in at least one pair, the
+     * argType is not assignable from the type of the argument value.
+     * IE, one is an Apple and the other is an Orange.
+     */
+    static int argumentsMatch(List argTypes, List arguments) {
+        if (argTypes.size() != arguments.size()) {
+            return DIFFERENT;
+        }
+
+        Iterator typeIter = argTypes.iterator();
+        Iterator valIter = arguments.iterator();
+        int result = SAME;
+
+        // If any pair aren't the same, change the
+        // result to ASSIGNABLE.  If any pair aren't
+        // assignable, return DIFFERENT
+        while (typeIter.hasNext()) {
+            Type argType = (Type)typeIter.next();
+            Value value = (Value)valIter.next();
+            if (value == null) {
+                // Null values can be passed to any non-primitive argument
+                if (primitiveTypeNames.contains(argType.name())) {
+                    return DIFFERENT;
+                }
+                // Else, we will assume that a null value
+                // exactly matches an object type.
+            }
+            if (!value.type().equals(argType)) {
+                if (isAssignableTo(value.type(), argType)) {
+                    result = ASSIGNABLE;
+                } else {
+                    return DIFFERENT;
+                }
+            }
+        }
+        return result;
+    }
+
+
+    // These is...AssignableTo methods are based on similar code in the JDI
+    // implementations of ClassType, ArrayType, and InterfaceType
+
+    static boolean isComponentAssignable(Type fromType, Type toType) {
+        if (fromType instanceof PrimitiveType) {
+            // Assignment of primitive arrays requires identical
+            // component types.
+            return fromType.equals(toType);
+        }
+        if (toType instanceof PrimitiveType) {
+            return false;
+        }
+        // Assignment of object arrays requires availability
+        // of widening conversion of component types
+        return isAssignableTo(fromType, toType);
+    }
+
+    static boolean isArrayAssignableTo(ArrayType fromType, Type toType) {
+        if (toType instanceof ArrayType) {
+            try {
+                Type toComponentType = ((ArrayType)toType).componentType();
+                return isComponentAssignable(fromType.componentType(), toComponentType);
+            } catch (ClassNotLoadedException e) {
+                // One or both component types has not yet been
+                // loaded => can't assign
+                return false;
+            }
+        }
+        if (toType instanceof InterfaceType) {
+            // Only valid InterfaceType assignee is Cloneable
+            return toType.name().equals("java.lang.Cloneable");
+        }
+        // Only valid ClassType assignee is Object
+        return toType.name().equals("java.lang.Object");
+    }
+
+    static boolean isAssignableTo(Type fromType, Type toType) {
+        if (fromType.equals(toType)) {
+            return true;
+        }
+
+        // If one is boolean, so must be the other.
+        if (fromType instanceof BooleanType) {
+            if (toType instanceof BooleanType) {
+                return true;
+            }
+            return false;
+        }
+        if (toType instanceof BooleanType) {
+            return false;
+        }
+
+        // Other primitive types are intermixable only with each other.
+        if (fromType instanceof PrimitiveType) {
+            if (toType instanceof PrimitiveType) {
+                return true;
+            }
+            return false;
+        }
+        if (toType instanceof PrimitiveType) {
+            return false;
+        }
+
+        // neither one is primitive.
+        if (fromType instanceof ArrayType) {
+            return isArrayAssignableTo((ArrayType)fromType, toType);
+        }
+        List interfaces;
+        if (fromType instanceof ClassType) {
+            ClassType superclazz = ((ClassType)fromType).superclass();
+            if ((superclazz != null) && isAssignableTo(superclazz, toType)) {
+                return true;
+            }
+            interfaces = ((ClassType)fromType).interfaces();
+        } else {
+            // fromType must be an InterfaceType
+            interfaces = ((InterfaceType)fromType).superinterfaces();
+        }
+        Iterator iter = interfaces.iterator();
+        while (iter.hasNext()) {
+            InterfaceType interfaze = (InterfaceType)iter.next();
+            if (isAssignableTo(interfaze, toType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static Method resolveOverload(List overloads, List arguments)
+                                       throws ParseException {
+
+        // If there is only one method to call, we'll just choose
+        // that without looking at the args.  If they aren't right
+        // the invoke will return a better error message than we
+        // could generate here.
+        if (overloads.size() == 1) {
+            return (Method)overloads.get(0);
+        }
+
+        // Resolving overloads is beyond the scope of this exercise.
+        // So, we will look for a method that matches exactly the
+        // types of the arguments.  If we can't find one, then
+        // if there is exactly one method whose param types are assignable
+        // from the arg types, we will use that.  Otherwise,
+        // it is an error.  We won't guess which of multiple possible
+        // methods to call. And, since casts aren't implemented,
+        // the user can't use them to pick a particular overload to call.
+        // IE, the user is out of luck in this case.
+        Iterator iter = overloads.iterator();
+        Method retVal = null;
+        int assignableCount = 0;
+        while (iter.hasNext()) {
+            Method mm = (Method)iter.next();
+            List argTypes;
+            try {
+                argTypes = mm.argumentTypes();
+            } catch (ClassNotLoadedException ee) {
+                // This probably won't happen for the
+                // method that we are really supposed to
+                // call.
+                continue;
+            }
+            int compare = argumentsMatch(argTypes, arguments);
+            if (compare == SAME) {
+                return mm;
+            }
+            if (compare == DIFFERENT) {
+                continue;
+            }
+            // Else, it is assignable.  Remember it.
+            retVal = mm;
+            assignableCount++;
+        }
+
+        // At this point, we didn't find an exact match,
+        // but we found one for which the args are assignable.
+        //
+        if (retVal != null) {
+            if (assignableCount == 1) {
+                return retVal;
+            }
+            throw new ParseException("Arguments match multiple methods");
+        }
+        throw new ParseException("Arguments match no method");
+    }
+
+    private static class LValueLocal extends LValue {
+        final StackFrame frame;
+        final LocalVariable var;
+
+        LValueLocal(StackFrame frame, LocalVariable var) {
+            this.frame = frame;
+            this.var = var;
+        }
+
+        Value getValue() {
+            if (jdiValue == null) {
+                jdiValue = frame.getValue(var);
+            }
+            return jdiValue;
+        }
+
+        void setValue0(Value val) throws InvalidTypeException,
+                                         ClassNotLoadedException {
+            frame.setValue(var, val);
+            jdiValue = val;
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            throw new ParseException(var.name() + " is not a method");
+        }
+    }
+
+    private static class LValueInstanceMember extends LValue {
+        final ObjectReference obj;
+        final ThreadReference thread;
+        final Field matchingField;
+        final List overloads;
+        Method matchingMethod = null;
+        List<Value> methodArguments = null;
+
+        LValueInstanceMember(Value value,
+                            String memberName,
+                            ThreadReference thread) throws ParseException {
+            if (!(value instanceof ObjectReference)) {
+                throw new ParseException(
+                       "Cannot access field of primitive type: " + value);
+            }
+            this.obj = (ObjectReference)value;
+            this.thread = thread;
+            ReferenceType refType = obj.referenceType();
+            /*
+             * Can't tell yet whether this LValue will be accessed as a
+             * field or method, so we keep track of all the possibilities
+             */
+            matchingField = LValue.fieldByName(refType, memberName,
+                                               LValue.INSTANCE);
+            overloads = LValue.methodsByName(refType, memberName,
+                                              LValue.INSTANCE);
+            if ((matchingField == null) && overloads.size() == 0) {
+                throw new ParseException("No instance field or method with the name "
+                               + memberName + " in " + refType.name());
+            }
+        }
+
+        Value getValue() throws InvocationException, InvalidTypeException,
+                                ClassNotLoadedException, IncompatibleThreadStateException,
+                                ParseException {
+            if (jdiValue != null) {
+                return jdiValue;
+            }
+            if (matchingMethod == null) {
+                if (matchingField == null) {
+                    throw new ParseException("No such field in " + obj.referenceType().name());
+                }
+                return jdiValue = obj.getValue(matchingField);
+            } else {
+                return jdiValue = obj.invokeMethod(thread, matchingMethod, methodArguments, 0);
+            }
+        }
+
+        void setValue0(Value val) throws ParseException,
+                                         InvalidTypeException,
+                                        ClassNotLoadedException {
+            if (matchingMethod != null) {
+                throw new ParseException("Cannot assign to a method invocation");
+            }
+            obj.setValue(matchingField, val);
+            jdiValue = val;
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            if (matchingMethod != null) {
+                throw new ParseException("Invalid consecutive invocations");
+            }
+            methodArguments = arguments;
+            matchingMethod = LValue.resolveOverload(overloads, arguments);
+        }
+    }
+
+    private static class LValueStaticMember extends LValue {
+        final ReferenceType refType;
+        final ThreadReference thread;
+        final Field matchingField;
+        final List overloads;
+        Method matchingMethod = null;
+        List<Value> methodArguments = null;
+
+        LValueStaticMember(ReferenceType refType,
+                          String memberName,
+                          ThreadReference thread) throws ParseException {
+            this.refType = refType;
+            this.thread = thread;
+            /*
+             * Can't tell yet whether this LValue will be accessed as a
+             * field or method, so we keep track of all the possibilities
+             */
+            matchingField = LValue.fieldByName(refType, memberName,
+                                               LValue.STATIC);
+            overloads = LValue.methodsByName(refType, memberName,
+                                              LValue.STATIC);
+            if ((matchingField == null) && overloads.size() == 0) {
+                throw new ParseException("No static field or method with the name "
+                               + memberName + " in " + refType.name());
+            }
+        }
+
+        Value getValue() throws InvocationException, InvalidTypeException,
+                                ClassNotLoadedException, IncompatibleThreadStateException,
+                                ParseException {
+            if (jdiValue != null) {
+                return jdiValue;
+            }
+            if (matchingMethod == null) {
+                return jdiValue = refType.getValue(matchingField);
+            } else if (refType instanceof ClassType) {
+                ClassType clazz = (ClassType)refType;
+                return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0);
+            } else {
+                throw new InvalidTypeException("Cannot invoke static method on " +
+                                         refType.name());
+            }
+        }
+
+        void setValue0(Value val)
+                           throws ParseException, InvalidTypeException,
+                                  ClassNotLoadedException {
+            if (matchingMethod != null) {
+                throw new ParseException("Cannot assign to a method invocation");
+            }
+            if (!(refType instanceof ClassType)) {
+                throw new ParseException(
+                       "Cannot set interface field: " + refType);
+            }
+            ((ClassType)refType).setValue(matchingField, val);
+            jdiValue = val;
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            if (matchingMethod != null) {
+                throw new ParseException("Invalid consecutive invocations");
+            }
+            methodArguments = arguments;
+            matchingMethod = LValue.resolveOverload(overloads, arguments);
+        }
+    }
+
+    private static class LValueArrayLength extends LValue {
+        /*
+         * Since one can code "int myLen = myArray.length;",
+         * one might expect that these JDI calls would get a Value
+         * object for the length of an array in the debugee:
+         *    Field xxx = ArrayType.fieldByName("length")
+         *    Value lenVal= ArrayReference.getValue(xxx)
+         *
+         * However, this doesn't work because the array length isn't
+         * really stored as a field, and can't be accessed as such
+         * via JDI.  Instead, the arrayRef.length() method has to be
+         * used.
+         */
+        final ArrayReference arrayRef;
+        LValueArrayLength (ArrayReference value) {
+            this.arrayRef = value;
+        }
+
+        Value getValue() {
+            if (jdiValue == null) {
+                jdiValue = arrayRef.virtualMachine().mirrorOf(arrayRef.length());
+            }
+            return jdiValue;
+        }
+
+        void setValue0(Value value) throws ParseException  {
+            throw new ParseException("Cannot set constant: " + value);
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            throw new ParseException("Array element is not a method");
+        }
+    }
+
+    private static class LValueArrayElement extends LValue {
+        final ArrayReference array;
+        final int index;
+
+        LValueArrayElement(Value value, int index) throws ParseException {
+            if (!(value instanceof ArrayReference)) {
+                throw new ParseException(
+                       "Must be array type: " + value);
+            }
+            this.array = (ArrayReference)value;
+            this.index = index;
+        }
+
+        Value getValue() {
+            if (jdiValue == null) {
+                jdiValue = array.getValue(index);
+            }
+            return jdiValue;
+        }
+
+        void setValue0(Value val) throws InvalidTypeException,
+                                         ClassNotLoadedException  {
+            array.setValue(index, val);
+            jdiValue = val;
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            throw new ParseException("Array element is not a method");
+        }
+    }
+
+    private static class LValueConstant extends LValue {
+        final Value value;
+
+        LValueConstant(Value value) {
+            this.value = value;
+        }
+
+        Value getValue() {
+            if (jdiValue == null) {
+                jdiValue = value;
+            }
+            return jdiValue;
+        }
+
+        void setValue0(Value val) throws ParseException {
+            throw new ParseException("Cannot set constant: " + value);
+        }
+
+        void invokeWith(List<Value> arguments) throws ParseException {
+            throw new ParseException("Constant is not a method");
+        }
+    }
+
+    static LValue make(VirtualMachine vm, boolean val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, byte val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, char val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, short val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, int val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, long val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, float val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, double val) {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue make(VirtualMachine vm, String val) throws ParseException {
+        return new LValueConstant(vm.mirrorOf(val));
+    }
+
+    static LValue makeBoolean(VirtualMachine vm, Token token) {
+        return make(vm, token.image.charAt(0) == 't');
+    }
+
+    static LValue makeCharacter(VirtualMachine vm, Token token) {
+        return make(vm, token.image.charAt(1));
+    }
+
+    static LValue makeFloat(VirtualMachine vm, Token token) {
+        return make(vm, Float.valueOf(token.image).floatValue());
+    }
+
+    static LValue makeDouble(VirtualMachine vm, Token token) {
+        return make(vm, Double.valueOf(token.image).doubleValue());
+    }
+
+    static LValue makeInteger(VirtualMachine vm, Token token) {
+        return make(vm, Integer.parseInt(token.image));
+    }
+
+    static LValue makeShort(VirtualMachine vm, Token token) {
+        return make(vm, Short.parseShort(token.image));
+    }
+
+    static LValue makeLong(VirtualMachine vm, Token token) {
+        return make(vm, Long.parseLong(token.image));
+    }
+
+    static LValue makeByte(VirtualMachine vm, Token token) {
+        return make(vm, Byte.parseByte(token.image));
+    }
+
+    static LValue makeString(VirtualMachine vm,
+                             Token token) throws ParseException {
+        int len = token.image.length();
+        return make(vm, token.image.substring(1,len-1));
+    }
+
+    static LValue makeNull(VirtualMachine vm,
+                           Token token) throws ParseException {
+        return new LValueConstant(null);
+    }
+
+    static LValue makeThisObject(VirtualMachine vm,
+                                 ExpressionParser.GetFrame frameGetter,
+                                 Token token) throws ParseException {
+        if (frameGetter == null) {
+            throw new ParseException("No current thread");
+        } else {
+            try {
+                StackFrame frame = frameGetter.get();
+                ObjectReference thisObject = frame.thisObject();
+
+                if (thisObject==null) {
+                        throw new ParseException(
+                            "No 'this'.  In native or static method");
+                } else {
+                        return new LValueConstant(thisObject);
+                }
+            } catch (IncompatibleThreadStateException exc) {
+                throw new ParseException("Thread not suspended");
+            }
+        }
+    }
+
+    static LValue makeNewObject(VirtualMachine vm,
+                                 ExpressionParser.GetFrame frameGetter,
+                                String className, List<Value> arguments) throws ParseException {
+        List classes = vm.classesByName(className);
+        if (classes.size() == 0) {
+            throw new ParseException("No class named: " + className);
+        }
+
+        if (classes.size() > 1) {
+            throw new ParseException("More than one class named: " +
+                                     className);
+        }
+        ReferenceType refType = (ReferenceType)classes.get(0);
+
+
+        if (!(refType instanceof ClassType)) {
+            throw new ParseException("Cannot create instance of interface " +
+                                     className);
+        }
+
+        ClassType classType = (ClassType)refType;
+        List<Method> methods = new ArrayList<Method>(classType.methods()); // writable
+        Iterator iter = methods.iterator();
+        while (iter.hasNext()) {
+            Method method = (Method)iter.next();
+            if (!method.isConstructor()) {
+                iter.remove();
+            }
+        }
+        Method constructor = LValue.resolveOverload(methods, arguments);
+
+        ObjectReference newObject;
+        try {
+            ThreadReference thread = frameGetter.get().thread();
+            newObject = classType.newInstance(thread, constructor, arguments, 0);
+        } catch (InvocationException ie) {
+            throw new ParseException("Exception in " + className + " constructor: " +
+                                     ie.exception().referenceType().name());
+        } catch (IncompatibleThreadStateException exc) {
+            throw new ParseException("Thread not suspended");
+        } catch (Exception e) {
+            /*
+             * TO DO: Better error handling
+             */
+            throw new ParseException("Unable to create " + className + " instance");
+        }
+        return new LValueConstant(newObject);
+    }
+
+    private static LValue nFields(LValue lval,
+                                  StringTokenizer izer,
+                                  ThreadReference thread)
+                                          throws ParseException {
+        if (!izer.hasMoreTokens()) {
+            return lval;
+        } else {
+            return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread);
+        }
+    }
+
+    static LValue makeName(VirtualMachine vm,
+                           ExpressionParser.GetFrame frameGetter,
+                           String name) throws ParseException {
+        StringTokenizer izer = new StringTokenizer(name, ".");
+        String first = izer.nextToken();
+        // check local variables
+        if (frameGetter != null) {
+            try {
+                StackFrame frame = frameGetter.get();
+                ThreadReference thread = frame.thread();
+                LocalVariable var;
+                try {
+                    var = frame.visibleVariableByName(first);
+                } catch (AbsentInformationException e) {
+                    var = null;
+                }
+                if (var != null) {
+                    return nFields(new LValueLocal(frame, var), izer, thread);
+                } else {
+                    ObjectReference thisObject = frame.thisObject();
+                    if (thisObject != null) {
+                        // check if it is a field of 'this'
+                        LValue thisLValue = new LValueConstant(thisObject);
+                        LValue fv;
+                        try {
+                            fv = thisLValue.memberLValue(first, thread);
+                        } catch (ParseException exc) {
+                            fv = null;
+                        }
+                        if (fv != null) {
+                            return nFields(fv, izer, thread);
+                        }
+                    }
+                }
+                // check for class name
+                while (izer.hasMoreTokens()) {
+                    List classes = vm.classesByName(first);
+                    if (classes.size() > 0) {
+                        if (classes.size() > 1) {
+                            throw new ParseException("More than one class named: " +
+                                                     first);
+                        } else {
+                            ReferenceType refType = (ReferenceType)classes.get(0);
+                            LValue lval = new LValueStaticMember(refType,
+                                                            izer.nextToken(), thread);
+                            return nFields(lval, izer, thread);
+                        }
+                    }
+                    first = first + '.' + izer.nextToken();
+                }
+            } catch (IncompatibleThreadStateException exc) {
+                throw new ParseException("Thread not suspended");
+            }
+        }
+        throw new ParseException("Name unknown: " + name);
+    }
+
+    static String stringValue(LValue lval, ExpressionParser.GetFrame frameGetter
+                              ) throws ParseException {
+        Value val = lval.getMassagedValue(frameGetter);
+        if (val == null) {
+            return "null";
+        }
+        if (val instanceof StringReference) {
+            return ((StringReference)val).value();
+        }
+        return val.toString();  // is this correct in all cases?
+    }
+
+    static LValue booleanOperation(VirtualMachine vm, Token token,
+                            LValue rightL,
+                            LValue leftL) throws ParseException {
+        String op = token.image;
+        Value right = rightL.interiorGetValue();
+        Value left = leftL.interiorGetValue();
+        if ( !(right instanceof PrimitiveValue) ||
+             !(left instanceof PrimitiveValue) ) {
+            if (op.equals("==")) {
+                return make(vm, right.equals(left));
+            } else if (op.equals("!=")) {
+                return make(vm, !right.equals(left));
+            } else {
+                throw new ParseException("Operands or '" + op +
+                                     "' must be primitive");
+            }
+        }
+        // can compare any numeric doubles
+        double rr = ((PrimitiveValue)right).doubleValue();
+        double ll = ((PrimitiveValue)left).doubleValue();
+        boolean res;
+        if (op.equals("<")) {
+            res = rr < ll;
+        } else if (op.equals(">")) {
+            res = rr > ll;
+        } else if (op.equals("<=")) {
+            res = rr <= ll;
+        } else if (op.equals(">=")) {
+            res = rr >= ll;
+        } else if (op.equals("==")) {
+            res = rr == ll;
+        } else if (op.equals("!=")) {
+            res = rr != ll;
+        } else {
+            throw new ParseException("Unknown operation: " + op);
+        }
+        return make(vm, res);
+    }
+
+    static LValue operation(VirtualMachine vm, Token token,
+                            LValue rightL, LValue leftL,
+                            ExpressionParser.GetFrame frameGetter
+                            ) throws ParseException {
+        String op = token.image;
+        Value right = rightL.interiorGetValue();
+        Value left = leftL.interiorGetValue();
+        if ((right instanceof StringReference) ||
+                              (left instanceof StringReference)) {
+            if (op.equals("+")) {
+                // If one is an ObjectRef, we will need to invoke
+                // toString on it, so we need the thread.
+                return make(vm, stringValue(rightL, frameGetter) +
+                            stringValue(leftL, frameGetter));
+            }
+        }
+        if ((right instanceof ObjectReference) ||
+                              (left instanceof ObjectReference)) {
+            if (op.equals("==")) {
+                return make(vm, right.equals(left));
+            } else if (op.equals("!=")) {
+                return make(vm, !right.equals(left));
+            } else {
+                throw new ParseException("Invalid operation '" +
+                                         op + "' on an Object");
+            }
+        }
+        if ((right instanceof BooleanValue) ||
+                              (left instanceof BooleanValue)) {
+            throw new ParseException("Invalid operation '" +
+                                     op + "' on a Boolean");
+        }
+        // from here on, we know it is a integer kind of type
+        PrimitiveValue primRight = (PrimitiveValue)right;
+        PrimitiveValue primLeft = (PrimitiveValue)left;
+        if ((primRight instanceof DoubleValue) ||
+                              (primLeft instanceof DoubleValue)) {
+            double rr = primRight.doubleValue();
+            double ll = primLeft.doubleValue();
+            double res;
+            if (op.equals("+")) {
+                res = rr + ll;
+            } else if (op.equals("-")) {
+                res = rr - ll;
+            } else if (op.equals("*")) {
+                res = rr * ll;
+            } else if (op.equals("/")) {
+                res = rr / ll;
+            } else {
+                throw new ParseException("Unknown operation: " + op);
+            }
+            return make(vm, res);
+        }
+        if ((primRight instanceof FloatValue) ||
+                              (primLeft instanceof FloatValue)) {
+            float rr = primRight.floatValue();
+            float ll = primLeft.floatValue();
+            float res;
+            if (op.equals("+")) {
+                res = rr + ll;
+            } else if (op.equals("-")) {
+                res = rr - ll;
+            } else if (op.equals("*")) {
+                res = rr * ll;
+            } else if (op.equals("/")) {
+                res = rr / ll;
+            } else {
+                throw new ParseException("Unknown operation: " + op);
+            }
+            return make(vm, res);
+        }
+        if ((primRight instanceof LongValue) ||
+                              (primLeft instanceof LongValue)) {
+            long rr = primRight.longValue();
+            long ll = primLeft.longValue();
+            long res;
+            if (op.equals("+")) {
+                res = rr + ll;
+            } else if (op.equals("-")) {
+                res = rr - ll;
+            } else if (op.equals("*")) {
+                res = rr * ll;
+            } else if (op.equals("/")) {
+                res = rr / ll;
+            } else {
+                throw new ParseException("Unknown operation: " + op);
+            }
+            return make(vm, res);
+        } else {
+            int rr = primRight.intValue();
+            int ll = primLeft.intValue();
+            int res;
+            if (op.equals("+")) {
+                res = rr + ll;
+            } else if (op.equals("-")) {
+                res = rr - ll;
+            } else if (op.equals("*")) {
+                res = rr * ll;
+            } else if (op.equals("/")) {
+                res = rr / ll;
+            } else {
+                throw new ParseException("Unknown operation: " + op);
+            }
+            return make(vm, res);
+        }
+    }
+}