001package daikon.chicory; 002 003import daikon.Chicory; 004import daikon.Daikon.BugInDaikon; 005import daikon.plumelib.bcelutil.SimpleLog; 006import daikon.plumelib.reflection.ReflectionPlume; 007import daikon.plumelib.reflection.Signatures; 008import java.lang.reflect.Constructor; 009import java.lang.reflect.Field; 010import java.lang.reflect.Member; 011import java.lang.reflect.Method; 012import java.lang.reflect.Modifier; 013import java.util.ArrayList; 014import java.util.Arrays; 015import java.util.Collections; 016import java.util.EnumSet; 017import java.util.HashSet; 018import java.util.Iterator; 019import java.util.List; 020import java.util.Set; 021import java.util.StringJoiner; 022import java.util.regex.Matcher; 023import org.checkerframework.checker.interning.qual.Interned; 024import org.checkerframework.checker.lock.qual.GuardSatisfied; 025import org.checkerframework.checker.nullness.qual.NonNull; 026import org.checkerframework.checker.nullness.qual.Nullable; 027import org.checkerframework.checker.nullness.qual.RequiresNonNull; 028import org.checkerframework.checker.signature.qual.BinaryName; 029import org.checkerframework.dataflow.qual.Pure; 030import org.checkerframework.dataflow.qual.SideEffectFree; 031 032/** 033 * Each DaikonVariableInfo object is a node in the tree structure of the variables in the target 034 * application. In general, the variable a will be the parent of the variables a.b and a.c in the 035 * tree, where b and c are fields in a's class. There is such a tree structure associated with every 036 * program point. 037 * 038 * <p>Each node can have any non-negative number of child nodes. DaikonVariableInfo is an abstract 039 * class. Its subtypes are designed to represent specific types of variables, such as arguments, 040 * arrays, etc. 041 * 042 * <p>The tree structure is built in the DeclWriter and traversed in the DTraceWriter. 043 * 044 * <p>This architecture makes it possible to avoid the issue of "traversal pattern duplication" in 045 * which both the DeclWriter and DTraceWriter must traverse the target application's variables 046 * identically. 047 */ 048public abstract class DaikonVariableInfo 049 implements Iterable<DaikonVariableInfo>, Comparable<DaikonVariableInfo> { 050 051 /** Enable experimental techniques on static constants. */ 052 public static boolean dkconfig_constant_infer = false; 053 054 /** The variable name. Sensible for all subtypes except RootInfo. */ 055 private final @Interned String name; 056 057 /** The child nodes. */ 058 public List<DaikonVariableInfo> children; 059 060 /** True iff this variable is an array. */ 061 protected final boolean isArray; 062 063 /** Print debug information about the variables. */ 064 static SimpleLog debug_vars = new SimpleLog(false); 065 066 private static SimpleLog debug_array = new SimpleLog(true); 067 068 /** Default string for comparability info. */ 069 private static final String compareInfoDefaultString = "22"; 070 071 // It's not enough to use one of the following 3 strings. 072 // You also need to set the flags that are returned by get_var_flags()! 073 074 /** Indicates that a given variable is non-null. */ 075 protected static final String isNonNullString = " # isNonNull=true"; 076 077 /** Indicates that a given variable is a parameter to a method. */ 078 protected static final String isParamString = " # isParam=true"; 079 080 /** Indicates that a given variable is non-null and a parameter. */ 081 protected static final String isNonNullParamString = " # isNonNull=true, isParam=true"; 082 083 // Certain hardcoded class names 084 protected static final String classClassName = "java.lang.Class"; 085 protected static final String stringClassName = "java.lang.String"; 086 087 // Suffix for "typeOf" (CLASSNAME) variables that represent a class, 088 // eg, "foo.getClass().getName()". 089 public static final String class_suffix = ".getClass().getName()"; 090 091 public static final String class_suffix_relative_name = class_suffix.substring(1); 092 093 /** 094 * The printed type that will appear in the .decls declaration. May include aux information at the 095 * end, such as isParamString. 096 * 097 * @see #getTypeName() 098 * @see #getTypeNameOnly() 099 */ 100 protected String typeName; 101 102 /** The printed representation type that will appear in the .decls declaration. */ 103 protected String repTypeName; 104 105 /** The printed comparability information that will appear in the .decls declaration. */ 106 protected String compareInfoString = compareInfoDefaultString; 107 108 /** Value of static constants. Access via {@link #get_const_val()} method. */ 109 @Nullable String const_val = null; 110 111 /** Arguments used to create a function. Access via {@link #get_function_args()} method. */ 112 @Nullable String function_args = null; 113 114 // It seems that declShouldPrint and dtraceShouldPrint always have the same value. 115 /** True iff the DeclWriter should print this variable. */ 116 protected boolean declShouldPrint = true; 117 118 /** True iff the DTraceWriter should print this variable. */ 119 protected boolean dtraceShouldPrint = true; 120 121 /** True iff the DTraceWriter should print the children of this variable. */ 122 protected boolean dtraceShouldPrintChildren = true; 123 124 /** 125 * If false, every field in an instrumented class is visible. If true, use standard Java behavior 126 * (if the field is in a class in a different package, it is only visible if public, etc.). 127 */ 128 public static boolean std_visibility = false; 129 130 /** 131 * Set of fully qualified static variable names for this ppt. Used to ensure that each static is 132 * only included once (regardless of how many other variables may include its declaring class). 133 */ 134 protected static Set<String> ppt_statics = new HashSet<>(); 135 136 /** 137 * Constructs a non-array-type DaikonVariableInfo object. 138 * 139 * @param theName the name of the variable 140 * @param typeName the name of the type 141 * @param repTypeName the name of the representation type 142 */ 143 protected DaikonVariableInfo(String theName, String typeName, String repTypeName) { 144 this(theName, typeName, repTypeName, false); 145 } 146 147 /** 148 * Constructs a DaikonVariableInfo object. 149 * 150 * @param theName the variable's name 151 * @param arr true iff the variable is an array 152 */ 153 protected DaikonVariableInfo(String theName, String typeName, String repTypeName, boolean arr) { 154 // Intern the names because there will be many of the 155 // same variable names at different program points within 156 // the same class. 157 name = theName.intern(); 158 this.typeName = typeName.intern(); 159 this.repTypeName = repTypeName.intern(); 160 161 debug_vars.log( 162 "Construct DaikonVariableInfo: %s : %s : %s", this.getClass().getName(), name, typeName); 163 164 children = new ArrayList<DaikonVariableInfo>(); 165 isArray = arr; 166 167 if ((theName != null) && (theName.contains("[..]") || theName.contains("[]")) && !isArray) { 168 debug_array.log("%s is not an array", theName); 169 debug_array.logStackTrace(); 170 } 171 } 172 173 /** Returns the name of this variable. */ 174 public @Nullable String getName(@GuardSatisfied DaikonVariableInfo this) { 175 if (name == null) { 176 return null; 177 } 178 return name.replaceFirst("\\[\\]", "[..]"); 179 } 180 181 /** 182 * Add a child to this node. Should only be called while the tree is being constructed. 183 * 184 * @param info the child object, must be non-null. The child's fields name, typeName, repTypeName, 185 * and compareInfoString should also be non-null. 186 */ 187 protected void addChild(DaikonVariableInfo info) { 188 assert info != null : "info cannot be null in DaikonVariableInfo.addChild()"; 189 assert info.name != null : "Child's name should not be null"; 190 assert info.typeName != null : "Child's type name should not be null"; 191 assert info.repTypeName != null : "Child's representation type name should not be null"; 192 assert info.compareInfoString != null : "Child's comparability information should not be null"; 193 194 debug_vars.log("Adding %s to %s", info, this); 195 children.add(info); 196 } 197 198 /** Returns a string representation of this node. */ 199 @SideEffectFree 200 @Override 201 public String toString(@GuardSatisfied DaikonVariableInfo this) { 202 return getClass().getName() + ":" + getName(); 203 } 204 205 /** 206 * Returns a string representation of this node and its descandants. 207 * 208 * @return a string representation of this node and its descandants 209 */ 210 public String treeString() { 211 return getStringBuilder(new StringBuilder("--")).toString(); 212 } 213 214 /** 215 * Return a StringBuilder that contains the name of this node and all ancestors of this node. 216 * Longer indentations correspond to deeper levels in the tree. 217 * 218 * @param offset the offset to begin each line with 219 * @return StringBuilder that contains all children of this node 220 */ 221 private StringBuilder getStringBuilder(StringBuilder offset) { 222 StringBuilder theBuf = new StringBuilder(); 223 224 theBuf.append( 225 offset + name + " [" + System.identityHashCode(this) + "]" + DaikonWriter.lineSep); 226 227 StringBuilder childOffset = new StringBuilder(offset); 228 childOffset.append("--"); 229 for (DaikonVariableInfo info : children) { 230 theBuf.append(info.getStringBuilder(childOffset)); 231 } 232 233 return theBuf; 234 } 235 236 /** 237 * Returns an iterator over all the node's children. Don't modify the list of children through the 238 * iterator, as an unmodifiable list is used to generate the iterator. 239 * 240 * @return an iterator over all the node's children 241 */ 242 @Override 243 public Iterator<DaikonVariableInfo> iterator() { 244 return Collections.unmodifiableList(children).iterator(); 245 } 246 247 /** Returns the complete tree of variables as a list. */ 248 public List<DaikonVariableInfo> tree_as_list() { 249 List<DaikonVariableInfo> list = new ArrayList<>(); 250 list.add(this); 251 for (DaikonVariableInfo dv : children) { 252 list.addAll(dv.tree_as_list()); 253 } 254 return list; 255 } 256 257 /** 258 * Given a value corresponding to the parent of this, return the value of this. 259 * 260 * <p>For instance, if the variable a has a field b, then calling getMyValFromParentVal(val_of_a) 261 * will return the value of a.b . 262 * 263 * @param parentVal the parent object. Can be null for static fields. 264 * @return the value for this, computed from {@code parentVal} 265 */ 266 public abstract @Nullable Object getMyValFromParentVal(Object parentVal); 267 268 /// 269 /// Printing 270 /// 271 272 /** 273 * Returns a String representation of this object suitable for a {@code .dtrace} file. 274 * 275 * @param val the object whose value to print 276 */ 277 @SuppressWarnings("unchecked") 278 public String getDTraceValueString(Object val) { 279 if (isArray) { 280 return getValueStringOfListWithMod((List<Object>) val); // unchecked cast 281 } else { 282 return getValueStringOfObjectWithMod(val, true); 283 } 284 } 285 286 /** Gets the value of an object and concatenates the associated "modified" integer. */ 287 protected String getValueStringOfObjectWithMod(Object theValue, boolean hashArray) { 288 String retString = getValueStringOfObject(theValue, hashArray) + DaikonWriter.lineSep; 289 290 if (theValue instanceof NonsensicalObject) { 291 retString += "2"; 292 } else { 293 retString += "1"; 294 } 295 296 return retString; 297 } 298 299 /** 300 * Gets the value, but with no endline. If hashArray is true, it prints the "hash code" of the 301 * array and not its separate values. 302 */ 303 private String getValueStringOfObject(Object theValue, boolean hashArray) { 304 if (theValue == null) { 305 return "null"; 306 } 307 308 Class<?> type = theValue.getClass(); 309 310 assert !type.isPrimitive() : "Objects cannot be primitive"; 311 312 if (theValue instanceof Runtime.PrimitiveWrapper) { 313 return getPrimitiveValueString(theValue); 314 } else if (!hashArray && type.isArray()) { 315 // show the full array 316 return getValueStringOfArray(theValue); 317 } else if (theValue instanceof NonsensicalObject) { 318 return "nonsensical"; 319 } else { 320 // basically, show the hashcode of theValue 321 return getObjectHashCode(theValue); 322 } 323 } 324 325 /** Get value string for a primitive (wrapped) object. */ 326 private String getPrimitiveValueString(Object obj) { 327 assert (obj instanceof Runtime.PrimitiveWrapper) 328 : "Objects passed to showPrimitive must implement PrimitiveWrapper" 329 + DaikonWriter.lineSep 330 + "This object is type: " 331 + obj.getClass().getName(); 332 333 // use wrapper classes toString methods to print value 334 return obj.toString(); 335 } 336 337 /** Gets a string representation of the values in an array. */ 338 private String getValueStringOfArray(Object array) { 339 List<Object> theList = DTraceWriter.getListFromArray(array); 340 return getValueStringOfList(theList); 341 } 342 343 /** Gets the Object's unique ID as a string. In other words, a "hash code". */ 344 private String getObjectHashCode(Object theObject) { 345 if (theObject == null) { 346 return "null"; 347 } else if (theObject instanceof NonsensicalObject) { 348 return "nonsensical"; 349 } else { 350 return Integer.toString(System.identityHashCode(theObject)); 351 } 352 } 353 354 /** 355 * Gets the list of values (as a string) from getValueStringOfList and concatenates the "modified" 356 * value. 357 */ 358 private String getValueStringOfListWithMod(List<Object> theValues) { 359 String retString = getValueStringOfList(theValues) + DaikonWriter.lineSep; 360 361 if (theValues instanceof NonsensicalList) { 362 retString += "2"; 363 } else { 364 retString += "1"; 365 } 366 367 return retString; 368 } 369 370 /** 371 * Returns a string representation of the values of a list of values as if it were an array. 372 * 373 * @param theValues the values to print out 374 */ 375 protected String getValueStringOfList(List<Object> theValues) { 376 if (theValues == null) { 377 return "null"; 378 } 379 380 if (theValues instanceof NonsensicalList) { 381 return "nonsensical"; 382 } 383 384 StringJoiner buf = new StringJoiner(" ", "[", "]"); 385 386 for (Iterator<Object> iter = theValues.iterator(); iter.hasNext(); ) { 387 Object elementVal = iter.next(); 388 389 // hash arrays... 390 // don't want to print arrays within arrays 391 buf.add(getValueStringOfObject(elementVal, true)); 392 } 393 394 return buf.toString(); 395 } 396 397 /// 398 /// Building the tre 399 /// 400 401 /** 402 * Add the parameters of the given method to this node. 403 * 404 * @param cinfo the method's class 405 * @param method the method 406 * @param argnames the method's arguments 407 * @param depth the remaining depth to print variables to 408 */ 409 protected void addParameters(ClassInfo cinfo, Member method, List<String> argnames, int depth) { 410 debug_vars.log("enter addParameters%n"); 411 412 Class<?>[] parameterTypes = 413 (method instanceof Constructor<?>) 414 ? ((Constructor<?>) method).getParameterTypes() 415 : ((Method) method).getParameterTypes(); 416 assert argnames.size() == parameterTypes.length; 417 418 int param_offset = 0; 419 for (int i = 0; i < parameterTypes.length; i++) { 420 Class<?> type = parameterTypes[i]; 421 String name = argnames.get(i); 422 if (type.getName().equals("daikon.dcomp.DCompMarker") 423 || type.getName().equals("java.lang.DCompMarker")) { 424 continue; 425 } 426 debug_vars.log("processing parameter '%s'%n", name); 427 debug_vars.indent(); 428 DaikonVariableInfo theChild = 429 addParamDeclVar(cinfo, type, name, /* offset= */ "", depth, i, param_offset); 430 param_offset++; 431 if ((type == Double.TYPE) || (type == Long.TYPE)) { 432 param_offset++; 433 } 434 assert cinfo.clazz != null : "@AssumeAssertion(nullness): need to check justification"; 435 theChild.addChildNodes(cinfo, type, name, /* offset= */ "", depth); 436 debug_vars.exdent(); 437 } 438 debug_vars.log("exit addParameters%n"); 439 } 440 441 /** 442 * Adds class variables (i.e., the fields) for the given type and attach new nodes as children of 443 * this node. 444 * 445 * @param type the class whose fields should all be added to this node 446 * @param offset the prefix for variables -- that is, the expression whose fields are being 447 * printed 448 * @param depth the remaining depth to print variables to 449 */ 450 @RequiresNonNull("#1.clazz") 451 protected void addClassVars( 452 ClassInfo cinfo, boolean dontPrintInstanceVars, Class<?> type, String offset, int depth) { 453 454 debug_vars.log("addClassVars: %s : %s : %s: [%s]%n", this, cinfo, type, offset); 455 456 boolean topLevelCall = offset.equals(""); // true if at first level of recursion 457 458 DaikonVariableInfo thisInfo; // DaikonVariableInfo corresponding to the "this" object 459 if (!dontPrintInstanceVars && topLevelCall) { 460 // "this" variable; must must be at the first level of recursion (not lower) to print it 461 thisInfo = new ThisObjInfo(type); 462 addChild(thisInfo); 463 464 // .class variable 465 if (shouldAddRuntimeClass(type)) { 466 DaikonVariableInfo thisClass = 467 new DaikonClassInfo( 468 "this" + class_suffix, classClassName, stringClassName, "this", false); 469 thisInfo.addChild(thisClass); 470 } 471 } else if (topLevelCall) { 472 // Create a non-printing root for static variables. 473 thisInfo = new StaticObjInfo(type); 474 addChild(thisInfo); 475 } else { 476 thisInfo = this; 477 } 478 479 // Get the fields 480 // System.out.printf("getting fields for %s%n", type); 481 482 // We need to get fields of superclass(es) as well. 483 List<Field> fields = new ArrayList<>(); 484 Class<?> c = type; 485 while (c != null && c != Object.class) { 486 fields.addAll(Arrays.asList(c.getDeclaredFields())); 487 c = c.getSuperclass(); 488 } 489 490 // if (fields.length > 50) 491 // System.out.printf("%d fields in %s%n", fields.length, type); 492 493 debug_vars.log( 494 "%s: [%s] %d dontPrintInstanceVars = %b, inArray = %b%n", 495 type, offset, fields.size(), dontPrintInstanceVars, isArray); 496 497 for (Field classField : fields) { 498 boolean is_static = Modifier.isStatic(classField.getModifiers()); 499 500 debug_vars.log("considering field %s -> %s%n", offset, classField); 501 502 // Skip some fields 503 504 // In the future, perhaps skip some synthetic fields. So far, the 505 // only ones we have seen are 'this$0' (a field in an inner class 506 // that contains a pointer to the instance of the outer class), 507 // 'this$1' for an inner inner class, and so on. These variables 508 // expose the outer class fields of an inner class to Daikon. 509 510 if (!is_static && dontPrintInstanceVars) { 511 debug_vars.log("--field (!static && dontPrintInstanceVars) %s%n", classField); 512 continue; 513 } 514 515 // Skip variables that match the ignore pattern 516 if (Chicory.omit_var != null) { 517 String fullname = offset + "." + classField.getName(); 518 if (is_static) { 519 fullname = classField.getDeclaringClass().getName() + "." + classField.getName(); 520 } 521 Matcher m = Chicory.omit_var.matcher(fullname); 522 if (m.find()) { 523 // System.out.printf("VAR %s matches omit pattern %s%n", fullname, Chicory.omit_var); 524 continue; 525 } 526 } 527 528 // Don't print arrays of the same static field 529 if (is_static && isArray) { 530 debug_vars.log("--field static and inArray%n"); 531 continue; 532 } 533 534 // Skip any statics that have been already included 535 if (is_static) { 536 String full_name = classField.getDeclaringClass().getName() + "." + classField.getName(); 537 if (ppt_statics.contains(full_name) && (depth <= 0)) { 538 debug_vars.log("already included static %s (no children)", full_name); 539 540 continue; 541 } 542 } 543 544 if (!isFieldVisible(cinfo.clazz, classField)) { 545 debug_vars.log("--field not visible%n"); 546 continue; 547 } 548 549 // ... end of code to skip some fields. 550 551 Class<?> fieldType = classField.getType(); 552 553 StringBuilder buf = new StringBuilder(); 554 DaikonVariableInfo newChild = thisInfo.addDeclVar(classField, offset, buf); 555 556 debug_vars.log("--Created DaikonVariable %s%n", newChild); 557 debug_vars.indent(); 558 559 String newOffset = buf.toString(); 560 newChild.addChildNodes(cinfo, fieldType, classField.getName(), newOffset, depth); 561 debug_vars.exdent(); 562 } 563 564 // If appropriate, print out decls information for pure methods 565 // and add to the tree. 566 // Check dontPrintInstanceVars is basically checking if the program point method 567 // (not the pure method) is static. If it is, don't continue because we can't 568 // call instance methods (all pure methods we consider are instance methods) 569 // from static methods. 570 if (ChicoryPremain.shouldDoPurity() && !dontPrintInstanceVars) { 571 ClassInfo typeInfo; 572 573 try { 574 typeInfo = Runtime.getClassInfoFromClass(type); 575 } catch (RuntimeException e) { 576 // Could not find the class... no further purity analysis 577 typeInfo = null; 578 } 579 580 if (typeInfo != null) { 581 // Pure methods with no parameters 582 for (MethodInfo meth : typeInfo.method_infos) { 583 if (meth.isPure() && meth.arg_names.length == 0) { 584 StringBuilder buf = new StringBuilder(); 585 DaikonVariableInfo newChild = 586 thisInfo.addPureMethodDecl( 587 cinfo, meth, new DaikonVariableInfo[] {}, offset, depth, buf); 588 String newOffset = buf.toString(); 589 debug_vars.log("Pure method"); 590 debug_vars.indent(); 591 assert meth.member != null 592 : "@AssumeAssertion(nullness): member of method_infos have" 593 + " .member field"; // dependent type 594 newChild.addChildNodes( 595 cinfo, 596 ((Method) meth.member).getReturnType(), 597 meth.member.getName(), 598 newOffset, 599 depth); 600 debug_vars.exdent(); 601 } 602 } 603 604 // List containing all class variables, excluding pure methods with parameters 605 List<DaikonVariableInfo> siblings = new ArrayList<>(thisInfo.children); 606 607 // Pure methods with one parameter 608 for (MethodInfo meth : typeInfo.method_infos) { 609 if (meth.isPure() && meth.arg_names.length == 1) { 610 for (DaikonVariableInfo sib : siblings) { 611 String sibType = sib.getTypeNameOnly(); 612 Class<?> sibClass; 613 614 // Get class type of the class variable 615 try { 616 sibClass = 617 ReflectionPlume.classForName(Signatures.binaryNameToClassGetName(sibType)); 618 } catch (ClassNotFoundException e) { 619 throw new Error(e); 620 } 621 622 // Add node if the class variable can be used as the pure method's parameter 623 if (ReflectionPlume.isSubtype(sibClass, meth.arg_types[0])) { 624 DaikonVariableInfo[] arg = {sib}; 625 StringBuilder buf = new StringBuilder(); 626 DaikonVariableInfo newChild = 627 thisInfo.addPureMethodDecl(cinfo, meth, arg, offset, depth, buf); 628 String newOffset = buf.toString(); 629 debug_vars.log("Pure method"); 630 debug_vars.indent(); 631 assert meth.member != null 632 : "@AssumeAssertion(nullness): member of" 633 + " method_infos have .member field"; // fix with dependent type 634 newChild.addChildNodes( 635 cinfo, 636 ((Method) meth.member).getReturnType(), 637 meth.member.getName(), 638 newOffset, 639 depth); 640 debug_vars.exdent(); 641 } 642 } 643 } 644 } 645 } 646 } 647 debug_vars.log("exit addClassVars%n"); 648 } 649 650 /** 651 * Adds the decl info for a single parameter as a child of this node. Also adds "derived" 652 * variables such as the runtime .class variable. 653 * 654 * @return the newly created DaikonVariableInfo object, whose parent is this 655 */ 656 protected DaikonVariableInfo addParamDeclVar( 657 ClassInfo cinfo, 658 Class<?> type, 659 String name, 660 String offset, 661 int depth, 662 int argNum, 663 int param_offset) { 664 debug_vars.log("enter addParamDeclVar%n"); 665 // add this variable to the tree as a child of curNode 666 DaikonVariableInfo newChild = new ParameterInfo(offset + name, argNum, type, param_offset); 667 668 addChild(newChild); 669 670 boolean ignore = newChild.check_for_dup_names(); 671 if (!ignore) { 672 newChild.checkForDerivedVariables(type, name, offset); 673 } 674 675 debug_vars.log("exit addParamDeclVar%n"); 676 return newChild; 677 } 678 679 /** Adds the decl info for a pure method. */ 680 // TODO factor out shared code with printDeclVar 681 @SuppressWarnings("deprecation") // in Java 9+, use canAccess instead of isAccessible 682 protected DaikonVariableInfo addPureMethodDecl( 683 ClassInfo curClass, 684 MethodInfo minfo, 685 DaikonVariableInfo[] args, 686 String offset, 687 int depth, 688 StringBuilder buf) { 689 String arr_str = ""; 690 if (isArray) { 691 arr_str = "[]"; 692 } 693 694 @SuppressWarnings("nullness") // method precondition 695 @NonNull Method meth = (Method) minfo.member; 696 697 boolean changedAccess = false; 698 699 // we want to access all fields... 700 if (!meth.isAccessible()) { 701 changedAccess = true; 702 meth.setAccessible(true); 703 } 704 705 Class<?> type = meth.getReturnType(); 706 assert type != null; 707 708 String theName = meth.getName() + "("; 709 if (args.length > 0) { 710 theName += args[0].getName(); 711 } 712 if (args.length > 1) { 713 for (int i = 1; i < args.length - 1; i++) { 714 theName += ", " + args[i].getName(); 715 } 716 } 717 theName += ")"; 718 719 if (offset.length() > 0) { 720 // offset already starts with "this" 721 } else { 722 offset = "this."; 723 } 724 725 String type_name = stdClassName(type); 726 // TODO: Passing incorrect receiver name???? 727 DaikonVariableInfo newPure = 728 new PureMethodInfo( 729 offset + theName, 730 minfo, 731 type_name + arr_str, 732 getRepName(type, isArray) + arr_str, 733 offset.substring(0, offset.length() - 1), 734 isArray, 735 args); 736 737 addChild(newPure); 738 739 newPure.checkForDerivedVariables(type, theName, offset); 740 741 buf.append(offset); 742 743 if (changedAccess) { 744 meth.setAccessible(false); 745 } 746 747 return newPure; 748 } 749 750 /** 751 * Adds the decl info for a single class variable (a field) as a child of this node. Also adds 752 * "derived" variables such as the runtime .class variable. 753 * 754 * @return the newly created DaikonVariableInfo object, whose parent is this 755 */ 756 @SuppressWarnings("deprecation") // in Java 9+, use canAccess instead of isAccessible 757 protected DaikonVariableInfo addDeclVar(Field field, String offset, StringBuilder buf) { 758 debug_vars.log("enter addDeclVar(field):%n"); 759 debug_vars.log(" field: %s, offset: %s%n", field, offset); 760 String arr_str = ""; 761 if (isArray) { 762 arr_str = "[]"; 763 } 764 765 // Temporarily make the field accessible. 766 boolean changedAccess = false; 767 if (!field.isAccessible()) { 768 changedAccess = true; 769 field.setAccessible(true); 770 } 771 772 int modifiers = field.getModifiers(); 773 if (Modifier.isStatic(modifiers)) { 774 offset = field.getDeclaringClass().getName() + "."; 775 } else if (offset.length() == 0) { // instance fld, 1st recursion step 776 offset = "this."; 777 } 778 779 Class<?> type = field.getType(); 780 String type_name = stdClassName(type) + arr_str + appendAuxInfo(field); 781 782 String theName = field.getName(); 783 784 // Convert the internal reflection name for an outer class 785 // 'this' field to the Java language format. 786 if (theName.startsWith("this$")) { 787 offset = ""; 788 theName = type.getName() + ".this"; 789 if (!type_name.contains("#")) { 790 type_name += " # isNonNull=true"; 791 } else { 792 type_name += ", isNonNull=true"; 793 } 794 } 795 796 DaikonVariableInfo newField = 797 new FieldInfo( 798 offset + theName, field, type_name, getRepName(type, false) + arr_str, isArray); 799 boolean ignore = newField.check_for_dup_names(); 800 801 if (DaikonWriter.isStaticConstField(field) && !isArray) { 802 ClassInfo cinfo = Runtime.getClassInfoFromClass(field.getDeclaringClass()); 803 String value = null; 804 boolean isPrimitive = true; 805 806 if (cinfo != null) { 807 value = cinfo.staticMap.get(theName); 808 809 if (DaikonVariableInfo.dkconfig_constant_infer) { 810 if (value == null) { 811 isPrimitive = false; 812 String className = field.getDeclaringClass().getName(); 813 // If the class has already been statically initialized, get its hash 814 if (Runtime.isInitialized(className)) { 815 try { 816 @SuppressWarnings("nullness") // the field is static, so null is OK as argument 817 Object fieldValue = field.get(null); 818 value = Integer.toString(System.identityHashCode(fieldValue)); 819 } catch (Exception e) { 820 throw new BugInDaikon("Problem with field " + field); 821 } 822 } 823 } 824 } 825 } 826 827 // System.out.printf("static final value = %s%n", value); 828 829 // in this case, we don't want to print this variable to 830 // the dtrace file 831 if (value != null) { 832 newField.repTypeName += " = " + value; 833 newField.const_val = value; 834 newField.dtraceShouldPrint = false; 835 if (DaikonVariableInfo.dkconfig_constant_infer && isPrimitive) { 836 newField.dtraceShouldPrintChildren = false; 837 } 838 } 839 // else 840 // { 841 // don't print anything 842 // because this field wasn't declared with an actual "hardcoded" constant 843 // } 844 845 } 846 847 addChild(newField); 848 849 if (!ignore) { 850 newField.checkForDerivedVariables(type, theName, offset); 851 } 852 853 buf.append(offset); 854 855 if (changedAccess) { 856 field.setAccessible(false); 857 } 858 859 debug_vars.log("exit addDeclVar(field)%n"); 860 return newField; 861 } 862 863 /** 864 * Returns the class name of the specified class as a binary name (i.e., as the class would have 865 * been declared in Java source code, except with '$' instead of '.' separating outer and inner 866 * classes). 867 */ 868 public static @BinaryName String stdClassName(Class<?> type) { 869 return Runtime.classGetNameToBinaryName(type.getName()); 870 } 871 872 /** 873 * Given a type, gets the representation type to be used in Daikon. For example, the 874 * representation type of a class object is "hashcode." 875 * 876 * @param type the type of the variable 877 * @param asArray whether the variable is being output as an array (true) or as a pointer (false) 878 * @return the representation type as a string 879 */ 880 public static String getRepName(Class<?> type, boolean asArray) { 881 if (type == null) { 882 return "hashcode"; 883 } else if (type.isPrimitive()) { 884 if (type.equals(Double.TYPE)) { 885 return "double"; 886 } else if (type.equals(Float.TYPE)) { 887 return "double"; 888 } else if (type.equals(Boolean.TYPE)) { 889 return "boolean"; 890 } else { 891 return "int"; 892 } 893 } else if (type.getName().equals("java.lang.String")) { 894 // if we are printing the actual array, the rep type is "java.lang.String" 895 if (true) { 896 return "hashcode"; 897 } 898 if (asArray) { 899 return "java.lang.String"; 900 } 901 // otherwise, it is just a hashcode 902 else { 903 return "hashcode"; 904 } 905 } else { 906 return "hashcode"; 907 } 908 } 909 910 /** 911 * Determines if type needs a corresponding .class runtime class variable. 912 * 913 * <p>The .class variable is printed for interfaces, abstract classes, and Object. For these 914 * types, the run-time class is always (or, for Object, usually) different than the declared type. 915 * An alternate, and possibly more useful, heuristic would be to print the .class variable for any 916 * type that has subtypes. 917 * 918 * @param type the variable's type 919 */ 920 protected static boolean shouldAddRuntimeClass(Class<?> type) { 921 // For some reason, abstracts seems to be set on arrays 922 // and primitives. This is a temporary fix to get things 923 // close. 924 if (type.isPrimitive()) { 925 return false; 926 } 927 if (type.isArray()) { 928 Class<?> eltType = type.getComponentType(); 929 assert eltType != null; // because type is an array 930 return !eltType.isPrimitive(); 931 } else if (type.getName().equals("java.lang.Object")) { 932 // Objects 933 // System.out.println ("type is object " + type); 934 return true; 935 } else if (Modifier.isAbstract(type.getModifiers())) { 936 // System.out.printf("Type [%s] is abstract %Xh %Xh %s%n", type, 937 // type.getModifiers(), Modifier.ABSTRACT, 938 // Modifier.toString (type.getModifiers())); 939 return true; 940 } else if (type.isInterface()) { 941 // System.out.println ("type is interface " + type); 942 return true; 943 } else { 944 return false; 945 } 946 } 947 948 /** 949 * Returns whether or not the specified field is visible from the Class current. All fields within 950 * instrumented classes are considered visible from everywhere (to match dfej behavior). 951 */ 952 public static boolean isFieldVisible(Class<?> current, Field field) { 953 Class<?> fclass = field.getDeclaringClass(); 954 int modifiers = field.getModifiers(); 955 956 // If the field is within the current class, it is always visible 957 if (current.equals(fclass)) { 958 return true; 959 } 960 961 if (!std_visibility) { 962 // If the field is in any instrumented class it is always visible 963 synchronized (SharedData.all_classes) { 964 for (ClassInfo ci : SharedData.all_classes) { 965 // System.out.printf("comparing %s vs %s%n", ci.class_name, 966 // fclass.getName()); 967 if (ci.class_name.equals(fclass.getName())) { 968 return true; 969 } 970 } 971 } 972 } 973 974 // Otherwise we consider the variable not to be visible, even 975 // though it is. This mimics dfej behavior 976 if (!std_visibility) { 977 return false; 978 } 979 980 // Everything in the same class is visible 981 if (current == fclass) { 982 return true; 983 } 984 985 // If the field is in the same package, it's visible if it is 986 // not private or protected. 987 if (current.getPackage() != null && current.getPackage().equals(fclass.getPackage())) { 988 return !(Modifier.isPrivate(modifiers) || Modifier.isProtected(modifiers)); 989 } 990 991 // The field must be in an unrelated class, it must be marked 992 // public to be visible 993 return Modifier.isPublic(modifiers); 994 } 995 996 // Appends as auxiliary information: 997 // the package name of the declaring class 998 private String appendAuxInfo(Field field) { 999 // int modifiers = field.getModifiers(); 1000 1001 Package p = field.getDeclaringClass().getPackage(); 1002 String pkgName = (p == null ? null : p.getName()); 1003 1004 // System.out.printf("Package name for type %s is %s%n", type, pkgName); 1005 1006 StringBuilder ret = new StringBuilder(); 1007 1008 // In Java 9+ package name is empty string for the unnamed package. 1009 if (pkgName != null && !pkgName.isEmpty()) { 1010 ret.append(" # declaringClassPackageName=" + pkgName); 1011 } 1012 1013 return ret.toString(); 1014 } 1015 1016 /** 1017 * Checks for "derived" Chicory variables: .class, .tostring, and java.util.List implementors and 1018 * adds appropriate children to this node. 1019 */ 1020 protected void checkForDerivedVariables(Class<?> type, String theName, String offset) { 1021 checkForListDecl(type, theName, offset); // implements java.util.List? 1022 1023 // Not fully implemented yet, don't call 1024 // checkForImplicitList(cinfo, type, name, offset, depth); 1025 1026 checkForRuntimeClass(type, theName, offset); // .class var 1027 checkForString(type, theName, offset); // .tostring var 1028 } 1029 1030 /** Determines if type implements list and prints associated decls, if necessary. */ 1031 protected void checkForListDecl(Class<?> type, String theName, String offset) { 1032 if (isArray || type.isPrimitive() || type.isArray()) { 1033 return; 1034 } 1035 1036 // System.out.printf("checking %s %sto for list implementation = %b%n", 1037 // type, theName, implementsList (type)); 1038 1039 if (implementsList(type)) { 1040 @SuppressWarnings("unchecked") 1041 DaikonVariableInfo child = 1042 new ListInfo(offset + theName + "[]", (Class<? extends List<?>>) type); 1043 1044 addChild(child); 1045 1046 boolean ignore = child.check_for_dup_names(); 1047 1048 // CLASSNAME var 1049 if (!ignore) { 1050 DaikonVariableInfo childClass = 1051 new DaikonClassInfo( 1052 offset + theName + "[]" + class_suffix, 1053 classClassName + "[]", 1054 stringClassName + "[]", 1055 offset + theName + "[]", 1056 true); 1057 1058 child.addChild(childClass); 1059 } 1060 } 1061 } 1062 1063 /** 1064 * Checks the given type to see if it requires a .class addition to the decls file. If so, it adds 1065 * the correct child to this node. 1066 */ 1067 protected void checkForRuntimeClass(Class<?> type, String theName, String offset) { 1068 if (!shouldAddRuntimeClass(type)) { 1069 return; 1070 } 1071 1072 String postString = ""; // either array braces or an empty string 1073 1074 if (theName.contains("[]") || offset.contains("[]")) { 1075 postString = "[]"; 1076 } 1077 1078 // add daikoninfo type 1079 DaikonVariableInfo classInfo = 1080 new DaikonClassInfo( 1081 offset + theName + class_suffix, 1082 classClassName + postString, 1083 stringClassName + postString, 1084 offset + theName, 1085 (offset + theName).contains("[]")); 1086 1087 addChild(classInfo); 1088 } 1089 1090 /** 1091 * Checks the given type to see if it is a string. If so, it adds the correct child to this node. 1092 */ 1093 private void checkForString(Class<?> type, String theName, String offset) { 1094 if (!type.equals(String.class)) { 1095 return; 1096 } 1097 1098 String postString = ""; // either array braces or an empty string 1099 if (isArray) { 1100 postString = "[]"; 1101 } 1102 1103 // add DaikonVariableInfo type 1104 DaikonVariableInfo stringInfo = 1105 new StringInfo( 1106 offset + theName + ".toString", 1107 stringClassName + postString, 1108 stringClassName + postString, 1109 offset + theName, 1110 isArray); 1111 1112 addChild(stringInfo); 1113 } 1114 1115 /** 1116 * Returns true iff type implements the List interface. 1117 * 1118 * @return true iff type implements the List interface 1119 */ 1120 public static boolean implementsList(Class<?> type) { 1121 if (type.equals(java.util.List.class)) { 1122 return true; 1123 } 1124 1125 // System.out.println(type); 1126 Class<?>[] interfaces = type.getInterfaces(); 1127 for (Class<?> inter : interfaces) { 1128 // System.out.println(" implements: " + inter.getName()); 1129 if (inter.equals(java.util.List.class)) { 1130 return true; 1131 } 1132 } 1133 return false; 1134 } 1135 1136 /** 1137 * Explores the tree one level deeper (see {@link DaikonVariableInfo}). This method adds child 1138 * nodes to this node. 1139 * 1140 * <p>For example: "recurse" on a hashcode array object to print the actual array of values or 1141 * recurse on hashcode variable to print its fields. Also accounts for derived variables (.class, 1142 * .tostring) and "recurses" on arrays (that is, adds a variable to print out the arrays's 1143 * elements as opposed to just the hashcode of the array). 1144 * 1145 * @param theName the name of the variable currently being examined, such as "ballCount" 1146 * @param offset the representation of the variables we have previously examined. For examples, 1147 * offset could be "this." in which case offset + name would be "this.ballCount.". 1148 */ 1149 @RequiresNonNull("#1.clazz") 1150 protected void addChildNodes( 1151 ClassInfo cinfo, Class<?> type, String theName, String offset, int depthRemaining) { 1152 1153 debug_vars.log("enter addChildNodes:%n"); 1154 debug_vars.log(" name: %s, offset: %s%n", theName, offset); 1155 1156 if (type.isPrimitive()) { 1157 return; 1158 } 1159 1160 // Convert the internal reflection name for an outer class 1161 // 'this' field to the Java language format. 1162 if (theName.startsWith("this$")) { 1163 theName = type.getName() + ".this"; 1164 offset = ""; 1165 } 1166 1167 if (type.isArray()) { 1168 // don't go into more than one dimension of a multi-dimensional array 1169 if (isArray) { 1170 return; 1171 } 1172 1173 Class<?> eltType = type.getComponentType(); 1174 assert eltType != null; // because type is an array 1175 if (eltType.isPrimitive()) { 1176 1177 DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType); 1178 1179 newChild.check_for_dup_names(); 1180 1181 addChild(newChild); 1182 } 1183 // multi-dimensional arrays (not currently used) 1184 else if (eltType.isArray()) { 1185 DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType); 1186 1187 newChild.check_for_dup_names(); 1188 1189 addChild(newChild); 1190 1191 debug_vars.log("Array variable"); 1192 debug_vars.indent(); 1193 newChild.addChildNodes(cinfo, eltType, "", offset + theName + "[]", depthRemaining); 1194 debug_vars.exdent(); 1195 } 1196 // array is 1-dimensional and element type is a regular class 1197 else { 1198 DaikonVariableInfo newChild = new ArrayInfo(offset + theName + "[]", eltType); 1199 1200 boolean ignore = newChild.check_for_dup_names(); 1201 1202 addChild(newChild); 1203 1204 // Print out the class of each element in the array. 1205 // The offset will only be equal to "" 1206 // if we are examining a local variable (parameter). 1207 if (!ignore) { 1208 if (!theName.equals("return") && !offset.equals("")) { 1209 newChild.checkForRuntimeClass(type, theName + "[]", offset); 1210 } 1211 1212 newChild.checkForString(eltType, theName + "[]", offset); 1213 } 1214 newChild.addClassVars(cinfo, false, eltType, offset + theName + "[].", depthRemaining - 1); 1215 } 1216 } 1217 // regular old class type 1218 else { 1219 debug_vars.log("**Depth Remaining = %d%n", depthRemaining); 1220 1221 if (depthRemaining <= 0) { 1222 // don't recurse any more! 1223 return; 1224 } 1225 if (!systemClass(type)) { 1226 addClassVars(cinfo, false, type, offset + theName + ".", depthRemaining - 1); 1227 } 1228 } 1229 debug_vars.log("exit addChildNodes%n"); 1230 } 1231 1232 /** 1233 * Returns whether or not the fields of the specified class should be included, based on whether 1234 * the Class type is a system class or not. Right now, any system classes are excluded, but a 1235 * better way of determining this is probably necessary. 1236 */ 1237 public static boolean systemClass(Class<?> type) { 1238 String class_name = type.getName(); 1239 // System.out.printf("type name is %s%n", class_name); 1240 return class_name.startsWith("java.") || class_name.startsWith("javax."); 1241 } 1242 1243 /** 1244 * Returns the declared type name of this variable. May include auxiliary information (represented 1245 * as a suffix starting with "#"). 1246 * 1247 * @see #getTypeNameOnly() 1248 */ 1249 public String getTypeName() { 1250 assert typeName != null : "Type name cannot be null"; 1251 1252 return typeName; 1253 } 1254 1255 /** 1256 * Return the type name without aux information. 1257 * 1258 * @see #getTypeName() 1259 */ 1260 @SuppressWarnings("signature") // substring 1261 public @BinaryName String getTypeNameOnly() { 1262 return typeName.replaceFirst(" # .*", ""); 1263 } 1264 1265 /** Returns the representation type name of this variable. */ 1266 public String getRepTypeName() { 1267 assert typeName != null : "Representation type name cannot be null"; 1268 1269 return repTypeName; 1270 } 1271 1272 /** Return the rep type name without the constant value. */ 1273 public String getRepTypeNameOnly() { 1274 return repTypeName.replaceFirst(" = .*", ""); 1275 } 1276 1277 /** 1278 * Returns the constant value of the variable. If the variable is not static and final, or if the 1279 * constant value is not available in the class file, returns null. 1280 */ 1281 public @Nullable String get_const_val() { 1282 return const_val; 1283 } 1284 1285 /** 1286 * Returns the function args of the variable. If the variable is not a function, or does not have 1287 * any arguments, returns null. 1288 */ 1289 public @Nullable String get_function_args() { 1290 return function_args; 1291 } 1292 1293 /** Returns the comparability information for this variable. */ 1294 public String getCompareString() { 1295 assert typeName != null : "Coparability info cannot be null"; 1296 1297 return compareInfoString; 1298 } 1299 1300 /** Return true iff the DeclWriter should print this node. */ 1301 public boolean declShouldPrint() { 1302 return declShouldPrint; 1303 } 1304 1305 /** Return true iff the DTraceWriter should print this node. */ 1306 public boolean dTraceShouldPrint() { 1307 return dtraceShouldPrint; 1308 } 1309 1310 public boolean dTraceShouldPrintChildren() { 1311 return dtraceShouldPrintChildren; 1312 } 1313 1314 /** Compares based on the name of the variable. */ 1315 @Pure 1316 @Override 1317 public int compareTo(@GuardSatisfied DaikonVariableInfo this, DaikonVariableInfo dv) { 1318 return name.compareTo(dv.name); 1319 } 1320 1321 /** Returns whether or not this variable is an array. */ 1322 public boolean isArray() { 1323 return isArray; 1324 } 1325 1326 /** Returns the direct child that is an array, null if one does not exist. */ 1327 public @Nullable DaikonVariableInfo array_child() { 1328 for (DaikonVariableInfo dv : children) { 1329 if (dv.isArray()) { 1330 return dv; 1331 } 1332 } 1333 return null; 1334 } 1335 1336 /** Returns whether or not this variable has a rep type of hashcode. */ 1337 public boolean isHashcode() { 1338 return getRepTypeName().equals("hashcode"); 1339 } 1340 1341 public boolean isHashcodeArray() { 1342 return getRepTypeName().equals("hashcode[]"); 1343 } 1344 1345 /** Returns whether or not the declared type of this variable is int. */ 1346 public boolean isInt() { 1347 String[] sarr = getTypeName().split(" *"); 1348 return sarr[0].equals("int"); 1349 } 1350 1351 /** Returns the kind of the variable (array, field, function, etc) */ 1352 public abstract VarKind get_var_kind(); 1353 1354 /** 1355 * Returns the name of this variable relative to its enclosing variable. For example the relative 1356 * name for 'this.a' is 'a'. 1357 */ 1358 public @Nullable String get_relative_name() { 1359 return null; 1360 } 1361 1362 /** Empty set of variable flags. */ 1363 private static EnumSet<VarFlags> empty_var_flags = EnumSet.noneOf(VarFlags.class); 1364 1365 /** 1366 * Returns the variable flags for this variable. Subclasses should call super(), then add in any 1367 * flags that they add. 1368 */ 1369 public EnumSet<VarFlags> get_var_flags() { 1370 return empty_var_flags.clone(); 1371 } 1372 1373 /** Returns true iff the variable is static. Overridden by subclasses that can be static. */ 1374 @Pure 1375 public boolean isStatic() { 1376 return false; 1377 } 1378 1379 /** 1380 * If the variable name has been seen before (which can happen with statics and children of 1381 * statics), set the flags so that the variable is not considered for decl or dtrace and return 1382 * true. Otherwise, do nothing and return false. 1383 * 1384 * @return true if this DaikonVariableInfo should be ignored (should not be printed) 1385 */ 1386 private boolean check_for_dup_names() { 1387 1388 if (ppt_statics.contains(name)) { 1389 debug_vars.log("ignoring already included variable %s [%s]", name, getClass()); 1390 // if (!isStatic()) { 1391 // System.out.printf("ignoring already included variable %s [%s]", name, getClass()); 1392 // } 1393 declShouldPrint = false; 1394 dtraceShouldPrint = false; 1395 return true; 1396 } else { // new variable 1397 ppt_statics.add(name); 1398 return false; 1399 } 1400 } 1401}