1 /* 2 * Copyright (c) 1998, 2003, 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 public String toString() { 160 try { 161 return interiorGetValue().toString(); 162 } catch (ParseException e) { 163 return "<Parse Exception>"; 164 } 165 } 166 167 static final int STATIC = 0; 168 static final int INSTANCE = 1; 169 170 static Field fieldByName(ReferenceType refType, String name, int kind) { 171 /* 172 * TO DO: Note that this currently fails to find superclass 173 * or implemented interface fields. This is due to a temporary 174 * limititation of RefType.fieldByName. Once that method is 175 * fixed, superclass fields will be found. 176 */ 177 Field field = refType.fieldByName(name); 178 if (field != null) { 179 boolean isStatic = field.isStatic(); 180 if (((kind == STATIC) && !isStatic) || 181 ((kind == INSTANCE) && isStatic)) { 182 field = null; 183 } 184 } 185 /*** 186 System.err.println("fieldByName: " + refType.name() + " " + 187 name + " " + 188 kind + " " + 189 (field != null)); 190 ***/ 191 return field; 192 } 193 194 static List<Method> methodsByName(ReferenceType refType, 195 String name, int kind) { 196 List<Method> list = refType.methodsByName(name); 197 Iterator<Method> iter = list.iterator(); 198 while (iter.hasNext()) { 199 Method method = iter.next(); 200 boolean isStatic = method.isStatic(); 201 if (((kind == STATIC) && !isStatic) || 202 ((kind == INSTANCE) && isStatic)) { 203 iter.remove(); 204 } 205 } 206 return list; 207 } 208 209 static List<String> primitiveTypeNames = new ArrayList<String>(); 210 static { 211 primitiveTypeNames.add("boolean"); 212 primitiveTypeNames.add("byte"); 213 primitiveTypeNames.add("char"); 214 primitiveTypeNames.add("short"); 215 primitiveTypeNames.add("int"); 216 primitiveTypeNames.add("long"); 217 primitiveTypeNames.add("float"); 218 primitiveTypeNames.add("double"); 219 } 220 221 222 static final int SAME = 0; 223 static final int ASSIGNABLE = 1; 224 static final int DIFFERENT = 2; 225 /* 226 * Return SAME, DIFFERENT or ASSIGNABLE. 227 * SAME means each arg type is the same as type of the corr. arg. 228 * ASSIGNABLE means that not all the pairs are the same, but 229 * for those that aren't, at least the argType is assignable 230 * from the type of the argument value. 231 * DIFFERENT means that in at least one pair, the 232 * argType is not assignable from the type of the argument value. 233 * IE, one is an Apple and the other is an Orange. 234 */ 235 static int argumentsMatch(List<Type> argTypes, List<Value> arguments) { 236 if (argTypes.size() != arguments.size()) { 237 return DIFFERENT; 238 } 239 240 Iterator<Type> typeIter = argTypes.iterator(); 241 Iterator<Value> valIter = arguments.iterator(); 242 int result = SAME; 243 244 // If any pair aren't the same, change the 245 // result to ASSIGNABLE. If any pair aren't 246 // assignable, return DIFFERENT 247 while (typeIter.hasNext()) { 248 Type argType = typeIter.next(); 249 Value value = valIter.next(); 250 if (value == null) { 251 // Null values can be passed to any non-primitive argument 252 if (primitiveTypeNames.contains(argType.name())) { 253 return DIFFERENT; 254 } 255 // Else, we will assume that a null value 256 // exactly matches an object type. 257 } 258 if (!value.type().equals(argType)) { 259 if (isAssignableTo(value.type(), argType)) { 260 result = ASSIGNABLE; 261 } else { 262 return DIFFERENT; 263 } 264 } 265 } 266 return result; 267 } 268 269 270 // These is...AssignableTo methods are based on similar code in the JDI 271 // implementations of ClassType, ArrayType, and InterfaceType 272 273 static boolean isComponentAssignable(Type fromType, Type toType) { 274 if (fromType instanceof PrimitiveType) { 275 // Assignment of primitive arrays requires identical 276 // component types. 277 return fromType.equals(toType); 278 } 279 if (toType instanceof PrimitiveType) { 280 return false; 281 } 282 // Assignment of object arrays requires availability 283 // of widening conversion of component types 284 return isAssignableTo(fromType, toType); 285 } 286 287 static boolean isArrayAssignableTo(ArrayType fromType, Type toType) { 288 if (toType instanceof ArrayType) { 289 try { 290 Type toComponentType = ((ArrayType)toType).componentType(); 291 return isComponentAssignable(fromType.componentType(), toComponentType); 292 } catch (ClassNotLoadedException e) { 293 // One or both component types has not yet been 294 // loaded => can't assign 295 return false; 296 } 297 } 298 if (toType instanceof InterfaceType) { 299 // Only valid InterfaceType assignee is Cloneable 300 return toType.name().equals("java.lang.Cloneable"); 301 } 302 // Only valid ClassType assignee is Object 303 return toType.name().equals("java.lang.Object"); 304 } 305 306 static boolean isAssignableTo(Type fromType, Type toType) { 307 if (fromType.equals(toType)) { 308 return true; 309 } 310 311 // If one is boolean, so must be the other. 312 if (fromType instanceof BooleanType) { 313 if (toType instanceof BooleanType) { 314 return true; 315 } 316 return false; 317 } 318 if (toType instanceof BooleanType) { 319 return false; 320 } 321 322 // Other primitive types are intermixable only with each other. 323 if (fromType instanceof PrimitiveType) { 324 if (toType instanceof PrimitiveType) { 325 return true; 326 } 327 return false; 328 } 329 if (toType instanceof PrimitiveType) { 330 return false; 331 } 332 333 // neither one is primitive. 334 if (fromType instanceof ArrayType) { 335 return isArrayAssignableTo((ArrayType)fromType, toType); 336 } 337 List<InterfaceType> interfaces; 338 if (fromType instanceof ClassType) { 339 ClassType superclazz = ((ClassType)fromType).superclass(); 340 if ((superclazz != null) && isAssignableTo(superclazz, toType)) { 341 return true; 342 } 343 interfaces = ((ClassType)fromType).interfaces(); 344 } else { 345 // fromType must be an InterfaceType 346 interfaces = ((InterfaceType)fromType).superinterfaces(); 347 } 348 for (InterfaceType interfaze : interfaces) { 349 if (isAssignableTo(interfaze, toType)) { 350 return true; 351 } 352 } 353 return false; 354 } 355 356 static Method resolveOverload(List<Method> overloads, 357 List<Value> arguments) 358 throws ParseException { 359 360 // If there is only one method to call, we'll just choose 361 // that without looking at the args. If they aren't right 362 // the invoke will return a better error message than we 363 // could generate here. 364 if (overloads.size() == 1) { 365 return overloads.get(0); 366 } 367 368 // Resolving overloads is beyond the scope of this exercise. 369 // So, we will look for a method that matches exactly the 370 // types of the arguments. If we can't find one, then 371 // if there is exactly one method whose param types are assignable 372 // from the arg types, we will use that. Otherwise, 373 // it is an error. We won't guess which of multiple possible 374 // methods to call. And, since casts aren't implemented, 375 // the user can't use them to pick a particular overload to call. 376 // IE, the user is out of luck in this case. 377 Method retVal = null; 378 int assignableCount = 0; 379 for (Method mm : overloads) { 380 List<Type> argTypes; 381 try { 382 argTypes = mm.argumentTypes(); 383 } catch (ClassNotLoadedException ee) { 384 // This probably won't happen for the 385 // method that we are really supposed to 386 // call. 387 continue; 388 } 389 int compare = argumentsMatch(argTypes, arguments); 390 if (compare == SAME) { 391 return mm; 392 } 393 if (compare == DIFFERENT) { 394 continue; 395 } 396 // Else, it is assignable. Remember it. 397 retVal = mm; 398 assignableCount++; 399 } 400 401 // At this point, we didn't find an exact match, 402 // but we found one for which the args are assignable. 403 // 404 if (retVal != null) { 405 if (assignableCount == 1) { 406 return retVal; 407 } 408 throw new ParseException("Arguments match multiple methods"); 409 } 410 throw new ParseException("Arguments match no method"); 411 } 412 413 private static class LValueLocal extends LValue { 414 final StackFrame frame; 415 final LocalVariable var; 416 417 LValueLocal(StackFrame frame, LocalVariable var) { 418 this.frame = frame; 419 this.var = var; 420 } 421 422 Value getValue() { 423 if (jdiValue == null) { 424 jdiValue = frame.getValue(var); 425 } 426 return jdiValue; 427 } 428 429 void setValue0(Value val) throws InvalidTypeException, 430 ClassNotLoadedException { 431 frame.setValue(var, val); 432 jdiValue = val; 433 } 434 435 void invokeWith(List<Value> arguments) throws ParseException { 436 throw new ParseException(var.name() + " is not a method"); 437 } 438 } 439 440 private static class LValueInstanceMember extends LValue { 441 final ObjectReference obj; 442 final ThreadReference thread; 443 final Field matchingField; 444 final List<Method> overloads; 445 Method matchingMethod = null; 446 List<Value> methodArguments = null; 447 448 LValueInstanceMember(Value value, 449 String memberName, 450 ThreadReference thread) throws ParseException { 451 if (!(value instanceof ObjectReference)) { 452 throw new ParseException( 453 "Cannot access field of primitive type: " + value); 454 } 455 this.obj = (ObjectReference)value; 456 this.thread = thread; 457 ReferenceType refType = obj.referenceType(); 458 /* 459 * Can't tell yet whether this LValue will be accessed as a 460 * field or method, so we keep track of all the possibilities 461 */ 462 matchingField = LValue.fieldByName(refType, memberName, 463 LValue.INSTANCE); 464 overloads = LValue.methodsByName(refType, memberName, 465 LValue.INSTANCE); 466 if ((matchingField == null) && overloads.size() == 0) { 467 throw new ParseException("No instance field or method with the name " 468 + memberName + " in " + refType.name()); 469 } 470 } 471 472 Value getValue() throws InvocationException, InvalidTypeException, 473 ClassNotLoadedException, IncompatibleThreadStateException, 474 ParseException { 475 if (jdiValue != null) { 476 return jdiValue; 477 } 478 if (matchingMethod == null) { 479 if (matchingField == null) { 480 throw new ParseException("No such field in " + obj.referenceType().name()); 481 } 482 return jdiValue = obj.getValue(matchingField); 483 } else { 484 return jdiValue = obj.invokeMethod(thread, matchingMethod, methodArguments, 0); 485 } 486 } 487 488 void setValue0(Value val) throws ParseException, 489 InvalidTypeException, 490 ClassNotLoadedException { 491 if (matchingMethod != null) { 492 throw new ParseException("Cannot assign to a method invocation"); 493 } 494 obj.setValue(matchingField, val); 495 jdiValue = val; 496 } 497 498 void invokeWith(List<Value> arguments) throws ParseException { 499 if (matchingMethod != null) { 500 throw new ParseException("Invalid consecutive invocations"); 501 } 502 methodArguments = arguments; 503 matchingMethod = LValue.resolveOverload(overloads, arguments); 504 } 505 } 506 507 private static class LValueStaticMember extends LValue { 508 final ReferenceType refType; 509 final ThreadReference thread; 510 final Field matchingField; 511 final List<Method> overloads; 512 Method matchingMethod = null; 513 List<Value> methodArguments = null; 514 515 LValueStaticMember(ReferenceType refType, 516 String memberName, 517 ThreadReference thread) throws ParseException { 518 this.refType = refType; 519 this.thread = thread; 520 /* 521 * Can't tell yet whether this LValue will be accessed as a 522 * field or method, so we keep track of all the possibilities 523 */ 524 matchingField = LValue.fieldByName(refType, memberName, 525 LValue.STATIC); 526 overloads = LValue.methodsByName(refType, memberName, 527 LValue.STATIC); 528 if ((matchingField == null) && overloads.size() == 0) { 529 throw new ParseException("No static field or method with the name " 530 + memberName + " in " + refType.name()); 531 } 532 } 533 534 Value getValue() throws InvocationException, InvalidTypeException, 535 ClassNotLoadedException, IncompatibleThreadStateException, 536 ParseException { 537 if (jdiValue != null) { 538 return jdiValue; 539 } 540 if (matchingMethod == null) { 541 return jdiValue = refType.getValue(matchingField); 542 } else if (refType instanceof ClassType) { 543 ClassType clazz = (ClassType)refType; 544 return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0); 545 } else { 546 throw new InvalidTypeException("Cannot invoke static method on " + 547 refType.name()); 548 } 549 } 550 551 void setValue0(Value val) 552 throws ParseException, InvalidTypeException, 553 ClassNotLoadedException { 554 if (matchingMethod != null) { 555 throw new ParseException("Cannot assign to a method invocation"); 556 } 557 if (!(refType instanceof ClassType)) { 558 throw new ParseException( 559 "Cannot set interface field: " + refType); 560 } 561 ((ClassType)refType).setValue(matchingField, val); 562 jdiValue = val; 563 } 564 565 void invokeWith(List<Value> arguments) throws ParseException { 566 if (matchingMethod != null) { 567 throw new ParseException("Invalid consecutive invocations"); 568 } 569 methodArguments = arguments; 570 matchingMethod = LValue.resolveOverload(overloads, arguments); 571 } 572 } 573 574 private static class LValueArrayLength extends LValue { 575 /* 576 * Since one can code "int myLen = myArray.length;", 577 * one might expect that these JDI calls would get a Value 578 * object for the length of an array in the debugee: 579 * Field xxx = ArrayType.fieldByName("length") 580 * Value lenVal= ArrayReference.getValue(xxx) 581 * 582 * However, this doesn't work because the array length isn't 583 * really stored as a field, and can't be accessed as such 584 * via JDI. Instead, the arrayRef.length() method has to be 585 * used. 586 */ 587 final ArrayReference arrayRef; 588 LValueArrayLength (ArrayReference value) { 589 this.arrayRef = value; 590 } 591 592 Value getValue() { 593 if (jdiValue == null) { 594 jdiValue = arrayRef.virtualMachine().mirrorOf(arrayRef.length()); 595 } 596 return jdiValue; 597 } 598 599 void setValue0(Value value) throws ParseException { 600 throw new ParseException("Cannot set constant: " + value); 601 } 602 603 void invokeWith(List<Value> arguments) throws ParseException { 604 throw new ParseException("Array element is not a method"); 605 } 606 } 607 608 private static class LValueArrayElement extends LValue { 609 final ArrayReference array; 610 final int index; 611 612 LValueArrayElement(Value value, int index) throws ParseException { 613 if (!(value instanceof ArrayReference)) { 614 throw new ParseException( 615 "Must be array type: " + value); 616 } 617 this.array = (ArrayReference)value; 618 this.index = index; 619 } 620 621 Value getValue() { 622 if (jdiValue == null) { 623 jdiValue = array.getValue(index); 624 } 625 return jdiValue; 626 } 627 628 void setValue0(Value val) throws InvalidTypeException, 629 ClassNotLoadedException { 630 array.setValue(index, val); 631 jdiValue = val; 632 } 633 634 void invokeWith(List<Value> arguments) throws ParseException { 635 throw new ParseException("Array element is not a method"); 636 } 637 } 638 639 private static class LValueConstant extends LValue { 640 final Value value; 641 642 LValueConstant(Value value) { 643 this.value = value; 644 } 645 646 Value getValue() { 647 if (jdiValue == null) { 648 jdiValue = value; 649 } 650 return jdiValue; 651 } 652 653 void setValue0(Value val) throws ParseException { 654 throw new ParseException("Cannot set constant: " + value); 655 } 656 657 void invokeWith(List<Value> arguments) throws ParseException { 658 throw new ParseException("Constant is not a method"); 659 } 660 } 661 662 static LValue make(VirtualMachine vm, boolean val) { 663 return new LValueConstant(vm.mirrorOf(val)); 664 } 665 666 static LValue make(VirtualMachine vm, byte val) { 667 return new LValueConstant(vm.mirrorOf(val)); 668 } 669 670 static LValue make(VirtualMachine vm, char val) { 671 return new LValueConstant(vm.mirrorOf(val)); 672 } 673 674 static LValue make(VirtualMachine vm, short val) { 675 return new LValueConstant(vm.mirrorOf(val)); 676 } 677 678 static LValue make(VirtualMachine vm, int val) { 679 return new LValueConstant(vm.mirrorOf(val)); 680 } 681 682 static LValue make(VirtualMachine vm, long val) { 683 return new LValueConstant(vm.mirrorOf(val)); 684 } 685 686 static LValue make(VirtualMachine vm, float val) { 687 return new LValueConstant(vm.mirrorOf(val)); 688 } 689 690 static LValue make(VirtualMachine vm, double val) { 691 return new LValueConstant(vm.mirrorOf(val)); 692 } 693 694 static LValue make(VirtualMachine vm, String val) throws ParseException { 695 return new LValueConstant(vm.mirrorOf(val)); 696 } 697 698 static LValue makeBoolean(VirtualMachine vm, Token token) { 699 return make(vm, token.image.charAt(0) == 't'); 700 } 701 702 static LValue makeCharacter(VirtualMachine vm, Token token) { 703 return make(vm, token.image.charAt(1)); 704 } 705 706 static LValue makeFloat(VirtualMachine vm, Token token) { 707 return make(vm, Float.valueOf(token.image).floatValue()); 708 } 709 710 static LValue makeDouble(VirtualMachine vm, Token token) { 711 return make(vm, Double.valueOf(token.image).doubleValue()); 712 } 713 714 static LValue makeInteger(VirtualMachine vm, Token token) { 715 return make(vm, Integer.parseInt(token.image)); 716 } 717 718 static LValue makeShort(VirtualMachine vm, Token token) { 719 return make(vm, Short.parseShort(token.image)); 720 } 721 722 static LValue makeLong(VirtualMachine vm, Token token) { 723 return make(vm, Long.parseLong(token.image)); 724 } 725 726 static LValue makeByte(VirtualMachine vm, Token token) { 727 return make(vm, Byte.parseByte(token.image)); 728 } 729 730 static LValue makeString(VirtualMachine vm, 731 Token token) throws ParseException { 732 int len = token.image.length(); 733 return make(vm, token.image.substring(1,len-1)); 734 } 735 736 static LValue makeNull(VirtualMachine vm, 737 Token token) throws ParseException { 738 return new LValueConstant(null); 739 } 740 741 static LValue makeThisObject(VirtualMachine vm, 742 ExpressionParser.GetFrame frameGetter, 743 Token token) throws ParseException { 744 if (frameGetter == null) { 745 throw new ParseException("No current thread"); 746 } else { 747 try { 748 StackFrame frame = frameGetter.get(); 749 ObjectReference thisObject = frame.thisObject(); 750 751 if (thisObject==null) { 752 throw new ParseException( 753 "No 'this'. In native or static method"); 754 } else { 755 return new LValueConstant(thisObject); 756 } 757 } catch (IncompatibleThreadStateException exc) { 758 throw new ParseException("Thread not suspended"); 759 } 760 } 761 } 762 763 static LValue makeNewObject(VirtualMachine vm, 764 ExpressionParser.GetFrame frameGetter, 765 String className, List<Value> arguments) throws ParseException { 766 List<ReferenceType> classes = vm.classesByName(className); 767 if (classes.size() == 0) { 768 throw new ParseException("No class named: " + className); 769 } 770 771 if (classes.size() > 1) { 772 throw new ParseException("More than one class named: " + 773 className); 774 } 775 ReferenceType refType = classes.get(0); 776 777 778 if (!(refType instanceof ClassType)) { 779 throw new ParseException("Cannot create instance of interface " + 780 className); 781 } 782 783 ClassType classType = (ClassType)refType; 784 List<Method> methods = new ArrayList<Method>(classType.methods()); // writable 785 Iterator<Method> iter = methods.iterator(); 786 while (iter.hasNext()) { 787 Method method = iter.next(); 788 if (!method.isConstructor()) { 789 iter.remove(); 790 } 791 } 792 Method constructor = LValue.resolveOverload(methods, arguments); 793 794 ObjectReference newObject; 795 try { 796 ThreadReference thread = frameGetter.get().thread(); 797 newObject = classType.newInstance(thread, constructor, arguments, 0); 798 } catch (InvocationException ie) { 799 throw new ParseException("Exception in " + className + " constructor: " + 800 ie.exception().referenceType().name()); 801 } catch (IncompatibleThreadStateException exc) { 802 throw new ParseException("Thread not suspended"); 803 } catch (Exception e) { 804 /* 805 * TO DO: Better error handling 806 */ 807 throw new ParseException("Unable to create " + className + " instance"); 808 } 809 return new LValueConstant(newObject); 810 } 811 812 private static LValue nFields(LValue lval, 813 StringTokenizer izer, 814 ThreadReference thread) 815 throws ParseException { 816 if (!izer.hasMoreTokens()) { 817 return lval; 818 } else { 819 return nFields(lval.memberLValue(izer.nextToken(), thread), izer, thread); 820 } 821 } 822 823 static LValue makeName(VirtualMachine vm, 824 ExpressionParser.GetFrame frameGetter, 825 String name) throws ParseException { 826 StringTokenizer izer = new StringTokenizer(name, "."); 827 String first = izer.nextToken(); 828 // check local variables 829 if (frameGetter != null) { 830 try { 831 StackFrame frame = frameGetter.get(); 832 ThreadReference thread = frame.thread(); 833 LocalVariable var; 834 try { 835 var = frame.visibleVariableByName(first); 836 } catch (AbsentInformationException e) { 837 var = null; 838 } 839 if (var != null) { 840 return nFields(new LValueLocal(frame, var), izer, thread); 841 } else { 842 ObjectReference thisObject = frame.thisObject(); 843 if (thisObject != null) { 844 // check if it is a field of 'this' 845 LValue thisLValue = new LValueConstant(thisObject); 846 LValue fv; 847 try { 848 fv = thisLValue.memberLValue(first, thread); 849 } catch (ParseException exc) { 850 fv = null; 851 } 852 if (fv != null) { 853 return nFields(fv, izer, thread); 854 } 855 } 856 } 857 // check for class name 858 while (izer.hasMoreTokens()) { 859 List<ReferenceType> classes = vm.classesByName(first); 860 if (classes.size() > 0) { 861 if (classes.size() > 1) { 862 throw new ParseException("More than one class named: " + 863 first); 864 } else { 865 ReferenceType refType = classes.get(0); 866 LValue lval = new LValueStaticMember(refType, 867 izer.nextToken(), thread); 868 return nFields(lval, izer, thread); 869 } 870 } 871 first = first + '.' + izer.nextToken(); 872 } 873 } catch (IncompatibleThreadStateException exc) { 874 throw new ParseException("Thread not suspended"); 875 } 876 } 877 throw new ParseException("Name unknown: " + name); 878 } 879 880 static String stringValue(LValue lval, ExpressionParser.GetFrame frameGetter 881 ) throws ParseException { 882 Value val = lval.getMassagedValue(frameGetter); 883 if (val == null) { 884 return "null"; 885 } 886 if (val instanceof StringReference) { 887 return ((StringReference)val).value(); 888 } 889 return val.toString(); // is this correct in all cases? 890 } 891 892 static LValue booleanOperation(VirtualMachine vm, Token token, 893 LValue rightL, 894 LValue leftL) throws ParseException { 895 String op = token.image; 896 Value right = rightL.interiorGetValue(); 897 Value left = leftL.interiorGetValue(); 898 if ( !(right instanceof PrimitiveValue) || 899 !(left instanceof PrimitiveValue) ) { 900 if (op.equals("==")) { 901 return make(vm, right.equals(left)); 902 } else if (op.equals("!=")) { 903 return make(vm, !right.equals(left)); 904 } else { 905 throw new ParseException("Operands or '" + op + 906 "' must be primitive"); 907 } 908 } 909 // can compare any numeric doubles 910 double rr = ((PrimitiveValue)right).doubleValue(); 911 double ll = ((PrimitiveValue)left).doubleValue(); 912 boolean res; 913 if (op.equals("<")) { 914 res = rr < ll; 915 } else if (op.equals(">")) { 916 res = rr > ll; 917 } else if (op.equals("<=")) { 918 res = rr <= ll; 919 } else if (op.equals(">=")) { 920 res = rr >= ll; 921 } else if (op.equals("==")) { 922 res = rr == ll; 923 } else if (op.equals("!=")) { 924 res = rr != ll; 925 } else { 926 throw new ParseException("Unknown operation: " + op); 927 } 928 return make(vm, res); 929 } 930 931 static LValue operation(VirtualMachine vm, Token token, 932 LValue rightL, LValue leftL, 933 ExpressionParser.GetFrame frameGetter 934 ) throws ParseException { 935 String op = token.image; 936 Value right = rightL.interiorGetValue(); 937 Value left = leftL.interiorGetValue(); 938 if ((right instanceof StringReference) || 939 (left instanceof StringReference)) { 940 if (op.equals("+")) { 941 // If one is an ObjectRef, we will need to invoke 942 // toString on it, so we need the thread. 943 return make(vm, stringValue(rightL, frameGetter) + 944 stringValue(leftL, frameGetter)); 945 } 946 } 947 if ((right instanceof ObjectReference) || 948 (left instanceof ObjectReference)) { 949 if (op.equals("==")) { 950 return make(vm, right.equals(left)); 951 } else if (op.equals("!=")) { 952 return make(vm, !right.equals(left)); 953 } else { 954 throw new ParseException("Invalid operation '" + 955 op + "' on an Object"); 956 } 957 } 958 if ((right instanceof BooleanValue) || 959 (left instanceof BooleanValue)) { 960 throw new ParseException("Invalid operation '" + 961 op + "' on a Boolean"); 962 } 963 // from here on, we know it is a integer kind of type 964 PrimitiveValue primRight = (PrimitiveValue)right; 965 PrimitiveValue primLeft = (PrimitiveValue)left; 966 if ((primRight instanceof DoubleValue) || 967 (primLeft instanceof DoubleValue)) { 968 double rr = primRight.doubleValue(); 969 double ll = primLeft.doubleValue(); 970 double res; 971 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 if ((primRight instanceof FloatValue) || 985 (primLeft instanceof FloatValue)) { 986 float rr = primRight.floatValue(); 987 float ll = primLeft.floatValue(); 988 float res; 989 if (op.equals("+")) { 990 res = rr + ll; 991 } else if (op.equals("-")) { 992 res = rr - ll; 993 } else if (op.equals("*")) { 994 res = rr * ll; 995 } else if (op.equals("/")) { 996 res = rr / ll; 997 } else { 998 throw new ParseException("Unknown operation: " + op); 999 } 1000 return make(vm, res); 1001 } 1002 if ((primRight instanceof LongValue) || 1003 (primLeft instanceof LongValue)) { 1004 long rr = primRight.longValue(); 1005 long ll = primLeft.longValue(); 1006 long res; 1007 if (op.equals("+")) { 1008 res = rr + ll; 1009 } else if (op.equals("-")) { 1010 res = rr - ll; 1011 } else if (op.equals("*")) { 1012 res = rr * ll; 1013 } else if (op.equals("/")) { 1014 res = rr / ll; 1015 } else { 1016 throw new ParseException("Unknown operation: " + op); 1017 } 1018 return make(vm, res); 1019 } else { 1020 int rr = primRight.intValue(); 1021 int ll = primLeft.intValue(); 1022 int res; 1023 if (op.equals("+")) { 1024 res = rr + ll; 1025 } else 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 { 1032 throw new ParseException("Unknown operation: " + op); 1033 } 1034 return make(vm, res); 1035 } 1036 } 1037 }