LValue.java revision 10444:f08705540498
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