History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/tools/example/debug/expr/LValue.java (1 of 18352 files). Show all

Unified · Split

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