1/*
2 * Copyright (c) 1998, 2013, 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/*
27 * This source code is provided to illustrate the usage of a given feature
28 * or technique and has been deliberately simplified. Additional steps
29 * required for a production-quality application, such as security checks,
30 * input validation and proper error handling, might not be present in
31 * this sample code.
32 */
33
34
35package com.sun.tools.example.debug.expr;
36
37import com.sun.jdi.*;
38import java.util.*;
39
40abstract class LValue {
41
42    // The JDI Value object for this LValue.  Once we have this Value,
43    // we have to remember it since after we return the LValue object
44    // to the ExpressionParser, it might decide that it needs
45    // the 'toString' value for the LValue in which case it will
46    // call getMassagedValue to get this toString value.  At that
47    // point, we don't want to call JDI a 2nd time to get the Value
48    // for the LValue.  This is especially wrong when the LValue
49    // represents a member function.  We would end up calling it
50    // a 2nd time.
51    //
52    // Unfortunately, there are several levels of calls to
53    // get/set values in this file.  To minimize confusion,
54    // jdiValue is set/tested at the lowest level - right
55    // next to the actual calls to JDI methods to get/set the
56    // value in the debuggee.
57    protected Value jdiValue;
58
59    abstract Value getValue() throws InvocationException,
60                                     IncompatibleThreadStateException,
61                                     InvalidTypeException,
62                                     ClassNotLoadedException,
63                                     ParseException;
64
65    abstract void setValue0(Value value)
66                   throws ParseException, InvalidTypeException,
67                          ClassNotLoadedException;
68
69    abstract void invokeWith(List<Value> arguments) throws ParseException;
70
71    void setValue(Value value) throws ParseException {
72        try {
73            setValue0(value);
74        } catch (InvalidTypeException exc) {
75            throw new ParseException(
76                "Attempt to set value of incorrect type" +
77                exc);
78        } catch (ClassNotLoadedException exc) {
79            throw new ParseException(
80                "Attempt to set value before " + exc.className() + " was loaded" +
81                exc);
82        }
83    }
84
85    void setValue(LValue lval) throws ParseException {
86        setValue(lval.interiorGetValue());
87    }
88
89    LValue memberLValue(ExpressionParser.GetFrame frameGetter,
90                        String fieldName) throws ParseException {
91        try {
92            return memberLValue(fieldName, frameGetter.get().thread());
93        } catch (IncompatibleThreadStateException exc) {
94            throw new ParseException("Thread not suspended");
95        }
96    }
97
98    LValue memberLValue(String fieldName, ThreadReference thread) throws ParseException {
99
100        Value val = interiorGetValue();
101        if ((val instanceof ArrayReference) &&
102            "length".equals(fieldName)){
103            return new LValueArrayLength((ArrayReference)val);
104        }
105        return new LValueInstanceMember(val, fieldName, thread);
106    }
107
108    // Return the Value for this LValue that would be used to concatenate
109    // to a String.  IE, if it is an Object, call toString in the debuggee.
110    Value getMassagedValue(ExpressionParser.GetFrame frameGetter) throws ParseException {
111        Value vv = interiorGetValue();
112
113        // If vv is an ObjectReference, then we have to
114        // do the implicit call to toString().
115        if (vv instanceof ObjectReference &&
116            !(vv instanceof StringReference) &&
117            !(vv instanceof ArrayReference)) {
118            StackFrame frame;
119            try {
120                frame = frameGetter.get();
121            } catch (IncompatibleThreadStateException exc) {
122                throw new ParseException("Thread not suspended");
123            }
124
125            ThreadReference thread = frame.thread();
126            LValue toStringMember = memberLValue("toString", thread);
127            toStringMember.invokeWith(new ArrayList<Value>());
128            return toStringMember.interiorGetValue();
129        }
130        return vv;
131    }
132
133    Value interiorGetValue() throws ParseException {
134        Value value;
135        try {
136            value = getValue();
137        } catch (InvocationException e) {
138            throw new ParseException("Unable to complete expression. Exception " +
139                                     e.exception() + " thrown");
140        } catch (IncompatibleThreadStateException itse) {
141            throw new ParseException("Unable to complete expression. Thread " +
142                                     "not suspended for method invoke");
143        } catch (InvalidTypeException ite) {
144            throw new ParseException("Unable to complete expression. Method " +
145                                     "argument type mismatch");
146        } catch (ClassNotLoadedException tnle) {
147            throw new ParseException("Unable to complete expression. Method " +
148                                     "argument type " + tnle.className() +
149                                     " not yet loaded");
150        }
151        return value;
152    }
153
154    LValue arrayElementLValue(LValue lval) throws ParseException {
155        Value indexValue = lval.interiorGetValue();
156        int index;
157        if ( (indexValue instanceof IntegerValue) ||
158             (indexValue instanceof ShortValue) ||
159             (indexValue instanceof ByteValue) ||
160             (indexValue instanceof CharValue) ) {
161            index = ((PrimitiveValue)indexValue).intValue();
162        } else {
163            throw new ParseException("Array index must be a integer type");
164        }
165        return new LValueArrayElement(interiorGetValue(), index);
166    }
167
168   @Override
169    public String toString() {
170        try {
171            return interiorGetValue().toString();
172        } catch (ParseException e) {
173            return "<Parse Exception>";
174        }
175    }
176
177    static final int STATIC = 0;
178    static final int INSTANCE = 1;
179
180    static Field fieldByName(ReferenceType refType, String name, int kind) {
181        /*
182         * TO DO: Note that this currently fails to find superclass
183         * or implemented interface fields. This is due to a temporary
184         * limititation of RefType.fieldByName. Once that method is
185         * fixed, superclass fields will be found.
186         */
187        Field field = refType.fieldByName(name);
188        if (field != null) {
189            boolean isStatic = field.isStatic();
190            if (((kind == STATIC) && !isStatic) ||
191                ((kind == INSTANCE) && isStatic)) {
192                field = null;
193            }
194        }
195/***
196        System.err.println("fieldByName: " + refType.name() + " " +
197                                             name + " " +
198                                             kind + " " +
199                                             (field != null));
200***/
201        return field;
202    }
203
204    static List<Method> methodsByName(ReferenceType refType,
205                                      String name, int kind) {
206        List<Method> list = refType.methodsByName(name);
207        Iterator<Method> iter = list.iterator();
208        while (iter.hasNext()) {
209            Method method = iter.next();
210            boolean isStatic = method.isStatic();
211            if (((kind == STATIC) && !isStatic) ||
212                ((kind == INSTANCE) && isStatic)) {
213                iter.remove();
214            }
215        }
216        return list;
217    }
218
219    static List<String> primitiveTypeNames = new ArrayList<String>();
220    static {
221        primitiveTypeNames.add("boolean");
222        primitiveTypeNames.add("byte");
223        primitiveTypeNames.add("char");
224        primitiveTypeNames.add("short");
225        primitiveTypeNames.add("int");
226        primitiveTypeNames.add("long");
227        primitiveTypeNames.add("float");
228        primitiveTypeNames.add("double");
229    }
230
231
232    static final int SAME = 0;
233    static final int ASSIGNABLE = 1;
234    static final int DIFFERENT = 2;
235    /*
236     * Return SAME, DIFFERENT or ASSIGNABLE.
237     * SAME means each arg type is the same as type of the corr. arg.
238     * ASSIGNABLE means that not all the pairs are the same, but
239     * for those that aren't, at least the argType is assignable
240     * from the type of the argument value.
241     * DIFFERENT means that in at least one pair, the
242     * argType is not assignable from the type of the argument value.
243     * IE, one is an Apple and the other is an Orange.
244     */
245    static int argumentsMatch(List<Type> argTypes, List<Value> arguments) {
246        if (argTypes.size() != arguments.size()) {
247            return DIFFERENT;
248        }
249
250        Iterator<Type> typeIter = argTypes.iterator();
251        Iterator<Value> valIter = arguments.iterator();
252        int result = SAME;
253
254        // If any pair aren't the same, change the
255        // result to ASSIGNABLE.  If any pair aren't
256        // assignable, return DIFFERENT
257        while (typeIter.hasNext()) {
258            Type argType = typeIter.next();
259            Value value = valIter.next();
260            if (value == null) {
261                // Null values can be passed to any non-primitive argument
262                if (primitiveTypeNames.contains(argType.name())) {
263                    return DIFFERENT;
264                }
265                // Else, we will assume that a null value
266                // exactly matches an object type.
267            }
268            if (!value.type().equals(argType)) {
269                if (isAssignableTo(value.type(), argType)) {
270                    result = ASSIGNABLE;
271                } else {
272                    return DIFFERENT;
273                }
274            }
275        }
276        return result;
277    }
278
279
280    // These is...AssignableTo methods are based on similar code in the JDI
281    // implementations of ClassType, ArrayType, and InterfaceType
282
283    static boolean isComponentAssignable(Type fromType, Type toType) {
284        if (fromType instanceof PrimitiveType) {
285            // Assignment of primitive arrays requires identical
286            // component types.
287            return fromType.equals(toType);
288        }
289        if (toType instanceof PrimitiveType) {
290            return false;
291        }
292        // Assignment of object arrays requires availability
293        // of widening conversion of component types
294        return isAssignableTo(fromType, toType);
295    }
296
297    static boolean isArrayAssignableTo(ArrayType fromType, Type toType) {
298        if (toType instanceof ArrayType) {
299            try {
300                Type toComponentType = ((ArrayType)toType).componentType();
301                return isComponentAssignable(fromType.componentType(), toComponentType);
302            } catch (ClassNotLoadedException e) {
303                // One or both component types has not yet been
304                // loaded => can't assign
305                return false;
306            }
307        }
308        if (toType instanceof InterfaceType) {
309            // Only valid InterfaceType assignee is Cloneable
310            return toType.name().equals("java.lang.Cloneable");
311        }
312        // Only valid ClassType assignee is Object
313        return toType.name().equals("java.lang.Object");
314    }
315
316    static boolean isAssignableTo(Type fromType, Type toType) {
317        if (fromType.equals(toType)) {
318            return true;
319        }
320
321        // If one is boolean, so must be the other.
322        if (fromType instanceof BooleanType) {
323            if (toType instanceof BooleanType) {
324                return true;
325            }
326            return false;
327        }
328        if (toType instanceof BooleanType) {
329            return false;
330        }
331
332        // Other primitive types are intermixable only with each other.
333        if (fromType instanceof PrimitiveType) {
334            if (toType instanceof PrimitiveType) {
335                return true;
336            }
337            return false;
338        }
339        if (toType instanceof PrimitiveType) {
340            return false;
341        }
342
343        // neither one is primitive.
344        if (fromType instanceof ArrayType) {
345            return isArrayAssignableTo((ArrayType)fromType, toType);
346        }
347        List<InterfaceType> interfaces;
348        if (fromType instanceof ClassType) {
349            ClassType superclazz = ((ClassType)fromType).superclass();
350            if ((superclazz != null) && isAssignableTo(superclazz, toType)) {
351                return true;
352            }
353            interfaces = ((ClassType)fromType).interfaces();
354        } else {
355            // fromType must be an InterfaceType
356            interfaces = ((InterfaceType)fromType).superinterfaces();
357        }
358        for (InterfaceType interfaze : interfaces) {
359            if (isAssignableTo(interfaze, toType)) {
360                return true;
361            }
362        }
363        return false;
364    }
365
366    static Method resolveOverload(List<Method> overloads,
367                                  List<Value> arguments)
368                                       throws ParseException {
369
370        // If there is only one method to call, we'll just choose
371        // that without looking at the args.  If they aren't right
372        // the invoke will return a better error message than we
373        // could generate here.
374        if (overloads.size() == 1) {
375            return overloads.get(0);
376        }
377
378        // Resolving overloads is beyond the scope of this exercise.
379        // So, we will look for a method that matches exactly the
380        // types of the arguments.  If we can't find one, then
381        // if there is exactly one method whose param types are assignable
382        // from the arg types, we will use that.  Otherwise,
383        // it is an error.  We won't guess which of multiple possible
384        // methods to call. And, since casts aren't implemented,
385        // the user can't use them to pick a particular overload to call.
386        // IE, the user is out of luck in this case.
387        Method retVal = null;
388        int assignableCount = 0;
389        for (Method mm : overloads) {
390            List<Type> argTypes;
391            try {
392                argTypes = mm.argumentTypes();
393            } catch (ClassNotLoadedException ee) {
394                // This probably won't happen for the
395                // method that we are really supposed to
396                // call.
397                continue;
398            }
399            int compare = argumentsMatch(argTypes, arguments);
400            if (compare == SAME) {
401                return mm;
402            }
403            if (compare == DIFFERENT) {
404                continue;
405            }
406            // Else, it is assignable.  Remember it.
407            retVal = mm;
408            assignableCount++;
409        }
410
411        // At this point, we didn't find an exact match,
412        // but we found one for which the args are assignable.
413        //
414        if (retVal != null) {
415            if (assignableCount == 1) {
416                return retVal;
417            }
418            throw new ParseException("Arguments match multiple methods");
419        }
420        throw new ParseException("Arguments match no method");
421    }
422
423    private static class LValueLocal extends LValue {
424        final StackFrame frame;
425        final LocalVariable var;
426
427        LValueLocal(StackFrame frame, LocalVariable var) {
428            this.frame = frame;
429            this.var = var;
430        }
431
432      @Override
433        Value getValue() {
434            if (jdiValue == null) {
435                jdiValue = frame.getValue(var);
436            }
437            return jdiValue;
438        }
439
440      @Override
441        void setValue0(Value val) throws InvalidTypeException,
442                                         ClassNotLoadedException {
443            frame.setValue(var, val);
444            jdiValue = val;
445        }
446
447      @Override
448        void invokeWith(List<Value> arguments) throws ParseException {
449            throw new ParseException(var.name() + " is not a method");
450        }
451    }
452
453    private static class LValueInstanceMember extends LValue {
454        final ObjectReference obj;
455        final ThreadReference thread;
456        final Field matchingField;
457        final List<Method> overloads;
458        Method matchingMethod = null;
459        List<Value> methodArguments = null;
460
461        LValueInstanceMember(Value value,
462                            String memberName,
463                            ThreadReference thread) throws ParseException {
464            if (!(value instanceof ObjectReference)) {
465                throw new ParseException(
466                       "Cannot access field of primitive type: " + value);
467            }
468            this.obj = (ObjectReference)value;
469            this.thread = thread;
470            ReferenceType refType = obj.referenceType();
471            /*
472             * Can't tell yet whether this LValue will be accessed as a
473             * field or method, so we keep track of all the possibilities
474             */
475            matchingField = LValue.fieldByName(refType, memberName,
476                                               LValue.INSTANCE);
477            overloads = LValue.methodsByName(refType, memberName,
478                                              LValue.INSTANCE);
479            if ((matchingField == null) && overloads.size() == 0) {
480                throw new ParseException("No instance field or method with the name "
481                               + memberName + " in " + refType.name());
482            }
483        }
484
485      @Override
486        Value getValue() throws InvocationException, InvalidTypeException,
487                                ClassNotLoadedException, IncompatibleThreadStateException,
488                                ParseException {
489            if (jdiValue != null) {
490                return jdiValue;
491            }
492            if (matchingMethod == null) {
493                if (matchingField == null) {
494                    throw new ParseException("No such field in " + obj.referenceType().name());
495                }
496                return jdiValue = obj.getValue(matchingField);
497            } else {
498                return jdiValue = obj.invokeMethod(thread, matchingMethod, methodArguments, 0);
499            }
500        }
501
502        @Override
503        void setValue0(Value val) throws ParseException,
504                                         InvalidTypeException,
505                                        ClassNotLoadedException {
506            if (matchingMethod != null) {
507                throw new ParseException("Cannot assign to a method invocation");
508            }
509            obj.setValue(matchingField, val);
510            jdiValue = val;
511        }
512
513        @Override
514        void invokeWith(List<Value> arguments) throws ParseException {
515            if (matchingMethod != null) {
516                throw new ParseException("Invalid consecutive invocations");
517            }
518            methodArguments = arguments;
519            matchingMethod = LValue.resolveOverload(overloads, arguments);
520        }
521    }
522
523    private static class LValueStaticMember extends LValue {
524        final ReferenceType refType;
525        final ThreadReference thread;
526        final Field matchingField;
527        final List<Method> overloads;
528        Method matchingMethod = null;
529        List<Value> methodArguments = null;
530
531        LValueStaticMember(ReferenceType refType,
532                          String memberName,
533                          ThreadReference thread) throws ParseException {
534            this.refType = refType;
535            this.thread = thread;
536            /*
537             * Can't tell yet whether this LValue will be accessed as a
538             * field or method, so we keep track of all the possibilities
539             */
540            matchingField = LValue.fieldByName(refType, memberName,
541                                               LValue.STATIC);
542            overloads = LValue.methodsByName(refType, memberName,
543                                              LValue.STATIC);
544            if ((matchingField == null) && overloads.size() == 0) {
545                throw new ParseException("No static field or method with the name "
546                               + memberName + " in " + refType.name());
547            }
548        }
549
550        @Override
551        Value getValue() throws InvocationException, InvalidTypeException,
552                                ClassNotLoadedException, IncompatibleThreadStateException,
553                                ParseException {
554            if (jdiValue != null) {
555                return jdiValue;
556            }
557            if (matchingMethod == null) {
558                return jdiValue = refType.getValue(matchingField);
559            } else if (refType instanceof ClassType) {
560                ClassType clazz = (ClassType)refType;
561                return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0);
562            } else if (refType instanceof InterfaceType) {
563                InterfaceType iface = (InterfaceType)refType;
564                return jdiValue = iface.invokeMethod(thread, matchingMethod, methodArguments, 0);
565            } else {
566                throw new InvalidTypeException("Cannot invoke static method on " +
567                                         refType.name());
568            }
569        }
570
571        @Override
572        void setValue0(Value val)
573                           throws ParseException, InvalidTypeException,
574                                  ClassNotLoadedException {
575            if (matchingMethod != null) {
576                throw new ParseException("Cannot assign to a method invocation");
577            }
578            if (!(refType instanceof ClassType)) {
579                throw new ParseException(
580                       "Cannot set interface field: " + refType);
581            }
582            ((ClassType)refType).setValue(matchingField, val);
583            jdiValue = val;
584        }
585
586        @Override
587        void invokeWith(List<Value> arguments) throws ParseException {
588            if (matchingMethod != null) {
589                throw new ParseException("Invalid consecutive invocations");
590            }
591            methodArguments = arguments;
592            matchingMethod = LValue.resolveOverload(overloads, arguments);
593        }
594    }
595
596    private static class LValueArrayLength extends LValue {
597        /*
598         * Since one can code "int myLen = myArray.length;",
599         * one might expect that these JDI calls would get a Value
600         * object for the length of an array in the debugee:
601         *    Field xxx = ArrayType.fieldByName("length")
602         *    Value lenVal= ArrayReference.getValue(xxx)
603         *
604         * However, this doesn't work because the array length isn't
605         * really stored as a field, and can't be accessed as such
606         * via JDI.  Instead, the arrayRef.length() method has to be
607         * used.
608         */
609        final ArrayReference arrayRef;
610        LValueArrayLength (ArrayReference value) {
611            this.arrayRef = value;
612        }
613
614        @Override
615        Value getValue() {
616            if (jdiValue == null) {
617                jdiValue = arrayRef.virtualMachine().mirrorOf(arrayRef.length());
618            }
619            return jdiValue;
620        }
621
622        @Override
623        void setValue0(Value value) throws ParseException  {
624            throw new ParseException("Cannot set constant: " + value);
625        }
626
627        @Override
628        void invokeWith(List<Value> arguments) throws ParseException {
629            throw new ParseException("Array element is not a method");
630        }
631    }
632
633    private static class LValueArrayElement extends LValue {
634        final ArrayReference array;
635        final int index;
636
637        LValueArrayElement(Value value, int index) throws ParseException {
638            if (!(value instanceof ArrayReference)) {
639                throw new ParseException(
640                       "Must be array type: " + value);
641            }
642            this.array = (ArrayReference)value;
643            this.index = index;
644        }
645
646        @Override
647        Value getValue() {
648            if (jdiValue == null) {
649                jdiValue = array.getValue(index);
650            }
651            return jdiValue;
652        }
653
654        @Override
655        void setValue0(Value val) throws InvalidTypeException,
656                                         ClassNotLoadedException  {
657            array.setValue(index, val);
658            jdiValue = val;
659        }
660
661        @Override
662        void invokeWith(List<Value> arguments) throws ParseException {
663            throw new ParseException("Array element is not a method");
664        }
665    }
666
667    private static class LValueConstant extends LValue {
668        final Value value;
669
670        LValueConstant(Value value) {
671            this.value = value;
672        }
673
674        @Override
675        Value getValue() {
676            if (jdiValue == null) {
677                jdiValue = value;
678            }
679            return jdiValue;
680        }
681
682        @Override
683        void setValue0(Value val) throws ParseException {
684            throw new ParseException("Cannot set constant: " + value);
685        }
686
687        @Override
688        void invokeWith(List<Value> arguments) throws ParseException {
689            throw new ParseException("Constant is not a method");
690        }
691    }
692
693    static LValue make(VirtualMachine vm, boolean val) {
694        return new LValueConstant(vm.mirrorOf(val));
695    }
696
697    static LValue make(VirtualMachine vm, byte val) {
698        return new LValueConstant(vm.mirrorOf(val));
699    }
700
701    static LValue make(VirtualMachine vm, char val) {
702        return new LValueConstant(vm.mirrorOf(val));
703    }
704
705    static LValue make(VirtualMachine vm, short val) {
706        return new LValueConstant(vm.mirrorOf(val));
707    }
708
709    static LValue make(VirtualMachine vm, int val) {
710        return new LValueConstant(vm.mirrorOf(val));
711    }
712
713    static LValue make(VirtualMachine vm, long val) {
714        return new LValueConstant(vm.mirrorOf(val));
715    }
716
717    static LValue make(VirtualMachine vm, float val) {
718        return new LValueConstant(vm.mirrorOf(val));
719    }
720
721    static LValue make(VirtualMachine vm, double val) {
722        return new LValueConstant(vm.mirrorOf(val));
723    }
724
725    static LValue make(VirtualMachine vm, String val) throws ParseException {
726        return new LValueConstant(vm.mirrorOf(val));
727    }
728
729    static LValue makeBoolean(VirtualMachine vm, Token token) {
730        return make(vm, token.image.charAt(0) == 't');
731    }
732
733    static LValue makeCharacter(VirtualMachine vm, Token token) {
734        return make(vm, token.image.charAt(1));
735    }
736
737    static LValue makeFloat(VirtualMachine vm, Token token) {
738        return make(vm, Float.valueOf(token.image).floatValue());
739    }
740
741    static LValue makeDouble(VirtualMachine vm, Token token) {
742        return make(vm, Double.valueOf(token.image).doubleValue());
743    }
744
745    static LValue makeInteger(VirtualMachine vm, Token token) {
746        String image = token.image;
747
748        // Here we have to deal with the fact that an INTEGER_LITERAL
749        // can be DECIMAL_LITERAL, HEX_LITERAL or OCTAL_LITERAL. All of these
750        // can have an optional "L" or "l" at the end signifying that it is
751        // a long value. Otherwise, we treat values that are in range for an
752        // int as int and anything else as long.
753
754        if (image.endsWith("L") || image.endsWith("l")) {
755          // This is a long without doubt - drop the final "Ll" and decode
756          image = image.substring(0, image.length() - 1);
757          return make(vm, Long.decode(image));
758        }
759
760        long longValue = Long.decode(image);
761        int intValue = (int) longValue;
762        if (intValue == longValue) {
763          // the value fits in an integer, lets return it as an integer
764          return make(vm, intValue);
765        }
766        else {
767          // otherwise treat it as a long
768          return make(vm, longValue);
769        }
770    }
771
772    static LValue makeShort(VirtualMachine vm, Token token) {
773        return make(vm, Short.parseShort(token.image));
774    }
775
776    static LValue makeLong(VirtualMachine vm, Token token) {
777        return make(vm, Long.parseLong(token.image));
778    }
779
780    static LValue makeByte(VirtualMachine vm, Token token) {
781        return make(vm, Byte.parseByte(token.image));
782    }
783
784    static LValue makeString(VirtualMachine vm,
785                             Token token) throws ParseException {
786        int len = token.image.length();
787        return make(vm, token.image.substring(1,len-1));
788    }
789
790    static LValue makeNull(VirtualMachine vm,
791                           Token token) throws ParseException {
792        return new LValueConstant(null);
793    }
794
795    static LValue makeThisObject(VirtualMachine vm,
796                                 ExpressionParser.GetFrame frameGetter,
797                                 Token token) throws ParseException {
798        if (frameGetter == null) {
799            throw new ParseException("No current thread");
800        } else {
801            try {
802                StackFrame frame = frameGetter.get();
803                ObjectReference thisObject = frame.thisObject();
804
805                if (thisObject==null) {
806                        throw new ParseException(
807                            "No 'this'.  In native or static method");
808                } else {
809                        return new LValueConstant(thisObject);
810                }
811            } catch (IncompatibleThreadStateException exc) {
812                throw new ParseException("Thread not suspended");
813            }
814        }
815    }
816
817    static LValue makeNewObject(VirtualMachine vm,
818                                 ExpressionParser.GetFrame frameGetter,
819                                String className, List<Value> arguments) throws ParseException {
820        List<ReferenceType> classes = vm.classesByName(className);
821        if (classes.size() == 0) {
822            throw new ParseException("No class named: " + className);
823        }
824
825        if (classes.size() > 1) {
826            throw new ParseException("More than one class named: " +
827                                     className);
828        }
829        ReferenceType refType = classes.get(0);
830
831
832        if (!(refType instanceof ClassType)) {
833            throw new ParseException("Cannot create instance of interface " +
834                                     className);
835        }
836
837        ClassType classType = (ClassType)refType;
838        List<Method> methods = new ArrayList<Method>(classType.methods()); // writable
839        Iterator<Method> iter = methods.iterator();
840        while (iter.hasNext()) {
841            Method method = iter.next();
842            if (!method.isConstructor()) {
843                iter.remove();
844            }
845        }
846        Method constructor = LValue.resolveOverload(methods, arguments);
847
848        ObjectReference newObject;
849        try {
850            ThreadReference thread = frameGetter.get().thread();
851            newObject = classType.newInstance(thread, constructor, arguments, 0);
852        } catch (InvocationException ie) {
853            throw new ParseException("Exception in " + className + " constructor: " +
854                                     ie.exception().referenceType().name());
855        } catch (IncompatibleThreadStateException exc) {
856            throw new ParseException("Thread not suspended");
857        } catch (Exception e) {
858            /*
859             * TO DO: Better error handling
860             */
861            throw new ParseException("Unable to create " + className + " instance");
862        }
863        return new LValueConstant(newObject);
864    }
865
866    private static LValue nFields(LValue lval,
867                                  StringTokenizer izer,
868                                  ThreadReference thread)
869                                          throws ParseException {
870        if (!izer.hasMoreTokens()) {
871            return lval;
872        } else {
873            return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread);
874        }
875    }
876
877    static LValue makeName(VirtualMachine vm,
878                           ExpressionParser.GetFrame frameGetter,
879                           String name) throws ParseException {
880        StringTokenizer izer = new StringTokenizer(name, ".");
881        String first = izer.nextToken();
882        // check local variables
883        if (frameGetter != null) {
884            try {
885                StackFrame frame = frameGetter.get();
886                ThreadReference thread = frame.thread();
887                LocalVariable var;
888                try {
889                    var = frame.visibleVariableByName(first);
890                } catch (AbsentInformationException e) {
891                    var = null;
892                }
893                if (var != null) {
894                    return nFields(new LValueLocal(frame, var), izer, thread);
895                } else {
896                    ObjectReference thisObject = frame.thisObject();
897                    if (thisObject != null) {
898                        // check if it is a field of 'this'
899                        LValue thisLValue = new LValueConstant(thisObject);
900                        LValue fv;
901                        try {
902                            fv = thisLValue.memberLValue(first, thread);
903                        } catch (ParseException exc) {
904                            fv = null;
905                        }
906                        if (fv != null) {
907                            return nFields(fv, izer, thread);
908                        }
909                    }
910                }
911                // check for class name
912                while (izer.hasMoreTokens()) {
913                    List<ReferenceType> classes = vm.classesByName(first);
914                    if (classes.size() > 0) {
915                        if (classes.size() > 1) {
916                            throw new ParseException("More than one class named: " +
917                                                     first);
918                        } else {
919                            ReferenceType refType = classes.get(0);
920                            LValue lval = new LValueStaticMember(refType,
921                                                            izer.nextToken(), thread);
922                            return nFields(lval, izer, thread);
923                        }
924                    }
925                    first = first + '.' + izer.nextToken();
926                }
927            } catch (IncompatibleThreadStateException exc) {
928                throw new ParseException("Thread not suspended");
929            }
930        }
931        throw new ParseException("Name unknown: " + name);
932    }
933
934    static String stringValue(LValue lval, ExpressionParser.GetFrame frameGetter
935                              ) throws ParseException {
936        Value val = lval.getMassagedValue(frameGetter);
937        if (val == null) {
938            return "null";
939        }
940        if (val instanceof StringReference) {
941            return ((StringReference)val).value();
942        }
943        return val.toString();  // is this correct in all cases?
944    }
945
946    static LValue booleanOperation(VirtualMachine vm, Token token,
947                            LValue rightL,
948                            LValue leftL) throws ParseException {
949        String op = token.image;
950        Value right = rightL.interiorGetValue();
951        Value left = leftL.interiorGetValue();
952        if ( !(right instanceof PrimitiveValue) ||
953             !(left instanceof PrimitiveValue) ) {
954            if (op.equals("==")) {
955                return make(vm, right.equals(left));
956            } else if (op.equals("!=")) {
957                return make(vm, !right.equals(left));
958            } else {
959                throw new ParseException("Operands or '" + op +
960                                     "' must be primitive");
961            }
962        }
963        // can compare any numeric doubles
964        double rr = ((PrimitiveValue)right).doubleValue();
965        double ll = ((PrimitiveValue)left).doubleValue();
966        boolean res;
967        if (op.equals("<")) {
968            res = rr < ll;
969        } else if (op.equals(">")) {
970            res = rr > ll;
971        } else 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
985    static LValue operation(VirtualMachine vm, Token token,
986                            LValue rightL, LValue leftL,
987                            ExpressionParser.GetFrame frameGetter
988                            ) throws ParseException {
989        String op = token.image;
990        Value right = rightL.interiorGetValue();
991        Value left = leftL.interiorGetValue();
992        if ((right instanceof StringReference) ||
993                              (left instanceof StringReference)) {
994            if (op.equals("+")) {
995                // If one is an ObjectRef, we will need to invoke
996                // toString on it, so we need the thread.
997                return make(vm, stringValue(rightL, frameGetter) +
998                            stringValue(leftL, frameGetter));
999            }
1000        }
1001        if ((right instanceof ObjectReference) ||
1002                              (left instanceof ObjectReference)) {
1003            if (op.equals("==")) {
1004                return make(vm, right.equals(left));
1005            } else if (op.equals("!=")) {
1006                return make(vm, !right.equals(left));
1007            } else {
1008                throw new ParseException("Invalid operation '" +
1009                                         op + "' on an Object");
1010            }
1011        }
1012        if ((right instanceof BooleanValue) ||
1013                              (left instanceof BooleanValue)) {
1014            throw new ParseException("Invalid operation '" +
1015                                     op + "' on a Boolean");
1016        }
1017        // from here on, we know it is a integer kind of type
1018        PrimitiveValue primRight = (PrimitiveValue)right;
1019        PrimitiveValue primLeft = (PrimitiveValue)left;
1020        if ((primRight instanceof DoubleValue) ||
1021                              (primLeft instanceof DoubleValue)) {
1022            double rr = primRight.doubleValue();
1023            double ll = primLeft.doubleValue();
1024            double res;
1025            if (op.equals("+")) {
1026                res = rr + ll;
1027            } else if (op.equals("-")) {
1028                res = rr - ll;
1029            } else if (op.equals("*")) {
1030                res = rr * ll;
1031            } else if (op.equals("/")) {
1032                res = rr / ll;
1033            } else {
1034                throw new ParseException("Unknown operation: " + op);
1035            }
1036            return make(vm, res);
1037        }
1038        if ((primRight instanceof FloatValue) ||
1039                              (primLeft instanceof FloatValue)) {
1040            float rr = primRight.floatValue();
1041            float ll = primLeft.floatValue();
1042            float res;
1043            if (op.equals("+")) {
1044                res = rr + ll;
1045            } else if (op.equals("-")) {
1046                res = rr - ll;
1047            } else if (op.equals("*")) {
1048                res = rr * ll;
1049            } else if (op.equals("/")) {
1050                res = rr / ll;
1051            } else {
1052                throw new ParseException("Unknown operation: " + op);
1053            }
1054            return make(vm, res);
1055        }
1056        if ((primRight instanceof LongValue) ||
1057                              (primLeft instanceof LongValue)) {
1058            long rr = primRight.longValue();
1059            long ll = primLeft.longValue();
1060            long res;
1061            if (op.equals("+")) {
1062                res = rr + ll;
1063            } else if (op.equals("-")) {
1064                res = rr - ll;
1065            } else if (op.equals("*")) {
1066                res = rr * ll;
1067            } else if (op.equals("/")) {
1068                res = rr / ll;
1069            } else {
1070                throw new ParseException("Unknown operation: " + op);
1071            }
1072            return make(vm, res);
1073        } else {
1074            int rr = primRight.intValue();
1075            int ll = primLeft.intValue();
1076            int res;
1077            if (op.equals("+")) {
1078                res = rr + ll;
1079            } else if (op.equals("-")) {
1080                res = rr - ll;
1081            } else if (op.equals("*")) {
1082                res = rr * ll;
1083            } else if (op.equals("/")) {
1084                res = rr / ll;
1085            } else {
1086                throw new ParseException("Unknown operation: " + op);
1087            }
1088            return make(vm, res);
1089        }
1090    }
1091
1092    static LValue operation(VirtualMachine vm, Token token, LValue rightL,
1093            ExpressionParser.GetFrame frameGetter)
1094            throws ParseException {
1095        String op = token.image;
1096        Value right = rightL.interiorGetValue();
1097        if (right instanceof ObjectReference) {
1098            throw new ParseException("Invalid operation '" + op
1099                    + "' on an Object");
1100        }
1101        if (right instanceof BooleanValue) {
1102            if (op.equals("!")) {
1103                boolean rr = ((BooleanValue) right).value();
1104                return make(vm, !rr);
1105            }
1106            throw new ParseException("Invalid operation '" + op
1107                    + "' on a Boolean");
1108        }
1109        // from here on, we know it is a integer kind of type
1110        PrimitiveValue primRight = (PrimitiveValue) right;
1111        if (primRight instanceof DoubleValue) {
1112            double rr = primRight.doubleValue();
1113            double res;
1114            if (op.equals("+")) {
1115                res = rr;
1116            } else if (op.equals("-")) {
1117                res = -rr;
1118            } else {
1119                throw new ParseException("Unknown operation: " + op);
1120            }
1121            return make(vm, res);
1122        }
1123        if (primRight instanceof FloatValue) {
1124            float rr = primRight.floatValue();
1125            float res;
1126            if (op.equals("+")) {
1127                res = rr;
1128            } else if (op.equals("-")) {
1129                res = -rr;
1130            } else {
1131                throw new ParseException("Unknown operation: " + op);
1132            }
1133            return make(vm, res);
1134        }
1135        if (primRight instanceof LongValue) {
1136            long rr = primRight.longValue();
1137            long res;
1138            if (op.equals("+")) {
1139                res = rr;
1140            } else if (op.equals("-")) {
1141                res = -rr;
1142            } else if (op.equals("~")) {
1143                res = ~rr;
1144            } else {
1145                throw new ParseException("Unknown operation: " + op);
1146            }
1147            return make(vm, res);
1148        } else {
1149            int rr = primRight.intValue();
1150            int res;
1151            if (op.equals("+")) {
1152                res = rr;
1153            } else if (op.equals("-")) {
1154                res = -rr;
1155            } else if (op.equals("~")) {
1156                res = ~rr;
1157            } else {
1158                throw new ParseException("Unknown operation: " + op);
1159            }
1160            return make(vm, res);
1161        }
1162    }
1163}
1164