1056 lines · 39.3 KB · Java
RawDownload
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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);    }    @Override    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<Method> methodsByName(ReferenceType refType,                                      String name, int kind) {        List<Method> list = refType.methodsByName(name);        Iterator<Method> iter = list.iterator();        while (iter.hasNext()) {            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<Type> argTypes, List<Value> arguments) {        if (argTypes.size() != arguments.size()) {            return DIFFERENT;        }         Iterator<Type> typeIter = argTypes.iterator();        Iterator<Value> 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 = typeIter.next();            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<InterfaceType> 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();        }        for (InterfaceType interfaze : interfaces) {            if (isAssignableTo(interfaze, toType)) {                return true;            }        }        return false;    }     static Method resolveOverload(List<Method> overloads,                                  List<Value> 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 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.        Method retVal = null;        int assignableCount = 0;        for (Method mm : overloads) {            List<Type> 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;        }       @Override        Value getValue() {            if (jdiValue == null) {                jdiValue = frame.getValue(var);            }            return jdiValue;        }       @Override        void setValue0(Value val) throws InvalidTypeException,                                         ClassNotLoadedException {            frame.setValue(var, val);            jdiValue = val;        }       @Override        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<Method> 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());            }        }       @Override        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);            }        }         @Override        void setValue0(Value val) throws ParseException,                                         InvalidTypeException,                                        ClassNotLoadedException {            if (matchingMethod != null) {                throw new ParseException("Cannot assign to a method invocation");            }            obj.setValue(matchingField, val);

Lines 1–500 of 1056.